Merge changes If500c1cc,I248385ea,I05e27dba into qt-qpr1-dev am: 316448ceab

Change-Id: Iabd68f26b93c45cc547d5e5cb888289d1c65d032
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 81e0620..c756510 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -241,4 +241,8 @@
     <integer name="config_mediaSourceChangedAutoplay">2</integer>
     <!-- Configuration to enable media center to autoplay on boot -->
     <integer name="config_mediaBootAutoplay">2</integer>
+
+    <!-- Disable switching the user while the system is resuming from Suspend to RAM.
+         This default says to prevent changing the user during Resume. -->
+    <bool name="config_disableUserSwitchDuringResume" translatable="false">true</bool>
 </resources>
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index c1341da..83c5051 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -54,6 +54,10 @@
  */
 public class CarPowerManagementService extends ICarPower.Stub implements
         CarServiceBase, PowerHalService.PowerEventListener {
+
+    private final Object mLock = new Object();
+    private final Object mSimulationWaitObject = new Object();
+
     private final Context mContext;
     private final PowerHalService mHal;
     private final SystemInterface mSystemInterface;
@@ -62,32 +66,38 @@
     // The listeners that must indicate asynchronous completion by calling finished().
     private final PowerManagerCallbackList mPowerManagerListenersWithCompletion =
                           new PowerManagerCallbackList();
-    private final Set<IBinder> mListenersWeAreWaitingFor = new HashSet<>();
-    private final Object mSimulationSleepObject = new Object();
 
-    @GuardedBy("this")
+    @GuardedBy("mSimulationWaitObject")
+    private boolean mWakeFromSimulatedSleep;
+    @GuardedBy("mSimulationWaitObject")
+    private boolean mInSimulatedDeepSleepMode;
+
+    @GuardedBy("mLock")
+    private final Set<IBinder> mListenersWeAreWaitingFor = new HashSet<>();
+    @GuardedBy("mLock")
     private CpmsState mCurrentState;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private Timer mTimer;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private long mProcessingStartTime;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private long mLastSleepEntryTime;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private final LinkedList<CpmsState> mPendingPowerStates = new LinkedList<>();
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private HandlerThread mHandlerThread;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private PowerHandler mHandler;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private boolean mTimerActive;
-    @GuardedBy("mSimulationSleepObject")
-    private boolean mInSimulatedDeepSleepMode = false;
-    @GuardedBy("mSimulationSleepObject")
-    private boolean mWakeFromSimulatedSleep = false;
-    private int mNextWakeupSec = 0;
-    private boolean mShutdownOnFinish = false;
+    @GuardedBy("mLock")
+    private int mNextWakeupSec;
+    @GuardedBy("mLock")
+    private boolean mShutdownOnFinish;
+    @GuardedBy("mLock")
     private boolean mIsBooting = true;
+    @GuardedBy("mLock")
+    private boolean mIsResuming;
 
     private final CarUserManagerHelper mCarUserManagerHelper;
 
@@ -160,7 +170,7 @@
 
     @Override
     public void init() {
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
             mHandlerThread.start();
             mHandler = new PowerHandler(mHandlerThread.getLooper());
@@ -180,11 +190,12 @@
     @Override
     public void release() {
         HandlerThread handlerThread;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             releaseTimerLocked();
             mCurrentState = null;
             mHandler.cancelAll();
             handlerThread = mHandlerThread;
+            mListenersWeAreWaitingFor.clear();
         }
         handlerThread.quitSafely();
         try {
@@ -194,7 +205,6 @@
         }
         mSystemInterface.stopDisplayStateMonitoring();
         mPowerManagerListeners.kill();
-        mListenersWeAreWaitingFor.clear();
         mSystemInterface.releaseAllWakeLocks();
     }
 
@@ -212,7 +222,7 @@
     @Override
     public void onApPowerStateChange(PowerState state) {
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             mPendingPowerStates.addFirst(new CpmsState(state));
             handler = mHandler;
         }
@@ -220,8 +230,11 @@
     }
 
     @VisibleForTesting
