diff options
Diffstat (limited to 'policy/src')
10 files changed, 333 insertions, 206 deletions
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 8569143..11b6c15 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -18,7 +18,6 @@ package com.android.internal.policy.impl; import android.app.Activity; import android.app.AlertDialog; -import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -57,8 +56,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private static final String TAG = "GlobalActions"; - private StatusBarManager mStatusBar; - private final Context mContext; private final AudioManager mAudioManager; @@ -103,13 +100,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac mKeyguardShowing = keyguardShowing; mDeviceProvisioned = isDeviceProvisioned; if (mDialog == null) { - mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE); mDialog = createDialog(); } prepareDialog(); - mStatusBar.disable(StatusBarManager.DISABLE_EXPAND); mDialog.show(); + mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND); } /** @@ -249,7 +245,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac /** {@inheritDoc} */ public void onDismiss(DialogInterface dialog) { - mStatusBar.disable(StatusBarManager.DISABLE_NONE); } /** {@inheritDoc} */ diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java index 86807ad..6614d79 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java @@ -186,6 +186,9 @@ class KeyguardStatusViewManager implements OnClickListener { mTransientTextManager = new TransientTextManager(mCarrierView); + mUpdateMonitor.registerInfoCallback(mInfoCallback); + mUpdateMonitor.registerSimStateCallback(mSimStateCallback); + resetStatusInfo(); refreshDate(); updateOwnerInfo(); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java index 303a3b5..2d8185b 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java @@ -226,7 +226,7 @@ public class KeyguardUpdateMonitor { // take a guess to start mSimState = IccCard.State.READY; - mBatteryStatus = BATTERY_STATUS_FULL; + mBatteryStatus = BATTERY_STATUS_UNKNOWN; mBatteryLevel = 100; mTelephonyPlmn = getDefaultPlmn(); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java index 59b546d..de156c9 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java @@ -50,8 +50,6 @@ public abstract class KeyguardViewBase extends FrameLayout { public KeyguardViewBase(Context context) { super(context); - setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); - // This is a faster way to draw the background on devices without hardware acceleration setBackgroundDrawable(new Drawable() { @Override diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java index 90972da..2fd165a 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java @@ -171,6 +171,17 @@ public class KeyguardViewManager implements KeyguardWindowController { } } + // Disable aspects of the system/status/navigation bars that are not appropriate or + // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities. + // Other disabled bits are handled by the KeyguardViewMediator talking directly to the + // status bar service. + int visFlags = + ( View.STATUS_BAR_DISABLE_BACK + | View.STATUS_BAR_DISABLE_HOME + | View.STATUS_BAR_DISABLE_CLOCK + ); + mKeyguardHost.setSystemUiVisibility(visFlags); + mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.setVisibility(View.VISIBLE); mKeyguardView.requestFocus(); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index f1b6ef9..0471dfe 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -1188,19 +1188,12 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } } + // Disable aspects of the system/status/navigation bars that must not be re-enabled by + // windows that appear on top, ever int flags = StatusBarManager.DISABLE_NONE; if (mShowing) { - // disable navigation status bar components if lock screen is up - flags |= StatusBarManager.DISABLE_NAVIGATION; - if (!mHidden) { - // showing lockscreen exclusively (no activities in front of it) - // disable back button too - flags |= StatusBarManager.DISABLE_BACK; - if (mUpdateMonitor.isClockVisible()) { - // lockscreen showing a clock, so hide statusbar clock - flags |= StatusBarManager.DISABLE_CLOCK; - } - } + // disable navigation status bar components (home, recents) if lock screen is up + flags |= StatusBarManager.DISABLE_RECENT; if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) { // showing secure lockscreen; disable expanding. flags |= StatusBarManager.DISABLE_EXPAND; diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 65cdd32..d1bb8d1 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -98,7 +98,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler private View mLockScreen; private View mUnlockScreen; - private boolean mScreenOn = false; + private volatile boolean mScreenOn = false; + private volatile boolean mWindowFocused = false; private boolean mEnableFallback = false; // assume no fallback UI until we know better private boolean mShowLockBeforeUnlock = false; @@ -110,14 +111,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler private boolean mFaceLockServiceRunning = false; private final Object mFaceLockServiceRunningLock = new Object(); + private final Object mFaceLockStartupLock = new Object(); private Handler mHandler; private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0; private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1; - // Long enough to stay black while dialer comes up - // Short enough to not be black if the user goes back immediately - private final int FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT = 1000; + // Long enough to stay visible while dialer comes up + // Short enough to not be visible if the user goes back immediately + private final int FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; + + // Long enough to stay visible while the service starts + // Short enough to not have to wait long for backup if service fails to start or crashes + // The service can take a couple of seconds to start on the first try after boot + private final int FACELOCK_VIEW_AREA_SERVICE_TIMEOUT = 3000; /** * The current {@link KeyguardScreen} will use this to communicate back to us. @@ -126,6 +133,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler private boolean mRequiresSim; + private volatile boolean mEmergencyCall; /** @@ -266,6 +274,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mUpdateMonitor = updateMonitor; mLockPatternUtils = lockPatternUtils; mWindowController = controller; + mEmergencyCall = false; mUpdateMonitor.registerInfoCallback(this); @@ -320,12 +329,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler } public void takeEmergencyCallAction() { + mEmergencyCall = true; // FaceLock must be stopped if it is running when emergency call is pressed stopAndUnbindFromFaceLock(); - // Delay hiding FaceLock area so unlock doesn't display while dialer is coming up - mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, - FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT); + // Continue showing FaceLock area until dialer comes up + showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT); pokeWakelock(EMERGENCY_CALL_TIMEOUT); if (TelephonyManager.getDefault().getCallState() @@ -504,6 +513,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler public void onScreenTurnedOff() { mScreenOn = false; mForgotPattern = false; + mEmergencyCall = false; if (mMode == Mode.LockScreen) { ((KeyguardScreen) mLockScreen).onPause(); } else { @@ -514,32 +524,56 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler stopAndUnbindFromFaceLock(); } - @Override - public void onScreenTurnedOn() { - mScreenOn = true; - show(); - - // When screen is turned on, need to bind to FaceLock service if we are using FaceLock - // But only if not dealing with a call + /** When screen is turned on and focused, need to bind to FaceLock service if we are using + * FaceLock, but only if we're not dealing with a call + */ + private void activateFaceLockIfAble() { final boolean transportInvisible = mTransportControlView == null ? true : mTransportControlView.getVisibility() != View.VISIBLE; if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE && transportInvisible) { bindToFaceLock(); - //Eliminate the black background so that the lockpattern will be visible - //If FaceUnlock is cancelled - mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, 4000); + // Show FaceLock area, but only for a little bit so lockpattern will become visible if + // FaceLock fails to start or crashes + showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT); } else { - mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); + hideFaceLockArea(); } } - /** Unbind from facelock if something covers this window (such as an alarm) */ + @Override + public void onScreenTurnedOn() { + boolean runFaceLock = false; + //Make sure to start facelock iff the screen is both on and focused + synchronized(mFaceLockStartupLock) { + mScreenOn = true; + runFaceLock = mWindowFocused; + } + show(); + if(runFaceLock) activateFaceLockIfAble(); + } + + /** Unbind from facelock if something covers this window (such as an alarm) + * bind to facelock if the lockscreen window just came into focus, and the screen is on + */ @Override public void onWindowFocusChanged (boolean hasWindowFocus) { + if(DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused"); + boolean runFaceLock = false; + //Make sure to start facelock iff the screen is both on and focused + synchronized(mFaceLockStartupLock) { + if(mScreenOn && !mWindowFocused) runFaceLock = hasWindowFocus; + mWindowFocused = hasWindowFocus; + } if(!hasWindowFocus) { stopAndUnbindFromFaceLock(); - mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); + hideFaceLockArea(); + } else if (runFaceLock) { + //Don't activate facelock while the user is calling 911! + if(mEmergencyCall) mEmergencyCall = false; + else { + activateFaceLockIfAble(); + } } } @@ -553,9 +587,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler if (mLockPatternUtils.usingBiometricWeak() && mLockPatternUtils.isBiometricWeakInstalled()) { - mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW); + showFaceLockArea(); } else { - mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); + hideFaceLockArea(); } } @@ -621,9 +655,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler //We need to stop faceunlock when a phonecall comes in @Override public void onPhoneStateChanged(int phoneState) { + if (DEBUG) Log.d(TAG, "phone state: " + phoneState); if(phoneState == TelephonyManager.CALL_STATE_RINGING) { stopAndUnbindFromFaceLock(); - mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); + hideFaceLockArea(); } } @@ -1056,6 +1091,32 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler return true; } + // Removes show and hide messages from the message queue + private void removeFaceLockAreaDisplayMessages() { + mHandler.removeMessages(MSG_SHOW_FACELOCK_AREA_VIEW); + mHandler.removeMessages(MSG_HIDE_FACELOCK_AREA_VIEW); + } + + // Shows the FaceLock area immediately + private void showFaceLockArea() { + // Remove messages to prevent a delayed hide message from undo-ing the show + removeFaceLockAreaDisplayMessages(); + mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW); + } + + // Hides the FaceLock area immediately + private void hideFaceLockArea() { + // Remove messages to prevent a delayed show message from undo-ing the hide + removeFaceLockAreaDisplayMessages(); + mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); + } + + // Shows the FaceLock area for a period of time + private void showFaceLockAreaWithTimeout(long timeoutMillis) { + showFaceLockArea(); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, timeoutMillis); + } + // Binds to FaceLock service, but does not tell it to start public void bindToFaceLock() { if (mLockPatternUtils.usingBiometricWeak() && @@ -1101,7 +1162,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler try { mFaceLockService.registerCallback(mFaceLockCallback); } catch (RemoteException e) { - throw new RuntimeException("Remote exception"); + Log.e(TAG, "Caught exception connecting to FaceLock: " + e.toString()); + mFaceLockService = null; + mBoundToFaceLockService = false; + return; } if (mFaceLockAreaView != null) { @@ -1118,6 +1182,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mFaceLockService = null; mFaceLockServiceRunning = false; } + mBoundToFaceLockService = false; Log.w(TAG, "Unexpected disconnect from FaceLock service"); } }; @@ -1133,7 +1198,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler try { mFaceLockService.startUi(windowToken, x, y, h, w); } catch (RemoteException e) { - throw new RuntimeException("Remote exception"); + Log.e(TAG, "Caught exception starting FaceLock: " + e.toString()); + return; } mFaceLockServiceRunning = true; } else { @@ -1157,7 +1223,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler if (DEBUG) Log.d(TAG, "Stopping FaceLock"); mFaceLockService.stopUi(); } catch (RemoteException e) { - throw new RuntimeException("Remote exception"); + Log.e(TAG, "Caught exception stopping FaceLock: " + e.toString()); } mFaceLockServiceRunning = false; } @@ -1172,7 +1238,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler @Override public void unlock() { if (DEBUG) Log.d(TAG, "FaceLock unlock()"); - mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW); // Keep fallback covered + showFaceLockArea(); // Keep fallback covered stopFaceLock(); mKeyguardScreenCallback.keyguardDone(true); @@ -1184,7 +1250,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler @Override public void cancel() { if (DEBUG) Log.d(TAG, "FaceLock cancel()"); - mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); // Expose fallback + hideFaceLockArea(); // Expose fallback stopFaceLock(); } diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index ec0072c..3ad716b 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -28,6 +28,7 @@ import com.android.internal.widget.PasswordEntryKeyboardView; import android.os.CountDownTimer; import android.os.SystemClock; +import android.provider.Settings; import android.security.KeyStore; import android.text.Editable; import android.text.InputType; @@ -109,6 +110,10 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen mPasswordEntry.setOnEditorActionListener(this); mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false); + mKeyboardHelper.setEnableHaptics( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, 0) + != 0); if (mIsAlpha) { // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA); @@ -150,9 +155,6 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen //KeyguardStatusViewManager.LOCK_ICON); } - mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ? - com.android.internal.R.array.config_virtualKeyVibePattern : 0); - // Poke the wakelock any time the text is selected or modified mPasswordEntry.setOnClickListener(new OnClickListener() { public void onClick(View v) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 2cd6eab..af86ae9 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -349,8 +349,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } // Already prepared (isPrepared will be reset to false later) - if (st.isPrepared) + if (st.isPrepared) { return true; + } if ((mPreparedPanel != null) && (mPreparedPanel != st)) { // Another Panel is prepared and possibly open, so close it @@ -800,14 +801,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { closePanel(st, true); } else if (st.isPrepared) { + boolean show = true; + if (st.refreshMenuContent) { + // Something may have invalidated the menu since we prepared it. + // Re-prepare it to refresh. + st.isPrepared = false; + show = preparePanel(st, event); + } - // Write 'menu opened' to event log - EventLog.writeEvent(50001, 0); + if (show) { + // Write 'menu opened' to event log + EventLog.writeEvent(50001, 0); - // Show menu - openPanel(st, event); + // Show menu + openPanel(st, event); - playSoundEffect = true; + playSoundEffect = true; + } } } @@ -1084,8 +1094,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } MenuView menuView = st.isInListMode() - ? st.getListMenuView(mPanelMenuPresenterCallback) - : st.getIconMenuView(mPanelMenuPresenterCallback); + ? st.getListMenuView(getContext(), mPanelMenuPresenterCallback) + : st.getIconMenuView(getContext(), mPanelMenuPresenterCallback); st.shownPanelView = (View) menuView; @@ -3251,11 +3261,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - MenuView getListMenuView(MenuPresenter.Callback cb) { + MenuView getListMenuView(Context context, MenuPresenter.Callback cb) { if (menu == null) return null; if (!isCompact) { - getIconMenuView(cb); // Need this initialized to know where our offset goes + getIconMenuView(context, cb); // Need this initialized to know where our offset goes } if (listMenuPresenter == null) { @@ -3275,11 +3285,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return result; } - MenuView getIconMenuView(MenuPresenter.Callback cb) { + MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) { if (menu == null) return null; if (iconMenuPresenter == null) { - iconMenuPresenter = new IconMenuPresenter(); + iconMenuPresenter = new IconMenuPresenter(context); iconMenuPresenter.setCallback(cb); iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter); menu.addMenuPresenter(iconMenuPresenter); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 487063d..aac3209 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -128,7 +128,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; import android.view.KeyCharacterMap.FallbackAction; -import android.view.WindowManagerPolicy.WindowManagerFuncs; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -180,26 +179,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int PRIORITY_PHONE_LAYER = 7; // like the ANR / app crashed dialogs static final int SYSTEM_ALERT_LAYER = 8; - // system-level error dialogs - static final int SYSTEM_ERROR_LAYER = 9; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_LAYER = 10; + static final int INPUT_METHOD_LAYER = 9; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_DIALOG_LAYER = 11; + static final int INPUT_METHOD_DIALOG_LAYER = 10; // the keyguard; nothing on top of these can take focus, since they are // responsible for power management when displayed. - static final int KEYGUARD_LAYER = 12; - static final int KEYGUARD_DIALOG_LAYER = 13; - static final int STATUS_BAR_SUB_PANEL_LAYER = 14; - static final int STATUS_BAR_LAYER = 15; - static final int STATUS_BAR_PANEL_LAYER = 16; + static final int KEYGUARD_LAYER = 11; + static final int KEYGUARD_DIALOG_LAYER = 12; + static final int STATUS_BAR_SUB_PANEL_LAYER = 13; + static final int STATUS_BAR_LAYER = 14; + static final int STATUS_BAR_PANEL_LAYER = 15; // the on-screen volume indicator and controller shown when the user // changes the device volume - static final int VOLUME_OVERLAY_LAYER = 17; + static final int VOLUME_OVERLAY_LAYER = 16; // things in here CAN NOT take focus, but are shown on top of everything else. - static final int SYSTEM_OVERLAY_LAYER = 18; + static final int SYSTEM_OVERLAY_LAYER = 17; // the navigation bar, if available, shows atop most things - static final int NAVIGATION_BAR_LAYER = 19; + static final int NAVIGATION_BAR_LAYER = 18; + // system-level error dialogs + static final int SYSTEM_ERROR_LAYER = 19; // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows static final int DRAG_LAYER = 20; @@ -267,7 +266,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mKeyguard = null; KeyguardViewMediator mKeyguardMediator; GlobalActions mGlobalActions; - volatile boolean mPowerKeyHandled; + volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread + boolean mPendingPowerKeyUpCanceled; RecentApplicationsDialog mRecentAppsDialog; Handler mHandler; @@ -403,8 +403,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int mLongPressOnHomeBehavior = -1; // Screenshot trigger states - private boolean mVolumeDownTriggered; - private boolean mPowerDownTriggered; + // Time to volume and power must be pressed within this interval of each other. + private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150; + private boolean mVolumeDownKeyTriggered; + private long mVolumeDownKeyTime; + private boolean mVolumeDownKeyConsumedByScreenshotChord; + private boolean mVolumeUpKeyTriggered; + private boolean mPowerKeyTriggered; + private long mPowerKeyTime; ShortcutManager mShortcutManager; PowerManager.WakeLock mBroadcastWakeLock; @@ -552,40 +558,67 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!mPowerKeyHandled) { mHandler.removeCallbacks(mPowerLongPress); return !canceled; - } else { - mPowerKeyHandled = true; - return false; } + return false; + } + + private void cancelPendingPowerKeyAction() { + if (!mPowerKeyHandled) { + mHandler.removeCallbacks(mPowerLongPress); + } + mPendingPowerKeyUpCanceled = true; + } + + private void interceptScreenshotChord() { + if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) { + final long now = SystemClock.uptimeMillis(); + if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS + && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { + mVolumeDownKeyConsumedByScreenshotChord = true; + cancelPendingPowerKeyAction(); + + mHandler.postDelayed(mScreenshotChordLongPress, + ViewConfiguration.getGlobalActionKeyTimeout()); + } + } + } + + private void cancelPendingScreenshotChordAction() { + mHandler.removeCallbacks(mScreenshotChordLongPress); } private final Runnable mPowerLongPress = new Runnable() { public void run() { - if (!mPowerKeyHandled) { - // The context isn't read - if (mLongPressOnPowerBehavior < 0) { - mLongPressOnPowerBehavior = mContext.getResources().getInteger( - com.android.internal.R.integer.config_longPressOnPowerBehavior); - } - switch (mLongPressOnPowerBehavior) { - case LONG_PRESS_POWER_NOTHING: - break; - case LONG_PRESS_POWER_GLOBAL_ACTIONS: - mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - showGlobalActionsDialog(); - break; - case LONG_PRESS_POWER_SHUT_OFF: - mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - ShutdownThread.shutdown(mContext, true); - break; - } + // The context isn't read + if (mLongPressOnPowerBehavior < 0) { + mLongPressOnPowerBehavior = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longPressOnPowerBehavior); + } + switch (mLongPressOnPowerBehavior) { + case LONG_PRESS_POWER_NOTHING: + break; + case LONG_PRESS_POWER_GLOBAL_ACTIONS: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + showGlobalActionsDialog(); + break; + case LONG_PRESS_POWER_SHUT_OFF: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + ShutdownThread.shutdown(mContext, true); + break; } } }; + private final Runnable mScreenshotChordLongPress = new Runnable() { + public void run() { + takeScreenshot(); + } + }; + void showGlobalActionsDialog() { if (mGlobalActions == null) { mGlobalActions = new GlobalActions(mContext); @@ -1381,11 +1414,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { + public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { final boolean keyguardOn = keyguardOn(); final int keyCode = event.getKeyCode(); final int repeatCount = event.getRepeatCount(); final int metaState = event.getMetaState(); + final int flags = event.getFlags(); final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); @@ -1394,6 +1428,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed); } + // If we think we might have a volume down & power key chord on the way + // but we're not sure, then tell the dispatcher to wait a little while and + // try again later before dispatching. + if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { + if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) { + final long now = SystemClock.uptimeMillis(); + final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; + if (now < timeoutTime) { + return timeoutTime - now; + } + } + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + && mVolumeDownKeyConsumedByScreenshotChord) { + if (!down) { + mVolumeDownKeyConsumedByScreenshotChord = false; + } + return -1; + } + } + // First we always handle the home key here, so applications // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second @@ -1425,7 +1479,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { Log.i(TAG, "Ignoring HOME; event canceled."); } - return true; + return -1; } // If a system window has focus, then it doesn't make sense @@ -1436,13 +1490,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (type == WindowManager.LayoutParams.TYPE_KEYGUARD || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { // the "app" is keyguard, so give it the key - return false; + return 0; } final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length; for (int i=0; i<typeCount; i++) { if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) { // don't do anything, but also don't pass it to the app - return true; + return -1; } } } @@ -1456,7 +1510,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - return true; + return -1; } else if (keyCode == KeyEvent.KEYCODE_MENU) { // Hijack modified menu keys for debugging features final int chordBug = KeyEvent.META_SHIFT_ON; @@ -1465,7 +1519,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { Intent intent = new Intent(Intent.ACTION_BUG_REPORT); mContext.sendOrderedBroadcast(intent, null); - return true; + return -1; } else if (SHOW_PROCESSES_ON_ALT_MENU && (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) { Intent service = new Intent(); @@ -1480,7 +1534,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } Settings.System.putInt( res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1); - return true; + return -1; } } } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { @@ -1493,15 +1547,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mShortcutKeyPressed = -1; if (mConsumeShortcutKeyUp) { mConsumeShortcutKeyUp = false; - return true; + return -1; } } - return false; + return 0; } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { if (down && repeatCount == 0) { showOrHideRecentAppsDialog(0, true /*dismissIfShown*/); } - return true; + return -1; } // Shortcuts are invoked through Search+key, so intercept those here @@ -1531,11 +1585,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "+" + KeyEvent.keyCodeToString(keyCode)); } } - return true; + return -1; } } - return false; + return 0; } /** {@inheritDoc} */ @@ -1606,7 +1660,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { flags, event.getSource(), null); int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true); if ((actions & ACTION_PASS_TO_USER) != 0) { - if (!interceptKeyBeforeDispatching(win, fallbackEvent, policyFlags)) { + long delayMillis = interceptKeyBeforeDispatching( + win, fallbackEvent, policyFlags); + if (delayMillis == 0) { if (DEBUG_FALLBACK) { Slog.d(TAG, "Performing fallback."); } @@ -2472,76 +2528,65 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Object mScreenshotLock = new Object(); ServiceConnection mScreenshotConnection = null; - Runnable mScreenshotTimeout = null; - void finishScreenshotLSS(ServiceConnection conn) { - if (mScreenshotConnection == conn) { - mContext.unbindService(conn); - mScreenshotConnection = null; - if (mScreenshotTimeout != null) { - mHandler.removeCallbacks(mScreenshotTimeout); - mScreenshotTimeout = null; + final Runnable mScreenshotTimeout = new Runnable() { + @Override public void run() { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + } } } - } + }; + // Assume this is called from the Handler thread. private void takeScreenshot() { - mHandler.post(new Runnable() { - @Override - public void run() { - synchronized (mScreenshotLock) { - if (mScreenshotConnection != null) { - return; - } - ComponentName cn = new ComponentName("com.android.systemui", - "com.android.systemui.screenshot.TakeScreenshotService"); - Intent intent = new Intent(); - intent.setComponent(cn); - ServiceConnection conn = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - synchronized (mScreenshotLock) { - if (mScreenshotConnection != this) { - return; - } - Messenger messenger = new Messenger(service); - Message msg = Message.obtain(null, 1); - final ServiceConnection myConn = this; - Handler h = new Handler(mHandler.getLooper()) { - @Override - public void handleMessage(Message msg) { - synchronized (mScreenshotLock) { - finishScreenshotLSS(myConn); - } - } - }; - msg.replyTo = new Messenger(h); - try { - messenger.send(msg); - } catch (RemoteException e) { - } - } + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + return; + } + ComponentName cn = new ComponentName("com.android.systemui", + "com.android.systemui.screenshot.TakeScreenshotService"); + Intent intent = new Intent(); + intent.setComponent(cn); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != this) { + return; } - @Override - public void onServiceDisconnected(ComponentName name) {} - }; - if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { - mScreenshotConnection = conn; - mScreenshotTimeout = new Runnable() { - @Override public void run() { + Messenger messenger = new Messenger(service); + Message msg = Message.obtain(null, 1); + final ServiceConnection myConn = this; + Handler h = new Handler(mHandler.getLooper()) { + @Override + public void handleMessage(Message msg) { synchronized (mScreenshotLock) { - if (mScreenshotConnection != null) { - finishScreenshotLSS(mScreenshotConnection); + if (mScreenshotConnection == myConn) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + mHandler.removeCallbacks(mScreenshotTimeout); } } } - }; - mHandler.postDelayed(mScreenshotTimeout, 10000); + msg.replyTo = new Messenger(h); + try { + messenger.send(msg); + } catch (RemoteException e) { + } } } + @Override + public void onServiceDisconnected(ComponentName name) {} + }; + if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { + mScreenshotConnection = conn; + mHandler.postDelayed(mScreenshotTimeout, 10000); } - }); + } } /** {@inheritDoc} */ @@ -2609,28 +2654,35 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: - if (down) { - if (isScreenOn) { - // If the power key down was already triggered, take the screenshot - if (mPowerDownTriggered) { - // Dismiss the power-key longpress - mHandler.removeCallbacks(mPowerLongPress); - mPowerKeyHandled = true; - - // Take the screenshot - takeScreenshot(); - - // Prevent the event from being passed through to the current activity - result &= ~ACTION_PASS_TO_USER; - break; + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_MUTE: { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + if (down) { + if (isScreenOn && !mVolumeDownKeyTriggered + && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { + mVolumeDownKeyTriggered = true; + mVolumeDownKeyTime = event.getDownTime(); + mVolumeDownKeyConsumedByScreenshotChord = false; + cancelPendingPowerKeyAction(); + interceptScreenshotChord(); + } + } else { + mVolumeDownKeyTriggered = false; + cancelPendingScreenshotChordAction(); + } + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + if (down) { + if (isScreenOn && !mVolumeUpKeyTriggered + && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { + mVolumeUpKeyTriggered = true; + cancelPendingPowerKeyAction(); + cancelPendingScreenshotChordAction(); } - mVolumeDownTriggered = true; + } else { + mVolumeUpKeyTriggered = false; + cancelPendingScreenshotChordAction(); } - } else { - mVolumeDownTriggered = false; } - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_MUTE: { if (down) { ITelephony telephonyService = getTelephonyService(); if (telephonyService != null) { @@ -2709,17 +2761,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { - if (isScreenOn) { - // If the volume down key has been triggered, then just take the screenshot - if (mVolumeDownTriggered) { - // Take the screenshot - takeScreenshot(); - mPowerKeyHandled = true; - - // Prevent the event from being passed through to the current activity - break; - } - mPowerDownTriggered = true; + if (isScreenOn && !mPowerKeyTriggered + && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { + mPowerKeyTriggered = true; + mPowerKeyTime = event.getDownTime(); + interceptScreenshotChord(); } ITelephony telephonyService = getTelephonyService(); @@ -2741,12 +2787,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Log.w(TAG, "ITelephony threw RemoteException", ex); } } - interceptPowerKeyDown(!isScreenOn || hungUp); + interceptPowerKeyDown(!isScreenOn || hungUp + || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered); } else { - mPowerDownTriggered = false; - if (interceptPowerKeyUp(canceled)) { + mPowerKeyTriggered = false; + cancelPendingScreenshotChordAction(); + if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) { result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; } + mPendingPowerKeyUpCanceled = false; } break; } |