1. 程式人生 > >Android N指紋識別

Android N指紋識別

第一部分 指紋模組流程分析

一、Fingerprint模組架構

Fingerprint模組架構圖如下,這裡分為application,framework,fingerprintd和FingerprintHal這幾個部分,不涉及指紋的IC庫和驅動這部分,這部分邏輯由指紋廠商來實現。 
這裡寫圖片描述

Setting中指紋程式碼

這裡寫圖片描述

SystemUI中指紋解鎖程式碼

這裡寫圖片描述

二、Fingerprint framework初始化流程

在系統開機的時候,會啟動各種Service,包括FingerprintService。從下圖的開機log(sys_log.boot)中可以看出:

05-29 10:37:57.870869  1127  1127 I SystemServiceManager: Starting com.android.server.dreams.DreamManagerService
05-29 10:37:57.874643  1127  1127 I SystemServer: StartAssetAtlasService
05-29 10:37:57.883240  1127  1127 I SystemServiceManager: Starting com.android.server.print.PrintManagerService
05-29 10:37:57.910104  1127  1127 I SystemServiceManager: Starting com.android.server.restrictions.RestrictionsManagerService
05-29 10:37:57.913926  1127  1127 I SystemServiceManager: Starting com.android.server.media.MediaSessionService
05-29 10:37:57.926584  1127  1127 I SystemServer: StartMediaRouterService
05-29 10:37:57.939619  1127  1127 I SystemServiceManager: Starting com.android.server.trust.TrustManagerService
05-29 10:37:57.952689  1127  1127 I SystemServiceManager: Starting com.android.server.fingerprint.FingerprintService
05-29 10:37:58.866228  1127  1127 V FingerprintService: Fingerprint HAL id: 488345235968
05-29 10:37:58.867305  1127  1127 I SystemServer: StartBackgroundDexOptService
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

FingerprintService的啟動在SystemServer.Java的startOtherService方法中

/**
* Starts a miscellaneous grab bag of stuff that has yet to be refactored
* and organized.
*/
private void startOtherServices() {
   final Context context = mSystemContext;
   VibratorService vibrator = null;
   IMountService mountService = null;
   .......
   //啟動FingerprintService
   if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
       mSystemServiceManager.startService(FingerprintService.class);
   }
        ......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這裡啟動的時候,會將FingerprintService新增到ServiceManager中去,如下圖: 
這裡寫圖片描述
將FingerprintService新增到ServiceManager中後,在SystemServiceRegistry.java中靜態程式碼塊中註冊服務的時候,可以從ServiceManager中獲取FingerprintService的Binder物件,從而可以構造出FingerprintManager物件,這樣app端就可以通過Context來獲取FingerprintManager物件。另外FingerprintService的onStart()方法中還會呼叫getFingerprintDaemon()來完成以下步驟:

//①獲取fingerprintd
//②向fingerprintd註冊回撥函式mDaemonCallback
//③呼叫獲取fingerprintd的openhal函式
public IFingerprintDaemon getFingerprintDaemon() {
             //①獲取fingerprintd
            mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(
            FINGERPRINTD));
            mDaemon.asBinder().linkToDeath(this, 0);
            //②向fingerprintd註冊回撥函式mDaemonCallback
            mDaemon.init(mDaemonCallback);
            //③呼叫獲取fingerprintd的openhal函式
            mHalDeviceId = mDaemon.openHal();
            ......
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

app端通過Context獲取FingerprintManager,通過呼叫FingerprintManager的介面來實現相應的功能,FingerprintManager轉調FingerprintService中方法,FingerprintService負責管理整個註冊,識別、刪除指紋、檢查許可權等流程的邏輯,FingerprintService呼叫fingerprintd的介面,通過fingerprintd和FingerprintHal層進行通訊。

三、fingerprintd

fingerprintd在system/core/fingerprintd目錄下

這裡寫圖片描述

fingerprintd可以分為四個部分: 
1. fingerprintd.cpp “負責將fingerprintd加入到ServiceManager中,以便FingerprintService能夠獲取” 
2. IFingerprintDaemon.h/IFingerprintDaemon.cpp “負責java層到fingerprintd的Binder通訊(我們指紋錄入和識別都會呼叫裡面的方法)” 
3. FingerprintDaemonProxy.h/FingerprintDaemonProxy.cpp “負責fingerprintd和Fignerprint hal層的通訊” 
4. IFingerprintDaemonCallback.h/IFingerprintDaemonCallback.cpp “負責將指紋的回撥結果傳給java層”

