blob: 812fd82a35f9e0f87382b237ea5a334138daa9d5 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.power;
import android.content.Context;
import android.hardware.thermal.V1_0.ThermalStatus;
import android.hardware.thermal.V1_0.ThermalStatusCode;
import android.hardware.thermal.V1_1.IThermalCallback;
import android.hardware.thermal.V2_0.IThermalChangedCallback;
import android.hardware.thermal.V2_0.ThrottlingSeverity;
import android.os.Binder;
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* This is a system service that listens to HAL thermal events and dispatch those to listeners.
* <p>The service will also trigger actions based on severity of the throttling status.</p>
*
* @hide
*/
public class ThermalManagerService extends SystemService {
private static final String TAG = ThermalManagerService.class.getSimpleName();
/** Registered observers of the thermal changed events. Cookie is used to store type */
@GuardedBy("mLock")
private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
new RemoteCallbackList<>();
/** Lock to protect HAL handles and listen list. */
private final Object mLock = new Object();
/** Newly registered callback. */
@GuardedBy("mLock")
private IThermalEventListener mNewListenerCallback = null;
/** Newly registered callback type, null means not filter type. */
@GuardedBy("mLock")
private Integer mNewListenerType = null;
/** Local PMS handle. */
private final PowerManager mPowerManager;
/** Proxy object for the Thermal HAL 2.0 service. */
@GuardedBy("mLock")
private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
/** Proxy object for the Thermal HAL 1.1 service. */
@GuardedBy("mLock")
private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
/** Cookie for matching the right end point. */
private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
/** HWbinder callback for Thermal HAL 2.0. */
private final IThermalChangedCallback.Stub mThermalCallback20 =
new IThermalChangedCallback.Stub() {
@Override
public void notifyThrottling(
android.hardware.thermal.V2_0.Temperature temperature) {
android.os.Temperature thermalSvcTemp = new android.os.Temperature(
temperature.value, temperature.type, temperature.name,
temperature.throttlingStatus);
final long token = Binder.clearCallingIdentity();
try {
notifyThrottlingImpl(thermalSvcTemp);
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
/** HWbinder callback for Thermal HAL 1.1. */
private final IThermalCallback.Stub mThermalCallback11 =
new IThermalCallback.Stub() {
@Override
public void notifyThrottling(boolean isThrottling,
android.hardware.thermal.V1_0.Temperature temperature) {
android.os.Temperature thermalSvcTemp = new android.os.Temperature(
temperature.currentValue, temperature.type, temperature.name,
isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
final long token = Binder.clearCallingIdentity();
try {
notifyThrottlingImpl(thermalSvcTemp);
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
public ThermalManagerService(Context context) {
super(context);
mPowerManager = context.getSystemService(PowerManager.class);
}
private void setNewListener(IThermalEventListener listener, Integer type) {
synchronized (mLock) {
mNewListenerCallback = listener;
mNewListenerType = type;
}
}
private void clearNewListener() {
synchronized (mLock) {
mNewListenerCallback = null;
mNewListenerType = null;
}
}
private final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
public void registerThermalEventListener(IThermalEventListener listener) {
synchronized (mLock) {
mThermalEventListeners.register(listener, null);
// Notify its callback after new client registered.
setNewListener(listener, null);
long token = Binder.clearCallingIdentity();
try {
notifyCurrentTemperaturesLocked();
} finally {
Binder.restoreCallingIdentity(token);
clearNewListener();
}
}
}
@Override
public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
synchronized (mLock) {
mThermalEventListeners.register(listener, new Integer(type));
setNewListener(listener, new Integer(type));
// Notify its callback after new client registered.
long token = Binder.clearCallingIdentity();
try {
notifyCurrentTemperaturesLocked();
} finally {
Binder.restoreCallingIdentity(token);
clearNewListener();
}
}
}
@Override
public void unregisterThermalEventListener(IThermalEventListener listener) {
synchronized (mLock) {
long token = Binder.clearCallingIdentity();
try {
mThermalEventListeners.unregister(listener);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public List<android.os.Temperature> getCurrentTemperatures() {
List<android.os.Temperature> ret;
long token = Binder.clearCallingIdentity();
try {
ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
} finally {
Binder.restoreCallingIdentity(token);
}
return ret;
}
@Override
public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
List<android.os.Temperature> ret;
long token = Binder.clearCallingIdentity();
try {
ret = getCurrentTemperaturesInternal(true, type);
} finally {
Binder.restoreCallingIdentity(token);
}
return ret;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
pw.println("ThermalEventListeners dump:");
synchronized (mLock) {
mThermalEventListeners.dump(pw, "\t");
pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
}
}
};
private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
int type) {
List<android.os.Temperature> ret = new ArrayList<>();
synchronized (mLock) {
if (mThermalHal20 == null) {
return ret;
}
try {
mThermalHal20.getCurrentTemperatures(shouldFilter, type,
(ThermalStatus status,
ArrayList<android.hardware.thermal.V2_0.Temperature>
temperatures) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V2_0.Temperature
temperature : temperatures) {
ret.add(new android.os.Temperature(
temperature.value, temperature.type, temperature.name,
temperature.throttlingStatus));
}
} else {
Slog.e(TAG,
"Couldn't get temperatures because of HAL error: "
+ status.debugMessage);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
connectToHalLocked();
// Post to listeners after reconnect to HAL.
notifyCurrentTemperaturesLocked();
}
}
return ret;
}
private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
Integer type) {
// Skip if listener registered with a different type
if (type != null && type != temperature.getType()) {
return;
}
final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
try {
listener.notifyThrottling(temperature);
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Thermal callback failed to call", e);
}
});
if (!thermalCallbackQueued) {
Slog.e(TAG, "Thermal callback failed to queue");
}
}
private void notifyThrottlingImpl(android.os.Temperature temperature) {
synchronized (mLock) {
// Thermal Shutdown for Skin temperature
if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
&& temperature.getType() == android.os.Temperature.TYPE_SKIN) {
final long token = Binder.clearCallingIdentity();
try {
mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
} finally {
Binder.restoreCallingIdentity(token);
}
}
if (mNewListenerCallback != null) {
// Only notify current newly added callback.
notifyListener(temperature, mNewListenerCallback, mNewListenerType);
} else {
final int length = mThermalEventListeners.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
final IThermalEventListener listener =
mThermalEventListeners.getBroadcastItem(i);
final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
notifyListener(temperature, listener, type);
}
} finally {
mThermalEventListeners.finishBroadcast();
}
}
}
}
@Override
public void onStart() {
publishBinderService(Context.THERMAL_SERVICE, mService);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
onActivityManagerReady();
}
}
private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
if (ThermalStatusCode.SUCCESS != status.code) {
Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
+ status.debugMessage);
return;
}
for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
android.os.Temperature thermal_svc_temp =
new android.os.Temperature(
temperature.value, temperature.type,
temperature.name,
temperature.throttlingStatus);
notifyThrottlingImpl(thermal_svc_temp);
}
}
private void notifyCurrentTemperaturesLocked() {
if (mThermalHal20 == null) {
return;
}
try {
mThermalHal20.getCurrentTemperatures(false, 0,
this::notifyCurrentTemperaturesCallbackLocked);
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
connectToHalLocked();
}
}
private void onActivityManagerReady() {
synchronized (mLock) {
connectToHalLocked();
// Post to listeners after connect to HAL.
notifyCurrentTemperaturesLocked();
}
}
final class DeathRecipient implements HwBinder.DeathRecipient {
@Override
public void serviceDied(long cookie) {
if (cookie == THERMAL_HAL_DEATH_COOKIE) {
Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
synchronized (mLock) {
connectToHalLocked();
// Post to listeners after reconnect to HAL.
notifyCurrentTemperaturesLocked();
}
}
}
}
private void connectToHalLocked() {
try {
mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
0 /* not used */);
} catch (NoSuchElementException | RemoteException e) {
Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
mThermalHal20 = null;
try {
mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
mThermalHal11.registerThermalCallback(mThermalCallback11);
} catch (NoSuchElementException | RemoteException e2) {
Slog.e(TAG,
"Thermal HAL 1.1 service not connected, no thermal call back "
+ "will be called.");
mThermalHal11 = null;
}
}
}
}