1 /* 2 * Copyright (C) 2012 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 package com.android.keyguard; 17 18 import android.content.Context; 19 import android.graphics.drawable.Drawable; 20 import android.os.RemoteException; 21 import android.os.ServiceManager; 22 import android.telephony.TelephonyManager; 23 import android.util.AttributeSet; 24 import android.util.Log; 25 import android.view.IRotationWatcher; 26 import android.view.IWindowManager; 27 import android.view.View; 28 import android.widget.ImageButton; 29 import android.widget.LinearLayout; 30 31 import com.android.internal.widget.LockPatternUtils; 32 33 import java.lang.Math; 34 35 public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView { 36 37 private static final String TAG = "FULKeyguardFaceUnlockView"; 38 private static final boolean DEBUG = KeyguardConstants.DEBUG; 39 private KeyguardSecurityCallback mKeyguardSecurityCallback; 40 private LockPatternUtils mLockPatternUtils; 41 private BiometricSensorUnlock mBiometricUnlock; 42 private View mFaceUnlockAreaView; 43 private ImageButton mCancelButton; 44 private SecurityMessageDisplay mSecurityMessageDisplay; 45 private View mEcaView; 46 private Drawable mBouncerFrame; 47 48 private boolean mIsBouncerVisibleToUser = false; 49 private final Object mIsBouncerVisibleToUserLock = new Object(); 50 51 private int mLastRotation; 52 private boolean mWatchingRotation; 53 private final IWindowManager mWindowManager = 54 IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 55 56 private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { 57 public void onRotationChanged(int rotation) { 58 if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation); 59 60 // If the difference between the new rotation value and the previous rotation value is 61 // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock 62 // and starts it in the new position. This is not performed for 90 degree rotations 63 // since a 90 degree rotation is a configuration change, which takes care of this for 64 // us. 65 if (Math.abs(rotation - mLastRotation) == 2) { 66 if (mBiometricUnlock != null) { 67 mBiometricUnlock.stop(); 68 maybeStartBiometricUnlock(); 69 } 70 } 71 mLastRotation = rotation; 72 } 73 }; 74 75 public KeyguardFaceUnlockView(Context context) { 76 this(context, null); 77 } 78 79 public KeyguardFaceUnlockView(Context context, AttributeSet attrs) { 80 super(context, attrs); 81 } 82 83 @Override 84 protected void onFinishInflate() { 85 super.onFinishInflate(); 86 87 initializeBiometricUnlockView(); 88 89 mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); 90 mEcaView = findViewById(R.id.keyguard_selector_fade_container); 91 View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); 92 if (bouncerFrameView != null) { 93 mBouncerFrame = bouncerFrameView.getBackground(); 94 } 95 } 96 97 @Override 98 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 99 mKeyguardSecurityCallback = callback; 100 // TODO: formalize this in the interface or factor it out 101 ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback); 102 } 103 104 @Override 105 public void setLockPatternUtils(LockPatternUtils utils) { 106 mLockPatternUtils = utils; 107 } 108 109 @Override 110 public void reset() { 111 112 } 113 114 @Override 115 public void onDetachedFromWindow() { 116 if (DEBUG) Log.d(TAG, "onDetachedFromWindow()"); 117 if (mBiometricUnlock != null) { 118 mBiometricUnlock.stop(); 119 } 120 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); 121 if (mWatchingRotation) { 122 try { 123 mWindowManager.removeRotationWatcher(mRotationWatcher); 124 mWatchingRotation = false; 125 } catch (RemoteException e) { 126 Log.e(TAG, "Remote exception when removing rotation watcher"); 127 } 128 } 129 } 130 131 @Override 132 public void onPause() { 133 if (DEBUG) Log.d(TAG, "onPause()"); 134 if (mBiometricUnlock != null) { 135 mBiometricUnlock.stop(); 136 } 137 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); 138 if (mWatchingRotation) { 139 try { 140 mWindowManager.removeRotationWatcher(mRotationWatcher); 141 mWatchingRotation = false; 142 } catch (RemoteException e) { 143 Log.e(TAG, "Remote exception when removing rotation watcher"); 144 } 145 } 146 } 147 148 @Override 149 public void onResume(int reason) { 150 if (DEBUG) Log.d(TAG, "onResume()"); 151 synchronized (mIsBouncerVisibleToUserLock) { 152 mIsBouncerVisibleToUser = isBouncerVisibleToUser(); 153 } 154 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); 155 156 // Registers a callback which handles stopping the biometric unlock and restarting it in 157 // the new position for a 180 degree rotation change. 158 if (!mWatchingRotation) { 159 try { 160 mLastRotation = mWindowManager.watchRotation(mRotationWatcher); 161 mWatchingRotation = true; 162 } catch (RemoteException e) { 163 Log.e(TAG, "Remote exception when adding rotation watcher"); 164 } 165 } 166 } 167 168 @Override 169 public boolean needsInput() { 170 return false; 171 } 172 173 @Override 174 public KeyguardSecurityCallback getCallback() { 175 return mKeyguardSecurityCallback; 176 } 177 178 @Override 179 protected void onLayout(boolean changed, int l, int t, int r, int b) { 180 super.onLayout(changed, l, t, r, b); 181 mBiometricUnlock.initializeView(mFaceUnlockAreaView); 182 } 183 184 private void initializeBiometricUnlockView() { 185 if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()"); 186 mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view); 187 if (mFaceUnlockAreaView != null) { 188 mBiometricUnlock = new FaceUnlock(mContext); 189 190 mCancelButton = (ImageButton) findViewById(R.id.face_unlock_cancel_button); 191 mCancelButton.setOnClickListener(new OnClickListener() { 192 @Override 193 public void onClick(View v) { 194 mBiometricUnlock.stopAndShowBackup(); 195 } 196 }); 197 } else { 198 Log.w(TAG, "Couldn't find biometric unlock view"); 199 } 200 } 201 202 /** 203 * Starts the biometric unlock if it should be started based on a number of factors. If it 204 * should not be started, it either goes to the back up, or remains showing to prepare for 205 * it being started later. 206 */ 207 private void maybeStartBiometricUnlock() { 208 if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()"); 209 if (mBiometricUnlock != null) { 210 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 211 final boolean backupIsTimedOut = ( 212 monitor.getFailedUnlockAttempts() >= 213 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT); 214 215 boolean isBouncerVisibleToUser; 216 synchronized(mIsBouncerVisibleToUserLock) { 217 isBouncerVisibleToUser = mIsBouncerVisibleToUser; 218 } 219 220 // Don't start it if the bouncer is not showing, but keep this view up because we want 221 // it here and ready for when the bouncer does show. 222 if (!isBouncerVisibleToUser) { 223 mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt. 224 return; 225 } 226 227 // Although these same conditions are handled in KeyguardSecurityModel, they are still 228 // necessary here. When a tablet is rotated 90 degrees, a configuration change is 229 // triggered and everything is torn down and reconstructed. That means 230 // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even 231 // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed. 232 // However, for a 180 degree rotation, no configuration change is triggered, so only 233 // the logic here is capable of suppressing Face Unlock. 234 if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE 235 && monitor.isAlternateUnlockEnabled() 236 && !monitor.getMaxBiometricUnlockAttemptsReached() 237 && !backupIsTimedOut) { 238 mBiometricUnlock.start(); 239 } else { 240 mBiometricUnlock.stopAndShowBackup(); 241 } 242 } 243 } 244 245 // Returns true if the device is currently in a state where the user is seeing the bouncer. 246 // This requires isKeyguardBouncer() to be true, but that doesn't imply that the screen is on or 247 // the keyguard visibility is set to true, so we must check those conditions as well. 248 private boolean isBouncerVisibleToUser() { 249 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 250 return updateMonitor.isKeyguardBouncer() && updateMonitor.isKeyguardVisible() && 251 updateMonitor.isScreenOn(); 252 } 253 254 // Starts the biometric unlock if the bouncer was not previously visible to the user, but is now 255 // visibile to the user. Stops the biometric unlock if the bouncer was previously visible to 256 // the user, but is no longer visible to the user. 257 private void handleBouncerUserVisibilityChanged() { 258 boolean wasBouncerVisibleToUser; 259 synchronized(mIsBouncerVisibleToUserLock) { 260 wasBouncerVisibleToUser = mIsBouncerVisibleToUser; 261 mIsBouncerVisibleToUser = isBouncerVisibleToUser(); 262 } 263 264 if (mBiometricUnlock != null) { 265 if (wasBouncerVisibleToUser && !mIsBouncerVisibleToUser) { 266 mBiometricUnlock.stop(); 267 } else if (!wasBouncerVisibleToUser && mIsBouncerVisibleToUser) { 268 maybeStartBiometricUnlock(); 269 } 270 } 271 } 272 273 KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { 274 // We need to stop the biometric unlock when a phone call comes in 275 @Override 276 public void onPhoneStateChanged(int phoneState) { 277 if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")"); 278 if (phoneState == TelephonyManager.CALL_STATE_RINGING) { 279 if (mBiometricUnlock != null) { 280 mBiometricUnlock.stopAndShowBackup(); 281 } 282 } 283 } 284 285 @Override 286 public void onUserSwitching(int userId) { 287 if (DEBUG) Log.d(TAG, "onUserSwitched(" + userId + ")"); 288 if (mBiometricUnlock != null) { 289 mBiometricUnlock.stop(); 290 } 291 // No longer required; static value set by KeyguardViewMediator 292 // mLockPatternUtils.setCurrentUser(userId); 293 } 294 295 @Override 296 public void onUserSwitchComplete(int userId) { 297 if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")"); 298 if (mBiometricUnlock != null) { 299 maybeStartBiometricUnlock(); 300 } 301 } 302 303 @Override 304 public void onKeyguardVisibilityChanged(boolean showing) { 305 if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")"); 306 handleBouncerUserVisibilityChanged(); 307 } 308 309 @Override 310 public void onKeyguardBouncerChanged(boolean bouncer) { 311 if (DEBUG) Log.d(TAG, "onKeyguardBouncerChanged(" + bouncer + ")"); 312 handleBouncerUserVisibilityChanged(); 313 } 314 315 @Override 316 public void onScreenTurnedOn() { 317 if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); 318 handleBouncerUserVisibilityChanged(); 319 } 320 321 @Override 322 public void onScreenTurnedOff(int why) { 323 if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); 324 handleBouncerUserVisibilityChanged(); 325 } 326 327 @Override 328 public void onEmergencyCallAction() { 329 if (mBiometricUnlock != null) { 330 mBiometricUnlock.stop(); 331 } 332 } 333 }; 334 335 @Override 336 public void showUsabilityHint() { 337 } 338 339 @Override 340 public void showBouncer(int duration) { 341 KeyguardSecurityViewHelper. 342 showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); 343 } 344 345 @Override 346 public void hideBouncer(int duration) { 347 KeyguardSecurityViewHelper. 348 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); 349 } 350 351 @Override 352 public void startAppearAnimation() { 353 // TODO. 354 } 355 356 @Override 357 public boolean startDisappearAnimation(Runnable finishRunnable) { 358 return false; 359 } 360 } 361