diff options
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui')
10 files changed, 1909 insertions, 310 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index cc87735..c7d29f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -20,20 +20,20 @@ import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardServiceConstants; import com.android.internal.policy.IKeyguardShowCallback; -import com.android.systemui.statusbar.CommandQueue; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarWindowManager; -import android.app.ActivityManagerNative; import android.app.Service; -import android.app.StatusBarManager; -import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; -import android.os.RemoteException; import android.util.Log; import android.view.MotionEvent; +import android.view.ViewGroup; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -43,11 +43,12 @@ public class KeyguardService extends Service { public static final String ACTION_STATUS_BAR_BIND = "action.status_bar_bind"; - private CommandQueue mCommandQueue; - private StatusBarManager mStatusBarManager; + private KeyguardViewMediator mKeyguardViewMediator; @Override public void onCreate() { + LockPatternUtils lockPatternUtils = new LockPatternUtils(this); + mKeyguardViewMediator = new KeyguardViewMediator(this, lockPatternUtils); } @Override @@ -67,205 +68,158 @@ public class KeyguardService extends Service { } } - private final KeyguardStatusBarBinder mKeyguardStatusBarBinder = - new KeyguardStatusBarBinder() { + private final KeyguardStatusBarBinder mKeyguardStatusBarBinder = new KeyguardStatusBarBinder() { @Override - public void register(CommandQueue commandQueue) { - mCommandQueue = commandQueue; - } - - @Override - public void dismissKeyguard() { - try { - mBinder.dismiss(); - } catch (RemoteException e) { - Log.e(TAG, "Could not dismiss keyguard", e); - } + public void registerStatusBar(PhoneStatusBar phoneStatusBar, ViewGroup container, + StatusBarWindowManager statusBarWindowManager) { + mKeyguardViewMediator.registerStatusBar(phoneStatusBar, container, + statusBarWindowManager); } }; private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() { - /** Whether the Keyguard is visible. */ - private boolean mShowing; - - /** - * Whether the Keyguard is hidden by a window with - * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}. So mShowing might - * be true, but also mHidden. So in the end, the Keyguard would not be visible. - */ - private boolean mHidden; - private boolean mShowingDream; + private boolean mIsOccluded; @Override - public synchronized boolean isShowing() { - return mShowing; + public boolean isShowing() { + return mKeyguardViewMediator.isShowing(); } @Override - public synchronized boolean isSecure() { - return true; + public boolean isSecure() { + return mKeyguardViewMediator.isSecure(); } @Override - public synchronized boolean isShowingAndNotHidden() { - return mShowing && !mHidden; + public boolean isShowingAndNotOccluded() { + return mKeyguardViewMediator.isShowingAndNotOccluded(); } @Override - public synchronized boolean isInputRestricted() { - return false; + public boolean isInputRestricted() { + return mKeyguardViewMediator.isInputRestricted(); } @Override - public synchronized void verifyUnlock(IKeyguardExitCallback callback) { + public void verifyUnlock(IKeyguardExitCallback callback) { + checkPermission(); + mKeyguardViewMediator.verifyUnlock(callback); } @Override - public synchronized void keyguardDone(boolean authenticated, boolean wakeup) { + public void keyguardDone(boolean authenticated, boolean wakeup) { checkPermission(); - mShowing = false; - adjustStatusBarLocked(); - if (mCommandQueue != null) { - mCommandQueue.setKeyguardShown(false, null, true); - } + mKeyguardViewMediator.keyguardDone(authenticated, wakeup); } @Override - public synchronized int setHidden(boolean isHidden) { + public int setOccluded(boolean isOccluded) { checkPermission(); - if (mHidden == isHidden) { - return IKeyguardServiceConstants.KEYGUARD_SERVICE_HIDE_RESULT_NONE; - } - mHidden = isHidden; - try { - ActivityManagerNative.getDefault().setLockScreenShown(mShowing && !mHidden - || mShowingDream); - } catch (RemoteException e) { - Log.e(TAG, "Could not update activity manager lock screen state", e); - } - adjustStatusBarLocked(); - if (mCommandQueue != null) { - mCommandQueue.setKeyguardShown(!isHidden, null, false); + synchronized (this) { + int result; + if (isOccluded && mKeyguardViewMediator.isShowing() + && !mIsOccluded) { + result = IKeyguardServiceConstants + .KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS; + } else if (!isOccluded && mKeyguardViewMediator.isShowing() + && mIsOccluded) { + result = IKeyguardServiceConstants + .KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS; + } else { + result = IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE; + } + if (mIsOccluded != isOccluded) { + mKeyguardViewMediator.setOccluded(isOccluded); + + // Cache the value so we always have a fresh view in whether Keyguard is occluded. + // If we would just call mKeyguardViewMediator.isOccluded(), this might be stale + // because that value gets updated in another thread. + mIsOccluded = isOccluded; + } + return result; } - return isShowingAndNotHidden() - ? IKeyguardServiceConstants.KEYGUARD_SERVICE_HIDE_RESULT_SET_FLAGS - : IKeyguardServiceConstants.KEYGUARD_SERVICE_HIDE_RESULT_UNSET_FLAGS; } @Override - public synchronized void dismiss() { + public void dismiss() { checkPermission(); - mShowing = false; - adjustStatusBarLocked(); - if (mCommandQueue != null) { - mCommandQueue.setKeyguardShown(false, null, true); - } + mKeyguardViewMediator.dismiss(); } @Override - public synchronized void onDreamingStarted() { + public void onDreamingStarted() { checkPermission(); - mShowingDream = true; + mKeyguardViewMediator.onDreamingStarted(); } @Override - public synchronized void onDreamingStopped() { + public void onDreamingStopped() { checkPermission(); - mShowingDream = false; + mKeyguardViewMediator.onDreamingStopped(); } @Override - public synchronized void onScreenTurnedOff(int reason) { + public void onScreenTurnedOff(int reason) { checkPermission(); + mKeyguardViewMediator.onScreenTurnedOff(reason); } @Override - public synchronized void onScreenTurnedOn(IKeyguardShowCallback callback) { + public void onScreenTurnedOn(IKeyguardShowCallback callback) { checkPermission(); - mShowing = true; - adjustStatusBarLocked(); - if (mCommandQueue != null) { - mCommandQueue.setKeyguardShown(isShowingAndNotHidden(), callback, true); - } + mKeyguardViewMediator.onScreenTurnedOn(callback); } @Override - public synchronized void setKeyguardEnabled(boolean enabled) { + public void setKeyguardEnabled(boolean enabled) { checkPermission(); + mKeyguardViewMediator.setKeyguardEnabled(enabled); } @Override - public synchronized boolean isDismissable() { - return !isSecure(); + public boolean isDismissable() { + return mKeyguardViewMediator.isDismissable(); } @Override - public synchronized void onSystemReady() { + public void onSystemReady() { checkPermission(); + mKeyguardViewMediator.onSystemReady(); } @Override - public synchronized void doKeyguardTimeout(Bundle options) { + public void doKeyguardTimeout(Bundle options) { checkPermission(); + mKeyguardViewMediator.doKeyguardTimeout(options); } @Override - public synchronized void setCurrentUser(int userId) { + public void setCurrentUser(int userId) { checkPermission(); + mKeyguardViewMediator.setCurrentUser(userId); } @Override - public synchronized void showAssistant() { + public void showAssistant() { checkPermission(); } @Override - public synchronized void dispatch(MotionEvent event) { + public void dispatch(MotionEvent event) { checkPermission(); } @Override - public synchronized void launchCamera() { + public void launchCamera() { checkPermission(); } @Override - public synchronized void onBootCompleted() { + public void onBootCompleted() { checkPermission(); - onScreenTurnedOn(null); - } - - private void adjustStatusBarLocked() { - if (mStatusBarManager == null) { - mStatusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE); - } - if (mStatusBarManager == null) { - Log.w(TAG, "Could not get status bar manager"); - } else { - // 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 (isShowing()) { - // Permanently disable components not available when keyguard is enabled - // (like recents). Temporary enable/disable (e.g. the "back" button) are - // done in KeyguardHostView. - flags |= StatusBarManager.DISABLE_RECENT; - if (isSecure()) { - // showing secure lockscreen; disable ticker and switch private notifications - // to show their public versions, if available. - flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS; - } - if (false) { - flags |= StatusBarManager.DISABLE_SEARCH; - } - } - if (isShowingAndNotHidden()) { - flags |= StatusBarManager.DISABLE_HOME; - } - mStatusBarManager.disable(flags); - } + mKeyguardViewMediator.onBootCompleted(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardStatusBarBinder.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardStatusBarBinder.java index 566943b..04b6c29 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardStatusBarBinder.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardStatusBarBinder.java @@ -16,17 +16,18 @@ package com.android.systemui.keyguard; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarWindowManager; import android.os.Binder; -import android.os.IBinder; +import android.view.ViewGroup; /** * Communication interface from status bar to {@link com.android.systemui.keyguard.KeyguardService}. */ public abstract class KeyguardStatusBarBinder extends Binder { - public abstract void register(CommandQueue commandQueue); - - public abstract void dismissKeyguard(); + public abstract void registerStatusBar(PhoneStatusBar phoneStatusBar, ViewGroup container, + StatusBarWindowManager statusBarWindowManager); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java new file mode 100644 index 0000000..39ec86f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -0,0 +1,1361 @@ +/* + * Copyright (C) 2014 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.systemui.keyguard; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.SearchManager; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.media.SoundPool; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; + +import com.android.internal.policy.IKeyguardExitCallback; +import com.android.internal.policy.IKeyguardShowCallback; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardDisplayManager; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.MultiUserAvatarCache; +import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.analytics.KeyguardAnalytics; +import com.android.keyguard.analytics.Session; +import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarWindowManager; + +import java.io.File; + +import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter; + + +/** + * Mediates requests related to the keyguard. This includes queries about the + * state of the keyguard, power management events that effect whether the keyguard + * should be shown or reset, callbacks to the phone window manager to notify + * it of when the keyguard is showing, and events from the keyguard view itself + * stating that the keyguard was succesfully unlocked. + * + * Note that the keyguard view is shown when the screen is off (as appropriate) + * so that once the screen comes on, it will be ready immediately. + * + * Example queries about the keyguard: + * - is {movement, key} one that should wake the keygaurd? + * - is the keyguard showing? + * - are input events restricted due to the state of the keyguard? + * + * Callbacks to the phone window manager: + * - the keyguard is showing + * + * Example external events that translate to keyguard view changes: + * - screen turned off -> reset the keyguard, and show it so it will be ready + * next time the screen turns on + * - keyboard is slid open -> if the keyguard is not secure, hide it + * + * Events from the keyguard view: + * - user succesfully unlocked keyguard -> hide keyguard view, and no longer + * restrict input events. + * + * Note: in addition to normal power managment events that effect the state of + * whether the keyguard should be showing, external apps and services may request + * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When + * false, this will override all other conditions for turning on the keyguard. + * + * Threading and synchronization: + * This class is created by the initialization routine of the {@link android.view.WindowManagerPolicy}, + * and runs on its thread. The keyguard UI is created from that thread in the + * constructor of this class. The apis may be called from other threads, including the + * {@link com.android.server.input.InputManagerService}'s and {@link android.view.WindowManager}'s. + * Therefore, methods on this class are synchronized, and any action that is pointed + * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI + * thread of the keyguard. + */ +public class KeyguardViewMediator { + private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; + final static boolean DEBUG = false; + private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE; + private final static boolean DBG_WAKE = false; + + private final static String TAG = "KeyguardViewMediator"; + + private static final String DELAYED_KEYGUARD_ACTION = + "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"; + + // used for handler messages + private static final int SHOW = 2; + private static final int HIDE = 3; + private static final int RESET = 4; + private static final int VERIFY_UNLOCK = 5; + private static final int NOTIFY_SCREEN_OFF = 6; + private static final int NOTIFY_SCREEN_ON = 7; + private static final int KEYGUARD_DONE = 9; + private static final int KEYGUARD_DONE_DRAWING = 10; + private static final int KEYGUARD_DONE_AUTHENTICATING = 11; + private static final int SET_OCCLUDED = 12; + private static final int KEYGUARD_TIMEOUT = 13; + private static final int DISMISS = 17; + + /** + * The default amount of time we stay awake (used for all key input) + */ + public static final int AWAKE_INTERVAL_DEFAULT_MS = 10000; + + /** + * How long to wait after the screen turns off due to timeout before + * turning on the keyguard (i.e, the user has this much time to turn + * the screen back on without having to face the keyguard). + */ + private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000; + + /** + * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()} + * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)} + * that is reenabling the keyguard. + */ + private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000; + + /** + * Allow the user to expand the status bar when the keyguard is engaged + * (without a pattern or password). + */ + private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true; + + /** + * Allow the user to expand the status bar when a SECURE keyguard is engaged + * and {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS} is set + * (private notifications will be masked). + */ + private static final boolean ENABLE_SECURE_STATUS_BAR_EXPAND = true; + + /** + * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}. + */ + private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false; + + /** + * Secure setting whether analytics are collected on the keyguard. + */ + private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics"; + + /** The stream type that the lock sounds are tied to. */ + private int mMasterStreamType; + + private Context mContext; + private AlarmManager mAlarmManager; + private AudioManager mAudioManager; + private StatusBarManager mStatusBarManager; + private boolean mSwitchingUser; + + private boolean mSystemReady; + + // Whether the next call to playSounds() should be skipped. Defaults to + // true because the first lock (on boot) should be silent. + private boolean mSuppressNextLockSound = true; + + + /** High level access to the power manager for WakeLocks */ + private PowerManager mPM; + + /** UserManager for querying number of users */ + private UserManager mUserManager; + + /** SearchManager for determining whether or not search assistant is available */ + private SearchManager mSearchManager; + + /** + * Used to keep the device awake while to ensure the keyguard finishes opening before + * we sleep. + */ + private PowerManager.WakeLock mShowKeyguardWakeLock; + + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + + private final KeyguardAnalytics mKeyguardAnalytics; + + // these are protected by synchronized (this) + + /** + * External apps (like the phone app) can tell us to disable the keygaurd. + */ + private boolean mExternallyEnabled = true; + + /** + * Remember if an external call to {@link #setKeyguardEnabled} with value + * false caused us to hide the keyguard, so that we need to reshow it once + * the keygaurd is reenabled with another call with value true. + */ + private boolean mNeedToReshowWhenReenabled = false; + + // cached value of whether we are showing (need to know this to quickly + // answer whether the input should be restricted) + private boolean mShowing; + + // true if the keyguard is hidden by another window + private boolean mOccluded = false; + + /** + * Helps remember whether the screen has turned on since the last time + * it turned off due to timeout. see {@link #onScreenTurnedOff(int)} + */ + private int mDelayedShowingSequence; + + /** + * If the user has disabled the keyguard, then requests to exit, this is + * how we'll ultimately let them know whether it was successful. We use this + * var being non-null as an indicator that there is an in progress request. + */ + private IKeyguardExitCallback mExitSecureCallback; + + // the properties of the keyguard + + private KeyguardUpdateMonitor mUpdateMonitor; + + private boolean mScreenOn; + + // last known state of the cellular connection + private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE; + + /** + * we send this intent when the keyguard is dismissed. + */ + private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT) + .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + + /** + * {@link #setKeyguardEnabled} waits on this condition when it reenables + * the keyguard. + */ + private boolean mWaitingUntilKeyguardVisible = false; + private LockPatternUtils mLockPatternUtils; + private boolean mKeyguardDonePending = false; + + private SoundPool mLockSounds; + private int mLockSoundId; + private int mUnlockSoundId; + private int mLockSoundStreamId; + + /** + * Tracks value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}. + */ + private boolean mAllowNotificationsWhenSecure; + + /** + * The volume applied to the lock/unlock sounds. + */ + private final float mLockSoundVolume; + + /** + * For managing external displays + */ + private KeyguardDisplayManager mKeyguardDisplayManager; + + KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { + + @Override + public void onUserSwitching(int userId) { + // Note that the mLockPatternUtils user has already been updated from setCurrentUser. + // We need to force a reset of the views, since lockNow (called by + // ActivityManagerService) will not reconstruct the keyguard if it is already showing. + synchronized (KeyguardViewMediator.this) { + mSwitchingUser = true; + resetStateLocked(); + adjustStatusBarLocked(); + // When we switch users we want to bring the new user to the biometric unlock even + // if the current user has gone to the backup. + KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); + } + } + + @Override + public void onUserSwitchComplete(int userId) { + mSwitchingUser = false; + } + + @Override + public void onUserRemoved(int userId) { + mLockPatternUtils.removeUser(userId); + MultiUserAvatarCache.getInstance().clear(userId); + } + + @Override + public void onUserInfoChanged(int userId) { + MultiUserAvatarCache.getInstance().clear(userId); + } + + @Override + public void onPhoneStateChanged(int phoneState) { + synchronized (KeyguardViewMediator.this) { + if (TelephonyManager.CALL_STATE_IDLE == phoneState // 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(null); + } + } + } + + @Override + public void onClockVisibilityChanged() { + adjustStatusBarLocked(); + } + + @Override + public void onDeviceProvisioned() { + sendUserPresentBroadcast(); + } + + @Override + public void onSimStateChanged(IccCardConstants.State simState) { + if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState); + + switch (simState) { + case NOT_READY: + case ABSENT: + // only force lock screen in case of missing sim if user hasn't + // gone through setup wizard + 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(null); + } else { + resetStateLocked(); + } + } + } + break; + case PIN_REQUIRED: + case PUK_REQUIRED: + synchronized (this) { + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't " + + "showing; need to show keyguard so user can enter sim pin"); + doKeyguardLocked(null); + } else { + resetStateLocked(); + } + } + break; + case PERM_DISABLED: + synchronized (this) { + if (!isShowing()) { + if (DEBUG) Log.d(TAG, "PERM_DISABLED and " + + "keygaurd isn't showing."); + doKeyguardLocked(null); + } else { + if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" + + "show permanently disabled message in lockscreen."); + resetStateLocked(); + } + } + break; + case READY: + synchronized (this) { + if (isShowing()) { + resetStateLocked(); + } + } + break; + } + } + + }; + + ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { + + public void userActivity() { + KeyguardViewMediator.this.userActivity(); + } + + public void userActivity(long holdMs) { + KeyguardViewMediator.this.userActivity(holdMs); + } + + public void keyguardDone(boolean authenticated) { + KeyguardViewMediator.this.keyguardDone(authenticated, true); + } + + public void keyguardDoneDrawing() { + mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING); + } + + @Override + public void setNeedsInput(boolean needsInput) { + mStatusBarKeyguardViewManager.setNeedsInput(needsInput); + } + + @Override + public void onUserActivityTimeoutChanged() { + mStatusBarKeyguardViewManager.updateUserActivityTimeout(); + } + + @Override + public void keyguardDonePending() { + mKeyguardDonePending = true; + } + + @Override + public void keyguardGone() { + mKeyguardDisplayManager.hide(); + } + }; + + private void userActivity() { + userActivity(AWAKE_INTERVAL_DEFAULT_MS); + } + + public void userActivity(long holdMs) { + // We ignore the hold time. Eventually we should remove it. + // Instead, the keyguard window has an explicit user activity timeout set on it. + mPM.userActivity(SystemClock.uptimeMillis(), false); + } + + /** + * Construct a KeyguardViewMediator + * @param context + * @param lockPatternUtils optional mock interface for LockPatternUtils + */ + public KeyguardViewMediator(Context context, LockPatternUtils lockPatternUtils) { + mContext = context; + mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); + mShowKeyguardWakeLock.setReferenceCounted(false); + + mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION)); + + mKeyguardDisplayManager = new KeyguardDisplayManager(context); + + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); + + mLockPatternUtils = lockPatternUtils != null + ? lockPatternUtils : new LockPatternUtils(mContext); + mLockPatternUtils.setCurrentUser(UserHandle.USER_OWNER); + + // Assume keyguard is showing (unless it's disabled) until we know for sure... + mShowing = (mUpdateMonitor.isDeviceProvisioned() || mLockPatternUtils.isSecure()) + && !mLockPatternUtils.isLockScreenDisabled(); + + mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext, mViewMediatorCallback, + lockPatternUtils); + WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + final ContentResolver cr = mContext.getContentResolver(); + + if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() && + Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) { + mKeyguardAnalytics = new KeyguardAnalytics(context, new SessionTypeAdapter() { + + @Override + public int getSessionType() { + return mLockPatternUtils.isSecure() ? Session.TYPE_KEYGUARD_SECURE + : Session.TYPE_KEYGUARD_INSECURE; + } + }, new File(mContext.getCacheDir(), "keyguard_analytics.bin")); + } else { + mKeyguardAnalytics = null; + } + + mScreenOn = mPM.isScreenOn(); + + mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0); + String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND); + if (soundPath != null) { + mLockSoundId = mLockSounds.load(soundPath, 1); + } + if (soundPath == null || mLockSoundId == 0) { + Log.w(TAG, "failed to load lock sound from " + soundPath); + } + soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND); + if (soundPath != null) { + mUnlockSoundId = mLockSounds.load(soundPath, 1); + } + if (soundPath == null || mUnlockSoundId == 0) { + Log.w(TAG, "failed to load unlock sound from " + soundPath); + } + int lockSoundDefaultAttenuation = context.getResources().getInteger( + com.android.internal.R.integer.config_lockSoundVolumeDb); + mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20); + } + + /** + * Let us know that the system is ready after startup. + */ + public void onSystemReady() { + mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); + synchronized (this) { + if (DEBUG) Log.d(TAG, "onSystemReady"); + mSystemReady = true; + mUpdateMonitor.registerCallback(mUpdateCallback); + + // Suppress biometric unlock right after boot until things have settled if it is the + // selected security method, otherwise unsuppress it. It must be unsuppressed if it is + // not the selected security method for the following reason: if the user starts + // without a screen lock selected, the biometric unlock would be suppressed the first + // time they try to use it. + // + // Note that the biometric unlock will still not show if it is not the selected method. + // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the + // selected method. + if (mLockPatternUtils.usingBiometricWeak() + && mLockPatternUtils.isBiometricWeakInstalled()) { + if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot"); + mUpdateMonitor.setAlternateUnlockEnabled(false); + } else { + mUpdateMonitor.setAlternateUnlockEnabled(true); + } + + doKeyguardLocked(null); + } + // Most services aren't available until the system reaches the ready state, so we + // send it here when the device first boots. + maybeSendUserPresentBroadcast(); + } + + /** + * Called to let us know the screen was turned off. + * @param why either {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_USER}, + * {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or + * {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}. + */ + public void onScreenTurnedOff(int why) { + synchronized (this) { + mScreenOn = false; + if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")"); + + mKeyguardDonePending = false; + + // Lock immediately based on setting if secure (user has a pin/pattern/password). + // This also "locks" the device when not secure to provide easy access to the + // camera while preventing unwanted input. + final boolean lockImmediately = + mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure(); + + if (mExitSecureCallback != null) { + if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled"); + try { + mExitSecureCallback.onKeyguardExitResult(false); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); + } + mExitSecureCallback = null; + if (!mExternallyEnabled) { + hideLocked(); + } + } else if (mShowing) { + notifyScreenOffLocked(); + resetStateLocked(); + } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT + || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) { + doKeyguardLaterLocked(); + } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) { + // Do not enable the keyguard if the prox sensor forced the screen off. + } else { + doKeyguardLocked(null); + } + if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { + mKeyguardAnalytics.getCallback().onScreenOff(); + } + } + KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why); + } + + private void doKeyguardLaterLocked() { + // if the screen turned off because of timeout or the user hit the power button + // and we don't need to lock immediately, set an alarm + // to enable it a little bit later (i.e, give the user a chance + // to turn the screen back on within a certain window without + // having to unlock the screen) + final ContentResolver cr = mContext.getContentResolver(); + + // From DisplaySettings + long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT, + KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT); + + // From SecuritySettings + final long lockAfterTimeout = Settings.Secure.getInt(cr, + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT); + + // From DevicePolicyAdmin + final long policyTimeout = mLockPatternUtils.getDevicePolicyManager() + .getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser()); + + long timeout; + if (policyTimeout > 0) { + // policy in effect. Make sure we don't go beyond policy limit. + displayTimeout = Math.max(displayTimeout, 0); // ignore negative values + timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout); + } else { + timeout = lockAfterTimeout; + } + + if (timeout <= 0) { + // Lock now + mSuppressNextLockSound = true; + doKeyguardLocked(null); + } else { + // Lock in the future + long when = SystemClock.elapsedRealtime() + timeout; + Intent intent = new Intent(DELAYED_KEYGUARD_ACTION); + intent.putExtra("seq", mDelayedShowingSequence); + PendingIntent sender = PendingIntent.getBroadcast(mContext, + 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender); + if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = " + + mDelayedShowingSequence); + } + } + + private void cancelDoKeyguardLaterLocked() { + mDelayedShowingSequence++; + } + + /** + * Let's us know the screen was turned on. + */ + public void onScreenTurnedOn(IKeyguardShowCallback callback) { + synchronized (this) { + mScreenOn = true; + cancelDoKeyguardLaterLocked(); + if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence); + if (callback != null) { + notifyScreenOnLocked(callback); + } + } + KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOn(); + maybeSendUserPresentBroadcast(); + } + + private void maybeSendUserPresentBroadcast() { + if (mSystemReady && mLockPatternUtils.isLockScreenDisabled() + && !mUserManager.isUserSwitcherEnabled()) { + // Lock screen is disabled because the user has set the preference to "None". + // In this case, send out ACTION_USER_PRESENT here instead of in + // handleKeyguardDone() + sendUserPresentBroadcast(); + } + } + + /** + * A dream started. We should lock after the usual screen-off lock timeout but only + * if there is a secure lock pattern. + */ + public void onDreamingStarted() { + synchronized (this) { + if (mScreenOn && mLockPatternUtils.isSecure()) { + doKeyguardLaterLocked(); + } + } + } + + /** + * A dream stopped. + */ + public void onDreamingStopped() { + synchronized (this) { + if (mScreenOn) { + cancelDoKeyguardLaterLocked(); + } + } + } + + /** + * Same semantics as {@link android.view.WindowManagerPolicy#enableKeyguard}; provide + * a way for external stuff to override normal keyguard behavior. For instance + * the phone app disables the keyguard when it receives incoming calls. + */ + public void setKeyguardEnabled(boolean enabled) { + synchronized (this) { + if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")"); + + mExternallyEnabled = enabled; + + if (!enabled && mShowing) { + if (mExitSecureCallback != null) { + if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring"); + // we're in the process of handling a request to verify the user + // can get past the keyguard. ignore extraneous requests to disable / reenable + return; + } + + // hiding keyguard that is showing, remember to reshow later + if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, " + + "disabling status bar expansion"); + mNeedToReshowWhenReenabled = true; + hideLocked(); + } else if (enabled && mNeedToReshowWhenReenabled) { + // reenabled after previously hidden, reshow + if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling " + + "status bar expansion"); + mNeedToReshowWhenReenabled = false; + + if (mExitSecureCallback != null) { + if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting"); + try { + mExitSecureCallback.onKeyguardExitResult(false); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); + } + mExitSecureCallback = null; + resetStateLocked(); + } else { + showLocked(null); + + // block until we know the keygaurd is done drawing (and post a message + // to unblock us after a timeout so we don't risk blocking too long + // and causing an ANR). + mWaitingUntilKeyguardVisible = true; + mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS); + if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false"); + while (mWaitingUntilKeyguardVisible) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible"); + } + } + } + } + + /** + * @see android.app.KeyguardManager#exitKeyguardSecurely + */ + public void verifyUnlock(IKeyguardExitCallback callback) { + synchronized (this) { + if (DEBUG) Log.d(TAG, "verifyUnlock"); + if (!mUpdateMonitor.isDeviceProvisioned()) { + // don't allow this api when the device isn't provisioned + if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned"); + try { + callback.onKeyguardExitResult(false); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); + } + } else if (mExternallyEnabled) { + // this only applies when the user has externally disabled the + // keyguard. this is unexpected and means the user is not + // using the api properly. + Log.w(TAG, "verifyUnlock called when not externally disabled"); + try { + callback.onKeyguardExitResult(false); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); + } + } else if (mExitSecureCallback != null) { + // already in progress with someone else + try { + callback.onKeyguardExitResult(false); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e); + } + } else { + mExitSecureCallback = callback; + verifyUnlockLocked(); + } + } + } + + /** + * Is the keyguard currently showing? + */ + public boolean isShowing() { + return mShowing; + } + + public boolean isOccluded() { + return mOccluded; + } + + /** + * Is the keyguard currently showing and not being force hidden? + */ + public boolean isShowingAndNotOccluded() { + return mShowing && !mOccluded; + } + + /** + * Notify us when the keyguard is occluded by another window + */ + public void setOccluded(boolean isOccluded) { + if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded); + mUpdateMonitor.sendKeyguardVisibilityChanged(!isOccluded); + mHandler.removeMessages(SET_OCCLUDED); + Message msg = mHandler.obtainMessage(SET_OCCLUDED, (isOccluded ? 1 : 0), 0); + mHandler.sendMessage(msg); + } + + /** + * Handles SET_OCCLUDED message sent by setOccluded() + */ + private void handleSetOccluded(boolean isOccluded) { + synchronized (KeyguardViewMediator.this) { + if (mOccluded != isOccluded) { + mOccluded = isOccluded; + mStatusBarKeyguardViewManager.setOccluded(isOccluded); + updateActivityLockScreenState(); + adjustStatusBarLocked(); + } + if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { + mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded); + } + } + } + + /** + * Used by PhoneWindowManager to enable the keyguard due to a user activity timeout. + * This must be safe to call from any thread and with any window manager locks held. + */ + public void doKeyguardTimeout(Bundle options) { + mHandler.removeMessages(KEYGUARD_TIMEOUT); + Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options); + mHandler.sendMessage(msg); + } + + /** + * Given the state of the keyguard, is the input restricted? + * Input is restricted when the keyguard is showing, or when the keyguard + * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet. + */ + public boolean isInputRestricted() { + return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned(); + } + + /** + * Enable the keyguard if the settings are appropriate. + */ + private void doKeyguardLocked(Bundle options) { + // 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; + } + + // note whether notification access should be allowed + mAllowNotificationsWhenSecure = ENABLE_SECURE_STATUS_BAR_EXPAND + && 0 != Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, + ALLOW_NOTIFICATIONS_DEFAULT ? 1 : 0); + + // if the keyguard is already showing, don't bother + if (mStatusBarKeyguardViewManager.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 IccCardConstants.State state = mUpdateMonitor.getSimState(); + final boolean lockedOrMissing = state.isPinLocked() + || ((state == IccCardConstants.State.ABSENT + || state == IccCardConstants.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 (!mUserManager.isUserSwitcherEnabled() + && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); + return; + } + + if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen"); + showLocked(options); + } + + /** + * Dismiss the keyguard through the security layers. + */ + public void handleDismiss() { + if (mShowing && !mOccluded) { + mStatusBarKeyguardViewManager.dismiss(); + } + } + + public void dismiss() { + mHandler.sendEmptyMessage(DISMISS); + } + + /** + * Send message to keyguard telling it to reset its state. + * @see #handleReset + */ + private void resetStateLocked() { + if (DEBUG) Log.e(TAG, "resetStateLocked"); + Message msg = mHandler.obtainMessage(RESET); + mHandler.sendMessage(msg); + } + + /** + * Send message to keyguard telling it to verify unlock + * @see #handleVerifyUnlock() + */ + private void verifyUnlockLocked() { + if (DEBUG) Log.d(TAG, "verifyUnlockLocked"); + mHandler.sendEmptyMessage(VERIFY_UNLOCK); + } + + + /** + * Send a message to keyguard telling it the screen just turned on. + * @see #onScreenTurnedOff(int) + * @see #handleNotifyScreenOff + */ + private void notifyScreenOffLocked() { + if (DEBUG) Log.d(TAG, "notifyScreenOffLocked"); + mHandler.sendEmptyMessage(NOTIFY_SCREEN_OFF); + } + + /** + * Send a message to keyguard telling it the screen just turned on. + * @see #onScreenTurnedOn + * @see #handleNotifyScreenOn + */ + private void notifyScreenOnLocked(IKeyguardShowCallback result) { + if (DEBUG) Log.d(TAG, "notifyScreenOnLocked"); + Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_ON, result); + mHandler.sendMessage(msg); + } + + /** + * Send message to keyguard telling it to show itself + * @see #handleShow + */ + private void showLocked(Bundle options) { + if (DEBUG) Log.d(TAG, "showLocked"); + // ensure we stay awake until we are finished displaying the keyguard + mShowKeyguardWakeLock.acquire(); + Message msg = mHandler.obtainMessage(SHOW, options); + mHandler.sendMessage(msg); + } + + /** + * Send message to keyguard telling it to hide itself + * @see #handleHide() + */ + private void hideLocked() { + if (DEBUG) Log.d(TAG, "hideLocked"); + Message msg = mHandler.obtainMessage(HIDE); + mHandler.sendMessage(msg); + } + + public boolean isSecure() { + return mLockPatternUtils.isSecure() + || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure(); + } + + /** + * Update the newUserId. Call while holding WindowManagerService lock. + * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing. + * + * @param newUserId The id of the incoming user. + */ + public void setCurrentUser(int newUserId) { + mLockPatternUtils.setCurrentUser(newUserId); + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) { + final int sequence = intent.getIntExtra("seq", 0); + if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = " + + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence); + synchronized (KeyguardViewMediator.this) { + if (mDelayedShowingSequence == sequence) { + // Don't play lockscreen SFX if the screen went off due to timeout. + mSuppressNextLockSound = true; + doKeyguardLocked(null); + } + } + } + } + }; + + public void keyguardDone(boolean authenticated, boolean wakeup) { + if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")"); + EventLog.writeEvent(70000, 2); + synchronized (this) { + mKeyguardDonePending = false; + } + Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0); + mHandler.sendMessage(msg); + } + + /** + * This handler will be associated with the policy thread, which will also + * be the UI thread of the keyguard. Since the apis of the policy, and therefore + * this class, can be called by other threads, any action that directly + * interacts with the keyguard ui should be posted to this handler, rather + * than called directly. + */ + private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SHOW: + handleShow((Bundle) msg.obj); + break; + case HIDE: + handleHide(); + break; + case RESET: + handleReset(); + break; + case VERIFY_UNLOCK: + handleVerifyUnlock(); + break; + case NOTIFY_SCREEN_OFF: + handleNotifyScreenOff(); + break; + case NOTIFY_SCREEN_ON: + handleNotifyScreenOn((IKeyguardShowCallback) msg.obj); + break; + case KEYGUARD_DONE: + handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0); + break; + case KEYGUARD_DONE_DRAWING: + handleKeyguardDoneDrawing(); + break; + case KEYGUARD_DONE_AUTHENTICATING: + keyguardDone(true, true); + break; + case SET_OCCLUDED: + handleSetOccluded(msg.arg1 != 0); + break; + case KEYGUARD_TIMEOUT: + synchronized (KeyguardViewMediator.this) { + doKeyguardLocked((Bundle) msg.obj); + } + break; + case DISMISS: + handleDismiss(); + break; + } + } + }; + + /** + * @see #keyguardDone + * @see #KEYGUARD_DONE + */ + private void handleKeyguardDone(boolean authenticated, boolean wakeup) { + if (DEBUG) Log.d(TAG, "handleKeyguardDone"); + + if (authenticated) { + mUpdateMonitor.clearFailedUnlockAttempts(); + } + + if (mExitSecureCallback != null) { + try { + mExitSecureCallback.onKeyguardExitResult(authenticated); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onKeyguardExitResult(" + authenticated + ")", e); + } + + mExitSecureCallback = null; + + if (authenticated) { + // after succesfully exiting securely, no need to reshow + // the keyguard when they've released the lock + mExternallyEnabled = true; + mNeedToReshowWhenReenabled = false; + } + } + + handleHide(); + sendUserPresentBroadcast(); + } + + private void sendUserPresentBroadcast() { + final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser()); + mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, currentUser); + } + + /** + * @see #keyguardDone + * @see #KEYGUARD_DONE_DRAWING + */ + private void handleKeyguardDoneDrawing() { + synchronized(this) { + if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing"); + if (mWaitingUntilKeyguardVisible) { + if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible"); + mWaitingUntilKeyguardVisible = false; + notifyAll(); + + // there will usually be two of these sent, one as a timeout, and one + // as a result of the callback, so remove any remaining messages from + // the queue + mHandler.removeMessages(KEYGUARD_DONE_DRAWING); + } + } + } + + private void playSounds(boolean locked) { + // User feedback for keyguard. + + if (mSuppressNextLockSound) { + mSuppressNextLockSound = false; + return; + } + + final ContentResolver cr = mContext.getContentResolver(); + if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) { + final int whichSound = locked + ? mLockSoundId + : mUnlockSoundId; + mLockSounds.stop(mLockSoundStreamId); + // Init mAudioManager + if (mAudioManager == null) { + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + if (mAudioManager == null) return; + mMasterStreamType = mAudioManager.getMasterStreamType(); + } + // If the stream is muted, don't play the sound + if (mAudioManager.isStreamMute(mMasterStreamType)) return; + + mLockSoundStreamId = mLockSounds.play(whichSound, + mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/); + } + } + + private void updateActivityLockScreenState() { + try { + ActivityManagerNative.getDefault().setLockScreenShown(mShowing && !mOccluded); + } catch (RemoteException e) { + } + } + + /** + * Handle message sent by {@link #showLocked}. + * @see #SHOW + */ + private void handleShow(Bundle options) { + synchronized (KeyguardViewMediator.this) { + if (!mSystemReady) { + if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready."); + return; + } else { + if (DEBUG) Log.d(TAG, "handleShow"); + } + + mStatusBarKeyguardViewManager.show(options); + mShowing = true; + mKeyguardDonePending = false; + updateActivityLockScreenState(); + adjustStatusBarLocked(); + userActivity(); + try { + ActivityManagerNative.getDefault().closeSystemDialogs("lock"); + } catch (RemoteException e) { + } + + // Do this at the end to not slow down display of the keyguard. + playSounds(true); + + mShowKeyguardWakeLock.release(); + } + mKeyguardDisplayManager.show(); + } + + /** + * Handle message sent by {@link #hideLocked()} + * @see #HIDE + */ + private void handleHide() { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleHide"); + + // only play "unlock" noises if not on a call (since the incall UI + // disables the keyguard) + if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) { + playSounds(false); + } + + mStatusBarKeyguardViewManager.hide(); + mShowing = false; + mKeyguardDonePending = false; + updateActivityLockScreenState(); + adjustStatusBarLocked(); + } + } + + private void adjustStatusBarLocked() { + if (mStatusBarManager == null) { + mStatusBarManager = (StatusBarManager) + mContext.getSystemService(Context.STATUS_BAR_SERVICE); + } + if (mStatusBarManager == null) { + Log.w(TAG, "Could not get status bar manager"); + } else { + // 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) { + // Permanently disable components not available when keyguard is enabled + // (like recents). Temporary enable/disable (e.g. the "back" button) are + // done in KeyguardHostView. + flags |= StatusBarManager.DISABLE_RECENT; + if ((isSecure() && !mAllowNotificationsWhenSecure) + || !ENABLE_INSECURE_STATUS_BAR_EXPAND) { + // showing secure lockscreen; disable expanding. + flags |= StatusBarManager.DISABLE_EXPAND; + } + if (isSecure()) { + // showing secure lockscreen; disable ticker and switch private notifications + // to show their public versions, if available. + flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS; + } + if (!isAssistantAvailable()) { + flags |= StatusBarManager.DISABLE_SEARCH; + } + } + + if (DEBUG) { + Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded + + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags)); + } + + if (!(mContext instanceof Activity)) { + mStatusBarManager.disable(flags); + } + } + } + + /** + * Handle message sent by {@link #resetStateLocked} + * @see #RESET + */ + private void handleReset() { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleReset"); + mStatusBarKeyguardViewManager.reset(); + } + } + + /** + * Handle message sent by {@link #verifyUnlock} + * @see #VERIFY_UNLOCK + */ + private void handleVerifyUnlock() { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleVerifyUnlock"); + mStatusBarKeyguardViewManager.verifyUnlock(); + mShowing = true; + updateActivityLockScreenState(); + } + } + + /** + * Handle message sent by {@link #notifyScreenOffLocked()} + * @see #NOTIFY_SCREEN_OFF + */ + private void handleNotifyScreenOff() { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleNotifyScreenOff"); + mStatusBarKeyguardViewManager.onScreenTurnedOff(); + } + } + + /** + * Handle message sent by {@link #notifyScreenOnLocked} + * @see #NOTIFY_SCREEN_ON + */ + private void handleNotifyScreenOn(IKeyguardShowCallback callback) { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleNotifyScreenOn"); + mStatusBarKeyguardViewManager.onScreenTurnedOn(callback); + } + } + + public boolean isDismissable() { + return mKeyguardDonePending || !isSecure(); + } + + private boolean isAssistantAvailable() { + return mSearchManager != null + && mSearchManager.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null; + } + + public void onBootCompleted() { + mUpdateMonitor.dispatchBootCompleted(); + } + + public void registerStatusBar(PhoneStatusBar phoneStatusBar, ViewGroup container, + StatusBarWindowManager statusBarWindowManager) { + mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container, + statusBarWindowManager); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 54e8815..73f1aa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -22,11 +22,9 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -79,8 +77,6 @@ import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; -import com.android.systemui.keyguard.KeyguardService; -import com.android.systemui.keyguard.KeyguardStatusBarBinder; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; import java.util.ArrayList; @@ -176,12 +172,6 @@ public abstract class BaseStatusBar extends SystemUI implements protected int mZenMode; - protected KeyguardStatusBarBinder mKeyguardService; - - public IStatusBarService getStatusBarService() { - return mBarService; - } - public boolean isDeviceProvisioned() { return mDeviceProvisioned; } @@ -320,7 +310,6 @@ public abstract class BaseStatusBar extends SystemUI implements createAndAddWindows(); - startKeyguardService(); disable(switches[0]); setSystemUiVisibility(switches[1], 0xffffffff); topAppWindowChanged(switches[2] != 0); @@ -371,25 +360,6 @@ public abstract class BaseStatusBar extends SystemUI implements updateRelatedUserCache(); } - private void startKeyguardService() { - Intent intent = new Intent(mContext, KeyguardService.class); - intent.setAction(KeyguardService.ACTION_STATUS_BAR_BIND); - if (!mContext.bindService(intent, new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mKeyguardService = (KeyguardStatusBarBinder) service; - mKeyguardService.register(mCommandQueue); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mKeyguardService = null; - } - }, Context.BIND_AUTO_CREATE)) { - throw new RuntimeException("Couldn't bind status bar keyguard."); - } - } - public void userSwitched(int newUserId) { // should be overridden } @@ -1363,7 +1333,7 @@ public abstract class BaseStatusBar extends SystemUI implements boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker))) && isAllowed && mPowerManager.isScreenOn() - && !keyguard.isShowingAndNotHidden() + && !keyguard.isShowingAndNotOccluded() && !keyguard.isInputRestricted(); try { interrupt = interrupt && !mDreamManager.isDreaming(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index cf6f60c..bbbe8fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -57,7 +57,6 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_PRELOAD_RECENT_APPS = 14 << MSG_SHIFT; private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 15 << MSG_SHIFT; private static final int MSG_SET_WINDOW_STATE = 16 << MSG_SHIFT; - private static final int MSG_SET_KEYGUARD_SHOWN = 17 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -100,8 +99,7 @@ public class CommandQueue extends IStatusBar.Stub { public void hideSearchPanel(); public void cancelPreloadRecentApps(); public void setWindowState(int window, int state); - public void setKeyguardShown(boolean showKeyguard, IKeyguardShowCallback callback, - boolean updateWindowFlags); + } public CommandQueue(Callbacks callbacks, StatusBarIconList list) { @@ -236,14 +234,6 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setKeyguardShown(boolean showKeyguard, IKeyguardShowCallback callback, - boolean updateWindowFlags) { - synchronized (mList) { - mHandler.removeMessages(MSG_SET_KEYGUARD_SHOWN); - mHandler.obtainMessage(MSG_SET_KEYGUARD_SHOWN, - showKeyguard ? 1 : 0, updateWindowFlags ? 1 : 0, callback).sendToTarget(); - } - } private final class H extends Handler { public void handleMessage(Message msg) { @@ -325,10 +315,7 @@ public class CommandQueue extends IStatusBar.Stub { case MSG_SET_WINDOW_STATE: mCallbacks.setWindowState(msg.arg1, msg.arg2); break; - case MSG_SET_KEYGUARD_SHOWN: - mCallbacks.setKeyguardShown(msg.arg1 != 0, (IKeyguardShowCallback) msg.obj, - msg.arg2 != 0); - break; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java index 1ea920d..68eaf51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java @@ -140,16 +140,16 @@ public class KeyguardTouchDelegate { return false; } - public boolean isShowingAndNotHidden() { + public boolean isShowingAndNotOccluded() { final IKeyguardService service = mService; if (service != null) { try { - return service.isShowingAndNotHidden(); + return service.isShowingAndNotOccluded(); } catch (RemoteException e) { Slog.w(TAG , "Remote Exception", e); } } else { - Slog.w(TAG, "isShowingAndNotHidden(): NO SERVICE!"); + Slog.w(TAG, "isShowingAndNotOccluded(): NO SERVICE!"); } return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 44f5e3a..bdcf83c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -35,9 +35,11 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -93,6 +95,8 @@ import com.android.systemui.DemoMode; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.keyguard.KeyguardService; +import com.android.systemui.keyguard.KeyguardStatusBarBinder; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; @@ -183,6 +187,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; private int mStatusBarWindowState = WINDOW_STATE_SHOWING; + private StatusBarWindowManager mStatusBarWindowManager; int mPixelFormat; Object mQueueLock = new Object(); @@ -274,6 +279,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + protected KeyguardStatusBarBinder mKeyguardService; // XXX: gesture research private final GestureRecorder mGestureRec = DEBUG_GESTURES ? new GestureRecorder("/sdcard/statusbar_gestures.dat") @@ -356,12 +362,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } }}; - private boolean mKeyguardShowing; - - private ViewGroup mKeyguard; - - private Button mDismissKeyguard; - @Override public void setZenMode(int mode) { super.setZenMode(mode); @@ -393,6 +393,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, mHeadsUpObserver); } + startKeyguard(); } // ================================================================================ @@ -443,7 +444,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } }); - mKeyguard = (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard); if (!ActivityManager.isHighEndGfx()) { mStatusBarWindow.setBackground(null); mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( @@ -712,6 +712,29 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { return mStatusBarView; } + private void startKeyguard() { + + // Create the connection to KeyguardService. + Intent intent = new Intent(mContext, KeyguardService.class); + intent.setAction(KeyguardService.ACTION_STATUS_BAR_BIND); + if (!mContext.bindService(intent, new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mKeyguardService = (KeyguardStatusBarBinder) service; + mKeyguardService.registerStatusBar(PhoneStatusBar.this, mStatusBarWindow, + mStatusBarWindowManager); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Keyguard disconnected."); + mKeyguardService = null; + } + }, Context.BIND_AUTO_CREATE)) { + throw new RuntimeException("Couldn't bind status bar keyguard."); + } + } + @Override protected void onShowSearchPanel() { if (mNavigationBarView != null) { @@ -789,10 +812,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } } - protected int getStatusBarGravity() { - return Gravity.TOP | Gravity.FILL_HORIZONTAL; - } - public int getStatusBarHeight() { if (mNaturalBarHeight < 0) { final Resources res = mContext.getResources(); @@ -1508,30 +1527,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // Expand the window to encompass the full screen in anticipation of the drag. // This is only possible to do atomically because the status bar is at the top of the screen! - expandWindow(); + mStatusBarWindowManager.setStatusBarExpanded(true); visibilityChanged(true); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } - private void expandWindow() { - WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); - lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - lp.height = ViewGroup.LayoutParams.MATCH_PARENT; - mWindowManager.updateViewLayout(mStatusBarWindow, lp); - } - - private void releaseFocus() { - if (mStatusBarWindow == null) return; - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); - lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - mWindowManager.updateViewLayout(mStatusBarWindow, lp); - } - public void animateCollapsePanels() { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } @@ -1544,7 +1546,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } // release focus immediately to kick off focus change transition - releaseFocus(); + mStatusBarWindowManager.setStatusBarFocusable(false); if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); @@ -1556,7 +1558,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL); } - if (mStatusBarWindow != null && !mKeyguardShowing) { + if (mStatusBarWindow != null) { mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapseAllPanels(true); } @@ -1768,7 +1770,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible + " mExpandedVisible=" + mExpandedVisible); - if (!mExpandedVisible || mStatusBarWindow == null || mKeyguardShowing) { + if (!mExpandedVisible || mStatusBarWindow == null) { return; } @@ -1802,7 +1804,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { visibilityChanged(false); // Shrink the window to the size of the status bar only - contractWindow(); + mStatusBarWindowManager.setStatusBarExpanded(false); if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); @@ -1819,15 +1821,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); } - private void contractWindow() { - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); - lp.height = getStatusBarHeight(); - lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - mWindowManager.updateViewLayout(mStatusBarWindow, lp); - } - /** * Enables or disables layers on the children of the notifications pile. * @@ -1936,7 +1929,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { && mStatusBarWindowState != state) { mStatusBarWindowState = state; if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); - if (!showing && !mKeyguardShowing) { + if (!showing) { mStatusBarView.collapseAllPanels(false); } } @@ -2361,29 +2354,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } private void addStatusBarWindow() { - // Put up the view - final int height = getStatusBarHeight(); - - // Now that the status bar window encompasses the sliding panel and its - // translucent backdrop, the entire thing is made TRANSLUCENT and is - // hardware-accelerated. - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - height, - WindowManager.LayoutParams.TYPE_STATUS_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, - PixelFormat.TRANSLUCENT); - - lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - lp.gravity = getStatusBarGravity(); - lp.setTitle("StatusBar"); - lp.packageName = mContext.getPackageName(); - makeStatusBarView(); - mWindowManager.addView(mStatusBarWindow, lp); + mStatusBarWindowManager = new StatusBarWindowManager(mContext); + mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); } void setNotificationIconVisibility(boolean visible, int anim) { @@ -2692,67 +2665,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } } - @Override - public void setKeyguardShown(boolean showKeyguard, final IKeyguardShowCallback callback, - boolean updateWindowFlags) { - mKeyguardShowing = showKeyguard; - if (updateWindowFlags) { - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); - if (showKeyguard) { - lp.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - } else { - lp.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; - lp.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - } - mWindowManager.updateViewLayout(mStatusBarWindow, lp); - } - if (!showKeyguard) { - mKeyguard.setVisibility(View.GONE); - mKeyguard.removeAllViews(); - contractWindow(); - } else { - expandWindow(); - mKeyguard.setVisibility(View.VISIBLE); - KeyguardSimpleHostView view = (KeyguardSimpleHostView) LayoutInflater.from(mContext) - .inflate(R.layout.keyguard_simple_host_view, mKeyguard, false); - mKeyguard.addView(view); - view.setOnDismissAction(new KeyguardHostView.OnDismissAction() { - @Override - public boolean onDismiss() { - contractWindow(); - if (mKeyguardService != null) { - mKeyguardService.dismissKeyguard(); - } - return false; - } - }); - view.show(); - } - if (callback != null) { - mStatusBarView.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this); - mStatusBarView.post(new Runnable() { - @Override - public void run() { - try { - callback.onShown(mStatusBarWindow.getWindowToken()); - } catch (RemoteException e) { - Log.e(TAG, "Could not call show callback", e); - } - } - }); - return true; - } - }); - } - - } - /** * Reload some of our resources when the configuration changes. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java new file mode 100644 index 0000000..74549ae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 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.systemui.statusbar.phone; + +import android.content.Context; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import com.android.internal.policy.IKeyguardShowCallback; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSimpleHostView; +import com.android.keyguard.R; +import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.keyguard.KeyguardViewMediator; + +/** + * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back + * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, + * which is in turn, reported to this class by the current + * {@link com.android.keyguard.KeyguardViewBase}. + */ +public class StatusBarKeyguardViewManager { + private static String TAG = "StatusBarKeyguardViewManager"; + + private final Context mContext; + + private LockPatternUtils mLockPatternUtils; + private ViewMediatorCallback mViewMediatorCallback; + private PhoneStatusBar mPhoneStatusBar; + + private KeyguardSimpleHostView mKeyguardView; + private ViewGroup mRoot; + private ViewGroup mContainer; + private StatusBarWindowManager mStatusBarWindowManager; + + private boolean mScreenOn = false; + private boolean mShowOnRegister; + + public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, + LockPatternUtils lockPatternUtils) { + mContext = context; + mViewMediatorCallback = callback; + mLockPatternUtils = lockPatternUtils; + + } + + public void registerStatusBar(PhoneStatusBar phoneStatusBar, + ViewGroup container, StatusBarWindowManager statusBarWindowManager) { + mPhoneStatusBar = phoneStatusBar; + mContainer = container; + mStatusBarWindowManager = statusBarWindowManager; + if (mShowOnRegister) { + mShowOnRegister = false; + show(null); + if (mScreenOn) { + onScreenTurnedOn(null); + } + } + } + + /** + * Show the keyguard. Will handle creating and attaching to the view manager + * lazily. + */ + public void show(Bundle options) { + if (mStatusBarWindowManager != null) { + ensureView(); + mStatusBarWindowManager.setKeyguardShowing(true); + mKeyguardView.requestFocus(); + } else { + mShowOnRegister = true; + } + } + + private void ensureView() { + if (mRoot == null) { + inflateView(); + } + } + + private void inflateView() { + removeView(); + mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); + mKeyguardView = (KeyguardSimpleHostView) mRoot.findViewById(R.id.keyguard_host_view); + mKeyguardView.setLockPatternUtils(mLockPatternUtils); + mKeyguardView.setViewMediatorCallback(mViewMediatorCallback); + mContainer.addView(mRoot, mContainer.getChildCount()); + mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME); + } + + private void removeView() { + if (mRoot != null && mRoot.getParent() == mContainer) { + mContainer.removeView(mRoot); + mRoot = null; + } + } + + /** + * Reset the state of the view. + */ + public void reset() { + inflateView(); + } + + public void onScreenTurnedOff() { + mScreenOn = false; + if (mKeyguardView != null) { + mKeyguardView.onScreenTurnedOff(); + } + } + + public void onScreenTurnedOn(final IKeyguardShowCallback callback) { + mScreenOn = true; + if (mKeyguardView != null) { + mKeyguardView.onScreenTurnedOn(); + if (callback != null) { + callbackAfterDraw(callback); + } + } else { + try { + if (callback != null) { + callback.onShown(null); + } + } catch (RemoteException e) { + Slog.w(TAG, "Exception calling onShown():", e); + } + } + } + + private void callbackAfterDraw(final IKeyguardShowCallback callback) { + mKeyguardView.post(new Runnable() { + @Override + public void run() { + try { + callback.onShown(mKeyguardView.getWindowToken()); + } catch (RemoteException e) { + Slog.w(TAG, "Exception calling onShown():", e); + } + } + }); + } + + public void verifyUnlock() { + show(null); + mKeyguardView.verifyUnlock(); + } + + public void setNeedsInput(boolean needsInput) { + if (mStatusBarWindowManager != null) { + mStatusBarWindowManager.setKeyguardNeedsInput(needsInput); + } + } + + public void updateUserActivityTimeout() { + + // Use the user activity timeout requested by the keyguard view, if any. + if (mKeyguardView != null) { + long timeout = mKeyguardView.getUserActivityTimeout(); + if (timeout >= 0) { + mStatusBarWindowManager.setKeyguardUserActivityTimeout(timeout); + return; + } + } + + // Otherwise, use the default timeout. + mStatusBarWindowManager.setKeyguardUserActivityTimeout( + KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS); + } + + public void setOccluded(boolean occluded) { + if (mStatusBarWindowManager != null) { + mStatusBarWindowManager.setKeyguardOccluded(occluded); + } + } + + /** + * Hides the keyguard view + */ + public void hide() { + if (mPhoneStatusBar != null) { + mStatusBarWindowManager.setKeyguardShowing(false); + if (mKeyguardView != null) { + mKeyguardView.cleanUp(); + mViewMediatorCallback.keyguardGone(); + } + removeView(); + } + mShowOnRegister = false; + } + + /** + * Dismisses the keyguard by going to the next screen or making it gone. + */ + public void dismiss() { + if (mScreenOn) { + mKeyguardView.dismiss(); + } + } + + /** + * @return Whether the keyguard is showing + */ + public boolean isShowing() { + return mRoot != null && mRoot.getVisibility() == View.VISIBLE; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java new file mode 100644 index 0000000..e418ac5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 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.systemui.statusbar.phone; + +import android.app.ActionBar; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.SystemProperties; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.android.keyguard.R; + +/** + * Encapsulates all logic for the status bar window state management. + */ +public class StatusBarWindowManager { + + private final Context mContext; + private final WindowManager mWindowManager; + private View mStatusBarView; + private WindowManager.LayoutParams mLp; + private int mBarHeight; + private final boolean mKeyguardScreenRotation; + + private final State mCurrentState = new State(); + + public StatusBarWindowManager(Context context) { + mContext = context; + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); + } + + private boolean shouldEnableKeyguardScreenRotation() { + Resources res = mContext.getResources(); + return SystemProperties.getBoolean("lockscreen.rot_override", false) + || res.getBoolean(R.bool.config_enableLockScreenRotation); + } + + /** + * Adds the status bar view to the window manager. + * + * @param statusBarView The view to add. + * @param barHeight The height of the status bar in collapsed state. + */ + public void add(View statusBarView, int barHeight) { + + // Now that the status bar window encompasses the sliding panel and its + // translucent backdrop, the entire thing is made TRANSLUCENT and is + // hardware-accelerated. + mLp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + barHeight, + WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, + PixelFormat.TRANSLUCENT); + + mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + mLp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; + mLp.setTitle("StatusBar"); + mLp.packageName = mContext.getPackageName(); + mStatusBarView = statusBarView; + mBarHeight = barHeight; + mWindowManager.addView(mStatusBarView, mLp); + } + + private void applyKeyguardFlags(State state) { + if (state.keyguardShowing) { + mLp.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + mLp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } else { + mLp.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + mLp.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } + } + + private void adjustScreenOrientation(State state) { + if (!state.isKeyguardShowingAndNotOccluded() || mKeyguardScreenRotation) { + mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; + } else { + mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; + } + } + + private void applyFocusableFlag(State state) { + if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput) { + mLp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } else if (state.isKeyguardShowingAndNotOccluded() || state.statusBarFocusable) { + mLp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } else { + mLp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } + } + + private void applyHeight(State state) { + boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded; + if (expanded) { + mLp.height = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + mLp.height = mBarHeight; + } + } + + private void applyUserActivityTimeout(State state) { + if (state.isKeyguardShowingAndNotOccluded()) { + mLp.userActivityTimeout = state.keyguardUserActivityTimeout; + } else { + mLp.userActivityTimeout = -1; + } + } + + private void apply(State state) { + applyKeyguardFlags(state); + applyFocusableFlag(state); + adjustScreenOrientation(state); + applyHeight(state); + applyUserActivityTimeout(state); + mWindowManager.updateViewLayout(mStatusBarView, mLp); + } + + public void setKeyguardShowing(boolean showing) { + mCurrentState.keyguardShowing = showing; + apply(mCurrentState); + } + + public void setKeyguardOccluded(boolean occluded) { + mCurrentState.keyguardOccluded = occluded; + apply(mCurrentState); + } + + public void setKeyguardNeedsInput(boolean needsInput) { + mCurrentState.keyguardNeedsInput = needsInput; + apply(mCurrentState); + } + + public void setStatusBarExpanded(boolean expanded) { + mCurrentState.statusBarExpanded = expanded; + mCurrentState.statusBarFocusable = expanded; + apply(mCurrentState); + } + + public void setStatusBarFocusable(boolean focusable) { + mCurrentState.statusBarFocusable = focusable; + apply(mCurrentState); + } + + public void setKeyguardUserActivityTimeout(long timeout) { + mCurrentState.keyguardUserActivityTimeout = timeout; + apply(mCurrentState); + } + + private static class State { + boolean keyguardShowing; + boolean keyguardOccluded; + boolean keyguardNeedsInput; + boolean statusBarExpanded; + boolean statusBarFocusable; + long keyguardUserActivityTimeout; + + private boolean isKeyguardShowingAndNotOccluded() { + return keyguardShowing && !keyguardOccluded; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2dc3373..a57a7b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -94,15 +94,6 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void setKeyguardShown(boolean showKeyguard, IKeyguardShowCallback callback, - boolean updateKeyguardFlags) { - } - - @Override - protected void createAndAddWindows() { - } - - @Override protected WindowManager.LayoutParams getSearchLayoutParams( LayoutParams layoutParams) { return null; @@ -151,6 +142,10 @@ public class TvStatusBar extends BaseStatusBar { } @Override + protected void createAndAddWindows() { + } + + @Override protected void refreshLayout(int layoutDirection) { } |