/*
|
* 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"));
|
}
|
}
|
}
|
|
}
|