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