-    protected void clearIsBooting() {
-        mIsBooting = false;
+    protected void clearIsBootingOrResuming() {
+        synchronized (mLock) {
+            mIsBooting = false;
+            mIsResuming = false;
+        }
     }
 
     /**
@@ -230,7 +243,7 @@
     private void onApPowerStateChange(int apState, int carPowerStateListenerState) {
         CpmsState newState = new CpmsState(apState, carPowerStateListenerState);
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             mPendingPowerStates.addFirst(newState);
             handler = mHandler;
         }
@@ -240,7 +253,7 @@
     private void doHandlePowerStateChange() {
         CpmsState state;
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             state = mPendingPowerStates.peekFirst();
             mPendingPowerStates.clear();
             if (state == null) {
@@ -304,10 +317,30 @@
     }
 
     private void handleOn() {
-        // Do not switch user if it is booting as there can be a race with CarServiceHelperService
-        if (mIsBooting) {
-            mIsBooting = false;
-        } else {
+        // Some OEMs have their own user-switching logic, which may not be coordinated with this
+        // code. To avoid contention, we don't switch users when we coming alive. The OEM's code
+        // should do the switch.
+        boolean allowUserSwitch = true;
+        synchronized (mLock) {
+            if (mIsBooting) {
+                // The system is booting, so don't switch users
+                allowUserSwitch = false;
+                mIsBooting = false;
+                mIsResuming = false;
+                Log.i(CarLog.TAG_POWER, "User switch disallowed while booting");
+            } else if (mIsResuming) {
+                // The system is resuming after a suspension. Optionally disable user switching.
+                allowUserSwitch = !mContext.getResources()
+                        .getBoolean(R.bool.config_disableUserSwitchDuringResume);
+                mIsBooting = false;
+                mIsResuming = false;
+                if (!allowUserSwitch) {
+                    Log.i(CarLog.TAG_POWER, "User switch disallowed while resuming");
+                }
+            }
+        }
+
+        if (allowUserSwitch) {
             int targetUserId = mCarUserManagerHelper.getInitialUser();
             if (targetUserId != UserHandle.USER_SYSTEM
                     && targetUserId != mCarUserManagerHelper.getCurrentForegroundUserId()) {
@@ -323,9 +356,11 @@
     private void handleShutdownPrepare(CpmsState newState) {
         mSystemInterface.setDisplayState(false);
         // Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
-        mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
-                || !mSystemInterface.isSystemSupportingDeepSleep()
-                || !newState.mCanSleep;
+        synchronized (mLock) {
+            mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
+                    || !mSystemInterface.isSystemSupportingDeepSleep()
+                    || !newState.mCanSleep;
+        }
         if (newState.mCanPostpone) {
             Log.i(CarLog.TAG_POWER, "starting shutdown prepare");
             sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE);
@@ -333,7 +368,7 @@
             doHandlePreprocessing();
         } else {
             Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
-            synchronized (CarPowerManagementService.this) {
+            synchronized (mLock) {
                 releaseTimerLocked();
             }
             // Notify hal that we are shutting down and since it is immediate, don't schedule next
@@ -355,21 +390,27 @@
 
     private void handleWaitForFinish(CpmsState state) {
         sendPowerManagerEvent(state.mCarPowerStateListenerState);
+        int wakeupSec;
+        synchronized (mLock) {
+            wakeupSec = mNextWakeupSec;
+        }
         switch (state.mCarPowerStateListenerState) {
             case CarPowerStateListener.SUSPEND_ENTER:
-                mHal.sendSleepEntry(mNextWakeupSec);
+                mHal.sendSleepEntry(wakeupSec);
                 break;
             case CarPowerStateListener.SHUTDOWN_ENTER:
-                mHal.sendShutdownStart(mNextWakeupSec);
+                mHal.sendShutdownStart(wakeupSec);
                 break;
         }
     }
 
     private void handleFinish() {
-        boolean mustShutDown;
         boolean simulatedMode;
-        synchronized (mSimulationSleepObject) {
+        synchronized (mSimulationWaitObject) {
             simulatedMode = mInSimulatedDeepSleepMode;
+        }
+        boolean mustShutDown;
+        synchronized (mLock) {
             mustShutDown = mShutdownOnFinish && !simulatedMode;
         }
         if (mustShutDown) {
@@ -380,15 +421,13 @@
         }
     }
 
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private void releaseTimerLocked() {
-        synchronized (CarPowerManagementService.this) {
-            if (mTimer != null) {
-                mTimer.cancel();
-            }
-            mTimer = null;
-            mTimerActive = false;
+        if (mTimer != null) {
+            mTimer.cancel();
         }
+        mTimer = null;
+        mTimerActive = false;
     }
 
     private void doHandlePreprocessing() {
@@ -407,7 +446,7 @@
         }
         Log.i(CarLog.TAG_POWER, "processing before shutdown expected for: "
                 + sShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             mProcessingStartTime = SystemClock.elapsedRealtime();
             releaseTimerLocked();
             mTimer = new Timer();
@@ -433,7 +472,7 @@
         // see the list go empty and we will think that we are done.
         boolean haveSomeCompleters = false;
         PowerManagerCallbackList completingListeners = new PowerManagerCallbackList();
-        synchronized (mListenersWeAreWaitingFor) {
+        synchronized (mLock) {
             mListenersWeAreWaitingFor.clear();
             int idx = mPowerManagerListenersWithCompletion.beginBroadcast();
             while (idx-- > 0) {
@@ -475,28 +514,33 @@
         // enterDeepSleep should force sleep entry even if wake lock is kept.
         mSystemInterface.switchToPartialWakeLock();
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             handler = mHandler;
         }
         handler.cancelProcessingComplete();
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             mLastSleepEntryTime = SystemClock.elapsedRealtime();
         }
         int nextListenerState;
         if (simulatedMode) {
-            simulateSleepByLooping();
+            simulateSleepByWaiting();
             nextListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
         } else {
             boolean sleepSucceeded = mSystemInterface.enterDeepSleep();
             if (!sleepSucceeded) {
-                // VHAL should transition CPMS to shutdown.
+                // Suspend failed! VHAL should transition CPMS to shutdown.
                 Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Now attempting to shut down.");
                 mSystemInterface.shutdown();
+                return;
             }
             nextListenerState = CarPowerStateListener.SUSPEND_EXIT;
         }
-        // On wake, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
-        mNextWakeupSec = 0;
+        // On resume, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
+        synchronized (mLock) {
+            mIsResuming = true;
+            mNextWakeupSec = 0;
+        }
+        Log.i(CarLog.TAG_POWER, "Resuming after suspending");
         mSystemInterface.refreshDisplayBrightness();
         onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState);
     }
@@ -537,26 +581,24 @@
     }
 
     private void doHandleProcessingComplete() {
-        synchronized (CarPowerManagementService.this) {
+        int listenerState;
+        synchronized (mLock) {
             releaseTimerLocked();
             if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {
                 // entered sleep after processing start. So this could be duplicate request.
                 Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
                 return;
             }
+            listenerState = mShutdownOnFinish
+                    ? CarPowerStateListener.SHUTDOWN_ENTER : CarPowerStateListener.SUSPEND_ENTER;
         }
-
-        if (mShutdownOnFinish) {
-            onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SHUTDOWN_ENTER);
-        } else {
-            onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SUSPEND_ENTER);
-        }
+        onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, listenerState);
     }
 
     @Override
     public void onDisplayBrightnessChange(int brightness) {
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             handler = mHandler;
         }
         handler.handleDisplayBrightnessChange(brightness);
@@ -572,7 +614,7 @@
 
     public void handleMainDisplayChanged(boolean on) {
         PowerHandler handler;
-        synchronized (CarPowerManagementService.this) {
+        synchronized (mLock) {
             handler = mHandler;
         }
         handler.handleMainDisplayStateChange(on);
@@ -586,8 +628,13 @@
         mHal.sendDisplayBrightness(brightness);
     }
 
-    public synchronized Handler getHandler() {
-        return mHandler;
+    /**
+     * Get the PowerHandler that we use to change power states
+     */
+    public Handler getHandler() {
+        synchronized (mLock) {
+            return mHandler;
+        }
     }
 
     // Binder interface for general use.
@@ -628,7 +675,9 @@
     @Override
     public void requestShutdownOnNextSuspend() {
         ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
-        mShutdownOnFinish = true;
+        synchronized (mLock) {
+            mShutdownOnFinish = true;
+        }
     }
 
     @Override
@@ -639,27 +688,31 @@
     }
 
     @Override
