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

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

下面我們就跟著源碼产还,解析這整個過程厘熟。

何時開始監(jiān)聽指紋傳感器屯蹦?

先來看下IKeyguardService這個binder接口有哪些回調(diào)吧

    // 當另一個窗口使用FLAG_SHOW_ON_LOCK_SCREEN解除Keyguard時PhoneWindowManager調(diào)用
    public void setOccluded(boolean isOccluded, boolean animate) throws android.os.RemoteException;
    // 添加鎖屏狀態(tài)回調(diào)
    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;
    // 屏保結(jié)束(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;
    // 已經(jīng)亮屏完成
    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;
    // 開機系統(tǒng)準備完成回調(diào)
    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;
    // 系統(tǒng)啟動完成回調(diào)
    public void onBootCompleted() throws android.os.RemoteException;
    // Keyguard后面的activity已經(jīng)繪制完成维哈,可以開始移除壁紙和Keyguard flag
    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) throws android.os.RemoteException;
    // 通知Keyguard對power鍵做特殊處理,使設備不進行休眠或喚醒而是啟動Home(目前是空實現(xiàn))
    public void onShortPowerPressedGoHome() throws android.os.RemoteException;

在這么多接口里登澜,有
onStartedGoingToSleep/
onFinishedGoingToSleep/
onScreenTurningOff/
onScreenTurnedOff
這四個接口是在power鍵按下后觸發(fā)阔挠,其中onStartedGoingToSleep最先被觸發(fā),他們的
調(diào)用順序我會在后文里講解脑蠕。

而我們的指紋傳感器監(jiān)聽,就是在onStartedGoingToSleep時開始的购撼。
代碼邏輯由在KeyguardService中由中間類KeyguardMediator調(diào)用到KeyguardUpdateMonitor
android/frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java

    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監(jiān)聽指紋傳感器

指紋傳感器的監(jiān)聽方法

    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);
        }
    }

真正到了使用指紋識別功能的時候谴仙,你會發(fā)現(xiàn)其實很簡單迂求,只是調(diào)用 FingerprintManager 類的的方法authenticate()而已,然后系統(tǒng)會有相應的回調(diào)反饋給我們晃跺,該方法如下:

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

該方法的幾個參數(shù)解釋如下:

  1. 第一個參數(shù)是一個加密對象揩局。目前為null
  2. 第二個參數(shù)是一個 CancellationSignal 對象,該對象提供了取消操作的能力掀虎。創(chuàng)建該對象也很簡單凌盯,使用 new CancellationSignal() 就可以了。
  3. 第三個參數(shù)是一個標志烹玉,默認為0驰怎。
  4. 第四個參數(shù)是 AuthenticationCallback 對象,它本身是 FingerprintManager 類里面的一個抽象類二打。該類提供了指紋識別的幾個回調(diào)方法县忌,包括指紋識別成功、失敗等继效。需要我們重寫芹枷。
  5. 最后一個 Handler,可以用于處理回調(diào)事件莲趣,可以傳null鸳慈。
  6. 用戶id

下面只需要在mAuthenticationCallback繼承AuthenticationCallback這個抽象方法,重寫回調(diào)接口


    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
        // 指紋驗證時回調(diào)
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
        }

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

獲取指紋后喧伞,Keyguard做了哪些事走芋?

從AuthenticationCallback里可以看出,獲取指紋回調(diào)首先發(fā)生在 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參數(shù)判斷是否正確獲取指紋翁逞,之后遍歷KeyguardUpdateMonitorCallback,進行回調(diào)溉仑。
重寫onFingerprintAcquired方法的只有FingerprintUnlockController挖函,F(xiàn)ingerprintUnlockController就是
用于協(xié)調(diào)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相關(guān)的浊竟,
獲取WakeLock怨喘,并發(fā)送一條延時消息津畸,15秒后,釋放WakeLock必怜。

