blob: 1552fd517d300c3568f4dc2b1b0ad3970ece57b7 [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.annotation.Nullable;
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.CoolingDevice;
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.Temperature;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 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();
/** Lock to protect listen list. */
private final Object mLock = new Object();
/**
* Registered observers of the thermal events. Cookie is used to store type as Integer, null
* means no filter.
*/
@GuardedBy("mLock")
private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
new RemoteCallbackList<>();
/** Registered observers of the thermal status. */
@GuardedBy("mLock")
private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
new RemoteCallbackList<>();
/** Current thermal status */
@GuardedBy("mLock")
private int mStatus;
/** If override status takes effect*/
@GuardedBy("mLock")
private boolean mIsStatusOverride;
/** Current thermal map, key as name */
@GuardedBy("mLock")
private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
/** HAL wrapper. */
private ThermalHalWrapper mHalWrapper;
/** Hal ready. */
private final AtomicBoolean mHalReady = new AtomicBoolean();
/** Invalid throttling status */
private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
public ThermalManagerService(Context context) {
this(context, null);
}
@VisibleForTesting
ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
super(context);
mHalWrapper = halWrapper;
// Initialize to invalid to send status onActivityManagerReady
mStatus = INVALID_THROTTLING;
}
@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 onActivityManagerReady() {
synchronized (mLock) {
// Connect to HAL and post to listeners.
boolean halConnected = (mHalWrapper != null);
if (!halConnected) {
mHalWrapper = new ThermalHal20Wrapper();
halConnected = mHalWrapper.connectToHal();
}
if (!halConnected) {
mHalWrapper = new ThermalHal11Wrapper();
halConnected = mHalWrapper.connectToHal();
}
if (!halConnected) {
mHalWrapper = new ThermalHal10Wrapper();
halConnected = mHalWrapper.connectToHal();
}
mHalWrapper.setCallback(this::onTemperatureChangedCallback);
if (!halConnected) {
return;
}
List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
0);
final int count = temperatures.size();
for (int i = 0; i < count; i++) {
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
mHalReady.set(true);
}
}
private void postStatusListener(IThermalStatusListener listener) {
final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
try {
listener.onStatusChange(mStatus);
} 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 notifyStatusListenersLocked() {
if (!Temperature.isValidStatus(mStatus)) {
return;
}
final int length = mThermalStatusListeners.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
final IThermalStatusListener listener =
mThermalStatusListeners.getBroadcastItem(i);
postStatusListener(listener);
}
} finally {
mThermalStatusListeners.finishBroadcast();
}
}
private void onTemperatureMapChangedLocked() {
int newStatus = INVALID_THROTTLING;
final int count = mTemperatureMap.size();
for (int i = 0; i < count; i++) {
Temperature t = mTemperatureMap.valueAt(i);
if (t.getStatus() >= newStatus) {
newStatus = t.getStatus();
}
}
// Do not update if override from shell
if (!mIsStatusOverride) {
setStatusLocked(newStatus);
}
}
private void setStatusLocked(int newStatus) {
if (newStatus != mStatus) {
mStatus = newStatus;
notifyStatusListenersLocked();
}
}
private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
@Nullable Integer type) {
synchronized (mLock) {
final int count = mTemperatureMap.size();
for (int i = 0; i < count; i++) {
postEventListener(mTemperatureMap.valueAt(i), listener,
type);
}
}
}
private void postEventListener(Temperature temperature,
IThermalEventListener listener,
@Nullable 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 notifyEventListenersLocked(Temperature temperature) {
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);
postEventListener(temperature, listener, type);
}
} finally {
mThermalEventListeners.finishBroadcast();
}
}
private void shutdownIfNeeded(Temperature temperature) {
if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
return;
}
final PowerManager powerManager = getContext().getSystemService(PowerManager.class);
switch (temperature.getType()) {
case Temperature.TYPE_CPU:
// Fall through
case Temperature.TYPE_GPU:
// Fall through
case Temperature.TYPE_NPU:
// Fall through
case Temperature.TYPE_SKIN:
powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
break;
case Temperature.TYPE_BATTERY:
powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
break;
}
}
private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
shutdownIfNeeded(temperature);
synchronized (mLock) {
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
if (old != null) {
if (old.getStatus() != temperature.getStatus()) {
notifyEventListenersLocked(temperature);
}
} else {
notifyEventListenersLocked(temperature);
}
if (sendStatus) {
onTemperatureMapChangedLocked();
}
}
}
/* HwBinder callback **/
private void onTemperatureChangedCallback(Temperature temperature) {
final long token = Binder.clearCallingIdentity();
try {
onTemperatureChanged(temperature, true);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@VisibleForTesting
final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
public boolean registerThermalEventListener(IThermalEventListener listener) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
if (!mThermalEventListeners.register(listener, null)) {
return false;
}
// Notify its callback after new client registered.
postEventListenerCurrentTemperatures(listener, null);
return true;
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
int type) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
if (!mThermalEventListeners.register(listener, new Integer(type))) {
return false;
}
// Notify its callback after new client registered.
postEventListenerCurrentTemperatures(listener, new Integer(type));
return true;
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public boolean unregisterThermalEventListener(IThermalEventListener listener) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
return mThermalEventListeners.unregister(listener);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public List<Temperature> getCurrentTemperatures() {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady.get()) {
return new ArrayList<>();
}
return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public List<Temperature> getCurrentTemperaturesWithType(int type) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady.get()) {
return new ArrayList<>();
}
return mHalWrapper.getCurrentTemperatures(true, type);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean registerThermalStatusListener(IThermalStatusListener listener) {
synchronized (mLock) {
// Notify its callback after new client registered.
final long token = Binder.clearCallingIdentity();
try {
if (!mThermalStatusListeners.register(listener)) {
return false;
}
// Notify its callback after new client registered.
postStatusListener(listener);
return true;
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
return mThermalStatusListeners.unregister(listener);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public int getCurrentThermalStatus() {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
return Temperature.isValidStatus(mStatus) ? mStatus
: Temperature.THROTTLING_NONE;
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public List<CoolingDevice> getCurrentCoolingDevices() {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady.get()) {
return new ArrayList<>();
}
return mHalWrapper.getCurrentCoolingDevices(false, 0);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public List<CoolingDevice> getCurrentCoolingDevicesWithType(int type) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady.get()) {
return new ArrayList<>();
}
return mHalWrapper.getCurrentCoolingDevices(true, type);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void dumpItemsLocked(PrintWriter pw, String prefix,
Collection<?> items) {
for (Iterator iterator = items.iterator(); iterator.hasNext();) {
pw.println(prefix + iterator.next().toString());
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
pw.println("IsStatusOverride: " + mIsStatusOverride);
pw.println("ThermalEventListeners:");
mThermalEventListeners.dump(pw, "\t");
pw.println("ThermalStatusListeners:");
mThermalStatusListeners.dump(pw, "\t");
pw.println("Thermal Status: " + mStatus);
pw.println("Cached temperatures:");
dumpItemsLocked(pw, "\t", mTemperatureMap.values());
pw.println("HAL Ready: " + mHalReady.get());
if (mHalReady.get()) {
pw.println("HAL connection:");
mHalWrapper.dump(pw, "\t");
pw.println("Current temperatures from HAL:");
dumpItemsLocked(pw, "\t",
mHalWrapper.getCurrentTemperatures(false, 0));
pw.println("Current cooling devices from HAL:");
dumpItemsLocked(pw, "\t",
mHalWrapper.getCurrentCoolingDevices(false, 0));
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean isCallerShell() {
final int callingUid = Binder.getCallingUid();
return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
if (!isCallerShell()) {
Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
return;
}
(new ThermalShellCommand()).exec(
this, in, out, err, args, callback, resultReceiver);
}
};
class ThermalShellCommand extends ShellCommand {
@Override
public int onCommand(String cmd) {
switch(cmd != null ? cmd : "") {
case "override-status":
return runOverrideStatus();
case "reset":
return runReset();
default:
return handleDefaultCommands(cmd);
}
}
private int runReset() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
mIsStatusOverride = false;
onTemperatureMapChangedLocked();
return 0;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private int runOverrideStatus() {
final long token = Binder.clearCallingIdentity();
try {
final PrintWriter pw = getOutPrintWriter();
int status;
try {
status = Integer.parseInt(getNextArgRequired());
} catch (RuntimeException ex) {
pw.println("Error: " + ex.toString());
return -1;
}
if (!Temperature.isValidStatus(status)) {
pw.println("Invalid status: " + status);
return -1;
}
synchronized (mLock) {
mIsStatusOverride = true;
setStatusLocked(status);
}
return 0;
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println("Thermal service (thermalservice) commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
pw.println(" override-status STATUS");
pw.println(" sets and locks the thermal status of the device to STATUS.");
pw.println(" status code is defined in android.os.Temperature.");
pw.println(" reset");
pw.println(" unlocks the thermal status of the device.");
pw.println();
}
}
abstract static class ThermalHalWrapper {
protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
/** Lock to protect HAL handle. */
protected final Object mHalLock = new Object();
@FunctionalInterface
interface TemperatureChangedCallback {
void onValues(Temperature temperature);
}
/** Temperature callback. */
protected TemperatureChangedCallback mCallback;
/** Cookie for matching the right end point. */
protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
@VisibleForTesting
protected void setCallback(TemperatureChangedCallback cb) {
mCallback = cb;
}
protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
int type);
protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type);
protected abstract boolean connectToHal();
protected abstract void dump(PrintWriter pw, String prefix);
protected void resendCurrentTemperatures() {
synchronized (mHalLock) {
List<Temperature> temperatures = getCurrentTemperatures(false, 0);
final int count = temperatures.size();
for (int i = 0; i < count; i++) {
mCallback.onValues(temperatures.get(i));
}
}
}
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 (mHalLock) {
connectToHal();
// Post to listeners after reconnect to HAL.
resendCurrentTemperatures();
}
}
}
}
}
static class ThermalHal10Wrapper extends ThermalHalWrapper {
/** Proxy object for the Thermal HAL 1.0 service. */
@GuardedBy("mHalLock")
private android.hardware.thermal.V1_0.IThermal mThermalHal10 = null;
@Override
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<Temperature> ret = new ArrayList<>();
if (mThermalHal10 == null) {
return ret;
}
try {
mThermalHal10.getTemperatures(
(ThermalStatus status,
ArrayList<android.hardware.thermal.V1_0.Temperature>
temperatures) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V1_0.Temperature
temperature : temperatures) {
if (shouldFilter && type != temperature.type) {
continue;
}
// Thermal HAL 1.0 doesn't report current throttling status
ret.add(new Temperature(
temperature.currentValue, temperature.type,
temperature.name,
Temperature.THROTTLING_NONE));
}
} 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);
connectToHal();
}
return ret;
}
}
@Override
protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<CoolingDevice> ret = new ArrayList<>();
if (mThermalHal10 == null) {
return ret;
}
try {
mThermalHal10.getCoolingDevices((status, coolingDevices) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V1_0.CoolingDevice
coolingDevice : coolingDevices) {
if (shouldFilter && type != coolingDevice.type) {
continue;
}
ret.add(new CoolingDevice(
(long) coolingDevice.currentValue,
coolingDevice.type,
coolingDevice.name));
}
} else {
Slog.e(TAG,
"Couldn't get cooling device because of HAL error: "
+ status.debugMessage);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
connectToHal();
}
return ret;
}
}
@Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService();
mThermalHal10.linkToDeath(new DeathRecipient(),
THERMAL_HAL_DEATH_COOKIE);
Slog.i(TAG,
"Thermal HAL 1.0 service connected, no thermal call back will be "
+ "called due to legacy API.");
} catch (NoSuchElementException | RemoteException e) {
Slog.e(TAG,
"Thermal HAL 1.0 service not connected.");
mThermalHal10 = null;
}
return (mThermalHal10 != null);
}
}
@Override
protected void dump(PrintWriter pw, String prefix) {
synchronized (mHalLock) {
pw.print(prefix);
pw.println("ThermalHAL 1.0 connected: " + (mThermalHal10 != null ? "yes"
: "no"));
}
}
}
static class ThermalHal11Wrapper extends ThermalHalWrapper {
/** Proxy object for the Thermal HAL 1.1 service. */
@GuardedBy("mHalLock")
private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
/** 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) {
Temperature thermalSvcTemp = new Temperature(
temperature.currentValue, temperature.type, temperature.name,
isThrottling ? ThrottlingSeverity.SEVERE
: ThrottlingSeverity.NONE);
final long token = Binder.clearCallingIdentity();
try {
mCallback.onValues(thermalSvcTemp);
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
@Override
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<Temperature> ret = new ArrayList<>();
if (mThermalHal11 == null) {
return ret;
}
try {
mThermalHal11.getTemperatures(
(ThermalStatus status,
ArrayList<android.hardware.thermal.V1_0.Temperature>
temperatures) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V1_0.Temperature
temperature : temperatures) {
if (shouldFilter && type != temperature.type) {
continue;
}
// Thermal HAL 1.1 doesn't report current throttling status
ret.add(new Temperature(
temperature.currentValue, temperature.type,
temperature.name,
Temperature.THROTTLING_NONE));
}
} 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);
connectToHal();
}
return ret;
}
}
@Override
protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<CoolingDevice> ret = new ArrayList<>();
if (mThermalHal11 == null) {
return ret;
}
try {
mThermalHal11.getCoolingDevices((status, coolingDevices) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V1_0.CoolingDevice
coolingDevice : coolingDevices) {
if (shouldFilter && type != coolingDevice.type) {
continue;
}
ret.add(new CoolingDevice(
(long) coolingDevice.currentValue,
coolingDevice.type,
coolingDevice.name));
}
} else {
Slog.e(TAG,
"Couldn't get cooling device because of HAL error: "
+ status.debugMessage);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
connectToHal();
}
return ret;
}
}
@Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
mThermalHal11.linkToDeath(new DeathRecipient(),
THERMAL_HAL_DEATH_COOKIE);
mThermalHal11.registerThermalCallback(mThermalCallback11);
} catch (NoSuchElementException | RemoteException e) {
Slog.e(TAG,
"Thermal HAL 1.1 service not connected, no thermal call back will be "
+ "called.");
mThermalHal11 = null;
}
return (mThermalHal11 != null);
}
}
@Override
protected void dump(PrintWriter pw, String prefix) {
synchronized (mHalLock) {
pw.print(prefix);
pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
: "no"));
}
}
}
static class ThermalHal20Wrapper extends ThermalHalWrapper {
/** Proxy object for the Thermal HAL 2.0 service. */
@GuardedBy("mHalLock")
private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
/** 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) {
Temperature thermalSvcTemp = new Temperature(
temperature.value, temperature.type, temperature.name,
temperature.throttlingStatus);
final long token = Binder.clearCallingIdentity();
try {
mCallback.onValues(thermalSvcTemp);
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
@Override
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<Temperature> ret = new ArrayList<>();
if (mThermalHal20 == null) {
return ret;
}
try {
mThermalHal20.getCurrentTemperatures(shouldFilter, type,
(status, temperatures) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V2_0.Temperature
temperature : temperatures) {
ret.add(new 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);
connectToHal();
}
return ret;
}
}
@Override
protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type) {
synchronized (mHalLock) {
List<CoolingDevice> ret = new ArrayList<>();
if (mThermalHal20 == null) {
return ret;
}
try {
mThermalHal20.getCurrentCoolingDevices(shouldFilter, type,
(status, coolingDevices) -> {
if (ThermalStatusCode.SUCCESS == status.code) {
for (android.hardware.thermal.V2_0.CoolingDevice
coolingDevice : coolingDevices) {
ret.add(new CoolingDevice(
coolingDevice.value, coolingDevice.type,
coolingDevice.name));
}
} else {
Slog.e(TAG,
"Couldn't get cooling device because of HAL error: "
+ status.debugMessage);
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
connectToHal();
}
return ret;
}
}
@Override
protected boolean connectToHal() {
synchronized (mHalLock) {
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;
}
return (mThermalHal20 != null);
}
}
@Override
protected void dump(PrintWriter pw, String prefix) {
synchronized (mHalLock) {
pw.print(prefix);
pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
: "no"));
}
}
}
}