1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.UserHandle; 23 import android.os.UserManager; 24 import android.util.Slog; 25 import android.view.KeyEvent; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.ViewTreeObserver; 30 import android.view.accessibility.AccessibilityEvent; 31 32 import com.android.internal.widget.LockPatternUtils; 33 import com.android.keyguard.KeyguardHostView; 34 import com.android.keyguard.KeyguardSecurityView; 35 import com.android.keyguard.KeyguardUpdateMonitor; 36 import com.android.keyguard.KeyguardUpdateMonitorCallback; 37 import com.android.keyguard.R; 38 import com.android.keyguard.ViewMediatorCallback; 39 import com.android.systemui.DejankUtils; 40 import com.android.systemui.classifier.FalsingManager; 41 import com.android.systemui.keyguard.DismissCallbackRegistry; 42 43 import static com.android.keyguard.KeyguardHostView.OnDismissAction; 44 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; 45 46 /** 47 * A class which manages the bouncer on the lockscreen. 48 */ 49 public class KeyguardBouncer { 50 51 final static private String TAG = "KeyguardBouncer"; 52 53 protected final Context mContext; 54 protected final ViewMediatorCallback mCallback; 55 protected final LockPatternUtils mLockPatternUtils; 56 protected final ViewGroup mContainer; 57 private final FalsingManager mFalsingManager; 58 private final DismissCallbackRegistry mDismissCallbackRegistry; 59 private final Handler mHandler; 60 protected KeyguardHostView mKeyguardView; 61 protected ViewGroup mRoot; 62 private boolean mShowingSoon; 63 private int mBouncerPromptReason; 64 private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = 65 new KeyguardUpdateMonitorCallback() { 66 @Override 67 public void onStrongAuthStateChanged(int userId) { 68 mBouncerPromptReason = mCallback.getBouncerPromptReason(); 69 } 70 }; 71 private final Runnable mRemoveViewRunnable = this::removeView; 72 KeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry)73 public KeyguardBouncer(Context context, ViewMediatorCallback callback, 74 LockPatternUtils lockPatternUtils, ViewGroup container, 75 DismissCallbackRegistry dismissCallbackRegistry) { 76 mContext = context; 77 mCallback = callback; 78 mLockPatternUtils = lockPatternUtils; 79 mContainer = container; 80 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 81 mFalsingManager = FalsingManager.getInstance(mContext); 82 mDismissCallbackRegistry = dismissCallbackRegistry; 83 mHandler = new Handler(); 84 } 85 show(boolean resetSecuritySelection)86 public void show(boolean resetSecuritySelection) { 87 final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser(); 88 if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) { 89 // In split system user mode, we never unlock system user. 90 return; 91 } 92 mFalsingManager.onBouncerShown(); 93 ensureView(); 94 if (resetSecuritySelection) { 95 // showPrimarySecurityScreen() updates the current security method. This is needed in 96 // case we are already showing and the current security method changed. 97 mKeyguardView.showPrimarySecurityScreen(); 98 } 99 if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) { 100 return; 101 } 102 103 final int activeUserId = ActivityManager.getCurrentUser(); 104 final boolean isSystemUser = 105 UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM; 106 final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId; 107 108 // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is 109 // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer. 110 if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) { 111 return; 112 } 113 114 // This condition may indicate an error on Android, so log it. 115 if (!allowDismissKeyguard) { 116 Slog.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId); 117 } 118 119 mShowingSoon = true; 120 121 // Split up the work over multiple frames. 122 DejankUtils.postAfterTraversal(mShowRunnable); 123 } 124 125 private final Runnable mShowRunnable = new Runnable() { 126 @Override 127 public void run() { 128 mRoot.setVisibility(View.VISIBLE); 129 mKeyguardView.onResume(); 130 showPromptReason(mBouncerPromptReason); 131 if (mKeyguardView.getHeight() != 0) { 132 mKeyguardView.startAppearAnimation(); 133 } else { 134 mKeyguardView.getViewTreeObserver().addOnPreDrawListener( 135 new ViewTreeObserver.OnPreDrawListener() { 136 @Override 137 public boolean onPreDraw() { 138 mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this); 139 mKeyguardView.startAppearAnimation(); 140 return true; 141 } 142 }); 143 mKeyguardView.requestLayout(); 144 } 145 mShowingSoon = false; 146 mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 147 } 148 }; 149 150 /** 151 * Show a string explaining why the security view needs to be solved. 152 * 153 * @param reason a flag indicating which string should be shown, see 154 * {@link KeyguardSecurityView#PROMPT_REASON_NONE} 155 * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART} 156 */ showPromptReason(int reason)157 public void showPromptReason(int reason) { 158 mKeyguardView.showPromptReason(reason); 159 } 160 showMessage(String message, int color)161 public void showMessage(String message, int color) { 162 mKeyguardView.showMessage(message, color); 163 } 164 cancelShowRunnable()165 private void cancelShowRunnable() { 166 DejankUtils.removeCallbacks(mShowRunnable); 167 mShowingSoon = false; 168 } 169 showWithDismissAction(OnDismissAction r, Runnable cancelAction)170 public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) { 171 ensureView(); 172 mKeyguardView.setOnDismissAction(r, cancelAction); 173 show(false /* resetSecuritySelection */); 174 } 175 hide(boolean destroyView)176 public void hide(boolean destroyView) { 177 if (isShowing()) { 178 mDismissCallbackRegistry.notifyDismissCancelled(); 179 } 180 mFalsingManager.onBouncerHidden(); 181 cancelShowRunnable(); 182 if (mKeyguardView != null) { 183 mKeyguardView.cancelDismissAction(); 184 mKeyguardView.cleanUp(); 185 } 186 if (mRoot != null) { 187 mRoot.setVisibility(View.INVISIBLE); 188 if (destroyView) { 189 190 // We have a ViewFlipper that unregisters a broadcast when being detached, which may 191 // be slow because of AM lock contention during unlocking. We can delay it a bit. 192 mHandler.postDelayed(mRemoveViewRunnable, 50); 193 } 194 } 195 } 196 197 /** 198 * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}. 199 */ startPreHideAnimation(Runnable runnable)200 public void startPreHideAnimation(Runnable runnable) { 201 if (mKeyguardView != null) { 202 mKeyguardView.startDisappearAnimation(runnable); 203 } else if (runnable != null) { 204 runnable.run(); 205 } 206 } 207 208 /** 209 * Reset the state of the view. 210 */ reset()211 public void reset() { 212 cancelShowRunnable(); 213 inflateView(); 214 mFalsingManager.onBouncerHidden(); 215 } 216 onScreenTurnedOff()217 public void onScreenTurnedOff() { 218 if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) { 219 mKeyguardView.onPause(); 220 } 221 } 222 isShowing()223 public boolean isShowing() { 224 return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE); 225 } 226 prepare()227 public void prepare() { 228 boolean wasInitialized = mRoot != null; 229 ensureView(); 230 if (wasInitialized) { 231 mKeyguardView.showPrimarySecurityScreen(); 232 } 233 mBouncerPromptReason = mCallback.getBouncerPromptReason(); 234 } 235 ensureView()236 protected void ensureView() { 237 mHandler.removeCallbacks(mRemoveViewRunnable); 238 if (mRoot == null) { 239 inflateView(); 240 } 241 } 242 inflateView()243 protected void inflateView() { 244 removeView(); 245 mHandler.removeCallbacks(mRemoveViewRunnable); 246 mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); 247 mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view); 248 mKeyguardView.setLockPatternUtils(mLockPatternUtils); 249 mKeyguardView.setViewMediatorCallback(mCallback); 250 mContainer.addView(mRoot, mContainer.getChildCount()); 251 mRoot.setVisibility(View.INVISIBLE); 252 } 253 removeView()254 protected void removeView() { 255 if (mRoot != null && mRoot.getParent() == mContainer) { 256 mContainer.removeView(mRoot); 257 mRoot = null; 258 } 259 } 260 onBackPressed()261 public boolean onBackPressed() { 262 return mKeyguardView != null && mKeyguardView.handleBackKey(); 263 } 264 265 /** 266 * @return True if and only if the security method should be shown before showing the 267 * notifications on Keyguard, like SIM PIN/PUK. 268 */ needsFullscreenBouncer()269 public boolean needsFullscreenBouncer() { 270 ensureView(); 271 if (mKeyguardView != null) { 272 SecurityMode mode = mKeyguardView.getSecurityMode(); 273 return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; 274 } 275 return false; 276 } 277 278 /** 279 * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which 280 * makes this method much faster. 281 */ isFullscreenBouncer()282 public boolean isFullscreenBouncer() { 283 if (mKeyguardView != null) { 284 SecurityMode mode = mKeyguardView.getCurrentSecurityMode(); 285 return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; 286 } 287 return false; 288 } 289 290 /** 291 * WARNING: This method might cause Binder calls. 292 */ isSecure()293 public boolean isSecure() { 294 return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None; 295 } 296 shouldDismissOnMenuPressed()297 public boolean shouldDismissOnMenuPressed() { 298 return mKeyguardView.shouldEnableMenuKey(); 299 } 300 interceptMediaKey(KeyEvent event)301 public boolean interceptMediaKey(KeyEvent event) { 302 ensureView(); 303 return mKeyguardView.interceptMediaKey(event); 304 } 305 notifyKeyguardAuthenticated(boolean strongAuth)306 public void notifyKeyguardAuthenticated(boolean strongAuth) { 307 ensureView(); 308 mKeyguardView.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser()); 309 } 310 } 311