Force Garage Mode, then reboot the system

For bench testing, trigger Garage Mode. When it completes, reboot
the system.

Garage Mode should not exit while app and system updates happening.
By rebooting the system, we guarantee that no updates happen after
Garage Mode exits. Thus, if all expected updates complete, we know
that Garage Mode did not terminate prematurely.

(This CL requires manual merge between Q and master.)

Bug: 148477933
Test: On the bench using ADB
Merged-In: I237a9481a848057113a26280576b98ca7917a798
Change-Id: I237a9481a848057113a26280576b98ca7917a798
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index c05d12e..0718d52 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -33,6 +33,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -107,6 +108,8 @@
     private boolean mIsBooting = true;
     @GuardedBy("mLock")
     private boolean mIsResuming;
+    @GuardedBy("mLock")
+    private boolean mRebootAfterGarageMode;
     private final boolean mDisableUserSwitchDuringResume;
     private final CarUserManagerHelper mCarUserManagerHelper;
     private final UserManager mUserManager;    // CarUserManagerHelper is deprecated...
@@ -248,7 +251,8 @@
         writer.print(",mShutdownOnNextSuspend:" + mShutdownOnNextSuspend);
         writer.print(",mShutdownOnFinish:" + mShutdownOnFinish);
         writer.print(",sShutdownPrepareTimeMs:" + sShutdownPrepareTimeMs);
-        writer.println(",mDisableUserSwitchDuringResume:" + mDisableUserSwitchDuringResume);
+        writer.print(",mDisableUserSwitchDuringResume:" + mDisableUserSwitchDuringResume);
+        writer.println(",mRebootAfterGarageMode:" + mRebootAfterGarageMode);
     }
 
     @Override
@@ -539,8 +543,21 @@
             simulatedMode = mInSimulatedDeepSleepMode;
         }
         boolean mustShutDown;
+        boolean forceReboot;
         synchronized (mLock) {
             mustShutDown = mShutdownOnFinish && !simulatedMode;
+            forceReboot = mRebootAfterGarageMode;
+            mRebootAfterGarageMode = false;
+        }
+        if (forceReboot) {
+            PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+            if (powerManager == null) {
+                Log.wtf(CarLog.TAG_POWER, "No PowerManager. Cannot reboot.");
+            } else {
+                Log.i(CarLog.TAG_POWER, "GarageMode has completed. Forcing reboot.");
+                powerManager.reboot("GarageModeReboot");
+                throw new AssertionError("Should not return from PowerManager.reboot()");
+            }
         }
         if (mustShutDown) {
             // shutdown HU
@@ -1117,18 +1134,23 @@
     }
 
     /**
-     * Manually enter simulated suspend (Deep Sleep) mode
-     * Invoked using "adb shell dumpsys activity service com.android.car suspend".
+     * Manually enter simulated suspend (Deep Sleep) mode, trigging Garage mode.
+     * If the parameter is 'true', reboot the system when Garage Mode completes.
+     *
+     * Invoked using "adb shell dumpsys activity service com.android.car suspend" or
+     * "adb shell dumpsys activity service com.android.car garage-mode reboot".
      * This is similar to 'onApPowerStateChange()' except that it needs to create a CpmsState
      * that is not directly derived from a VehicleApPowerStateReq.
      */
-    public void forceSimulatedSuspend() {
+    @VisibleForTesting
+    void forceSuspendAndMaybeReboot(boolean shouldReboot) {
         synchronized (mSimulationWaitObject) {
             mInSimulatedDeepSleepMode = true;
             mWakeFromSimulatedSleep = false;
         }
         PowerHandler handler;
         synchronized (mLock) {
+            mRebootAfterGarageMode = shouldReboot;
             mPendingPowerStates.addFirst(new CpmsState(CpmsState.SIMULATE_SLEEP,
                                                        CarPowerStateListener.SHUTDOWN_PREPARE));
             handler = mHandler;
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 6b11d8b..cace0da 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -568,6 +568,7 @@
         private static final String PARAM_ON_MODE = "on";
         private static final String PARAM_OFF_MODE = "off";
         private static final String PARAM_QUERY_MODE = "query";
+        private static final String PARAM_REBOOT = "reboot";
 
 
         private void dumpHelp(PrintWriter pw) {
@@ -582,8 +583,9 @@
             pw.println("\t  Inject an error event from VHAL for testing.");
             pw.println("\tenable-uxr true|false");
             pw.println("\t  Enable/Disable UX restrictions and App blocking.");
-            pw.println("\tgarage-mode [on|off|query]");
-            pw.println("\t  Force into garage mode or check status.");
+            pw.println("\tgarage-mode [on|off|query|reboot]");
+            pw.println("\t  Force into or out of garage mode, or check status.");
+            pw.println("\t  With 'reboot', enter garage mode, then reboot when it completes.");
             pw.println("\tget-do-activities pkgname");
             pw.println("\t  Get Distraction Optimized activities in given package.");
             pw.println("\tget-carpropertyconfig [propertyId]");
@@ -722,7 +724,7 @@
                     writer.println("Resume: Simulating resuming from Deep Sleep");
                     break;
                 case COMMAND_SUSPEND:
-                    mCarPowerManagementService.forceSimulatedSuspend();
+                    mCarPowerManagementService.forceSuspendAndMaybeReboot(false);
                     writer.println("Resume: Simulating powering down to Deep Sleep");
                     break;
                 case COMMAND_ENABLE_TRUSTED_DEVICE:
@@ -906,9 +908,13 @@
                 case PARAM_QUERY_MODE:
                     mGarageModeService.dump(writer);
                     break;
+                case PARAM_REBOOT:
+                    mCarPowerManagementService.forceSuspendAndMaybeReboot(true);
+                    writer.println("Entering Garage Mode. Will reboot when it completes.");
+                    break;
                 default:
                     writer.println("Unknown value. Valid argument: " + PARAM_ON_MODE + "|"
-                            + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE);
+                            + PARAM_OFF_MODE + "|" + PARAM_QUERY_MODE + "|" + PARAM_REBOOT);
             }
         }