Merge "Tell PhoneWindowManager when we start/finish interactive changes." into mnc-dev
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index eebcd84..57121ddd 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -945,17 +945,30 @@
     public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
 
     /**
-     * Called when the device is waking up.
+     * Called when the device has started waking up.
      */
-    public void wakingUp();
+    public void startedWakingUp();
 
     /**
-     * Called when the device is going to sleep.
-     *
-     * @param why {@link #OFF_BECAUSE_OF_USER} or
-     * {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * Called when the device has finished waking up.
      */
-    public void goingToSleep(int why);
+    public void finishedWakingUp();
+
+    /**
+     * Called when the device has started going to sleep.
+     *
+     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
+     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     */
+    public void startedGoingToSleep(int why);
+
+    /**
+     * Called when the device has finished going to sleep.
+     *
+     * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
+     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     */
+    public void finishedGoingToSleep(int why);
 
     /**
      * Called when the device is about to turn on the screen to show content.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a63aab..a1e1aef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1455,7 +1455,8 @@
 
         // Match current screen state.
         if (!mPowerManager.isInteractive()) {
-            goingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+            startedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+            finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
         }
 
         mWindowManagerInternal.registerAppTransitionListener(
@@ -5233,9 +5234,15 @@
 
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void goingToSleep(int why) {
+    public void startedGoingToSleep(int why) {
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")");
+    }
+
+    // Called on the PowerManager's Notifier thread.
+    @Override
+    public void finishedGoingToSleep(int why) {
         EventLog.writeEvent(70000, 0);
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Going to sleep...");
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")");
 
         // We must get this work done here because the power manager will drop
         // the wake lock and let the system suspend once this function returns.
@@ -5252,24 +5259,11 @@
         }
     }
 
-    private void wakeUpFromPowerKey(long eventTime) {
-        wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey);
-    }
-
-    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode) {
-        if (!wakeInTheaterMode && isTheaterModeEnabled()) {
-            return false;
-        }
-
-        mPowerManager.wakeUp(wakeTime);
-        return true;
-    }
-
     // Called on the PowerManager's Notifier thread.
     @Override
-    public void wakingUp() {
+    public void startedWakingUp() {
         EventLog.writeEvent(70000, 1);
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Waking up...");
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
 
         // Since goToSleep performs these functions synchronously, we must
         // do the same here.  We cannot post this work to a handler because
@@ -5297,6 +5291,25 @@
         }
     }
 
+    // Called on the PowerManager's Notifier thread.
+    @Override
+    public void finishedWakingUp() {
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Finished waking up...");
+    }
+
+    private void wakeUpFromPowerKey(long eventTime) {
+        wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey);
+    }
+
+    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode) {
+        if (!wakeInTheaterMode && isTheaterModeEnabled()) {
+            return false;
+        }
+
+        mPowerManager.wakeUp(wakeTime);
+        return true;
+    }
+
     private void finishKeyguardDrawn() {
         synchronized (mLock) {
             if (!mAwake || mKeyguardDrawComplete) {
@@ -5789,7 +5802,7 @@
         synchronized (mLock) {
             mSystemBooted = true;
         }
-        wakingUp();
+        startedWakingUp();
         screenTurningOn(null);
     }
 
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index fd98010..5a391f4 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -95,11 +95,19 @@
     private final Intent mScreenOffIntent;
     private final Intent mScreenBrightnessBoostIntent;
 
-    // The current interactive state.
-    private int mActualInteractiveState;
-    private int mLastReason;
+    // The current interactive state.  This is set as soon as an interactive state
+    // transition begins so as to capture the reason that it happened.  At some point
+    // this state will propagate to the pending state then eventually to the
+    // broadcasted state over the course of reporting the transition asynchronously.
+    private boolean mInteractive = true;
+    private int mInteractiveChangeReason;
+    private boolean mInteractiveChanging;
 
-    // True if there is a pending transition that needs to be reported.
+    // The pending interactive state that we will eventually want to broadcast.
+    // This is designed so that we can collapse redundant sequences of awake/sleep
+    // transition pairs while still guaranteeing that at least one transition is observed
+    // whenever this happens.
+    private int mPendingInteractiveState;
     private boolean mPendingWakeUpBroadcast;
     private boolean mPendingGoToSleepBroadcast;
 
@@ -244,45 +252,17 @@
 
     /**
      * Notifies that the device is changing wakefulness.
+     * This function may be called even if the previous change hasn't finished in
+     * which case it will assume that the state did not fully converge before the
+     * next transition began and will recover accordingly.
      */
