Merge "Allow max 3 minutes before going into suspend-to-RAM" into rvc-qpr-dev
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c47330d..9c26c5e 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -363,4 +363,8 @@
 
     <!-- A configuration flag to enable ending an ongoing call using the physical Call button. -->
     <bool name="config_callButtonEndsOngoingCall">false</bool>
+
+    <!-- Number of milliseconds to wait before the system goes into Suspend-to-RAM.
+         The valid range is 0 to 3 * 60 * 1000. The default duration is 3 min (= 3 * 60 * 1000). -->
+    <integer name="config_maxSuspendWaitDuration">180000</integer>
 </resources>
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index df647bb..7c2cea1 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -93,9 +93,13 @@
     private static final String WIFI_STATE_MODIFIED = "forcibly_disabled";
     private static final String WIFI_STATE_ORIGINAL = "original";
     // If Suspend to RAM fails, we retry with an exponential back-off:
-    // 10 msec, 20 msec, ..., 1280 msec, for a maximum of about 2.5 seconds.
-    private static final int MAX_SUSPEND_TRIES = 9; // Initial + 8 retries
+    // The wait interval will be 10 msec, 20 msec, 40 msec, ...
+    // Once the wait interval goes beyond 1000 msec, it is fixed at 1000 msec.
     private static final long INITIAL_SUSPEND_RETRY_INTERVAL_MS = 10;
+    private static final long MAX_RETRY_INTERVAL_MS = 1000;
+    // Minimum and maximum wait duration before the system goes into Suspend to RAM.
+    private static final long MIN_SUSPEND_WAIT_DURATION_MS = 0;
+    private static final long MAX_SUSPEND_WAIT_DURATION_MS = 3 * 60 * 1000;
 
     private final Object mLock = new Object();
     private final Object mSimulationWaitObject = new Object();
@@ -175,6 +179,12 @@
     // This is a temp work-around to reduce user switching delay after wake-up.
     private final boolean mSwitchGuestUserBeforeSleep;
 
+    // CPMS tries to enter Suspend to RAM within the duration specified at
+    // mMaxSuspendWaitDurationMs. The default max duration is MAX_SUSPEND_WAIT_DURATION, and can be
+    // overridden by setting config_maxSuspendWaitDuration in an overrlay resource.
+    // The valid range is MIN_SUSPEND_WAIT_DRATION to MAX_SUSPEND_WAIT_DURATION.
+    private final long mMaxSuspendWaitDurationMs;
+
     private class PowerManagerCallbackList extends RemoteCallbackList<ICarPowerStateListener> {
         /**
          * Old version of {@link #onCallbackDied(E, Object)} that
@@ -225,6 +235,8 @@
         mWifiManager = context.getSystemService(WifiManager.class);
         mWifiStateFile = new AtomicFile(
                 new File(mSystemInterface.getSystemCarDir(), WIFI_STATE_FILENAME));
+        mMaxSuspendWaitDurationMs = Math.max(MIN_SUSPEND_WAIT_DURATION_MS,
+                Math.min(getMaxSuspendWaitDurationConfig(), MAX_SUSPEND_WAIT_DURATION_MS));
     }
 
     @VisibleForTesting
@@ -287,6 +299,8 @@
             writer.print(",mDisableUserSwitchDuringResume:" + mDisableUserSwitchDuringResume);
             writer.println(",mRebootAfterGarageMode:" + mRebootAfterGarageMode);
             writer.println("mSwitchGuestUserBeforeSleep:" + mSwitchGuestUserBeforeSleep);
+            writer.print("mMaxSuspendWaitDurationMs:" + mMaxSuspendWaitDurationMs);
+            writer.println(", config_maxSuspendWaitDuration:" + getMaxSuspendWaitDurationConfig());
         }
         mInitialUserSetter.dump(writer);
     }
@@ -1199,7 +1213,7 @@
     // Returns true if we successfully suspended.
     private boolean suspendWithRetries() {
         long retryIntervalMs = INITIAL_SUSPEND_RETRY_INTERVAL_MS;
-        int tryCount = 0;
+        long totalWaitDurationMs = 0;
 
         while (true) {
             Slog.i(TAG, "Entering Suspend to RAM");
@@ -1207,20 +1221,20 @@
             if (suspendSucceeded) {
                 return true;
             }
-            tryCount++;
-            if (tryCount >= MAX_SUSPEND_TRIES) {
+            if (totalWaitDurationMs >= mMaxSuspendWaitDurationMs) {
                 break;
             }
             // We failed to suspend. Block the thread briefly and try again.
             synchronized (mLock) {
                 if (mPendingPowerStates.isEmpty()) {
-                    Slog.w(TAG, "Failed to Suspend; will retry later.");
+                    Slog.w(TAG, "Failed to Suspend; will retry after " + retryIntervalMs + "ms.");
                     try {
                         mLock.wait(retryIntervalMs);
                     } catch (InterruptedException ignored) {
                         Thread.currentThread().interrupt();
                     }
-                    retryIntervalMs *= 2;
+                    totalWaitDurationMs += retryIntervalMs;
+                    retryIntervalMs = Math.min(retryIntervalMs * 2, MAX_RETRY_INTERVAL_MS);
                 }
                 // Check for a new power state now, before going around the loop again
                 if (!mPendingPowerStates.isEmpty()) {
@@ -1230,7 +1244,8 @@
             }
         }
         // Too many failures trying to suspend. Shut down.
-        Slog.w(TAG, "Could not Suspend to RAM. Shutting down.");
+        Slog.w(TAG, "Could not Suspend to RAM after " + totalWaitDurationMs
+                + "ms long trial. Shutting down.");
         mSystemInterface.shutdown();
         return false;
     }
@@ -1431,4 +1446,8 @@
         }
         Slog.i(TAG, "Exit Deep Sleep simulation");
     }
+
+    private int getMaxSuspendWaitDurationConfig() {
+        return mContext.getResources().getInteger(R.integer.config_maxSuspendWaitDuration);
+    }
 }