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 
     17 package com.android.keyguard;
     18 
     19 import com.android.internal.policy.IFaceLockCallback;
     20 import com.android.internal.policy.IFaceLockInterface;
     21 import com.android.internal.widget.LockPatternUtils;
     22 
     23 import android.app.admin.DevicePolicyManager;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.ServiceConnection;
     28 import android.os.Handler;
     29 import android.os.IBinder;
     30 import android.os.Looper;
     31 import android.os.Message;
     32 import android.os.PowerManager;
     33 import android.os.RemoteException;
     34 import android.os.UserHandle;
     35 import android.util.Log;
     36 import android.view.View;
     37 
     38 public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
     39 
     40     private static final boolean DEBUG = false;
     41     private static final String TAG = "FULLockscreen";
     42 
     43     private final Context mContext;
     44     private final LockPatternUtils mLockPatternUtils;
     45 
     46     // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
     47     private boolean mServiceRunning = false;
     48     // TODO: now that the code has been restructure to do almost all operations from a handler, this
     49     // lock may no longer be necessary.
     50     private final Object mServiceRunningLock = new Object();
     51     private IFaceLockInterface mService;
     52     private boolean mBoundToService = false;
     53     private View mFaceUnlockView;
     54 
     55     private Handler mHandler;
     56     private final int MSG_SERVICE_CONNECTED = 0;
     57     private final int MSG_SERVICE_DISCONNECTED = 1;
     58     private final int MSG_UNLOCK = 2;
     59     private final int MSG_CANCEL = 3;
     60     private final int MSG_REPORT_FAILED_ATTEMPT = 4;
     61     private final int MSG_POKE_WAKELOCK = 5;
     62 
     63     // TODO: This was added for the purpose of adhering to what the biometric interface expects
     64     // the isRunning() function to return.  However, it is probably not necessary to have both
     65     // mRunning and mServiceRunning.  I'd just rather wait to change that logic.
     66     private volatile boolean mIsRunning = false;
     67 
     68     // So the user has a consistent amount of time when brought to the backup method from Face
     69     // Unlock
     70     private final int BACKUP_LOCK_TIMEOUT = 5000;
     71 
     72     KeyguardSecurityCallback mKeyguardScreenCallback;
     73 
     74     /**
     75      * Stores some of the structures that Face Unlock will need to access and creates the handler
     76      * will be used to execute messages on the UI thread.
     77      */
     78     public FaceUnlock(Context context) {
     79         mContext = context;
     80         mLockPatternUtils = new LockPatternUtils(context);
     81         mHandler = new Handler(this);
     82     }
     83 
     84     public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
     85         mKeyguardScreenCallback = keyguardScreenCallback;
     86     }
     87 
     88     /**
     89      * Stores and displays the view that Face Unlock is allowed to draw within.
     90      * TODO: since the layout object will eventually be shared by multiple biometric unlock
     91      * methods, we will have to add our other views (background, cancel button) here.
     92      */
     93     public void initializeView(View biometricUnlockView) {
     94         Log.d(TAG, "initializeView()");
     95         mFaceUnlockView = biometricUnlockView;
     96     }
     97 
     98     /**
     99      * Indicates whether Face Unlock is currently running.
    100      */
    101     public boolean isRunning() {
    102         return mIsRunning;
    103     }
    104 
    105     /**
    106      * Dismisses face unlock and goes to the backup lock
    107      */
    108     public void stopAndShowBackup() {
    109         if (DEBUG) Log.d(TAG, "stopAndShowBackup()");
    110         mHandler.sendEmptyMessage(MSG_CANCEL);
    111     }
    112 
    113     /**
    114      * Binds to the Face Unlock service.  Face Unlock will be started when the bind completes.  The
    115      * Face Unlock view is displayed to hide the backup lock while the service is starting up.
    116      * Called on the UI thread.
    117      */
    118     public boolean start() {
    119         if (DEBUG) Log.d(TAG, "start()");
    120         if (mHandler.getLooper() != Looper.myLooper()) {
    121             Log.e(TAG, "start() called off of the UI thread");
    122         }
    123 
    124         if (mIsRunning) {
    125             Log.w(TAG, "start() called when already running");
    126         }
    127 
    128         if (!mBoundToService) {
    129             Log.d(TAG, "Binding to Face Unlock service for user="
    130                     + mLockPatternUtils.getCurrentUser());
    131             mContext.bindServiceAsUser(new Intent(IFaceLockInterface.class.getName()),
    132                     mConnection,
    133                     Context.BIND_AUTO_CREATE,
    134                     new UserHandle(mLockPatternUtils.getCurrentUser()));
    135             mBoundToService = true;
    136         } else {
    137             Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
    138         }
    139 
    140         mIsRunning = true;
    141         return true;
    142     }
    143 
    144     /**
    145      * Stops Face Unlock and unbinds from the service.  Called on the UI thread.
    146      */
    147     public boolean stop() {
    148         if (DEBUG) Log.d(TAG, "stop()");
    149         if (mHandler.getLooper() != Looper.myLooper()) {
    150             Log.e(TAG, "stop() called from non-UI thread");
    151         }
    152 
    153         // Clearing any old service connected messages.
    154         mHandler.removeMessages(MSG_SERVICE_CONNECTED);
    155 
    156         boolean mWasRunning = mIsRunning;
    157 
    158         stopUi();
    159 
    160         if (mBoundToService) {
    161             if (mService != null) {
    162                 try {
    163                     mService.unregisterCallback(mFaceUnlockCallback);
    164                 } catch (RemoteException e) {
    165                     // Not much we can do
    166                 }
    167             }
    168             Log.d(TAG, "Unbinding from Face Unlock service");
    169             mContext.unbindService(mConnection);
    170             mBoundToService = false;
    171         } else {
    172             // This is usually not an error when this happens.  Sometimes we will tell it to
    173             // unbind multiple times because it's called from both onWindowFocusChanged and
    174             // onDetachedFromWindow.
    175             if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
    176         }
    177         mIsRunning = false;
    178         return mWasRunning;
    179     }
    180 
    181     /**
    182      * Frees up resources used by Face Unlock and stops it if it is still running.
    183      */
    184     public void cleanUp() {
    185         if (DEBUG) Log.d(TAG, "cleanUp()");
    186         if (mService != null) {
    187             try {
    188                 mService.unregisterCallback(mFaceUnlockCallback);
    189             } catch (RemoteException e) {
    190                 // Not much we can do
    191             }
    192             stopUi();
    193             mService = null;
    194         }
    195     }
    196 
    197     /**
    198      * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
    199      */
    200     public int getQuality() {
    201         return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
    202     }
    203 
    204     /**
    205      * Handles messages such that everything happens on the UI thread in a deterministic order.
    206      * Calls from the Face Unlock service come from binder threads.  Calls from lockscreen typically
    207      * come from the UI thread.  This makes sure there are no race conditions between those calls.
    208      */
    209     public boolean handleMessage(Message msg) {
    210         switch (msg.what) {
    211             case MSG_SERVICE_CONNECTED:
    212                 handleServiceConnected();
    213                 break;
    214             case MSG_SERVICE_DISCONNECTED:
    215                 handleServiceDisconnected();
    216                 break;
    217             case MSG_UNLOCK:
    218                 handleUnlock(msg.arg1);
    219                 break;
    220             case MSG_CANCEL:
    221                 handleCancel();
    222                 break;
    223             case MSG_REPORT_FAILED_ATTEMPT:
    224                 handleReportFailedAttempt();
    225                 break;
    226             case MSG_POKE_WAKELOCK:
    227                 handlePokeWakelock(msg.arg1);
    228                 break;
    229             default:
    230                 Log.e(TAG, "Unhandled message");
    231                 return false;
    232         }
    233         return true;
    234     }
    235 
    236     /**
    237      * Tells the service to start its UI via an AIDL interface.  Called when the
    238      * onServiceConnected() callback is received.
    239      */
    240     void handleServiceConnected() {
    241         Log.d(TAG, "handleServiceConnected()");
    242 
    243         // It is possible that an unbind has occurred in the time between the bind and when this
    244         // function is reached.  If an unbind has already occurred, proceeding on to call startUi()
    245         // can result in a fatal error.  Note that the onServiceConnected() callback is
    246         // asynchronous, so this possibility would still exist if we executed this directly in
    247         // onServiceConnected() rather than using a handler.
    248         if (!mBoundToService) {
    249             Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
    250             return;
    251         }
    252 
    253         try {
    254             mService.registerCallback(mFaceUnlockCallback);
    255         } catch (RemoteException e) {
    256             Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
    257             mService = null;
    258             mBoundToService = false;
    259             mIsRunning = false;
    260             return;
    261         }
    262 
    263         if (mFaceUnlockView != null) {
    264             IBinder windowToken = mFaceUnlockView.getWindowToken();
    265             if (windowToken != null) {
    266                 // When switching between portrait and landscape view while Face Unlock is running,
    267                 // the screen will eventually go dark unless we poke the wakelock when Face Unlock
    268                 // is restarted.
    269                 mKeyguardScreenCallback.userActivity(0);
    270 
    271                 int[] position;
    272                 position = new int[2];
    273                 mFaceUnlockView.getLocationInWindow(position);
    274                 startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
    275                         mFaceUnlockView.getHeight());
    276             } else {
    277                 Log.e(TAG, "windowToken is null in handleServiceConnected()");
    278             }
    279         }
    280     }
    281 
    282     /**
    283      * Called when the onServiceDisconnected() callback is received.  This should not happen during
    284      * normal operation.  It indicates an error has occurred.
    285      */
    286     void handleServiceDisconnected() {
    287         Log.e(TAG, "handleServiceDisconnected()");
    288         // TODO: this lock may no longer be needed now that everything is being called from a
    289         // handler
    290         synchronized (mServiceRunningLock) {
    291             mService = null;
    292             mServiceRunning = false;
    293         }
    294         mBoundToService = false;
    295         mIsRunning = false;
    296     }
    297 
    298     /**
    299      * Stops the Face Unlock service and tells the device to grant access to the user.
    300      */
    301     void handleUnlock(int authenticatedUserId) {
    302         if (DEBUG) Log.d(TAG, "handleUnlock()");
    303         stop();
    304         int currentUserId = mLockPatternUtils.getCurrentUser();
    305         if (authenticatedUserId == currentUserId) {
    306             if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId);
    307             mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
    308             mKeyguardScreenCallback.dismiss(true);
    309         } else {
    310             Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId +
    311                     ") because the current user is " + currentUserId);
    312         }
    313     }
    314 
    315     /**
    316      * Stops the Face Unlock service and goes to the backup lock.
    317      */
    318     void handleCancel() {
    319         if (DEBUG) Log.d(TAG, "handleCancel()");
    320         // We are going to the backup method, so we don't want to see Face Unlock again until the
    321         // next time the user visits keyguard.
    322         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
    323 
    324         mKeyguardScreenCallback.showBackupSecurity();
    325         stop();
    326         mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
    327     }
    328 
    329     /**
    330      * Increments the number of failed Face Unlock attempts.
    331      */
    332     void handleReportFailedAttempt() {
    333         if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
    334         // We are going to the backup method, so we don't want to see Face Unlock again until the
    335         // next time the user visits keyguard.
    336         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
    337 
    338         mKeyguardScreenCallback.reportFailedUnlockAttempt();
    339     }
    340 
    341     /**
    342      * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific
    343      * amount of time.
    344      */
    345     void handlePokeWakelock(int millis) {
    346       PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    347       if (powerManager.isScreenOn()) {
    348         mKeyguardScreenCallback.userActivity(millis);
    349       }
    350     }
    351 
    352     /**
    353      * Implements service connection methods.
    354      */
    355     private ServiceConnection mConnection = new ServiceConnection() {
    356         /**
    357          * Called when the Face Unlock service connects after calling bind().
    358          */
    359         public void onServiceConnected(ComponentName className, IBinder iservice) {
    360             Log.d(TAG, "Connected to Face Unlock service");
    361             mService = IFaceLockInterface.Stub.asInterface(iservice);
    362             mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED);
    363         }
    364 
    365         /**
    366          * Called if the Face Unlock service unexpectedly disconnects.  This indicates an error.
    367          */
    368         public void onServiceDisconnected(ComponentName className) {
    369             Log.e(TAG, "Unexpected disconnect from Face Unlock service");
    370             mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
    371         }
    372     };
    373 
    374     /**
    375      * Tells the Face Unlock service to start displaying its UI and start processing.
    376      */
    377     private void startUi(IBinder windowToken, int x, int y, int w, int h) {
    378         if (DEBUG) Log.d(TAG, "startUi()");
    379         synchronized (mServiceRunningLock) {
    380             if (!mServiceRunning) {
    381                 Log.d(TAG, "Starting Face Unlock");
    382                 try {
    383                     mService.startUi(windowToken, x, y, w, h,
    384                             mLockPatternUtils.isBiometricWeakLivelinessEnabled());
    385                 } catch (RemoteException e) {
    386                     Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
    387                     return;
    388                 }
    389                 mServiceRunning = true;
    390             } else {
    391                 Log.w(TAG, "startUi() attempted while running");
    392             }
    393         }
    394     }
    395 
    396     /**
    397      * Tells the Face Unlock service to stop displaying its UI and stop processing.
    398      */
    399     private void stopUi() {
    400         if (DEBUG) Log.d(TAG, "stopUi()");
    401         // Note that attempting to stop Face Unlock when it's not running is not an issue.
    402         // Face Unlock can return, which stops it and then we try to stop it when the
    403         // screen is turned off.  That's why we check.
    404         synchronized (mServiceRunningLock) {
    405             if (mServiceRunning) {
    406                 Log.d(TAG, "Stopping Face Unlock");
    407                 try {
    408                     mService.stopUi();
    409                 } catch (RemoteException e) {
    410                     Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
    411                 }
    412                 mServiceRunning = false;
    413             } else {
    414                 // This is usually not an error when this happens.  Sometimes we will tell it to
    415                 // stop multiple times because it's called from both onWindowFocusChanged and
    416                 // onDetachedFromWindow.
    417                 if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
    418             }
    419         }
    420     }
    421 
    422     /**
    423      * Implements the AIDL biometric unlock service callback interface.
    424      */
    425     private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
    426         /**
    427          * Called when Face Unlock wants to grant access to the user.
    428          */
    429         public void unlock() {
    430             if (DEBUG) Log.d(TAG, "unlock()");
    431             Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1);
    432             mHandler.sendMessage(message);
    433         }
    434 
    435         /**
    436          * Called when Face Unlock wants to go to the backup.
    437          */
    438         public void cancel() {
    439             if (DEBUG) Log.d(TAG, "cancel()");
    440             mHandler.sendEmptyMessage(MSG_CANCEL);
    441         }
    442 
    443         /**
    444          * Called when Face Unlock wants to increment the number of failed attempts.
    445          */
    446         public void reportFailedAttempt() {
    447             if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
    448             mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
    449         }
    450 
    451         /**
    452          * Called when Face Unlock wants to keep the screen alive and active for a specific amount
    453          * of time.
    454          */
    455         public void pokeWakelock(int millis) {
    456             if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms");
    457             Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1);
    458             mHandler.sendMessage(message);
    459         }
    460 
    461     };
    462 }
    463