四、指紋錄製流程

這裡寫圖片描述

指紋錄製的activity為FingerprintEnrollEnrolling實現了FingerprintEnrollSidecar.Listener 介面。

    //指紋錄製時提示(比如太快,移動手指之類)
    @Override
    public void onEnrollmentHelp(CharSequence helpString) {
        mErrorText.setText(helpString);
    }
    //提示指紋錄製過程中超時,或者未註冊。
    @Override
    public void onEnrollmentError(int errMsgId, CharSequence errString) {
        int msgId;
         ......
         showErrorDialog(getText(msgId), errMsgId);
        ......
    }
    //錄製過程中進度的變化
    @Override
    public void onEnrollmentProgressChange(int steps, int remaining) {
        updateProgress(true /* animate */);//更新進度
        updateDescription();//更新描述
        animateFlash();//更新動畫
        ......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

FingerprintEnrollSidecar的onStart方法中呼叫了startEnrollment(),該方法中呼叫FingeprintManager的enroll方法,並且傳入了EnrollmentCallback物件,EnrollmentCallback是指紋錄入結果的回撥,分別呼叫了FingerprintEnrollSidecar.Listener 介面中的方法,這樣就能更新指紋錄製的進度和錄製結果。

    private void startEnrollment() {
        ......
        mFingerprintManager.enroll(mToken, mEnrollmentCancel,
                0 /* flags */, mUserId, mEnrollmentCallback);
        mEnrolling = true;
    }

    private FingerprintManager.EnrollmentCallback mEnrollmentCallback
            = new FingerprintManager.EnrollmentCallback() {

        @Override
        public void onEnrollmentProgress(int remaining) {
           ......
                mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
            }
        }

        @Override
        public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
            if (mListener != null) {
                mListener.onEnrollmentHelp(helpString);
            }
        }

        @Override
        public void onEnrollmentError(int errMsgId, CharSequence errString) {
            if (mListener != null) {
                mListener.onEnrollmentError(errMsgId, errString);
            }
            mEnrolling = false;
        }
    };

    public void enroll(byte [] token, CancellationSignal cancel, int flags,
            int userId, EnrollmentCallback callback) {
            ......
            mService.enroll(mToken, token, userId, mServiceReceiver, flags,
                    mContext.getOpPackageName());
            ......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

注意傳入的mServiceReceiver物件,這個物件會通過handler傳送相關訊息去呼叫EnrollmentCallback或者AuthenticationCallback中方法。

 private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
        @Override // binder call
        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
        }

        @Override // binder call
        public void onAcquired(long deviceId, int acquireInfo) {
            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
        }

        @Override // binder call
        public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
        }

        @Override // binder call
        public void onAuthenticationFailed(long deviceId) {
            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
        }

        @Override // binder call
        public void onError(long deviceId, int error) {
            mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
        }

        @Override // binder call
        public void onRemoved(long deviceId, int fingerId, int groupId) {
            mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
        }
    };

public void handleMessage(android.os.Message msg) {
            switch(msg.what) {
                case MSG_ENROLL_RESULT:
                    sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
                    break;
                case MSG_ACQUIRED:
                    sendAcquiredResult((Long) msg.obj /* deviceId */, 
                    msg.arg1 /* acquire info */);
                    break;
                case MSG_AUTHENTICATION_SUCCEEDED:
                    sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
                    break;
                case MSG_AUTHENTICATION_FAILED:
                    sendAuthenticatedFailed();
                    break;
                case MSG_ERROR:
                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
                    break;
                case MSG_REMOVED:
                    sendRemovedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
                            msg.arg2 /* groupId */);
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

FingerprintManager與FingerprintService直接通過aidl進行通訊,在FingerprintService中內部類FingerprintServiceWrapper實現了IFingerprintService.Stub,我們呼叫的FingerManger的enroll方法就是呼叫FingerprintServiceWrapper類中的enroll()。

private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
        ......
        @Override // Binder call
        public void enroll(final IBinder token, final byte[] cryptoToken, 
                final int userId,final IFingerprintServiceReceiver receiver,
                final int flags,
                final String opPackageName) {
            checkPermission(MANAGE_FINGERPRINT);
            final int limit =  mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);

            final int enrolled = FingerprintService.this.
                    getEnrolledFingerprints(userId).size();
            if (enrolled >= limit) {
                Slog.w(TAG, "Too many fingerprints registered");
                return;
            }
            ......
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    startEnrollment(token, cryptoToken, userId, receiver, 
                    flags,restricted, opPackageName);
                }
            });
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

