Improvements to power manager turning on screen.

The keyguard/window manager recently got a facility to report when it is
okay to turn the screen on, when it knows the lock screen is displayed.

The power manager was using this wrong, just using it to drive the
flags given to the input system.  Duh.

This change now uses the information to determine when to turn the screen
brightness up from 0.  For an OLED screen, this is the time when the
user can actually see anything on the screen.

For LCD screens this may not be optimal, because the LCD may start running
before its backlight is turned on, so if you look carefully you may see
stuff before it is lit up.  On the other hand, it is good to turn on the
display as early as possible (before waiting for the keyguard) because it
can take a little bit of time to get that and the touch screen going.  By
only waiting on the display brightness, we allow turning on the screen
in the kernel to proceed in parallel with ensuring the keyguard is displayed.

Change-Id: I7ee4ce19fd4efd5b51872b855af6263f53cd6c30
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 0934cd0..785db98 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -79,6 +79,8 @@
     private static final String TAG = "PowerManagerService";
     static final String PARTIAL_NAME = "PowerManagerService";
 
+    static final boolean DEBUG_SCREEN_ON = false;
+
     private static final boolean LOG_PARTIAL_WL = false;
 
     // Indicates whether touch-down cycles should be logged as part of the
@@ -162,6 +164,8 @@
     private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private final int[] mBroadcastWhy = new int[3];
     private boolean mPreparingForScreenOn = false;
+    private boolean mSkippedScreenOn = false;
+    private boolean mInitialized = false;
     private int mPartialCount = 0;
     private int mPowerState;
     // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
@@ -557,6 +561,9 @@
         nativeInit();
         synchronized (mLocks) {
             updateNativePowerStateLocked();
+            // We make sure to start out with the screen on due to user activity.
+            // (They did just boot their device, after all.)
+            forceUserActivityLocked();
         }
     }
 
@@ -1123,7 +1130,8 @@
                     + " " + ((mNextTimeout-now)/1000) + "s from now");
             pw.println("  mDimScreen=" + mDimScreen
                     + " mStayOnConditions=" + mStayOnConditions
-                    + " mPreparingForScreenOn=" + mPreparingForScreenOn);
+                    + " mPreparingForScreenOn=" + mPreparingForScreenOn
+                    + " mSkippedScreenOn=" + mSkippedScreenOn);
             pw.println("  mScreenOffReason=" + mScreenOffReason
                     + " mUserState=" + mUserState);
             pw.println("  mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1]
@@ -1312,8 +1320,16 @@
         }
     }
 
-    private void sendNotificationLocked(boolean on, int why)
-    {
+    private void sendNotificationLocked(boolean on, int why) {
+        if (!mInitialized) {
+            // No notifications sent until first initialization is done.
+            // This is so that when we are moving from our initial state
+            // which looks like the screen was off to it being on, we do not
+            // go through the process of waiting for the higher-level user
+            // space to be ready before turning up the display brightness.
+            // (And also do not send needless broadcasts about the screen.)
+            return;
+        }
         if (!on) {
             mStillNeedSleepNotification = false;
         }
@@ -1360,7 +1376,9 @@
 
         // The broadcast queue has changed; make sure the screen is on if it
         // is now possible for it to be.
-        updateNativePowerStateLocked();
+        if (mSkippedScreenOn) {
+            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
+        }
 
         // Now send the message.
         if (index >= 0) {
@@ -1380,7 +1398,7 @@
                     synchronized (mLocks) {
                         if (mPreparingForScreenOn) {
                             mPreparingForScreenOn = false;
-                            updateNativePowerStateLocked();
+                            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP,
                                     4, mBroadcastWakeLock.mCount);
                             mBroadcastWakeLock.release();
@@ -1453,7 +1471,7 @@
                         synchronized (mLocks) {
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
                                     mBroadcastWakeLock.mCount);
-                            updateNativePowerStateLocked();
+                            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
                             mBroadcastWakeLock.release();
                         }
                     }
@@ -1646,6 +1664,11 @@
         };
 
     private int setScreenStateLocked(boolean on) {
+        if (DEBUG_SCREEN_ON) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Slog.i(TAG, "Set screen state: " + on, e);
+        }
         int err = Power.setScreenState(on);
         if (err == 0) {
             mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
@@ -1696,7 +1719,7 @@
             } else {
                 newState &= ~BATTERY_LOW_BIT;
             }
-            if (newState == mPowerState) {
+            if (newState == mPowerState && mInitialized) {
                 return;
             }
 
@@ -1722,10 +1745,7 @@
                          + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0));
             }
 