-    public synchronized void scheduleNextWakeupTime(int seconds) {
+    public void scheduleNextWakeupTime(int seconds) {
         if (seconds < 0) {
-            Log.w(CarLog.TAG_POWER, "Next wake up can not be in negative time. Ignoring!");
+            Log.w(CarLog.TAG_POWER, "Next wake up time is negative. Ignoring!");
             return;
         }
-        if (!mHal.isTimedWakeupAllowed()) {
-            Log.w(CarLog.TAG_POWER, "Setting timed wakeups are disabled in HAL. Skipping");
-            mNextWakeupSec = 0;
-            return;
-        }
-        if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) {
-            mNextWakeupSec = seconds;
-        } else {
-            Log.d(CarLog.TAG_POWER, "Tried to schedule next wake up, but already had shorter "
-                    + "scheduled time");
+        boolean timedWakeupAllowed = mHal.isTimedWakeupAllowed();
+        synchronized (mLock) {
+            if (!timedWakeupAllowed) {
+                Log.w(CarLog.TAG_POWER, "Setting timed wakeups are disabled in HAL. Skipping");
+                mNextWakeupSec = 0;
+                return;
+            }
+            if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) {
+                // The new value is sooner than the old value. Take the new value.
+                mNextWakeupSec = seconds;
+            } else {
+                Log.d(CarLog.TAG_POWER, "Tried to schedule next wake up, but already had shorter "
+                        + "scheduled time");
+            }
         }
     }
 
     private void finishedImpl(IBinder binder) {
         boolean allAreComplete = false;
-        synchronized (mListenersWeAreWaitingFor) {
+        synchronized (mLock) {
             boolean oneWasRemoved = mListenersWeAreWaitingFor.remove(binder);
             allAreComplete = oneWasRemoved && mListenersWeAreWaitingFor.isEmpty();
         }
@@ -673,7 +726,7 @@
                 || mCurrentState.mState == CpmsState.SIMULATE_SLEEP) {
             PowerHandler powerHandler;
             // All apps are ready to shutdown/suspend.
-            synchronized (CarPowerManagementService.this) {
+            synchronized (mLock) {
                 if (!mShutdownOnFinish) {
                     if (mLastSleepEntryTime > mProcessingStartTime
                             && mLastSleepEntryTime < SystemClock.elapsedRealtime()) {
@@ -765,7 +818,7 @@
 
         @Override
         public void run() {
-            synchronized (CarPowerManagementService.this) {
+            synchronized (mLock) {
                 if (!mTimerActive) {
                     // Ignore timer expiration since we got cancelled
                     return;
@@ -927,9 +980,9 @@
         }
         handler.handlePowerStateChange();
 
-        synchronized (mSimulationSleepObject) {
+        synchronized (mSimulationWaitObject) {
             mWakeFromSimulatedSleep = true;
-            mSimulationSleepObject.notify();
+            mSimulationWaitObject.notify();
         }
     }
 
@@ -940,12 +993,12 @@
      * that is not directly derived from a VehicleApPowerStateReq.
      */
     public void forceSimulatedSuspend() {
-        synchronized (mSimulationSleepObject) {
+        synchronized (mSimulationWaitObject) {
             mInSimulatedDeepSleepMode = true;
             mWakeFromSimulatedSleep = false;
         }
         PowerHandler handler;
-        synchronized (this) {
+        synchronized (mLock) {
             mPendingPowerStates.addFirst(new CpmsState(CpmsState.SIMULATE_SLEEP,
                                                        CarPowerStateListener.SHUTDOWN_PREPARE));
             handler = mHandler;
@@ -956,19 +1009,20 @@
     // In a real Deep Sleep, the hardware removes power from the CPU (but retains power
     // on the RAM). This puts the processor to sleep. Upon some external signal, power
     // is re-applied to the CPU, and processing resumes right where it left off.
-    // We simulate this behavior by simply going into a loop.
-    // We exit the loop when forceResume() is called.
-    private void simulateSleepByLooping() {
-        Log.i(CarLog.TAG_POWER, "Starting to simulate Deep Sleep by looping");
-        synchronized (mSimulationSleepObject) {
+    // We simulate this behavior by calling wait().
+    // We continue from wait() when forceSimulatedResume() is called.
+    private void simulateSleepByWaiting() {
+        Log.i(CarLog.TAG_POWER, "Starting to simulate Deep Sleep by waiting");
+        synchronized (mSimulationWaitObject) {
             while (!mWakeFromSimulatedSleep) {
                 try {
-                    mSimulationSleepObject.wait();
+                    mSimulationWaitObject.wait();
                 } catch (InterruptedException ignored) {
+                    Thread.currentThread().interrupt(); // Restore interrupted status
                 }
             }
             mInSimulatedDeepSleepMode = false;
         }
-        Log.i(CarLog.TAG_POWER, "Exit Deep Sleep simulation loop");
+        Log.i(CarLog.TAG_POWER, "Exit Deep Sleep simulation");
     }
 }
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 fa82f90..945a9d6 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -197,7 +197,7 @@
         // second processing after wakeup
         assertFalse(mDisplayInterface.getDisplayState());
         // do not skip user switching part.
-        mService.clearIsBooting();
+        mService.clearIsBootingOrResuming();
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
         assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
         // user switching should have been requested.