startEnrollment方法中會呼叫EnrollClient的start方法,EnrollClient是為給定的客戶端跟蹤指紋錄製狀態。

/**
 * A class to keep track of the enrollment state for a given client.
 */
public abstract class EnrollClient extends ClientMonitor {
    ......
    public EnrollClient(Context context, long halDeviceId, IBinder token,
            IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
            boolean restricted, String owner) {
        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
        ......
    }

    @Override
    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
        ......
        return sendEnrollResult(fingerId, groupId, remaining);
    }

    /*
     * @return true if we're done.
     */
    private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
        IFingerprintServiceReceiver receiver = getReceiver();
            ......
            receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
            ......
    }

    @Override
    public int start() {
        IFingerprintDaemon daemon = getFingerprintDaemon();
            ......
            final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
            ......
        return 0; // success
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

start方法會呼叫fingerprintd,呼叫底層的指紋庫,底層庫返回結果後會呼叫onEnrollResult來反饋結果給receiver,在往上層反饋。這就是指紋的錄製流程。

五、指紋的解鎖流程

指紋解鎖的核心類FingerprintUnlockController,實現了KeyguardUpdateMonitorCallback

public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
    @Override
    public void onFingerprintAuthenticated(int userId) {
        ......
        switch (mMode) {
            case MODE_DISMISS_BOUNCER://亮屏並且出現圖案鎖
                mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
                        false /* strongAuth */);
                break;
            case MODE_UNLOCK://亮屏不出現圖案鎖
                if (!wasDeviceInteractive) {
                    mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
                }
                mStatusBarKeyguardViewManager.animateCollapsePanels(
                        FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
                break;
            ......
            case MODE_WAKE_AND_UNLOCK://息屏鎖屏解鎖
                mStatusBarWindowManager.setStatusBarFocusable(false);
                mDozeScrimController.abortPulsing();
                mKeyguardViewMediator.onWakeAndUnlocking();
                mScrimController.setWakeAndUnlocking();
                if (mPhoneStatusBar.getNavigationBarView() != null) {
                    mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
                }
                break;
            ......
            case MODE_DISMISS_KEYGUARD://通話介面息屏解鎖
                mStatusBarWindowManager.setStatusBarFocusable(false);
                mKeyguardViewMediator.onWakeAndUnlocking();
                if (mPhoneStatusBar.getNavigationBarView() != null) {
                    mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
                }
                break;
        }
        ......
    }

    @Override
    public void onFinishedGoingToSleep(int why) {
        ......
    }
    @Override
    public void onFingerprintAuthFailed() {
        cleanup();
    }

    @Override
    public void onFingerprintError(int msgId, String errString) {
        cleanup();

        if(Settings.Global.getInt(mContext.getContentResolver(),"persist.fg.errorshow",0) == 0){
            if(!mPowerManager.isScreenOn()){
                mPowerManager.wakeUp(SystemClock.uptimeMillis());
            }else {
                mStatusBarKeyguardViewManager.animateCollapsePanels(
                            FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
            }
            Settings.Global.putInt(mContext.getContentResolver(),"persist.fg.errorshow",1);
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

KeyguardUpdateMonitor類為解鎖屏模組的監聽者,它負責監聽時間、sim卡、運營商資訊、電池資訊、電話資訊等狀態的變化,並通知keyguard View模組更新顯示。這個類裡面還有有監聽指紋的方法startListeningForFingerprint, 該方法呼叫了FingerprintManager的authenticate()方法並且引數中傳入了AuthenticationCallback物件,AuthenticationCallback類中呼叫了KeyguardUpdateMonitorCallback的方法,FingerprintUnlockController實現了KeyguardUpdateMonitorCallback這樣就能實現指紋解鎖。

private void startListeningForFingerprint() {
        ......
        //呼叫了FingerprintManager的authenticate()方法
        mFpm.authenticate(null, mFingerprintCancelSignal, 0, 
        mAuthenticationCallback, null, userId);
        ......
        }

 private FingerprintManager.AuthenticationCallback mAuthenticationCallback
            = new AuthenticationCallback() {
        //指紋匹配失敗(沒超過5次)
        @Override
        public void onAuthenticationFailed() {
            handleFingerprintAuthFailed();
        };
        //指紋匹配成功
        @Override
        public void onAuthenticationSucceeded(AuthenticationResult result) {
            handleFingerprintAuthenticated(result.getUserId());
        }
        //指紋匹配提示
        @Override
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
            handleFingerprintHelp(helpMsgId, helpString.toString());
        }
        //指紋匹配錯誤(匹配失敗次數超過5次)
        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            handleFingerprintError(errMsgId, errString.toString());
        }
        //獲得到指紋
        @Override
        public void onAuthenticationAcquired(int acquireInfo) {
           if(Settings.Global.getInt(mContext.getContentResolver(),"persist.
            fg.errorshow",0) == 1){
                Settings.Global.putInt(mContext.getContentResolver(),"persist
                .fg.errorshow",0);
            }

            handleFingerprintAcquired(acquireInfo);
        }
    };

    private void handleFingerprintAuthenticated(int authUserId) {
        ......
        onFingerprintAuthenticated(userId);
        ......

    private void onFingerprintAuthenticated(int userId) {
        mUserFingerprintAuthenticated.put(userId, true);
        ......
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                //這裡會呼叫FingerprintUnLockController中的onFingerprintAuthenticated方法處理解鎖
                cb.onFingerprintAuthenticated(userId);
            }
        }
        .....
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

回來繼續看FingerprintManager的authenticate()方法,此方法繼續呼叫了FingerprintServiceWarpper的authenticate方法(FingerprintManager與FingerprintService直接通過aidl來通訊)。

public void authenticate(@Nullable CryptoObject crypto, @Nullable 
        ......
        mService.authenticate(mToken, sessionId, userId, mServiceReceiver, 
                    flags,mContext.getOpPackageName());
        ......
    }
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
        @Override // Binder call
        public void authenticate(final IBinder token, final long opId, 
        final int groupId,final IFingerprintServiceReceiver receiver,
        final int flags,final String opPackageName) {
            startAuthentication(token, opId, callingUserId, groupId, 
            receiver, flags, restricted, opPackageName);
    }
    startAuthentication方法會呼叫AuthenticationClient的start方法,AuthenticationClient
    是為給定的客戶端跟蹤指紋認證狀態。
    public abstract class AuthenticationClient extends ClientMonitor {

        public AuthenticationClient(Context context, long halDeviceId, IBinder token,
        IFingerprintServiceReceiver receiver, int targetUserId,
        int groupId, long opId,boolean restricted, String owner) {
            super(context, halDeviceId, token, receiver, targetUserId, 
            groupId, restricted, owner);
        }
        //指紋庫處理結果呼叫
        @Override
        public boolean onAuthenticated(int fingerId, int groupId) {
            ......
            boolean authenticated = fingerId != 0;
            if (!authenticated) {
                //認證失敗
                receiver.onAuthenticationFailed(getHalDeviceId());
            } else {
                //認證成功
                receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
            }
            if (!authenticated) {
                if (inLockoutMode) {
                    //認證錯誤
                    receiver.onError(getHalDeviceId(),
                             FingerprintManager.FINGERPRINT_ERROR_LOCKOUT);
                  }
            }
            ......
        }
        /**
         * Start authentication
         */
        @Override
        public int start() {
            IFingerprintDaemon daemon = getFingerprintDaemon();
            ......
            final int result = daemon.authenticate(mOpId, getGroupId());
            ...... 
            return 0; // success
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

start方法會呼叫fingerprintd,呼叫底層的指紋庫,底層庫返回結果後會呼叫onAuthenticated來反饋結果給receiver,在往上層反饋。這就是指紋的識別流程。