-            if (mPowerState != newState) {
-                updateLightsLocked(newState, 0);
-                mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
-            }
+            final boolean stateChanged = mPowerState != newState;
 
             if (oldScreenOn != newScreenOn) {
                 if (newScreenOn) {
@@ -1777,10 +1797,24 @@
                     EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason,
                             mTotalTouchDownTime, mTouchCycles);
                     if (err == 0) {
-                        mPowerState |= SCREEN_ON_BIT;
                         sendNotificationLocked(true, -1);
+                        // Update the lights *after* taking care of turning the
+                        // screen on, so we do this after our notifications are
+                        // enqueued and thus will delay turning on the screen light
+                        // until the windows are correctly displayed.
+                        if (stateChanged) {
+                            updateLightsLocked(newState, 0);
+                        }
+                        mPowerState |= SCREEN_ON_BIT;
                     }
+
                 } else {
+                    // Update the lights *before* taking care of turning the
+                    // screen off, so we can initiate any animations that are desired.
+                    if (stateChanged) {
+                        updateLightsLocked(newState, 0);
+                    }
+
                     // cancel light sensor task
                     mHandler.removeCallbacks(mAutoBrightnessTask);
                     mLightSensorPendingDecrease = false;
@@ -1803,30 +1837,20 @@
                         mLastTouchDown = 0;
                     }
                 }
+            } else if (stateChanged) {
+                // Screen on/off didn't change, but lights may have.
+                updateLightsLocked(newState, 0);
             }
-            
+
+            mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
+
             updateNativePowerStateLocked();
+
+            mInitialized = true;
         }
     }
-    
+
     private void updateNativePowerStateLocked() {
-        if ((mPowerState & SCREEN_ON_BIT) != 0) {
-            // Don't turn screen on until we know we are really ready to.
-            // This is to avoid letting the screen go on before things like the
-            // lock screen have been displayed.
-            if (mPreparingForScreenOn) {
-                // Currently waiting for confirmation from the policy that it
-                // is okay to turn on the screen.  Don't allow the screen to go
-                // on until that is done.
-                return;
-            }
-            for (int i=0; i<mBroadcastQueue.length; i++) {
-                if (mBroadcastQueue[i] == 1) {
-                    // A screen on is currently enqueued.
-                    return;
-                }
-            }
-        }
         nativeSetPowerState(
                 (mPowerState & SCREEN_ON_BIT) != 0,
                 (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
@@ -1852,8 +1876,43 @@
                 mBatteryService.getBatteryLevel() <= Power.LOW_BATTERY_THRESHOLD);
     }
 
+    private boolean shouldDeferScreenOnLocked() {
+        if (mPreparingForScreenOn) {
+            // Currently waiting for confirmation from the policy that it
+            // is okay to turn on the screen.  Don't allow the screen to go
+            // on until that is done.
+            if (DEBUG_SCREEN_ON) Slog.i(TAG,
+                    "updateLights: delaying screen on due to mPreparingForScreenOn");
+            return true;
+        } else {
+            // If there is a screen-on command in the notification queue, we
+            // can't turn the screen on until it has been processed (and we
+            // have set mPreparingForScreenOn) or it has been dropped.
+            for (int i=0; i<mBroadcastQueue.length; i++) {
+                if (mBroadcastQueue[i] == 1) {
+                    if (DEBUG_SCREEN_ON) Slog.i(TAG,
+                            "updateLights: delaying screen on due to notification queue");
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void updateLightsLocked(int newState, int forceState) {
         final int oldState = mPowerState;
+
+        // If the screen is not currently on, we will want to delay actually
+        // turning the lights on if we are still getting the UI put up.
+        if ((oldState&SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
+            // Don't turn screen on until we know we are really ready to.
+            // This is to avoid letting the screen go on before things like the
+            // lock screen have been displayed.
+            if ((mSkippedScreenOn=shouldDeferScreenOnLocked())) {
+                newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT);
+            }
+        }
+
         if ((newState & SCREEN_ON_BIT) != 0) {
             // Only turn on the buttons or keyboard if the screen is also on.
             // We should never see the buttons on but not the screen.
@@ -1960,6 +2019,13 @@
             }
             mScreenBrightness.setTargetLocked(brightness, steps,
                     INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
+            if (DEBUG_SCREEN_ON) {
+                RuntimeException e = new RuntimeException("here");
+                e.fillInStackTrace();
+                Slog.i(TAG, "Setting screen brightness: " + brightness, e);
+                mScreenBrightness.setTargetLocked(brightness, steps,
+                        INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
+            }
         }
 
         if (mSpew) {