Android8.1 SystemUI Keyguard之指紋解鎖流程

手指在指紋傳感器上摸一下就能解鎖,Keyguard是怎麼作到的呢?java

下面咱們就跟着源碼,解析這整個過程。android

什麼時候開始監聽指紋傳感器?

先來看下IKeyguardService這個binder接口有哪些回調吧併發

// 當另外一個窗口使用FLAG_SHOW_ON_LOCK_SCREEN解除Keyguard時PhoneWindowManager調用
    public void setOccluded(boolean isOccluded, boolean animate) throws android.os.RemoteException;
    // 添加鎖屏狀態回調
    public void addStateMonitorCallback(com.android.internal.policy.IKeyguardStateCallback callback) throws android.os.RemoteException;
    // 覈驗解鎖(用於快捷啓動)
    public void verifyUnlock(com.android.internal.policy.IKeyguardExitCallback callback) throws android.os.RemoteException;
    // 解除鎖屏
    public void dismiss(com.android.internal.policy.IKeyguardDismissCallback callback) throws android.os.RemoteException;
    // 屏保開始(Intent.ACTION_DREAMING_STARTED)
    public void onDreamingStarted() throws android.os.RemoteException;
    // 屏保結束(Intent.ACTION_DREAMING_STOPPED)
    public void onDreamingStopped() throws android.os.RemoteException;
    // 設備開始休眠 reason:OFF_BECAUSE_OF_USER/OFF_BECAUSE_OF_ADMIN/OFF_BECAUSE_OF_TIMEOUT
    public void onStartedGoingToSleep(int reason) throws android.os.RemoteException;
    // 休眠完成
    public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) throws android.os.RemoteException;
    // 設備開始喚醒
    public void onStartedWakingUp() throws android.os.RemoteException;
    // 喚醒完成
    public void onFinishedWakingUp() throws android.os.RemoteException;
    // 正在亮屏
    public void onScreenTurningOn(com.android.internal.policy.IKeyguardDrawnCallback callback) throws android.os.RemoteException;
    // 已經亮屏完成
    public void onScreenTurnedOn() throws android.os.RemoteException;
    // 正在滅屏
    public void onScreenTurningOff() throws android.os.RemoteException;
    // 滅屏完成
    public void onScreenTurnedOff() throws android.os.RemoteException;
    // 外部應用取消Keyguard接口
    public void setKeyguardEnabled(boolean enabled) throws android.os.RemoteException;
    // 開機系統準備完成回調
    public void onSystemReady() throws android.os.RemoteException;
    // 延時鎖屏 (用於自動休眠)
    public void doKeyguardTimeout(android.os.Bundle options) throws android.os.RemoteException;
    // 切換用戶中
    public void setSwitchingUser(boolean switching) throws android.os.RemoteException;
    // 設置當前用戶
    public void setCurrentUser(int userId) throws android.os.RemoteException;
    // 系統啓動完成回調
    public void onBootCompleted() throws android.os.RemoteException;
    // Keyguard後面的activity已經繪製完成,能夠開始移除壁紙和Keyguard flag
    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) throws android.os.RemoteException;
    // 通知Keyguard對power鍵作特殊處理,使設備不進行休眠或喚醒而是啓動Home(目前是空實現)
    public void onShortPowerPressedGoHome() throws android.os.RemoteException;

在這麼多接口裏,有
onStartedGoingToSleep/
onFinishedGoingToSleep/
onScreenTurningOff/
onScreenTurnedOff
這四個接口是在power鍵按下後觸發,其中onStartedGoingToSleep最早被觸發,他們的
調用順序我會在後文裏講解。ide

而咱們的指紋傳感器監聽,就是在onStartedGoingToSleep時開始的。
代碼邏輯由在KeyguardService中由中間類KeyguardMediator調用到KeyguardUpdateMonitor
android/frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.javapost

protected void handleStartedGoingToSleep(int arg1) {
        ...
        updateFingerprintListeningState();
    }

    private void updateFingerprintListeningState() {
        // If this message exists, we should not authenticate again until this message is
        // consumed by the handler
        if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
            return;
        }
        mHandler.removeCallbacks(mRetryFingerprintAuthentication);
        boolean shouldListenForFingerprint = shouldListenForFingerprint();
        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
            stopListeningForFingerprint();
        } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
                && shouldListenForFingerprint) {
            startListeningForFingerprint();
        }
    }

在同時判斷mFingerprintRunningState和shouldListenForFingerprint後,
Keyguard在startListeningForFingerprint中真正使用FingerprintManager監聽指紋傳感器ui

指紋傳感器的監聽方法

private void startListeningForFingerprint() {
        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
            return;
        }
        if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
        int userId = ActivityManager.getCurrentUser();
        if (isUnlockWithFingerprintPossible(userId)) {
            if (mFingerprintCancelSignal != null) {
                mFingerprintCancelSignal.cancel();
            }
            mFingerprintCancelSignal = new CancellationSignal();
            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
            setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
        }
    }

真正到了使用指紋識別功能的時候,你會發現其實很簡單,只是調用 FingerprintManager 類的的方法authenticate()而已,而後系統會有相應的回調反饋給咱們,該方法以下:this

public void authenticate(CryptoObject crypto, CancellationSignal cancel, int flags, AuthenticationCallback callback, Handler handler, int userId)加密

