From 29aae6f36e565b8f2a99f2193597b964bb800ee8 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 18 Aug 2011 18:30:09 -0700 Subject: [PATCH] Fix issue #4279860: previous UI flashes before showing lock screen... ...(when turning display on after recently turning it off) Also clean up when we decide to turn the screen on to improve that transition. There are still problems here with turning it on before the wallpaper gets dispayed. Change-Id: I2bc56c12e5ad75a1ce5a0546f43a845bf0823e66 --- core/java/android/view/IWindowManager.aidl | 5 + core/java/android/view/WindowManager.java | 10 +- .../widget/multiwaveview/TargetDrawable.java | 8 +- .../impl/KeyguardStatusViewManager.java | 2 +- .../policy/impl/KeyguardViewMediator.java | 233 +++++++++++------- .../policy/impl/PhoneWindowManager.java | 23 +- .../android/server/PowerManagerService.java | 30 +++ .../server/am/ActivityManagerService.java | 2 +- .../com/android/server/am/ActivityStack.java | 2 +- .../server/wm/WindowManagerService.java | 133 ++++++++-- .../bridge/android/BridgeWindowManager.java | 4 + 11 files changed, 330 insertions(+), 122 deletions(-) diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c7bf8e3aa3d82..0dc781f18c534 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -218,4 +218,9 @@ interface IWindowManager * Called by the settings application to temporarily set the pointer speed. */ void setPointerSpeed(int speed); + + /** + * Block until all windows the window manager knows about have been drawn. + */ + void waitForAllDrawn(); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 6b09049966a96..fdd9b2c5e047b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -177,7 +177,8 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"), @ViewDebug.IntToString(from = TYPE_INPUT_METHOD, to = "TYPE_INPUT_METHOD"), @ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG, to = "TYPE_INPUT_METHOD_DIALOG"), - @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY") + @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY"), + @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS") }) public int type; @@ -400,6 +401,13 @@ public interface WindowManager extends ViewManager { */ public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20; + /** + * Window type: The boot progress dialog, goes on top of everything + * in the world. + * @hide + */ + public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21; + /** * End of types of system windows. */ diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java index d3baa2bb73a38..aa9fa451b0681 100644 --- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java +++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java @@ -25,6 +25,8 @@ import android.util.Log; public class TargetDrawable { private static final String TAG = "TargetDrawable"; + private static final boolean DEBUG = false; + public static final int[] STATE_ACTIVE = { android.R.attr.state_enabled, android.R.attr.state_active }; public static final int[] STATE_INACTIVE = @@ -139,11 +141,13 @@ public class TargetDrawable { maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth()); maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight()); } - Log.v(TAG, "union of childDrawable rects " + d + " to: " + maxWidth + "x" + maxHeight); + if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: " + + maxWidth + "x" + maxHeight); d.setBounds(0, 0, maxWidth, maxHeight); for (int i = 0; i < d.getStateCount(); i++) { Drawable childDrawable = d.getStateDrawable(i); - Log.v(TAG, "sizing drawable " + childDrawable + " to: " + maxWidth + "x" + maxHeight); + if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: " + + maxWidth + "x" + maxHeight); childDrawable.setBounds(0, 0, maxWidth, maxHeight); } } else if (mDrawable != null) { diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java index afa92f1a9eed9..9629702bbb1f0 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java @@ -43,7 +43,7 @@ import android.widget.TextView; * */ class KeyguardStatusViewManager implements OnClickListener { - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final String TAG = "KeyguardStatusView"; public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock; diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 431f8e0bf1150..56611169899b4 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -116,6 +116,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private static final int KEYGUARD_DONE_AUTHENTICATING = 11; private static final int SET_HIDDEN = 12; private static final int KEYGUARD_TIMEOUT = 13; + private static final int REPORT_SHOW_DONE = 14; /** * The default amount of time we stay awake (used for all key input) @@ -238,6 +239,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private boolean mScreenOn = false; + private boolean mShowPending = false; + // last known state of the cellular connection private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE; @@ -306,7 +309,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, synchronized (this) { if (DEBUG) Log.d(TAG, "onSystemReady"); mSystemReady = true; - doKeyguard(); + doKeyguardLocked(); } } @@ -363,7 +366,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, if (timeout <= 0) { // Lock now mSuppressNextLockSound = true; - doKeyguard(); + doKeyguardLocked(); } else { // Lock in the future long when = SystemClock.elapsedRealtime() + timeout; @@ -379,7 +382,19 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) { // Do not enable the keyguard if the prox sensor forced the screen off. } else { - doKeyguard(); + if (!doKeyguardLocked() && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) { + // The user has explicitly turned off the screen, causing it + // to lock. We want to block here until the keyguard window + // has shown, so the power manager won't complete the screen + // off flow until that point, so we know it won't turn *on* + // the screen until this is done. + while (mShowPending) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } } } } @@ -553,56 +568,58 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } /** - * Enable the keyguard if the settings are appropriate. + * Enable the keyguard if the settings are appropriate. Return true if all + * work that will happen is done; returns false if the caller can wait for + * the keyguard to be shown. */ - private void doKeyguard() { - synchronized (this) { - // if another app is disabling us, don't show - if (!mExternallyEnabled) { - if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); + private boolean doKeyguardLocked() { + // if another app is disabling us, don't show + if (!mExternallyEnabled) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); - // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes - // for an occasional ugly flicker in this situation: - // 1) receive a call with the screen on (no keyguard) or make a call - // 2) screen times out - // 3) user hits key to turn screen back on - // instead, we reenable the keyguard when we know the screen is off and the call - // ends (see the broadcast receiver below) - // TODO: clean this up when we have better support at the window manager level - // for apps that wish to be on top of the keyguard - return; - } - - // if the keyguard is already showing, don't bother - if (mKeyguardViewManager.isShowing()) { - if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing"); - return; - } - - // if the setup wizard hasn't run yet, don't show - final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", - false); - final boolean provisioned = mUpdateMonitor.isDeviceProvisioned(); - final IccCard.State state = mUpdateMonitor.getSimState(); - final boolean lockedOrMissing = state.isPinLocked() - || ((state == IccCard.State.ABSENT - || state == IccCard.State.PERM_DISABLED) - && requireSim); - - if (!lockedOrMissing && !provisioned) { - if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned" - + " and the sim is not locked or missing"); - return; - } - - if (mLockPatternUtils.isLockScreenDisabled()) { - if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); - return; - } - - if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen"); - showLocked(); + // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes + // for an occasional ugly flicker in this situation: + // 1) receive a call with the screen on (no keyguard) or make a call + // 2) screen times out + // 3) user hits key to turn screen back on + // instead, we reenable the keyguard when we know the screen is off and the call + // ends (see the broadcast receiver below) + // TODO: clean this up when we have better support at the window manager level + // for apps that wish to be on top of the keyguard + return true; } + + // if the keyguard is already showing, don't bother + if (mKeyguardViewManager.isShowing()) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing"); + return true; + } + + // if the setup wizard hasn't run yet, don't show + final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", + false); + final boolean provisioned = mUpdateMonitor.isDeviceProvisioned(); + final IccCard.State state = mUpdateMonitor.getSimState(); + final boolean lockedOrMissing = state.isPinLocked() + || ((state == IccCard.State.ABSENT + || state == IccCard.State.PERM_DISABLED) + && requireSim); + + if (!lockedOrMissing && !provisioned) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned" + + " and the sim is not locked or missing"); + return true; + } + + if (mLockPatternUtils.isLockScreenDisabled()) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); + return true; + } + + if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen"); + mShowPending = true; + showLocked(); + return false; } /** @@ -696,41 +713,49 @@ public class KeyguardViewMediator implements KeyguardViewCallback, case ABSENT: // only force lock screen in case of missing sim if user hasn't // gone through setup wizard - if (!mUpdateMonitor.isDeviceProvisioned()) { - if (!isShowing()) { - if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing," - + " we need to show the keyguard since the " - + "device isn't provisioned yet."); - doKeyguard(); - } else { - resetStateLocked(); + synchronized (this) { + if (!mUpdateMonitor.isDeviceProvisioned()) { + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing," + + " we need to show the keyguard since the " + + "device isn't provisioned yet."); + doKeyguardLocked(); + } else { + resetStateLocked(); + } } } break; case PIN_REQUIRED: case PUK_REQUIRED: - if (!isShowing()) { - if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need " - + "to show the keyguard so the user can enter their sim pin"); - doKeyguard(); - } else { - resetStateLocked(); + synchronized (this) { + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need " + + "to show the keyguard so the user can enter their sim pin"); + doKeyguardLocked(); + } else { + resetStateLocked(); + } } break; case PERM_DISABLED: - if (!isShowing()) { - if (DEBUG) Log.d(TAG, "PERM_DISABLED and " - + "keygaurd isn't showing."); - doKeyguard(); - } else { - if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" - + "show permanently disabled message in lockscreen."); - resetStateLocked(); + synchronized (this) { + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "PERM_DISABLED and " + + "keygaurd isn't showing."); + doKeyguardLocked(); + } else { + if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" + + "show permanently disabled message in lockscreen."); + resetStateLocked(); + } } break; case READY: - if (isShowing()) { - resetStateLocked(); + synchronized (this) { + if (isShowing()) { + resetStateLocked(); + } } break; } @@ -751,27 +776,31 @@ public class KeyguardViewMediator implements KeyguardViewCallback, if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = " + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence); - if (mDelayedShowingSequence == sequence) { - // Don't play lockscreen SFX if the screen went off due to - // timeout. - mSuppressNextLockSound = true; - - doKeyguard(); + synchronized (KeyguardViewMediator.this) { + if (mDelayedShowingSequence == sequence) { + // Don't play lockscreen SFX if the screen went off due to + // timeout. + mSuppressNextLockSound = true; + + doKeyguardLocked(); + } } } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { mPhoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE); - if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState) // call ending - && !mScreenOn // screen off - && mExternallyEnabled) { // not disabled by any app - - // note: this is a way to gracefully reenable the keyguard when the call - // ends and the screen is off without always reenabling the keyguard - // each time the screen turns off while in call (and having an occasional ugly - // flicker while turning back on the screen and disabling the keyguard again). - if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the " - + "keyguard is showing"); - doKeyguard(); + synchronized (KeyguardViewMediator.this) { + if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState) // call ending + && !mScreenOn // screen off + && mExternallyEnabled) { // not disabled by any app + + // note: this is a way to gracefully reenable the keyguard when the call + // ends and the screen is off without always reenabling the keyguard + // each time the screen turns off while in call (and having an occasional ugly + // flicker while turning back on the screen and disabling the keyguard again). + if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the " + + "keyguard is showing"); + doKeyguardLocked(); + } } } } @@ -962,7 +991,15 @@ public class KeyguardViewMediator implements KeyguardViewCallback, handleSetHidden(msg.arg1 != 0); break; case KEYGUARD_TIMEOUT: - doKeyguard(); + synchronized (KeyguardViewMediator.this) { + doKeyguardLocked(); + } + break; + case REPORT_SHOW_DONE: + synchronized (KeyguardViewMediator.this) { + mShowPending = false; + KeyguardViewMediator.this.notifyAll(); + } break; } } @@ -1062,8 +1099,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, if (DEBUG) Log.d(TAG, "handleShow"); if (!mSystemReady) return; - playSounds(true); - mKeyguardViewManager.show(); mShowing = true; adjustUserActivityLocked(); @@ -1072,7 +1107,17 @@ public class KeyguardViewMediator implements KeyguardViewCallback, ActivityManagerNative.getDefault().closeSystemDialogs("lock"); } catch (RemoteException e) { } + + // Do this at the end to not slow down display of the keyguard. + playSounds(true); + mShowKeyguardWakeLock.release(); + + // We won't say the show is done yet because the view hierarchy + // still needs to do the traversal. Posting this message allows + // us to hold off until that is done. + Message msg = mHandler.obtainMessage(REPORT_SHOW_DONE); + mHandler.sendMessage(msg); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 1d5fbc0acec73..be129a8eef85b 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -120,6 +120,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_POINTER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; import android.view.KeyCharacterMap.FallbackAction; @@ -197,8 +198,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // things in here CAN NOT take focus, but are shown on top of everything else. static final int SYSTEM_OVERLAY_LAYER = 20; static final int SECURE_SYSTEM_OVERLAY_LAYER = 21; + static final int BOOT_PROGRESS_LAYER = 22; // the (mouse) pointer layer - static final int POINTER_LAYER = 22; + static final int POINTER_LAYER = 23; static final int APPLICATION_MEDIA_SUBLAYER = -2; static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; @@ -1095,6 +1097,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return POINTER_LAYER; case TYPE_NAVIGATION_BAR: return NAVIGATION_BAR_LAYER; + case TYPE_BOOT_PROGRESS: + return BOOT_PROGRESS_LAYER; } Log.e(TAG, "Unknown window type: " + type); return APPLICATION_LAYER; @@ -2797,13 +2801,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ public void screenTurnedOff(int why) { EventLog.writeEvent(70000, 0); - mKeyguardMediator.onScreenTurnedOff(why); synchronized (mLock) { mScreenOn = false; + } + mKeyguardMediator.onScreenTurnedOff(why); + synchronized (mLock) { updateOrientationListenerLp(); updateLockScreenTimeout(); updateScreenSaverTimeoutLocked(); } + try { + mWindowManager.waitForAllDrawn(); + } catch (RemoteException e) { + } + // Wait for one frame to give surface flinger time to do its + // compositing. Yes this is a hack, but I am really not up right now for + // implementing some mechanism to block until SF is done. :p + try { + Thread.sleep(20); + } catch (InterruptedException e) { + } } /** {@inheritDoc} */ @@ -3092,7 +3109,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); mBootMsgDialog.setIndeterminate(true); mBootMsgDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY); + WindowManager.LayoutParams.TYPE_BOOT_PROGRESS); mBootMsgDialog.getWindow().addFlags( WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index d80a2cd41c2db..cbd986ff35fb0 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -161,6 +161,7 @@ public class PowerManagerService extends IPowerManager.Stub private int mStayOnConditions = 0; private final int[] mBroadcastQueue = new int[] { -1, -1, -1 }; private final int[] mBroadcastWhy = new int[3]; + private boolean mBroadcastingScreenOff = false; private int mPartialCount = 0; private int mPowerState; // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER, @@ -1342,6 +1343,10 @@ public class PowerManagerService extends IPowerManager.Stub mBroadcastWakeLock.release(); } + // The broadcast queue has changed; make sure the screen is on if it + // is now possible for it to be. + updateNativePowerStateLocked(); + // Now send the message. if (index >= 0) { // Acquire the broadcast wake lock before changing the power @@ -1370,6 +1375,9 @@ public class PowerManagerService extends IPowerManager.Stub mBroadcastWhy[i] = mBroadcastWhy[i+1]; } policy = getPolicyLocked(); + if (value == 0) { + mBroadcastingScreenOff = true; + } } if (value == 1) { mScreenOnStart = SystemClock.uptimeMillis(); @@ -1412,6 +1420,8 @@ public class PowerManagerService extends IPowerManager.Stub synchronized (mLocks) { EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, mBroadcastWakeLock.mCount); + mBroadcastingScreenOff = false; + updateNativePowerStateLocked(); mBroadcastWakeLock.release(); } } @@ -1442,6 +1452,10 @@ public class PowerManagerService extends IPowerManager.Stub synchronized (mLocks) { EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount); + synchronized (mLocks) { + mBroadcastingScreenOff = false; + updateNativePowerStateLocked(); + } mBroadcastWakeLock.release(); } } @@ -1768,6 +1782,22 @@ public class PowerManagerService extends IPowerManager.Stub } private void updateNativePowerStateLocked() { + if ((mPowerState & SCREEN_ON_BIT) != 0) { + // Don't turn screen on if we are currently reporting a screen off. + // This is to avoid letting the screen go on before things like the + // lock screen have been displayed due to it going off. + if (mBroadcastingScreenOff) { + // Currently broadcasting that the screen is off. Don't + // allow screen to go on until that is done. + return; + } + for (int i=0; i