Home | History | Annotate | Download | only in keyguard
      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