Merge "Add thermal status API for app and unit test"
This commit is contained in:
@@ -254,6 +254,7 @@ java_defaults {
|
||||
":statsd_aidl",
|
||||
"core/java/android/os/ISystemUpdateManager.aidl",
|
||||
"core/java/android/os/IThermalEventListener.aidl",
|
||||
"core/java/android/os/IThermalStatusListener.aidl",
|
||||
"core/java/android/os/IThermalService.aidl",
|
||||
"core/java/android/os/IUpdateLock.aidl",
|
||||
"core/java/android/os/IUserManager.aidl",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.os;
|
||||
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.IThermalStatusListener;
|
||||
import android.os.Temperature;
|
||||
|
||||
import java.util.List;
|
||||
@@ -30,31 +31,60 @@ interface IThermalService {
|
||||
* @param listener the IThermalEventListener to be notified.
|
||||
* {@hide}
|
||||
*/
|
||||
void registerThermalEventListener(in IThermalEventListener listener);
|
||||
boolean registerThermalEventListener(in IThermalEventListener listener);
|
||||
|
||||
/**
|
||||
* Register a listener for thermal events on given temperature type.
|
||||
* @param listener the IThermalEventListener to be notified.
|
||||
* @param type the temperature type IThermalEventListener to be notified.
|
||||
* @return true if registered successfully.
|
||||
* {@hide}
|
||||
*/
|
||||
void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
|
||||
boolean registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
|
||||
|
||||
/**
|
||||
* Unregister a previously-registered listener for thermal events.
|
||||
* @param listener the IThermalEventListener to no longer be notified.
|
||||
* @return true if unregistered successfully.
|
||||
* {@hide}
|
||||
*/
|
||||
void unregisterThermalEventListener(in IThermalEventListener listener);
|
||||
boolean unregisterThermalEventListener(in IThermalEventListener listener);
|
||||
|
||||
/**
|
||||
* Get current temperature with its throttling status.
|
||||
* @return list of android.os.Temperature
|
||||
* {@hide}
|
||||
*/
|
||||
List<Temperature> getCurrentTemperatures();
|
||||
|
||||
/**
|
||||
* Get current temperature with its throttling status on given temperature type.
|
||||
* @param type the temperature type to query.
|
||||
* @return list of android.os.Temperature
|
||||
* @return list of {@link android.os.Temperature}.
|
||||
* {@hide}
|
||||
*/
|
||||
List<Temperature> getCurrentTemperaturesWithType(in int type);
|
||||
|
||||
/**
|
||||
* Register a listener for thermal status change.
|
||||
* @param listener the IThermalStatusListener to be notified.
|
||||
* @return true if registered successfully.
|
||||
* {@hide}
|
||||
*/
|
||||
boolean registerThermalStatusListener(in IThermalStatusListener listener);
|
||||
|
||||
/**
|
||||
* Unregister a previously-registered listener for thermal status.
|
||||
* @param listener the IThermalStatusListener to no longer be notified.
|
||||
* @return true if unregistered successfully.
|
||||
* {@hide}
|
||||
*/
|
||||
boolean unregisterThermalStatusListener(in IThermalStatusListener listener);
|
||||
|
||||
/**
|
||||
* Get current thermal status.
|
||||
* @return status defined in {@link android.os.Temperature}.
|
||||
* {@hide}
|
||||
*/
|
||||
int getCurrentStatus();
|
||||
}
|
||||
|
||||
29
core/java/android/os/IThermalStatusListener.aidl
Normal file
29
core/java/android/os/IThermalStatusListener.aidl
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
** Copyright 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 android.os;
|
||||
|
||||
/**
|
||||
* Listener for thermal status.
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IThermalStatusListener {
|
||||
/**
|
||||
* Called when overall thermal throttling status changed.
|
||||
* @param status defined in {@link android.os.Temperature#ThrottlingStatus}.
|
||||
*/
|
||||
void onStatusChange(int status);
|
||||
}
|
||||
@@ -25,9 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Temperature values used by IThermalService.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class Temperature implements Parcelable {
|
||||
@@ -40,7 +38,6 @@ public class Temperature implements Parcelable {
|
||||
/** The level of the sensor is currently in throttling */
|
||||
private int mStatus;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "THROTTLING_" }, value = {
|
||||
THROTTLING_NONE,
|
||||
THROTTLING_LIGHT,
|
||||
@@ -62,7 +59,6 @@ public class Temperature implements Parcelable {
|
||||
public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
|
||||
public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "TYPE_" }, value = {
|
||||
TYPE_UNKNOWN,
|
||||
TYPE_CPU,
|
||||
@@ -95,19 +91,28 @@ public class Temperature implements Parcelable {
|
||||
*
|
||||
* @return true if a temperature type is valid otherwise false.
|
||||
*/
|
||||
public static boolean isValidType(int type) {
|
||||
public static boolean isValidType(@Type int type) {
|
||||
return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a valid throttling status.
|
||||
*
|
||||
* @return true if a status is valid otherwise false.
|
||||
*/
|
||||
public static boolean isValidStatus(@ThrottlingStatus int status) {
|
||||
return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
|
||||
}
|
||||
|
||||
public Temperature() {
|
||||
this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
|
||||
}
|
||||
|
||||
public Temperature(float value, @Type int type, String name, int status) {
|
||||
public Temperature(float value, @Type int type, String name, @ThrottlingStatus int status) {
|
||||
mValue = value;
|
||||
mType = isValidType(type) ? type : TYPE_UNKNOWN;
|
||||
mName = name;
|
||||
mStatus = status;
|
||||
mStatus = isValidStatus(status) ? status : THROTTLING_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
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;
|
||||
@@ -26,12 +27,16 @@ import android.os.Binder;
|
||||
import android.os.HwBinder;
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.IThermalService;
|
||||
import android.os.IThermalStatusListener;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
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;
|
||||
@@ -39,6 +44,7 @@ import com.android.server.SystemService;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
@@ -51,210 +57,154 @@ import java.util.NoSuchElementException;
|
||||
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 */
|
||||
/** 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<>();
|
||||
|
||||
/** Lock to protect HAL handles and listen list. */
|
||||
private final Object mLock = new Object();
|
||||
|
||||
/** Newly registered callback. */
|
||||
/** Registered observers of the thermal status. */
|
||||
@GuardedBy("mLock")
|
||||
private IThermalEventListener mNewListenerCallback = null;
|
||||
private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
|
||||
new RemoteCallbackList<>();
|
||||
|
||||
/** Newly registered callback type, null means not filter type. */
|
||||
/** Current thermal status */
|
||||
@GuardedBy("mLock")
|
||||
private Integer mNewListenerType = null;
|
||||
private int mStatus;
|
||||
|
||||
/** Current thermal map, key as name */
|
||||
@GuardedBy("mLock")
|
||||
private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
|
||||
|
||||
/** Local PMS handle. */
|
||||
private final PowerManager mPowerManager;
|
||||
|
||||
/** Proxy object for the Thermal HAL 2.0 service. */
|
||||
/** HAL wrapper. */
|
||||
private ThermalHalWrapper mHalWrapper;
|
||||
|
||||
/** Hal ready. */
|
||||
@GuardedBy("mLock")
|
||||
private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
|
||||
private boolean mHalReady;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
/** 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);
|
||||
mPowerManager = context.getSystemService(PowerManager.class);
|
||||
mHalWrapper = halWrapper;
|
||||
// Initialize to invalid to send status onActivityManagerReady
|
||||
mStatus = INVALID_THROTTLING;
|
||||
}
|
||||
|
||||
private void setNewListener(IThermalEventListener listener, Integer type) {
|
||||
synchronized (mLock) {
|
||||
mNewListenerCallback = listener;
|
||||
mNewListenerType = type;
|
||||
@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 clearNewListener() {
|
||||
private void onActivityManagerReady() {
|
||||
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();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
mHalWrapper.setCallback(this::onTemperatureChangedCallback);
|
||||
if (!halConnected) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterThermalEventListener(IThermalEventListener listener) {
|
||||
synchronized (mLock) {
|
||||
long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mThermalEventListeners.unregister(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
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 = halConnected /* true */;
|
||||
}
|
||||
|
||||
@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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -271,11 +221,26 @@ public class ThermalManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyThrottlingImpl(android.os.Temperature temperature) {
|
||||
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 onTemperatureChanged(Temperature temperature, boolean sendStatus) {
|
||||
synchronized (mLock) {
|
||||
// Thermal Shutdown for Skin temperature
|
||||
if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
|
||||
&& temperature.getType() == android.os.Temperature.TYPE_SKIN) {
|
||||
if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
|
||||
&& temperature.getType() == Temperature.TYPE_SKIN) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
|
||||
@@ -284,107 +249,420 @@ public class ThermalManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
if (mNewListenerCallback != null) {
|
||||
// Only notify current newly added callback.
|
||||
notifyListener(temperature, mNewListenerCallback, mNewListenerType);
|
||||
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
|
||||
if (old != null) {
|
||||
if (old.getStatus() != temperature.getStatus()) {
|
||||
notifyEventListenersLocked(temperature);
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
notifyEventListenersLocked(temperature);
|
||||
}
|
||||
if (sendStatus) {
|
||||
onTemperatureMapChangedLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
publishBinderService(Context.THERMAL_SERVICE, mService);
|
||||
private void onTemperatureChangedCallback(Temperature temperature) {
|
||||
onTemperatureChanged(temperature, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
|
||||
onActivityManagerReady();
|
||||
private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
|
||||
Collection<Temperature> temperatures) {
|
||||
for (Temperature t : temperatures) {
|
||||
pw.print(prefix);
|
||||
String out = String.format("Name: %s, Type: %d, Status: %d, Value: %f",
|
||||
t.getName(),
|
||||
t.getType(),
|
||||
t.getStatus(),
|
||||
t.getValue()
|
||||
);
|
||||
pw.println(out);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@VisibleForTesting
|
||||
final IThermalService.Stub mService = new IThermalService.Stub() {
|
||||
@Override
|
||||
public void serviceDied(long cookie) {
|
||||
if (cookie == THERMAL_HAL_DEATH_COOKIE) {
|
||||
Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
|
||||
public boolean registerThermalEventListener(IThermalEventListener listener) {
|
||||
synchronized (mLock) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mThermalEventListeners.register(listener, null)) {
|
||||
return false;
|
||||
}
|
||||
if (mHalReady) {
|
||||
// Notify its callback after new client registered.
|
||||
postEventListenerCurrentTemperatures(listener, null);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
|
||||
int type) {
|
||||
synchronized (mLock) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mThermalEventListeners.register(listener, new Integer(type))) {
|
||||
return false;
|
||||
}
|
||||
if (mHalReady) {
|
||||
// Notify its callback after new client registered.
|
||||
postEventListenerCurrentTemperatures(listener, new Integer(type));
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterThermalEventListener(IThermalEventListener listener) {
|
||||
synchronized (mLock) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mThermalEventListeners.unregister(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Temperature> getCurrentTemperatures() {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Temperature> getCurrentTemperaturesWithType(int type) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady) {
|
||||
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;
|
||||
}
|
||||
if (mHalReady) {
|
||||
// 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 getCurrentStatus() {
|
||||
synchronized (mLock) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return Temperature.isValidStatus(mStatus) ? mStatus
|
||||
: Temperature.THROTTLING_NONE;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
connectToHalLocked();
|
||||
// Post to listeners after reconnect to HAL.
|
||||
notifyCurrentTemperaturesLocked();
|
||||
pw.println("ThermalEventListeners:");
|
||||
mThermalEventListeners.dump(pw, "\t");
|
||||
pw.println("ThermalStatusListeners:");
|
||||
mThermalStatusListeners.dump(pw, "\t");
|
||||
pw.println("Thermal Status: " + Integer.toString(mStatus));
|
||||
pw.println("Cached temperatures:");
|
||||
dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
|
||||
pw.println("HAL Ready: " + Boolean.toString(mHalReady));
|
||||
if (mHalReady) {
|
||||
pw.println("HAL connection:");
|
||||
mHalWrapper.dump(pw, "\t");
|
||||
pw.println("Current temperatures from HAL:");
|
||||
dumpTemperaturesLocked(pw, "\t",
|
||||
mHalWrapper.getCurrentTemperatures(false, 0));
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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 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,
|
||||
(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 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 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.IThermalEventListener;
|
||||
import android.os.IThermalStatusListener;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Temperature;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
|
||||
* /power/ThermalManagerServiceTest.java
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ThermalManagerServiceTest {
|
||||
private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
|
||||
private ThermalManagerService mService;
|
||||
private ThermalHalFake mFakeHal;
|
||||
private PowerManager mPowerManager;
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private IPowerManager mIPowerManagerMock;
|
||||
@Mock
|
||||
private IThermalEventListener mEventListener1;
|
||||
@Mock
|
||||
private IThermalEventListener mEventListener2;
|
||||
@Mock
|
||||
private IThermalStatusListener mStatusListener1;
|
||||
@Mock
|
||||
private IThermalStatusListener mStatusListener2;
|
||||
|
||||
/**
|
||||
* Fake Hal class.
|
||||
*/
|
||||
private class ThermalHalFake extends ThermalHalWrapper {
|
||||
private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
|
||||
private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
|
||||
private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
|
||||
INIT_STATUS);
|
||||
private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
|
||||
INIT_STATUS);
|
||||
private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
|
||||
INIT_STATUS);
|
||||
private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
|
||||
INIT_STATUS);
|
||||
|
||||
ThermalHalFake() {
|
||||
mTemperatureList.add(mSkin1);
|
||||
mTemperatureList.add(mSkin2);
|
||||
mTemperatureList.add(mBattery);
|
||||
mTemperatureList.add(mUsbPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
|
||||
return mTemperatureList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean connectToHal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(PrintWriter pw, String prefix) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTemperatureEquals(List<Temperature> expected, List<Temperature> value) {
|
||||
assertEquals(new HashSet<>(expected), new HashSet<>(value));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mFakeHal = new ThermalHalFake();
|
||||
mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
|
||||
when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
|
||||
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
|
||||
resetListenerMock();
|
||||
mService = new ThermalManagerService(mContext, mFakeHal);
|
||||
// Register callbacks before AMS ready and no callback sent
|
||||
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
|
||||
assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
|
||||
Temperature.TYPE_SKIN));
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).notifyThrottling(any(Temperature.class));
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).notifyThrottling(any(Temperature.class));
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
|
||||
ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(4)).notifyThrottling(captor.capture());
|
||||
assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
|
||||
captor = ArgumentCaptor.forClass(Temperature.class);
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(2)).notifyThrottling(captor.capture());
|
||||
assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
|
||||
captor.getAllValues());
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
|
||||
}
|
||||
|
||||
private void resetListenerMock() {
|
||||
reset(mEventListener1);
|
||||
reset(mStatusListener1);
|
||||
reset(mEventListener2);
|
||||
reset(mStatusListener2);
|
||||
doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
|
||||
doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
|
||||
doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
|
||||
doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegister() throws RemoteException {
|
||||
// Unregister all
|
||||
assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
|
||||
assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
|
||||
assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener2));
|
||||
resetListenerMock();
|
||||
// Register callbacks and verify they are called
|
||||
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
|
||||
ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(4)).notifyThrottling(captor.capture());
|
||||
assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
|
||||
// Register new callbacks and verify old ones are not called (remained same) while new
|
||||
// ones are called
|
||||
assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
|
||||
Temperature.TYPE_SKIN));
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(4)).notifyThrottling(any(Temperature.class));
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
|
||||
captor = ArgumentCaptor.forClass(Temperature.class);
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(2)).notifyThrottling(captor.capture());
|
||||
assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
|
||||
captor.getAllValues());
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotify() throws RemoteException {
|
||||
int status = Temperature.THROTTLING_SEVERE;
|
||||
Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
|
||||
mFakeHal.mCallback.onValues(newBattery);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newBattery);
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(status);
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).notifyThrottling(newBattery);
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(status);
|
||||
resetListenerMock();
|
||||
// Should only notify event not status
|
||||
Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
|
||||
mFakeHal.mCallback.onValues(newSkin);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newSkin);
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newSkin);
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
resetListenerMock();
|
||||
// Back to None, should only notify event not status
|
||||
status = Temperature.THROTTLING_NONE;
|
||||
newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
|
||||
mFakeHal.mCallback.onValues(newBattery);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newBattery);
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).notifyThrottling(newBattery);
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(0)).onStatusChange(anyInt());
|
||||
resetListenerMock();
|
||||
// Should also notify status
|
||||
newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
|
||||
mFakeHal.mCallback.onValues(newSkin);
|
||||
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newSkin);
|
||||
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(status);
|
||||
verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).notifyThrottling(newSkin);
|
||||
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).onStatusChange(status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentTemperatures() throws RemoteException {
|
||||
assertTemperatureEquals(mFakeHal.getCurrentTemperatures(false, 0),
|
||||
mService.mService.getCurrentTemperatures());
|
||||
assertTemperatureEquals(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
|
||||
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentStatus() throws RemoteException {
|
||||
int status = Temperature.THROTTLING_WARNING;
|
||||
Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
|
||||
mFakeHal.mCallback.onValues(newSkin);
|
||||
assertEquals(status, mService.mService.getCurrentStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThermalShutdown() throws RemoteException {
|
||||
int status = Temperature.THROTTLING_SHUTDOWN;
|
||||
Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
|
||||
mFakeHal.mCallback.onValues(newSkin);
|
||||
verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
|
||||
.times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoHal() throws RemoteException {
|
||||
mService = new ThermalManagerService(mContext);
|
||||
// Do no call onActivityManagerReady to skip connect HAL
|
||||
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
|
||||
assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
|
||||
assertEquals(0, mService.mService.getCurrentTemperatures().size());
|
||||
assertEquals(0,
|
||||
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
|
||||
assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user