下一步就發(fā)生在肉拓,onFingerprintAuthenticated回調(diào)中了,實現(xiàn)onFingerprintAuthenticated接口的不止一處梳庆,但真正實現(xiàn)解鎖的還是在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;
    }

這段代碼邏輯很清晰暖途,就是根據(jù)鎖屏的狀態(tài)計算指紋解鎖的模式

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

startWakeAndUnlock中的代碼經(jīng)過簡化后,只剩三部分:
1.先判斷設備喚醒狀態(tài)膏执,是用PowerManager的wakeUp接口點亮屏幕
2.然后釋放在acquire階段獲取的WakeLock
3.最后在根據(jù)上面calculateMode得出的解鎖模式驻售,進行真正的解鎖動作,這在之前的解鎖流程中已經(jīng)分析過更米,這里不再做分析欺栗。

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

PowerManager的wakeUp接口

我們知道上層應用要喚醒系統(tǒng)一般只能依靠兩種方式:
1.在應用啟動Activity時候設置相應的window的flags壳快,通過WMS來喚醒系統(tǒng);
即通過WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
2.在應用申請wakelock鎖時附帶ACQUIRE_CAUSES_WAKEUP標志

PowerManager的wakeup接口屬性是@hide的镇草,對一般應用是不可見的眶痰,而我們的SystemUI就不存在調(diào)用問題。
SystemUI通過調(diào)用第三種方式:PowerManager的wakeup接口梯啤,來強制喚醒系統(tǒng)竖伯,如果該設備處于睡眠狀態(tài),調(diào)用該接口會立即喚醒系統(tǒng)因宇,比如按Power鍵七婴,來電,鬧鐘等場景都會調(diào)用該接口察滑。喚醒系統(tǒng)需要android.Manifest.permission#DEVICE_POWER的權(quán)限打厘,我們可以看到SystemUI的Manifest文件里已經(jīng)添加了該權(quán)限。
PowerManagerService喚醒的流程請看流程圖:


PMS的wakeUp流程

從流程可以看到贺辰,亮屏流程可以和KeyguardService中的回調(diào)對應上了户盯。

總結(jié)

其實指紋解鎖的本質(zhì)是在KeyguardService收到從PMS到WMS的調(diào)用中,在StartedGoingToSleep時就開始使用FingerprintManager的authticate開始監(jiān)聽感器饲化,在FIngerManager驗證成功時莽鸭,使用PowerManagerService點亮屏幕,進行解鎖流程吃靠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硫眨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子巢块,更是在濱河造成了極大的恐慌礁阁,老刑警劉巖巧号,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異氮兵,居然都是意外死亡裂逐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門泣栈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卜高,“玉大人,你說我怎么就攤上這事南片〔籼危” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵疼进,是天一觀的道長薪缆。 經(jīng)常有香客問我,道長伞广,這世上最難降的妖魔是什么拣帽? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮嚼锄,結(jié)果婚禮上减拭,老公的妹妹穿的比我還像新娘。我一直安慰自己区丑,他們只是感情好拧粪,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沧侥,像睡著了一般可霎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宴杀,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天癣朗,我揣著相機與錄音,去河邊找鬼旺罢。 笑死斯棒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的主经。 我是一名探鬼主播荣暮,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罩驻!你這毒婦竟也來了穗酥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砾跃,沒想到半個月后骏啰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡抽高,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年判耕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翘骂。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡壁熄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碳竟,到底是詐尸還是另有隱情草丧,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布莹桅,位于F島的核電站昌执,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诈泼。R本人自食惡果不足惜懂拾,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铐达。 院中可真熱鬧岖赋,春花似錦、人聲如沸娶桦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衷畦。三九已至,卻和暖如春知牌,著一層夾襖步出監(jiān)牢的瞬間祈争,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工角寸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留菩混,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓扁藕,卻偏偏與公主長得像沮峡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子亿柑,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容