Allow "suspend" after a "shutdown" command is canceled

CarPowerManagementService needs to re-evaluate whether to
power down or suspend for each "shutdown" command. Otherwise
it will remember a previous power down command, even if that
command is canceled.

By re-evaluating, it makes takes the correct action, regardless
of previous commands.

Fixes: 144786716
Test: Added CarPowerManagementServiceTest.testShutdownCancel
Test: Added CarPowerManagementServiceTest.testShutdownOnSuspend
Test: Added CarPowerManagementServiceTest.testSuspend
Merged-In: Id856e5d8546e38a7f3d568b5f871b9e945d95773
Change-Id: Id856e5d8546e38a7f3d568b5f871b9e945d95773
(cherry picked from commit cc1f7fb852230159e0daec9c40bbc8348078f46c)
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index 83c5051..a1f067b 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -95,6 +95,8 @@
     @GuardedBy("mLock")
     private boolean mShutdownOnFinish;
     @GuardedBy("mLock")
+    private boolean mShutdownOnNextSuspend;
+    @GuardedBy("mLock")
     private boolean mIsBooting = true;
     @GuardedBy("mLock")
     private boolean mIsResuming;
@@ -215,6 +217,7 @@
         writer.print(",mProcessingStartTime:" + mProcessingStartTime);
         writer.print(",mLastSleepEntryTime:" + mLastSleepEntryTime);
         writer.print(",mNextWakeupSec:" + mNextWakeupSec);
+        writer.print(",mShutdownOnNextSuspend:" + mShutdownOnNextSuspend);
         writer.print(",mShutdownOnFinish:" + mShutdownOnFinish);
         writer.println(",sShutdownPrepareTimeMs:" + sShutdownPrepareTimeMs);
     }
@@ -308,6 +311,7 @@
                 mHal.sendWaitForVhal();
                 break;
             case CarPowerStateListener.SHUTDOWN_CANCELLED:
+                mShutdownOnNextSuspend = false; // This cancels the "NextSuspend"
                 mHal.sendShutdownCancel();
                 break;
             case CarPowerStateListener.SUSPEND_EXIT:
@@ -357,7 +361,8 @@
         mSystemInterface.setDisplayState(false);
         // Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
         synchronized (mLock) {
-            mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
+            mShutdownOnFinish = mShutdownOnNextSuspend
+                    || !mHal.isDeepSleepAllowed()
                     || !mSystemInterface.isSystemSupportingDeepSleep()
                     || !newState.mCanSleep;
         }
@@ -419,6 +424,7 @@
         } else {
             doHandleDeepSleep(simulatedMode);
         }
+        mShutdownOnNextSuspend = false;
     }
 
     @GuardedBy("mLock")
@@ -503,7 +509,7 @@
                 listener.onStateChanged(newState);
             } catch (RemoteException e) {
                 // It's likely the connection snapped. Let binder death handle the situation.
-                Log.e(CarLog.TAG_POWER, "onStateChanged() call failed: " + e, e);
+                Log.e(CarLog.TAG_POWER, "onStateChanged() call failed", e);
             }
         }
         listenerList.finishBroadcast();
@@ -676,7 +682,7 @@
     public void requestShutdownOnNextSuspend() {
         ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
         synchronized (mLock) {
-            mShutdownOnFinish = true;
+            mShutdownOnNextSuspend = true;
         }
     }
 
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index 945a9d6..056fe9c 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -142,6 +142,86 @@
         mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
     }
 
+    public void testSuspend() throws Exception {
+        final int wakeupTime = 100;
+        initTest(wakeupTime);
+
+        // Start in the ON state
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
+        assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+        // Request suspend
+        mPowerHal.setCurrentPowerState(
+                new PowerState(
+                        VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+                        VehicleApPowerStateShutdownParam.CAN_SLEEP));
+        // Verify suspend
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime);
+    }
+
+    public void testShutdownOnSuspend() throws Exception {
+        final int wakeupTime = 100;
+        initTest(wakeupTime);
+
+        // Start in the ON state
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
+        assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+        // Tell it to shutdown
+        mService.requestShutdownOnNextSuspend();
+        // Request suspend
+        mPowerHal.setCurrentPowerState(
+                new PowerState(
+                        VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+                        VehicleApPowerStateShutdownParam.CAN_SLEEP));
+        // Verify shutdown
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_LONG_MS, wakeupTime);
+        mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
+        // Send the finished signal
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0));
+        mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+        // Cancel the shutdown
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.CANCEL_SHUTDOWN, 0));
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_SHUTDOWN_CANCELLED, WAIT_TIMEOUT_LONG_MS, 0);
+
+        // Request suspend again
+        mPowerHal.setCurrentPowerState(
+                new PowerState(
+                        VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+                        VehicleApPowerStateShutdownParam.CAN_SLEEP));
+        // Verify suspend
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime);
+    }
+
+    public void testShutdownCancel() throws Exception {
+        final int wakeupTime = 100;
+        initTest(wakeupTime);
+
+        // Start in the ON state
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
+        assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+        // Start shutting down
+        mPowerHal.setCurrentPowerState(
+                new PowerState(
+                        VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+                        VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY));
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_LONG_MS, 0);
+        // Cancel the shutdown
+        mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.CANCEL_SHUTDOWN, 0));
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_SHUTDOWN_CANCELLED, WAIT_TIMEOUT_LONG_MS, 0);
+        // Go to suspend
+        mPowerHal.setCurrentPowerState(
+                new PowerState(
+                        VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+                        VehicleApPowerStateShutdownParam.CAN_SLEEP));
+        assertStateReceivedForShutdownOrSleepWithPostpone(
+                PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime);
+    }
+
     public void testShutdownWithProcessing() throws Exception {
         final int wakeupTime = 100;
         initTest(wakeupTime);
diff --git a/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java b/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
index 7359a03..770cc85 100644
--- a/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
+++ b/tests/carservice_unit_test/src/com/android/car/MockedPowerHalService.java
@@ -87,6 +87,12 @@
         doSendState(SET_SHUTDOWN_START, wakeupTimeSec);
     }
 
+    @Override
+    public void sendShutdownCancel() {
+        Log.i(TAG, "sendShutdownCancel");
+        doSendState(SET_SHUTDOWN_CANCELLED, 0);
+    }
+
     public synchronized int[] waitForSend(long timeoutMs) throws Exception {
         if (mSentStates.size() == 0) {
             wait(timeoutMs);