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