-    public void onWakefulnessChangeStarted(int wakefulness, int reason) {
+    public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
+        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
         if (DEBUG) {
             Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
-                    + ", reason=" + reason);
+                    + ", reason=" + reason + ", interactive=" + interactive);
         }
 
-        // We handle interactive state changes once they start so that the system can
-        // set everything up or the user to begin interacting with applications.
-        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
-        if (interactive) {
-            handleWakefulnessChange(wakefulness, interactive, reason);
-        } else {
-            mLastReason = reason;
-        }
-
-        // Start input as soon as we start waking up or going to sleep.
-        mInputManagerInternal.setInteractive(interactive);
-    }
-
-    /**
-     * Notifies that the device has finished changing wakefulness.
-     */
-    public void onWakefulnessChangeFinished(int wakefulness) {
-        if (DEBUG) {
-            Slog.d(TAG, "onWakefulnessChangeFinished: wakefulness=" + wakefulness);
-        }
-
-        // Handle interactive state changes once they are finished so that the system can
-        // finish pending transitions (such as turning the screen off) before causing
-        // applications to change state visibly.
-        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
-        if (!interactive) {
-            handleWakefulnessChange(wakefulness, interactive, mLastReason);
-        }
-    }
-
-    private void handleWakefulnessChange(final int wakefulness, boolean interactive,
-            final int reason) {
         // Tell the activity manager about changes in wakefulness, not just interactivity.
         // It needs more granularity than other components.
         mHandler.post(new Runnable() {
@@ -292,65 +272,132 @@
             }
         });
 
-        // Handle changes in the overall interactive state.
-        boolean interactiveChanged = false;
-        synchronized (mLock) {
-            // Broadcast interactive state changes.
-            if (interactive) {
-                // Waking up...
-                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);
-                if (interactiveChanged) {
-                    mActualInteractiveState = INTERACTIVE_STATE_AWAKE;
-                    mPendingWakeUpBroadcast = true;
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
-                            mPolicy.wakingUp();
-                        }
-                    });
-                    updatePendingBroadcastLocked();
-                }
-            } else {
-                // Going to sleep...
-                // This is a good time to make transitions that we don't want the user to see,
-                // such as bringing the key guard to focus.  There's no guarantee for this,
-                // however because the user could turn the device on again at any time.
-                // Some things may need to be protected by other mechanisms that defer screen on.
-                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_ASLEEP);
-                if (interactiveChanged) {
-                    mActualInteractiveState = INTERACTIVE_STATE_ASLEEP;
-                    mPendingGoToSleepBroadcast = true;
-                    if (mUserActivityPending) {
-                        mUserActivityPending = false;
-                        mHandler.removeMessages(MSG_USER_ACTIVITY);
-                    }
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
-                            switch (reason) {
-                                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
-                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
-                                    break;
-                                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
-                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
-                                    break;
-                            }
-                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
-                            mPolicy.goingToSleep(why);
-                        }
-                    });
-                    updatePendingBroadcastLocked();
-                }
+        // Handle any early interactive state changes.
+        // Finish pending incomplete ones from a previous cycle.
+        if (mInteractive != interactive) {
+            // Finish up late behaviors if needed.
+            if (mInteractiveChanging) {
+                handleLateInteractiveChange();
             }
-        }
 
