diff options
| author | 2018-11-21 18:34:49 +0000 | |
|---|---|---|
| committer | 2018-11-21 18:34:49 +0000 | |
| commit | 2ca566b525dba8b9da8a917ca999c2df0caa0de0 (patch) | |
| tree | 314ea3efe96c359bb6a60100bcf53adf305ef316 | |
| parent | 8db41f6bef11b029256520ba80821a878d6ea199 (diff) | |
| parent | 37b175448a9771c091a2dc002c3053a208075f2f (diff) | |
Merge "Add thermal status API for app and unit test"
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | core/java/android/os/IThermalService.aidl | 38 | ||||
| -rw-r--r-- | core/java/android/os/IThermalStatusListener.aidl | 29 | ||||
| -rw-r--r-- | core/java/android/os/Temperature.java | 21 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/ThermalManagerService.java | 732 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java | 299 |
6 files changed, 881 insertions, 239 deletions
diff --git a/Android.bp b/Android.bp index 1012bb811ace..c3cd4b1d1977 100644 --- a/Android.bp +++ b/Android.bp @@ -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", diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl index 287a5edde03f..81603384636e 100644 --- a/core/java/android/os/IThermalService.aidl +++ b/core/java/android/os/IThermalService.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(); } diff --git a/core/java/android/os/IThermalStatusListener.aidl b/core/java/android/os/IThermalStatusListener.aidl new file mode 100644 index 000000000000..a6da7d02eff8 --- /dev/null +++ b/core/java/android/os/IThermalStatusListener.aidl @@ -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); +} diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java index 37ed52c1fd2c..bf85fbd36e6a 100644 --- a/core/java/android/os/Temperature.java +++ b/core/java/android/os/Temperature.java @@ -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; } /** diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 812fd82a35f9..79e2688c6429 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -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,130 +57,277 @@ 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(); + /** Registered observers of the thermal status. */ + @GuardedBy("mLock") + private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners = + new RemoteCallbackList<>(); - /** Newly registered callback. */ + /** Current thermal status */ @GuardedBy("mLock") - private IThermalEventListener mNewListenerCallback = null; + private int mStatus; - /** Newly registered callback type, null means not filter type. */ + /** Current thermal map, key as name */ @GuardedBy("mLock") - private Integer mNewListenerType = null; + private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>(); /** Local PMS handle. */ private final PowerManager mPowerManager; - /** Proxy object for the Thermal HAL 2.0 service. */ - @GuardedBy("mLock") - private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null; + /** HAL wrapper. */ + private ThermalHalWrapper mHalWrapper; - /** Proxy object for the Thermal HAL 1.1 service. */ + /** Hal ready. */ @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); - } - } - }; + private boolean mHalReady; + + /** 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) { + @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) { - mNewListenerCallback = listener; - mNewListenerType = type; + // 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(); + } + } + 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 = halConnected /* true */; } } - private void clearNewListener() { + 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) { - mNewListenerCallback = null; - mNewListenerType = null; + final int count = mTemperatureMap.size(); + for (int i = 0; i < count; i++) { + postEventListener(mTemperatureMap.valueAt(i), listener, + type); + } } } - private final IThermalService.Stub mService = new IThermalService.Stub() { + 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 onTemperatureChanged(Temperature temperature, boolean sendStatus) { + synchronized (mLock) { + // Thermal Shutdown for Skin temperature + 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); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + Temperature old = mTemperatureMap.put(temperature.getName(), temperature); + if (old != null) { + if (old.getStatus() != temperature.getStatus()) { + notifyEventListenersLocked(temperature); + } + } else { + notifyEventListenersLocked(temperature); + } + if (sendStatus) { + onTemperatureMapChangedLocked(); + } + } + } + + private void onTemperatureChangedCallback(Temperature temperature) { + onTemperatureChanged(temperature, true); + } + + 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); + } + } + + @VisibleForTesting + final IThermalService.Stub mService = new IThermalService.Stub() { @Override - public void registerThermalEventListener(IThermalEventListener listener) { + public boolean registerThermalEventListener(IThermalEventListener listener) { synchronized (mLock) { - mThermalEventListeners.register(listener, null); - // Notify its callback after new client registered. - setNewListener(listener, null); - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { - notifyCurrentTemperaturesLocked(); + 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); - clearNewListener(); } } } @Override - public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) { + public boolean 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(); + final long token = Binder.clearCallingIdentity(); try { - notifyCurrentTemperaturesLocked(); + 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); - clearNewListener(); } } } @Override - public void unregisterThermalEventListener(IThermalEventListener listener) { + public boolean unregisterThermalEventListener(IThermalEventListener listener) { synchronized (mLock) { - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { - mThermalEventListeners.unregister(listener); + return mThermalEventListeners.unregister(listener); } finally { Binder.restoreCallingIdentity(token); } @@ -182,209 +335,334 @@ public class ThermalManagerService extends SystemService { } @Override - public List<android.os.Temperature> getCurrentTemperatures() { - List<android.os.Temperature> ret; - long token = Binder.clearCallingIdentity(); + public List<Temperature> getCurrentTemperatures() { + final long token = Binder.clearCallingIdentity(); try { - ret = getCurrentTemperaturesInternal(false, 0 /* not used */); + if (!mHalReady) { + return new ArrayList<>(); + } + return mHalWrapper.getCurrentTemperatures(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(); + public List<Temperature> getCurrentTemperaturesWithType(int type) { + final long token = Binder.clearCallingIdentity(); try { - ret = getCurrentTemperaturesInternal(true, type); + if (!mHalReady) { + return new ArrayList<>(); + } + return mHalWrapper.getCurrentTemperatures(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:"); + public boolean registerThermalStatusListener(IThermalStatusListener listener) { 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(); + // 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); + } } } - return ret; - } - private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener, - Integer type) { - // Skip if listener registered with a different type - if (type != null && type != temperature.getType()) { - return; - } - final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> { - try { - listener.notifyThrottling(temperature); - } catch (RemoteException | RuntimeException e) { - Slog.e(TAG, "Thermal callback failed to call", e); + @Override + public boolean unregisterThermalStatusListener(IThermalStatusListener listener) { + synchronized (mLock) { + final long token = Binder.clearCallingIdentity(); + try { + return mThermalStatusListeners.unregister(listener); + } finally { + Binder.restoreCallingIdentity(token); + } } - }); - if (!thermalCallbackQueued) { - Slog.e(TAG, "Thermal callback failed to queue"); } - } - private void notifyThrottlingImpl(android.os.Temperature temperature) { - synchronized (mLock) { - // Thermal Shutdown for Skin temperature - if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN - && temperature.getType() == android.os.Temperature.TYPE_SKIN) { + @Override + public int getCurrentStatus() { + synchronized (mLock) { final long token = Binder.clearCallingIdentity(); try { - mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false); + return Temperature.isValidStatus(mStatus) ? mStatus + : Temperature.THROTTLING_NONE; } finally { Binder.restoreCallingIdentity(token); } } + } - if (mNewListenerCallback != null) { - // Only notify current newly added callback. - notifyListener(temperature, mNewListenerCallback, mNewListenerType); - } else { - final int length = mThermalEventListeners.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - final IThermalEventListener listener = - mThermalEventListeners.getBroadcastItem(i); - final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i); - notifyListener(temperature, listener, type); + @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("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 { - mThermalEventListeners.finishBroadcast(); } + + } finally { + Binder.restoreCallingIdentity(token); } } - } + }; - @Override - public void onStart() { - publishBinderService(Context.THERMAL_SERVICE, mService); - } + abstract static class ThermalHalWrapper { + protected static final String TAG = ThermalHalWrapper.class.getSimpleName(); - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { - onActivityManagerReady(); + /** Lock to protect HAL handle. */ + protected final Object mHalLock = new Object(); + + @FunctionalInterface + interface TemperatureChangedCallback { + void onValues(Temperature temperature); } - } - 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; + /** 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; } - 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); + + 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 notifyCurrentTemperaturesLocked() { - if (mThermalHal20 == null) { - return; + 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; + } } - try { - mThermalHal20.getCurrentTemperatures(false, 0, - this::notifyCurrentTemperaturesCallbackLocked); - } catch (RemoteException e) { - Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e); - connectToHalLocked(); + + @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); + } } - } - private void onActivityManagerReady() { - synchronized (mLock) { - connectToHalLocked(); - // Post to listeners after connect to HAL. - notifyCurrentTemperaturesLocked(); + @Override + protected void dump(PrintWriter pw, String prefix) { + synchronized (mHalLock) { + pw.print(prefix); + pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" + : "no")); + } } } - final class DeathRecipient implements HwBinder.DeathRecipient { + 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 - public void serviceDied(long cookie) { - if (cookie == THERMAL_HAL_DEATH_COOKIE) { - Slog.e(TAG, "Thermal HAL service died cookie: " + cookie); - synchronized (mLock) { - connectToHalLocked(); - // Post to listeners after reconnect to HAL. - notifyCurrentTemperaturesLocked(); + 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; } } - } - 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; + @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")); } } } diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java new file mode 100644 index 000000000000..7cf7df1343af --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -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()); + } +} |