Merge "Address API review comments for Thermal API"
diff --git a/api/current.txt b/api/current.txt
index 9a99039..395f5b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35118,6 +35118,8 @@
   }
 
   public final class PowerManager {
+    method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
+    method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     method public int getCurrentThermalStatus();
     method public int getLocationPowerSaveMode();
     method public boolean isDeviceIdleMode();
@@ -35129,8 +35131,7 @@
     method public boolean isWakeLockLevelSupported(int);
     method public android.os.PowerManager.WakeLock newWakeLock(int, String);
     method public void reboot(String);
-    method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor);
-    method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback);
+    method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -35155,9 +35156,8 @@
     field public static final int THERMAL_STATUS_SHUTDOWN = 6; // 0x6
   }
 
-  public abstract static class PowerManager.ThermalStatusCallback {
-    ctor public PowerManager.ThermalStatusCallback();
-    method public void onStatusChange(int);
+  public static interface PowerManager.OnThermalStatusChangedListener {
+    method public void onThermalStatusChanged(int);
   }
 
   public final class PowerManager.WakeLock {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c906d33..728b215 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -809,7 +809,8 @@
     final Handler mHandler;
 
     IThermalService mThermalService;
-    private ArrayMap<ThermalStatusCallback, IThermalStatusListener> mCallbackMap = new ArrayMap<>();
+    private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
+            mListenerMap = new ArrayMap<>();
 
     IDeviceIdleController mIDeviceIdleController;
 
@@ -1769,51 +1770,73 @@
     }
 
     /**
-     * Callback passed to
-     * {@link PowerManager#registerThermalStatusCallback} and
-     * {@link PowerManager#unregisterThermalStatusCallback}
-     * to notify caller of thermal status.
+     * Listener passed to
+     * {@link PowerManager#addThermalStatusListener} and
+     * {@link PowerManager#removeThermalStatusListener}
+     * to notify caller of thermal status has changed.
      */
-    public abstract static class ThermalStatusCallback {
+    public interface OnThermalStatusChangedListener {
 
         /**
          * Called when overall thermal throttling status changed.
          * @param status defined in {@link android.os.Temperature}.
          */
-        public void onStatusChange(@ThermalStatus int status) {}
+        void onThermalStatusChanged(@ThermalStatus int status);
     }
 
+
     /**
-     * This function registers a callback for thermal status change.
+     * This function adds a listener for thermal status change, listen call back will be
+     * enqueued tasks on the main thread
      *
-     * @param callback callback to be registered.
-     * @param executor {@link Executor} to handle the callbacks.
+     * @param listener listener to be added,
      */
-    public void registerThermalStatusCallback(
-            @NonNull ThermalStatusCallback callback, @NonNull @CallbackExecutor Executor executor) {
-        Preconditions.checkNotNull(callback, "callback cannnot be null");
-        Preconditions.checkNotNull(executor, "executor cannnot be null");
+    public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
+        Preconditions.checkNotNull(listener, "listener cannot be null");
         synchronized (this) {
             if (mThermalService == null) {
                 mThermalService = IThermalService.Stub.asInterface(
                         ServiceManager.getService(Context.THERMAL_SERVICE));
             }
-            try {
-                if (mCallbackMap.containsKey(callback)) {
-                    throw new IllegalArgumentException("ThermalStatusCallback already registered");
-                }
-                IThermalStatusListener listener = new IThermalStatusListener.Stub() {
-                    @Override
-                    public void onStatusChange(int status) {
+            this.addThermalStatusListener(mContext.getMainExecutor(), listener);
+        }
+    }
+
+    /**
+     * This function adds a listener for thermal status change.
+     *
+     * @param executor {@link Executor} to handle listener callback.
+     * @param listener listener to be added.
+     */
+    public void addThermalStatusListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnThermalStatusChangedListener listener) {
+        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        synchronized (this) {
+            if (mThermalService == null) {
+                mThermalService = IThermalService.Stub.asInterface(
+                        ServiceManager.getService(Context.THERMAL_SERVICE));
+            }
+            Preconditions.checkArgument(!mListenerMap.containsKey(listener),
+                    "Listener already registered: " + listener);
+            IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+                @Override
+                public void onStatusChange(int status) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
                         executor.execute(() -> {
-                            callback.onStatusChange(status);
+                            listener.onThermalStatusChanged(status);
                         });
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
                     }
-                };
-                if (mThermalService.registerThermalStatusListener(listener)) {
-                    mCallbackMap.put(callback, listener);
+                }
+            };
+            try {
+                if (mThermalService.registerThermalStatusListener(internalListener)) {
+                    mListenerMap.put(listener, internalListener);
                 } else {
-                    throw new RuntimeException("ThermalStatusCallback failed to register");
+                    throw new RuntimeException("Listener failed to set");
                 }
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -1822,28 +1845,24 @@
     }
 
     /**
-     * This function unregisters a callback for thermal status change.
+     * This function removes a listener for thermal status change
      *
-     * @param callback to be unregistered.
-     *
-     * see {@link #registerThermalStatusCallback}
+     * @param listener listener to be removed
      */
-    public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) {
-        Preconditions.checkNotNull(callback, "callback cannnot be null");
+    public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
+        Preconditions.checkNotNull(listener, "listener cannot be null");
         synchronized (this) {
             if (mThermalService == null) {
                 mThermalService = IThermalService.Stub.asInterface(
                         ServiceManager.getService(Context.THERMAL_SERVICE));
             }
+            IThermalStatusListener internalListener = mListenerMap.get(listener);
+            Preconditions.checkArgument(internalListener != null, "Listener was not added");
             try {
-                IThermalStatusListener listener = mCallbackMap.get(callback);
-                if (listener == null) {
-                    throw new IllegalArgumentException("ThermalStatusCallback not registered");
-                }
-                if (mThermalService.unregisterThermalStatusListener(listener)) {
-                    mCallbackMap.remove(callback);
+                if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+                    mListenerMap.remove(listener);
                 } else {
-                    throw new RuntimeException("ThermalStatusCallback failed to unregister");
+                    throw new RuntimeException("Listener failed to remove");
                 }
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 1b587dd..576ac73 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -41,7 +41,9 @@
     private UiDevice mUiDevice;
     private Executor mExec = Executors.newSingleThreadExecutor();
     @Mock
-    private PowerManager.ThermalStatusCallback mCallback;
+    private PowerManager.OnThermalStatusChangedListener mListener1;
+    @Mock
+    private PowerManager.OnThermalStatusChangedListener mListener2;
     private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
 
     /**
@@ -169,6 +171,11 @@
         // TODO: Threaded test (needs handler) to make sure timed wakelocks work too
     }
 
+    /**
+     * Confirm that we can get thermal status.
+     *
+     * @throws Exception
+     */
     @Test
     public void testGetThermalStatus() throws Exception {
         int status = 0;
@@ -179,24 +186,57 @@
         assertEquals(status, mPm.getCurrentThermalStatus());
     }
 
+    /**
+     * Confirm that we can add/remove thermal status listener.
+     *
+     * @throws Exception
+     */
     @Test
     public void testThermalStatusCallback() throws Exception {
-        mPm.registerThermalStatusCallback(mCallback, mExec);
-        verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
-                .times(1)).onStatusChange(0);
-        reset(mCallback);
-        int status = 3;
+        // Initial override status is THERMAL_STATUS_NONE
+        int status = PowerManager.THERMAL_STATUS_NONE;
+        // Add listener1
+        mPm.addThermalStatusListener(mExec, mListener1);
+        verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onThermalStatusChanged(status);
+        reset(mListener1);
+        status = PowerManager.THERMAL_STATUS_SEVERE;
         mUiDevice.executeShellCommand("cmd thermalservice override-status "
                 + Integer.toString(status));
-        verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
-                .times(1)).onStatusChange(status);
-        reset(mCallback);
-        mPm.unregisterThermalStatusCallback(mCallback);
-        status = 2;
+        verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onThermalStatusChanged(status);
+        reset(mListener1);
+        // Add listener1 again
+        try {
+            mPm.addThermalStatusListener(mListener1);
+            fail("Expected exception not thrown");
+        } catch (IllegalArgumentException expectedException) {
+        }
+        // Add listener2 on main thread.
+        mPm.addThermalStatusListener(mListener2);
+        status = PowerManager.THERMAL_STATUS_MODERATE;
         mUiDevice.executeShellCommand("cmd thermalservice override-status "
                 + Integer.toString(status));
-        verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
-                .times(0)).onStatusChange(status);
-
+        verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onThermalStatusChanged(status);
+        verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onThermalStatusChanged(status);
+        reset(mListener1);
+        reset(mListener2);
+        // Remove listener1
+        mPm.removeThermalStatusListener(mListener1);
+        // Remove listener1 again
+        try {
+            mPm.removeThermalStatusListener(mListener1);
+            fail("Expected exception not thrown");
+        } catch (IllegalArgumentException expectedException) {
+        }
+        status = PowerManager.THERMAL_STATUS_LIGHT;
+        mUiDevice.executeShellCommand("cmd thermalservice override-status "
+                + Integer.toString(status));
+        verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onThermalStatusChanged(status);
+        verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onThermalStatusChanged(status);
     }
 }