Thermal: Add set-status command to turn thermal status

Bug: 111086696
Bug: 119413961
Test: adb shell cmd thermalservice set-status 3
Test: adb shell cmd thermalservice reset
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
Change-Id: Ide114d1aadad67cbbbae645b56c6c082aceade21
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8160338..9280cb9 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -67,7 +67,7 @@
 
     /**
       * Register a listener for thermal status change.
-      * @param listener the IThermalStatusListener to be notified.
+      * @param listener the {@link android.os.IThermalStatusListener} to be notified.
       * @return true if registered successfully.
       * {@hide}
       */
@@ -75,7 +75,7 @@
 
     /**
       * Unregister a previously-registered listener for thermal status.
-      * @param listener the IThermalStatusListener to no longer be notified.
+      * @param listener the {@link android.os.IThermalStatusListener} to no longer be notified.
       * @return true if unregistered successfully.
       * {@hide}
       */
@@ -86,5 +86,5 @@
       * @return status defined in {@link android.os.Temperature}.
       * {@hide}
       */
-    int getCurrentStatus();
+    int getCurrentThermalStatus();
 }
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index bf85fbd..21aba59 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -28,7 +28,7 @@
  *
  * @hide
  */
-public class Temperature implements Parcelable {
+public final class Temperature implements Parcelable {
     /** Temperature value */
     private float mValue;
     /** A temperature type from ThermalHAL */
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 79e2688..07bebad 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -29,8 +29,12 @@
 import android.os.IThermalService;
 import android.os.IThermalStatusListener;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
 import android.os.Temperature;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -77,6 +81,10 @@
     @GuardedBy("mLock")
     private int mStatus;
 
+    /** If override status takes effect*/
+    @GuardedBy("mLock")
+    private boolean mIsStatusOverride;
+
     /** Current thermal map, key as name */
     @GuardedBy("mLock")
     private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
@@ -184,13 +192,19 @@
                 newStatus = t.getStatus();
             }
         }
+        // Do not update if override from shell
+        if (!mIsStatusOverride) {
+            setStatusLocked(newStatus);
+        }
+    }
+
+    private void setStatusLocked(int newStatus) {
         if (newStatus != mStatus) {
             mStatus = newStatus;
             notifyStatusListenersLocked();
         }
     }
 
-
     private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
             @Nullable Integer type) {
         synchronized (mLock) {
@@ -241,12 +255,7 @@
             // 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);
-                }
+                mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
             }
 
             Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
@@ -263,8 +272,14 @@
         }
     }
 
+    /* HwBinder callback **/
     private void onTemperatureChangedCallback(Temperature temperature) {
-        onTemperatureChanged(temperature, true);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            onTemperatureChanged(temperature, true);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
@@ -393,7 +408,7 @@
         }
 
         @Override
-        public int getCurrentStatus() {
+        public int getCurrentThermalStatus() {
             synchronized (mLock) {
                 final long token = Binder.clearCallingIdentity();
                 try {
@@ -434,8 +449,93 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        private boolean isCallerShell() {
+            final int callingUid = Binder.getCallingUid();
+            return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+        }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out,
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            if (!isCallerShell()) {
+                Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
+                return;
+            }
+            (new ThermalShellCommand()).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
+
     };
 
+    class ThermalShellCommand extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            switch(cmd != null ? cmd : "") {
+                case "override-status":
+                    return runOverrideStatus();
+                case "reset":
+                    return runReset();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        }
+
+        private int runReset() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mIsStatusOverride = false;
+                    onTemperatureMapChangedLocked();
+                    return 0;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private int runOverrideStatus() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final PrintWriter pw = getOutPrintWriter();
+                int status;
+                try {
+                    status = Integer.parseInt(getNextArgRequired());
+                } catch (RuntimeException ex) {
+                    pw.println("Error: " + ex.toString());
+                    return -1;
+                }
+                if (!Temperature.isValidStatus(status)) {
+                    pw.println("Invalid status: " + Integer.toString(status));
+                    return -1;
+                }
+                synchronized (mLock) {
+                    mIsStatusOverride = true;
+                    setStatusLocked(status);
+                }
+                return 0;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onHelp() {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println("Thermal service (thermalservice) commands:");
+            pw.println("  help");
+            pw.println("    Print this help text.");
+            pw.println("");
+            pw.println("  override-status STATUS");
+            pw.println("    sets and locks the thermal status of the device to STATUS.");
+            pw.println("    status code is defined in android.os.Temperature.");
+            pw.println("  reset");
+            pw.println("    unlocks the thermal status of the device.");
+            pw.println();
+        }
+    }
+
     abstract static class ThermalHalWrapper {
         protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
 
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 7cf7df13..5077f92 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -271,7 +271,7 @@
         int status = Temperature.THROTTLING_WARNING;
         Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
         mFakeHal.mCallback.onValues(newSkin);
-        assertEquals(status, mService.mService.getCurrentStatus());
+        assertEquals(status, mService.mService.getCurrentThermalStatus());
     }
 
     @Test
@@ -294,6 +294,6 @@
         assertEquals(0, mService.mService.getCurrentTemperatures().size());
         assertEquals(0,
                 mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
-        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
     }
 }