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.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