-        // Notify battery stats.
-        if (interactiveChanged) {
+            // Start input as soon as we start waking up or going to sleep.
+            mInputManagerInternal.setInteractive(interactive);
+
+            // Notify battery stats.
             try {
                 mBatteryStats.noteInteractive(interactive);
             } catch (RemoteException ex) { }
+
+            // Handle early behaviors.
+            mInteractive = interactive;
+            mInteractiveChangeReason = reason;
+            mInteractiveChanging = true;
+            handleEarlyInteractiveChange();
+        }
+    }
+
+    /**
+     * Notifies that the device has finished changing wakefulness.
+     */
+    public void onWakefulnessChangeFinished() {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakefulnessChangeFinished");
+        }
+
+        if (mInteractiveChanging) {
+            mInteractiveChanging = false;
+            handleLateInteractiveChange();
+        }
+    }
+
+    /**
+     * Handle early interactive state changes such as getting applications or the lock
+     * screen running and ready for the user to see (such as when turning on the screen).
+     */
+    private void handleEarlyInteractiveChange() {
+        synchronized (mLock) {
+            if (mInteractive) {
+                // Waking up...
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
+                        mPolicy.startedWakingUp();
+                    }
+                });
+
+                // Send interactive broadcast.
+                mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
+                mPendingWakeUpBroadcast = true;
+                updatePendingBroadcastLocked();
+            } else {
+                // Going to sleep...
+                // Tell the policy that we started going to sleep.
+                final int why = translateOffReason(mInteractiveChangeReason);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mPolicy.startedGoingToSleep(why);
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Handle late interactive state changes once they are finished so that the system can
+     * finish pending transitions (such as turning the screen off) before causing
+     * applications to change state visibly.
+     */
+    private void handleLateInteractiveChange() {
+        synchronized (mLock) {
+            if (mInteractive) {
+                // Finished waking up...
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mPolicy.finishedWakingUp();
+                    }
+                });
+            } else {
+                // Finished going to sleep...
+                // This is a good time to make transitions that we don't want the user to see,
+                // such as bringing the key guard to focus.  There's no guarantee for this
+                // however because the user could turn the device on again at any time.
+                // Some things may need to be protected by other mechanisms that defer screen on.
+
+                // Cancel pending user activity.
+                if (mUserActivityPending) {
+                    mUserActivityPending = false;
+                    mHandler.removeMessages(MSG_USER_ACTIVITY);
+                }
+
+                // Tell the policy we finished going to sleep.
+                final int why = translateOffReason(mInteractiveChangeReason);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
+                        mPolicy.finishedGoingToSleep(why);
+                    }
+                });
+
+                // Send non-interactive broadcast.
+                mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
+                mPendingGoToSleepBroadcast = true;
+                updatePendingBroadcastLocked();
+            }
+        }
+    }
+
+    private static int translateOffReason(int reason) {
+        switch (reason) {
+            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+                return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
+            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+                return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
+            default:
+                return WindowManagerPolicy.OFF_BECAUSE_OF_USER;
         }
     }
 
@@ -367,6 +414,7 @@
         msg.setAsynchronous(true);
         mHandler.sendMessage(msg);
     }
+
     /**
      * Called when there has been user activity.
      */
@@ -407,9 +455,9 @@
 
     private void updatePendingBroadcastLocked() {
         if (!mBroadcastInProgress
-                && mActualInteractiveState != INTERACTIVE_STATE_UNKNOWN
+                && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
                 && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualInteractiveState != mBroadcastedInteractiveState)) {
+                        || mPendingInteractiveState != mBroadcastedInteractiveState)) {
             mBroadcastInProgress = true;
             mSuspendBlocker.acquire();
             Message msg = mHandler.obtainMessage(MSG_BROADCAST);
@@ -444,7 +492,7 @@
             } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
                 // Broadcasted power state is awake.  Send asleep if needed.
                 if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualInteractiveState == INTERACTIVE_STATE_ASLEEP) {
+                        || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
                     mPendingGoToSleepBroadcast = false;
                     mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
                 } else {
@@ -454,7 +502,7 @@
             } else {
                 // Broadcasted power state is asleep.  Send awake if needed.
                 if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualInteractiveState == INTERACTIVE_STATE_AWAKE) {
+                        || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
                     mPendingWakeUpBroadcast = false;
                     mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
                 } else {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1b5391e..c1fe984 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1217,8 +1217,6 @@
 
     private void setWakefulnessLocked(int wakefulness, int reason) {
         if (mWakefulness != wakefulness) {
-            finishWakefulnessChangeLocked();
-
             mWakefulness = wakefulness;
             mWakefulnessChanging = true;
             mDirty |= DIRTY_WAKEFULNESS;
@@ -1226,10 +1224,14 @@
         }
     }
 
-    private void finishWakefulnessChangeLocked() {
-        if (mWakefulnessChanging) {
-            mNotifier.onWakefulnessChangeFinished(mWakefulness);
+    private void finishWakefulnessChangeIfNeededLocked() {
+        if (mWakefulnessChanging && mDisplayReady) {
+            if (mWakefulness == WAKEFULNESS_DOZING
+                    && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
+                return; // wait until dream has enabled dozing
+            }
             mWakefulnessChanging = false;
+            mNotifier.onWakefulnessChangeFinished();
         }
     }
 
@@ -1280,9 +1282,7 @@
             updateDreamLocked(dirtyPhase2, displayBecameReady);
 
             // Phase 4: Send notifications, if needed.
-            if (mDisplayReady) {
-                finishWakefulnessChangeLocked();
-            }
+            finishWakefulnessChangeIfNeededLocked();
 
             // Phase 5: Update suspend blocker.
             // Because we might release the last suspend blocker here, we need to make sure