/**
* Block until the given window has been drawn to the screen.
+ * Returns true if really waiting, false if the window does not exist.
*/
- void waitForWindowDrawn(IBinder token, in IRemoteCallback callback);
+ boolean waitForWindowDrawn(IBinder token, in IRemoteCallback callback);
/**
* Device has a software navigation bar (separate from the status bar).
* Called when we have started keeping the screen on because a window
* requesting this has become visible.
*/
- public void screenOnStartedLw();
+ public void keepScreenOnStartedLw();
/**
* Called when we have stopped keeping the screen on because the last window
* requesting this is no longer visible.
*/
- public void screenOnStoppedLw();
-
- /**
- * Return false to disable key repeat events from being generated.
- */
- public boolean allowKeyRepeat();
+ public void keepScreenOnStoppedLw();
/**
* Inform the policy that the user has chosen a preferred orientation ("rotation lock").
}
};
- /** {@inheritDoc} */
+ @Override
public void screenTurnedOff(int why) {
EventLog.writeEvent(70000, 0);
synchronized (mLock) {
}
}
- /** {@inheritDoc} */
+ @Override
public void screenTurningOn(final ScreenOnListener screenOnListener) {
EventLog.writeEvent(70000, 1);
if (false) {
here.fillInStackTrace();
Slog.i(TAG, "Screen turning on...", here);
}
- if (screenOnListener != null) {
- if (mKeyguardMediator != null) {
- try {
- mWindowManager.setEventDispatching(true);
- } catch (RemoteException unhandled) {
- }
+
+ synchronized (mLock) {
+ mScreenOnEarly = true;
+ updateOrientationListenerLp();
+ updateLockScreenTimeout();
+ }
+
+ try {
+ mWindowManager.setEventDispatching(true);
+ } catch (RemoteException unhandled) {
+ }
+
+ waitForKeyguard(screenOnListener);
+ }
+
+ private void waitForKeyguard(final ScreenOnListener screenOnListener) {
+ if (mKeyguardMediator != null) {
+ if (screenOnListener != null) {
mKeyguardMediator.onScreenTurnedOn(new KeyguardViewManager.ShowListener() {
- @Override public void onShown(IBinder windowToken) {
- if (windowToken != null) {
- try {
- mWindowManager.waitForWindowDrawn(windowToken,
- new IRemoteCallback.Stub() {
- @Override public void sendResult(Bundle data) {
- Slog.i(TAG, "Lock screen displayed!");
- screenOnListener.onScreenOn();
- synchronized (mLock) {
- mScreenOnFully = true;
- }
- }
- });
- } catch (RemoteException e) {
- }
- } else {
- Slog.i(TAG, "No lock screen!");
- screenOnListener.onScreenOn();
- synchronized (mLock) {
- mScreenOnFully = true;
- }
- }
+ @Override
+ public void onShown(IBinder windowToken) {
+ waitForKeyguardWindowDrawn(windowToken, screenOnListener);
}
});
- }
- } else {
- if (mKeyguardMediator != null) {
- // Must set mScreenOn = true.
+ return;
+ } else {
mKeyguardMediator.onScreenTurnedOn(null);
}
- synchronized (mLock) {
- mScreenOnFully = true;
+ } else {
+ Slog.i(TAG, "No keyguard mediator!");
+ }
+ finishScreenTurningOn(screenOnListener);
+ }
+
+ private void waitForKeyguardWindowDrawn(IBinder windowToken,
+ final ScreenOnListener screenOnListener) {
+ if (windowToken != null) {
+ try {
+ if (mWindowManager.waitForWindowDrawn(
+ windowToken, new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ Slog.i(TAG, "Lock screen displayed!");
+ finishScreenTurningOn(screenOnListener);
+ }
+ })) {
+ return;
+ }
+ } catch (RemoteException ex) {
+ // Can't happen in system process.
}
}
+
+ Slog.i(TAG, "No lock screen!");
+ finishScreenTurningOn(screenOnListener);
+ }
+
+ private void finishScreenTurningOn(ScreenOnListener screenOnListener) {
synchronized (mLock) {
- mScreenOnEarly = true;
- updateOrientationListenerLp();
- updateLockScreenTimeout();
+ mScreenOnFully = true;
+ }
+
+ if (screenOnListener != null) {
+ screenOnListener.onScreenOn();
}
}
- /** {@inheritDoc} */
+ @Override
public boolean isScreenOnEarly() {
return mScreenOnEarly;
}
-
- /** {@inheritDoc} */
+
+ @Override
public boolean isScreenOnFully() {
return mScreenOnFully;
}
-
+
/** {@inheritDoc} */
public void enableKeyguard(boolean enabled) {
if (mKeyguardMediator != null) {
}
return true;
}
-
- public void screenOnStartedLw() {
- }
- public void screenOnStoppedLw() {
- if (mPowerManager.isScreenOn()) {
- if (mKeyguardMediator != null && !mKeyguardMediator.isShowingAndNotHidden()) {
- long curTime = SystemClock.uptimeMillis();
- mPowerManager.userActivity(curTime, false);
- }
- }
+ @Override
+ public void keepScreenOnStartedLw() {
}
- public boolean allowKeyRepeat() {
- // disable key repeat when screen is off
- return mScreenOnEarly;
+ @Override
+ public void keepScreenOnStoppedLw() {
+ if (mKeyguardMediator != null && !mKeyguardMediator.isShowingAndNotHidden()) {
+ long curTime = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(curTime, false);
+ }
}
private int updateSystemUiVisibilityLw() {
final boolean mustNotify;
boolean mustInitialize = false;
boolean updateAutoBrightness = mTwilightChanged;
+ boolean screenOnWasBlocked = false;
mTwilightChanged = false;
synchronized (mLock) {
if (mScreenOffBecauseOfProximity
&& mProximity != PROXIMITY_POSITIVE) {
mScreenOffBecauseOfProximity = false;
- setScreenOn(true);
sendOnProximityNegative();
}
} else {
// It is relatively short but if we cancel it and switch to the
// on animation immediately then the results are pretty ugly.
if (!mElectronBeamOffAnimator.isStarted()) {
- setScreenOn(true);
- if (USE_ELECTRON_BEAM_ON_ANIMATION) {
- if (!mElectronBeamOnAnimator.isStarted()) {
- if (mPowerState.getElectronBeamLevel() == 1.0f) {
- mPowerState.dismissElectronBeam();
- } else if (mPowerState.prepareElectronBeam(true)) {
- mElectronBeamOnAnimator.start();
- } else {
- mElectronBeamOnAnimator.end();
- }
+ if (mPowerRequest.blockScreenOn && !mPowerState.isScreenOn()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Blocked screen on while screen currently off.");
}
+ screenOnWasBlocked = true;
} else {
- mPowerState.setElectronBeamLevel(1.0f);
- mPowerState.dismissElectronBeam();
+ setScreenOn(true);
+ if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 1.0f) {
+ mPowerState.dismissElectronBeam();
+ } else if (mPowerState.prepareElectronBeam(true)) {
+ mElectronBeamOnAnimator.start();
+ } else {
+ mElectronBeamOnAnimator.end();
+ }
+ }
+ } else {
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
+ }
+ }
+ } else {
+ // FIXME: If the electron beam off animation is playing then we have a bit
+ // of a problem. The window manager policy would only have requested
+ // to block screen on if it was about to start preparing the keyguard.
+ // It's already too late to do anything about that. Ideally we would
+ // let the animation play out first but that would require making
+ // some pretty deep changes to the power manager and we don't have
+ // time just now. For now, short-circuit the animation and get ready.
+ if (mPowerRequest.blockScreenOn) {
+ if (DEBUG) {
+ Slog.d(TAG, "Blocked screen on while screen off animation running.");
+ }
+ screenOnWasBlocked = true;
+ setScreenOn(false);
+ mElectronBeamOffAnimator.end();
}
}
} else {
// We mostly care about the screen state here, ignoring brightness changes
// which will be handled asynchronously.
if (mustNotify
+ && !screenOnWasBlocked
&& !mElectronBeamOnAnimator.isStarted()
&& !mElectronBeamOffAnimator.isStarted()
&& mPowerState.waitUntilClean(mCleanListener)) {
synchronized (mLock) {
if (!mPendingRequestChangedLocked) {
mDisplayReadyLocked = true;
+
+ if (DEBUG) {
+ Slog.d(TAG, "Display ready!");
+ }
}
}
sendOnStateChanged();
// If true, enables automatic brightness control.
public boolean useAutoBrightness;
+ // If true, prevents the screen from turning on if it is currently off.
+ // The display does not enter a "ready" state if this flag is true and the screen
+ // is off and is being prevented from turning on. The window manager policy blocks
+ // screen on while it prepares the keyguard to prevent the user from seeing
+ // intermediate updates.
+ public boolean blockScreenOn;
+
public DisplayPowerRequest() {
screenState = SCREEN_STATE_BRIGHT;
useProximitySensor = false;
screenBrightness = PowerManager.BRIGHTNESS_ON;
screenAutoBrightnessAdjustment = 0.0f;
useAutoBrightness = false;
+ blockScreenOn = false;
}
public DisplayPowerRequest(DisplayPowerRequest other) {
screenBrightness = other.screenBrightness;
screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
useAutoBrightness = other.useAutoBrightness;
+ blockScreenOn = other.blockScreenOn;
}
@Override
&& useProximitySensor == other.useProximitySensor
&& screenBrightness == other.screenBrightness
&& screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
- && useAutoBrightness == other.useAutoBrightness;
+ && useAutoBrightness == other.useAutoBrightness
+ && blockScreenOn == other.blockScreenOn;
}
@Override
+ ", useProximitySensor=" + useProximitySensor
+ ", screenBrightness=" + screenBrightness
+ ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
- + ", useAutoBrightness=" + useAutoBrightness;
+ + ", useAutoBrightness=" + useAutoBrightness
+ + ", blockScreenOn=" + blockScreenOn;
}
}
import android.util.EventLog;
import android.util.Slog;
import android.view.WindowManagerPolicy;
-import android.view.WindowManagerPolicy.ScreenOnListener;
/**
* Sends broadcasts about important power state changes.
private final Context mContext;
private final IBatteryStats mBatteryStats;
private final SuspendBlocker mSuspendBlocker;
+ private final ScreenOnBlocker mScreenOnBlocker;
private final WindowManagerPolicy mPolicy;
- private final ScreenOnListener mScreenOnListener;
private final NotifierHandler mHandler;
private final Intent mScreenOnIntent;
// True if a user activity message should be sent.
private boolean mUserActivityPending;
+ // True if the screen on blocker has been acquired.
+ private boolean mScreenOnBlockerAcquired;
+
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- ScreenOnListener screenOnListener) {
+ SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker,
+ WindowManagerPolicy policy) {
mContext = context;
mBatteryStats = batteryStats;
mSuspendBlocker = suspendBlocker;
+ mScreenOnBlocker = screenOnBlocker;
mPolicy = policy;
- mScreenOnListener = screenOnListener;
mHandler = new NotifierHandler(looper);
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
if (mActualPowerState != POWER_STATE_AWAKE) {
mActualPowerState = POWER_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
+ if (!mScreenOnBlockerAcquired) {
+ mScreenOnBlockerAcquired = true;
+ mScreenOnBlocker.acquire();
+ }
updatePendingBroadcastLocked();
}
}
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
mPolicy.screenTurningOn(mScreenOnListener);
+
try {
ActivityManagerNative.getDefault().wakingUp();
} catch (RemoteException e) {
}
}
+ private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
+ new WindowManagerPolicy.ScreenOnListener() {
+ @Override
+ public void onScreenOn() {
+ synchronized (mLock) {
+ if (mScreenOnBlockerAcquired && !mPendingWakeUpBroadcast) {
+ mScreenOnBlockerAcquired = false;
+ mScreenOnBlocker.release();
+ }
+ }
+ }
+ };
+
private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a napping or dreaming state.
private static final int MSG_SANDMAN = 2;
+ // Message: Sent when the screen on blocker is released.
+ private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
private static final int DIRTY_BATTERY_STATE = 1 << 8;
// Dirty bit: proximity state changed
private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9;
+ // Dirty bit: screen on blocker state became held or unheld
+ private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10;
// Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
// The screen should be off or in the process of being turned off by the display controller.
// The suspend blocker used to keep the CPU alive when wake locks have been acquired.
private final SuspendBlocker mWakeLockSuspendBlocker;
+ // The screen on blocker used to keep the screen from turning on while the lock
+ // screen is coming up.
+ private final ScreenOnBlockerImpl mScreenOnBlocker;
+
// True if systemReady() has been called.
private boolean mSystemReady;
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
mWakeLockSuspendBlocker.acquire();
+ mScreenOnBlocker = new ScreenOnBlockerImpl();
mHoldingWakeLockSuspendBlocker = true;
mWakefulness = WAKEFULNESS_AWAKE;
}
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
- mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats,
+ // The notifier runs on the system server's main looper so as not to interfere
+ // with the animations and other critical functions of the power manager.
+ mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
- mPolicy, mScreenOnListener);
+ mScreenOnBlocker, mPolicy);
+
+ // The display power controller runs on the power manager service's
+ // own handler thread.
mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
mContext, mNotifier, mLightsService, twilight,
createSuspendBlockerLocked("PowerManagerService.Display"),
}
}
+ private void handleScreenOnBlockerReleased() {
+ synchronized (mLock) {
+ mDirty |= DIRTY_SCREEN_ON_BLOCKER_RELEASED;
+ updatePowerStateLocked();
+ }
+ }
+
/**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true. The display
private void updateDisplayPowerStateLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
- | DIRTY_SETTINGS)) != 0) {
- int newScreenState = getDesiredScreenPowerState();
+ | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
+ int newScreenState = getDesiredScreenPowerStateLocked();
if (newScreenState != mDisplayPowerRequest.screenState) {
if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
&& mDisplayPowerRequest.screenState
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+ mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
+
mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
- Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady
+ Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
+ ", newScreenState=" + newScreenState
+ ", mWakefulness=" + mWakefulness
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
return value >= -1.0f && value <= 1.0f;
}
- private int getDesiredScreenPowerState() {
+ private int getDesiredScreenPowerStateLocked() {
if (mWakefulness == WAKEFULNESS_ASLEEP) {
return DisplayPowerRequest.SCREEN_STATE_OFF;
}
pw.println(" " + sb);
}
+ pw.println();
+ pw.println("Screen On Blocker: " + mScreenOnBlocker);
+
dpc = mDisplayPowerController;
}
}
}
- private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
- new WindowManagerPolicy.ScreenOnListener() {
- @Override
- public void onScreenOn() {
- }
- };
-
/**
* Handler for asynchronous operations performed by the power manager.
*/
case MSG_SANDMAN:
handleSandman();
break;
+ case MSG_SCREEN_ON_BLOCKER_RELEASED:
+ handleScreenOnBlockerReleased();
+ break;
}
}
}
}
}
}
+
+ private final class ScreenOnBlockerImpl implements ScreenOnBlocker {
+ private int mNestCount;
+
+ public boolean isHeld() {
+ synchronized (this) {
+ return mNestCount != 0;
+ }
+ }
+
+ @Override
+ public void acquire() {
+ synchronized (this) {
+ mNestCount += 1;
+ if (DEBUG) {
+ Slog.d(TAG, "Screen on blocked: mNestCount=" + mNestCount);
+ }
+ }
+ }
+
+ @Override
+ public void release() {
+ synchronized (this) {
+ mNestCount -= 1;
+ if (mNestCount < 0) {
+ Log.wtf(TAG, "Screen on blocker was released without being acquired!",
+ new Throwable());
+ mNestCount = 0;
+ }
+ if (mNestCount == 0) {
+ mHandler.sendEmptyMessage(MSG_SCREEN_ON_BLOCKER_RELEASED);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount;
+ }
+ }
+ };
}
--- /dev/null
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+/**
+ * Low-level screen on blocker mechanism which is used to keep the screen off
+ * until the window manager is ready to show new content.
+ */
+interface ScreenOnBlocker {
+ /**
+ * Acquires the screen on blocker.
+ * Prevents the screen from turning on.
+ *
+ * Calls to acquire() nest and must be matched by the same number
+ * of calls to release().
+ */
+ void acquire();
+
+ /**
+ * Releases the screen on blocker.
+ * Allows the screen to turn on.
+ *
+ * It is an error to call release() if the screen on blocker has not been acquired.
+ * The system may crash.
+ */
+ void release();
+}
}
}
- public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
- synchronized (mWindowMap) {
- WindowState win = windowForClientLocked(null, token, true);
- if (win != null) {
- Pair<WindowState, IRemoteCallback> pair =
- new Pair<WindowState, IRemoteCallback>(win, callback);
- Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
- mH.sendMessageDelayed(m, 2000);
- mWaitingForDrawn.add(pair);
- checkDrawnWindowsLocked();
+ public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
+ if (token != null && callback != null) {
+ synchronized (mWindowMap) {
+ WindowState win = windowForClientLocked(null, token, true);
+ if (win != null) {
+ Pair<WindowState, IRemoteCallback> pair =
+ new Pair<WindowState, IRemoteCallback>(win, callback);
+ Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
+ mH.sendMessageDelayed(m, 2000);
+ mWaitingForDrawn.add(pair);
+ checkDrawnWindowsLocked();
+ return true;
+ }
}
}
+ return false;
}
void setHoldScreenLocked(final Session newHoldScreen) {
final boolean state = mHoldingScreenWakeLock.isHeld();
if (hold != state) {
if (hold) {
- mPolicy.screenOnStartedLw();
mHoldingScreenWakeLock.acquire();
+ mPolicy.keepScreenOnStartedLw();
} else {
- mPolicy.screenOnStoppedLw();
+ mPolicy.keepScreenOnStoppedLw();
mHoldingScreenWakeLock.release();
}
}
}
@Override
- public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
- // TODO Auto-generated method stub
+ public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
+ return false;
}
@Override