該方法的幾個參數解釋以下:spa

  1. 第一個參數是一個加密對象。目前爲null
  2. 第二個參數是一個 CancellationSignal 對象,該對象提供了取消操做的能力。建立該對象也很簡單,使用 new CancellationSignal() 就能夠了。
  3. 第三個參數是一個標誌,默認爲0。
  4. 第四個參數是 AuthenticationCallback 對象,它自己是 FingerprintManager 類裏面的一個抽象類。該類提供了指紋識別的幾個回調方法,包括指紋識別成功、失敗等。須要咱們重寫。
  5. 最後一個 Handler,能夠用於處理回調事件,能夠傳null。
  6. 用戶id

下面只須要在mAuthenticationCallback繼承AuthenticationCallback這個抽象方法,重寫回調接口code

private FingerprintManager.AuthenticationCallback mAuthenticationCallback
            = new AuthenticationCallback() {

        @Override
        public void onAuthenticationFailed() {
            handleFingerprintAuthFailed();
        };

        @Override
        public void onAuthenticationSucceeded(AuthenticationResult result) {
            Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
            handleFingerprintAuthenticated(result.getUserId());
            Trace.endSection();
        }

        @Override
        // 指紋驗證
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
            handleFingerprintHelp(helpMsgId, helpString.toString());
        }

        @Override
        // 指紋驗證時回調
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
        }

        @Override
        // 獲取到指紋時回調
        public void onAuthenticationAcquired(int acquireInfo) {
            handleFingerprintAcquired(acquireInfo);
        }
    };

獲取指紋後,Keyguard作了哪些事?

從AuthenticationCallback裏能夠看出,獲取指紋回調首先發生在 onAuthenticationAcquired 中, 咱們先看代碼

private void handleFingerprintAcquired(int acquireInfo) {
        if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
            return;
        }
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onFingerprintAcquired();
            }
        }
    }

首先用acquireInfo參數判斷是否正確獲取指紋,以後遍歷KeyguardUpdateMonitorCallback,進行回調。
重寫onFingerprintAcquired方法的只有FingerprintUnlockController,FingerprintUnlockController就是
用於協調UI的全部指紋解鎖操做的控制器。

@Override
    public void onFingerprintAcquired() {
        ...
            mWakeLock = mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
            mWakeLock.acquire();
            mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
                    FINGERPRINT_WAKELOCK_TIMEOUT_MS);
        }
        ...
    }

onFingerprintAcquired的核心邏輯所有是和WakeLock相關的,
獲取WakeLock,併發送一條延時消息,15秒後,釋放WakeLock。

下一步就發生在,onFingerprintAuthenticated回調中了,實現onFingerprintAuthenticated接口的不止一處,但真正實現解鎖的仍是在FingerprintUnlockController中

@Override
    public void onFingerprintAuthenticated(int userId) {
        ...
        startWakeAndUnlock(calculateMode());
    }

    private int calculateMode() {
        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
        boolean deviceDreaming = mUpdateMonitor.isDreaming();

        if (!mUpdateMonitor.isDeviceInteractive()) {
            if (!mStatusBarKeyguardViewManager.isShowing()) {
                return MODE_ONLY_WAKE;
            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
                return MODE_WAKE_AND_UNLOCK_PULSING;
            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                return MODE_WAKE_AND_UNLOCK;
            } else {
                return MODE_SHOW_BOUNCER;
            }
        }
        if (unlockingAllowed && deviceDreaming) {
            return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
        }
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
                return MODE_DISMISS_BOUNCER;
            } else if (unlockingAllowed) {
                return MODE_UNLOCK;
            } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                return MODE_SHOW_BOUNCER;
            }
        }
        return MODE_NONE;
    }

這段代碼邏輯很清晰,就是根據鎖屏的狀態計算指紋解鎖的模式

public void startWakeAndUnlock(int mode) {
        ...
        boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
        mMode = mode;
        if (!wasDeviceInteractive) {
            mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
        }
        
        releaseFingerprintWakeLock();
        
        switch (mMode) {
          ...
        }
    }

startWakeAndUnlock中的代碼通過簡化後,只剩三部分:
1.先判斷設備喚醒狀態,是用PowerManager的wakeUp接口點亮屏幕
2.而後釋放在acquire階段獲取的WakeLock
3.最後在根據上面calculateMode得出的解鎖模式,進行真正的解鎖動做,這在以前的解鎖流程中已經分析過,這裏再也不作分析。

這裏面值得咱們注意的是wakeUp接口, 下面咱們稍微對該接口進行一點探究

PowerManager的wakeUp接口

咱們知道上層應用要喚醒系統通常只能依靠兩種方式:
1.在應用啓動Activity時候設置相應的window的flags,經過WMS來喚醒系統;
即經過WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
2.在應用申請wakelock鎖時附帶ACQUIRE_CAUSES_WAKEUP標誌

PowerManager的wakeup接口屬性是@hide的,對通常應用是不可見的,而咱們的SystemUI就不存在調用問題。
SystemUI經過調用第三種方式:PowerManager的wakeup接口,來強制喚醒系統,若是該設備處於睡眠狀態,調用該接口會當即喚醒系統,好比按Power鍵,來電,鬧鐘等場景都會調用該接口。喚醒系統須要android.Manifest.permission#DEVICE_POWER的權限,咱們能夠看到SystemUI的Manifest文件裏已經添加了該權限。
PowerManagerService喚醒的流程請看流程圖:

 

PMS的wakeUp流程

 

從流程能夠看到,亮屏流程能夠和KeyguardService中的回調對應上了。

總結

其實指紋解鎖的本質是在KeyguardService收到從PMS到WMS的調用中,在StartedGoingToSleep時就開始使用FingerprintManager的authticate開始監聽感器,在FIngerManager驗證成功時,使用PowerManagerService點亮屏幕,進行解鎖流程。

相關文章
相關標籤/搜索