From 416c49c4049f572134273e228d7988904a51b990 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 26 May 2015 19:50:18 -0700 Subject: [PATCH] Tell PhoneWindowManager when we start/finish interactive changes. Added some new callbacks that can be used to more precisely trigger certain behaviors that need to happen around the time the device is put to sleep and locked. Fixed an issue where the going to sleep signal might be sent too early on devices that don't support ambient mode due to the extra wakefulness change between DOZING and ASLEEP. We are now track the early / late interactive change work separately from the rest. Bug: 21375811 Change-Id: I95387195e216ae92a6e45485bd1bd362e41aa45a --- .../android/view/WindowManagerPolicy.java | 27 +- .../server/policy/PhoneWindowManager.java | 51 ++-- .../com/android/server/power/Notifier.java | 240 +++++++++++------- .../server/power/PowerManagerService.java | 16 +- 4 files changed, 204 insertions(+), 130 deletions(-) diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 9199af1041be8..106493dbff839 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -941,17 +941,30 @@ public interface WindowManagerPolicy { 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 a4e9c6808899a..5ef7c1e390b8f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1451,7 +1451,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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( @@ -5215,9 +5216,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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. @@ -5234,24 +5241,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - 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 @@ -5279,6 +5273,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // 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) { @@ -5771,7 +5784,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 fd98010e5032c..5a391f4945838 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 @@ final class Notifier { 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 @@ final class Notifier { /** * 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 @@ final class Notifier { } }); - // 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 @@ final class Notifier { msg.setAsynchronous(true); mHandler.sendMessage(msg); } + /** * Called when there has been user activity. */ @@ -407,9 +455,9 @@ final class Notifier { 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 @@ final class Notifier { } 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 @@ final class Notifier { } 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 5aea7462fb3e2..d7043e839925b 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 @@ public final class PowerManagerService extends SystemService private void setWakefulnessLocked(int wakefulness, int reason) { if (mWakefulness != wakefulness) { - finishWakefulnessChangeLocked(); - mWakefulness = wakefulness; mWakefulnessChanging = true; mDirty |= DIRTY_WAKEFULNESS; @@ -1226,10 +1224,14 @@ public final class PowerManagerService extends SystemService } } - 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 @@ public final class PowerManagerService extends SystemService 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