息屏指紋解鎖性能優化 TP LCD

需求背景

。客戶反饋我司的指紋解鎖機器冷屏(息屏)解鎖下速度太慢,體驗很差,而對比機卻非常快

。對比發現,我司機器跟市面品牌機的冷屏解鎖速度差了不是一個等級, 急待改善.

既然要優化功能,首先要做到理解功能實現原理,如此才能找到性能瓶頸,打開突破點.

1、解鎖流程

Google默認的冷屏指紋解鎖基本框架流程如下:

 

(1)指紋IC檢測到手指觸摸模組,HW觸發irq,被Linux kernel接收到;

(2)fingerprintd守護進程接到kernel上報的irq後調用TZ的系統調用接口發起指紋數據採圖、比對auth請求;

(3)指紋數據auth比對成功,從TZ返回kernel再返回fingerprintd,此處耗費約180-260ms;

(4)fingerpritnd接到auth成功消息,binder回調FingerprintService對象onAuthenticated接口;

(5)auth success結果回調到keyguard,準備wakeup system;

(6)keyguard調用PowerManager.wakeUp() 喚醒系統,耗費約350+200 ms

(7)Wakeup completed,keyguard調用keyguardDone解鎖完成後,耗費約200-300ms

(8)解鎖完成,調用系統接口點亮背光,此刻完成整個解鎖全過程。

從以上流程可以看出,整個息屏解鎖流程的大部分時間集中在三個過程:指紋運算比對、系統和外設wakeup、解鎖繪圖,這三塊時間之和即爲

整個解鎖流程完成的理論時間之和約:900 ~1100 ms

 

2、影響耗時因素

 

從上面的流程看到幾個信息:wakeup、authenticated、keyguard unlock屬於最耗時的三個部分,而且是串行執行的,下面分別分析這幾個部分是否有改善空間.

2.1 Sys wakeup

指紋比對成功後keyguard進程會通過PowerManagerService的wakeup()接口執行喚醒系統的操作,該函數最耗時部分在於底層驅動設備的resume過程,其中佔比重最大的屬LCD,TP的resume,所以此處屬於重點優化部分內容。另外,系統要顯示數據,肯定要等LCD resume完成纔可以,這個可以理解,但是爲什麼TP 的resume會影響到系統亮屏呢?這個原因其實可以從代碼中找到,後面源碼分析將會介紹,TP resume的動作是放在fb unblank的線程中的,而且會發現系統要亮屏其實是可以不等TP resume的。

2.2 指紋運算比對

fingerprintd收到指紋IRQ後,會通過系統調用接口向Trustzoon端的指紋TA(trust app)發起指紋圖像採集、模板對比操作,而這個耗時業內標準是200ms左右,這塊各個方案供應商表現差別不大,此部分的優化完全依靠方案提供商。

2.3 解鎖繪製

現有的方案是要等執行解鎖動作完成後再去點亮背光,如果不等解鎖完成就去點亮背光會看到解鎖退去的動畫閃動,用戶體驗不佳。這部分的耗時比較多,從log看至 200-300 ms的delay,屬於重點優化部分。若能解決解鎖時候不出現“動畫退去的閃動”也是個不錯的方法。

 

3、競品分析

既然對比機比我們的速度快這麼多,拋開客觀條件(CPU,flash讀寫速度等),還是可以認真的研究一番,看看優秀的產品是怎麼去做的,我們可以嘗試參考優秀做的實現方式用到我們的產品上,師夷長技以制夷,下面簡單描述下兩款對比機的分析過程.

3.1 某品牌機1

老實說,要不是因爲這個分析我還真沒聽過這個牌子的手機,不過人家的冷屏解鎖做的如此之快,確實很不錯,很值得我們研究學習一番。經過log分析,機器代碼逆向發現,該機器不走尋常路,簡單來說就是:

。收到指紋中斷後就另外開線程直接去wakup系統,而不是等指紋auth success再去做,這樣做的好處就是wakeup系統跟auth同時進行,由原來串行改爲並行,重疊部分的時間就相當於省下了.

。系統休眠的時候先自動上鎖,然後再將keyguard鎖解掉!這樣系統滅屏的時候相當於沒有上鎖狀態,指紋冷屏解鎖就相當於只有wakeup 、指紋auth,點亮背光的操作了,沒有keyguard unlock的操作,這樣keyguard unlock的200-300ms直接爲0 了!一想這樣明顯會有問題啊,息屏了沒上鎖,我按power鍵類似的亮屏不是不用解鎖就進入系統了麼?對沒錯,所以需要把這些地方堵住,在非指紋冷屏解鎖亮屏的條件下亮屏會先去給keyguard上鎖再亮屏,這樣非指紋冷屏解鎖的亮屏就一樣會看到keyguard鎖。

收到指紋中斷:

08-30 10:09:20.251 2054 6017 D btl_algo: -- btl_api_waitSignal 08-30 10:09:20.251 2054 6017 D btl_algo: Finger is pressed 08-30 10:09:20.251 2054 6017 D fingerprintd: onAcquired(0)

...

比對成功,耗時:217ms

08-30 10:09:20.468 2054 6017 D fingerprintd: onAuthenticated(fid=2, gid=0) 08-30 10:09:20.469 2399 2399 D PowerManagerNotifier: onUserActivity: event=2, uid=1000 ...

亮背光: 08-30 10:09:20.472 3447 3461 D btl_jni : FpEnBlackLight 08-30 10:09:20.472 3447 3461 D btl_jni : User_EnBlackLight enable: 1

整個流程時間耗費:221ms,非常之快.

這裏看到它自己調用jni去亮背光,我們知道要點亮背光led之前需要wakeup顯示系統纔可以成功,所以這裏推斷它是在收到指紋中斷的時候就去執行wakeup的操作,可由於log太少,所以沒有看到相關的log信息.

3.2 某品牌機2

某品牌機2也做了很多優化跟某品牌機1類似,收到irq後報個key==304上來,然後直接wakeup系統:

01-03 02:40:38.105 1470 1856 D WindowManager: interceptKeyTq keycode=304 interactive=false keyguardActive=true policyFlags=2000000 01-03 02:40:38.105 1470 1856 D WindowManager: isPhicalHomeKey = false ,result = 0 01-03 02:40:38.105 2151 2161 V PowerEffectManager: noteInputState key = 304 01-03 02:40:38.105 1470 1856 D WindowManager: before sendMessage MSG_WAKEUP_BY_WHO_FINGERPRINT 01-03 02:40:38.105 1470 1854 D FingerprintWakeHook: onWakeUpByWho: eventTime:6238453 who:FingerPrint uid:1000 ident:4294967297470

...

01-03 02:40:38.275 787 1912 D fingerprintd: onAuthenticated(fid=265437463, gid=0) --- Auth success!

01-03 02:40:38.285 1983 2507 D KeyguardViewMediator: keyguardDone(true)--- 開始解鎖 .... 01-03 02:40:38.305 1470 1470 I frontfingerprintkey: fingerprintReceiver Receiver action: android.intent.action.USER_PRESENT --解鎖完成 ... 01-03 02:40:38.395 1470 1941 D DisplayPowerState: Updating screen state: state=ON, backlight=39, backlightChanged=true mPowerAssistantMode=0 01-03 02:40:38.395 1470 1941 D BBKTouchScreenService: Set LCD backlight state ON ---- 點亮背光

整體耗時:

指紋數據比對耗時:275-105 == 170 ms

解鎖完成到亮屏耗時:395-285 == 110 ms

整個週期耗時:170 + 110 == 280 ms

解鎖時間總體很快,但比某品牌機1的221ms略低,可是沒有不上鎖啊,說明它還是有解鎖keyguard的時間耗時的,但是它是如何做到這麼快的呢?帶着疑問,嘗試逆向代碼看會發現:

com/android/server/wm/WindowManagerService.java:

 

指紋認證成功後會先執行hideKeyguardLocked函數,從上面代碼猜測是1939行設置取消動畫,1942-1946,1952行設置keyguardWindow爲透明,如果是這樣那麼就可以不用管解鎖的“閃動”導致的用戶體驗差的問題了,因爲已經透明瞭,對用戶不可見了,這是個很不錯的優化keyguard解鎖時間的思路。

4、任務分解

4.1 LCD resume時間優化

此部分屬於耗時大頭,跟系統亮屏速度強關聯,同時關係到wakeup跟sleep的速度,影響很大,目前我司v12xx系列的機器優化的還不錯,大概是190-240 ms左右,亮屏速度體驗較好。7201/6901系列就非常慢,大概需要330-350 ms,亮屏體驗差。此部分的優化需要對應的LCD驅動工程師配合IC廠工程師聯合調試.

4.2 TP resume時間優化

系統要正常亮屏顯示,lcd肯定要先準備好,所以lcd resume會影響亮屏時間,但是tp resume跟系統亮滅屏又有什麼關係呢?要知道原因,分析代碼,看tp resume是如何被call到的。首先找到你手機用的TP驅動是哪一個,這裏是用ft5364i,代碼在如下位置:

drivers/input/touchscreen/ft5364i/focaltech_core.c

搜索resume發現執行tp resume的是這個函數:

 

 

從函數名字可以猜到,是在執行fb blank的unblank,也就是亮屏的線程被“通知”執行的,那爲什麼會這樣執行呢?繼續看fb_notifier_callback在哪裏調用,首先看到會再tp驅動的probe函數中註冊到fb 的通知鏈中,

那註冊的過程做了什麼呢?進一步深究,如下代碼:

 

我們可以看看這個 fb_notifier_list 的定義:

static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);

典型的訂閱-發佈模式設計,從宏定義名字可以知道就是一個通知鏈的頭指針,也就是說上面fb_register_client的作用就是將新的這個node(nb)加入到fb_notifier_list頭指針的鏈表中管理起來,這個是我們從函數名字上猜想的結果,實際上是如何呢?繼續看代碼看具體做了什麼,如下最終會調用到這個函數:

其中nl就是上面傳下來的fb_notifier_list頭指針,n就是需要新加入的節點指針,從上面代碼可以看到一個細節,新節點的插入會根據優先級來按順序實現,而不是單單從調用關係先後而決定的.

 

上面的分析搞清楚了fb resume是註冊到fb_notifier_list鏈表裏面,那什麼時候執行呢?看如下代碼:

drivers/video/fbdev/core/fbmem.c

我們知道系統亮滅屏的時候SurfaceFlinger經常會調用驅動跑fb_blank的代碼,亮屏就是unblank參數,滅屏就是blank參數,而這裏就會去調用fb_notifier_call_chain函數,看看這個函數:

 

是不是很眼熟?fb_notifier_list,就是上面看到的註冊試試的那個頭指針,理解了上面的註冊過程,那麼這個也就很容易理解了,就是去依次遍歷每一個註冊到該鏈表的節點,依次回調notifier_call函數,而TP 的resume就是在這個函數裏面的,所以自然就會影響到fb blank整個函數的執行週期,如果tp resume耗時太久也就自然對亮屏會有影響了。

 

4.3 指紋解鎖流程調整

4.3.1 調整wakeup策略:

默認的解鎖流程都是串行執行的,沒有有效的利用相同的時間內做更多的事情,現在都是多核CPU,完全可以在收到指紋irq立即去執行wakeup動作,然後等Auth success就直接去點亮背光,這樣做就可以在指紋auth的200ms內一起把wakeup的事情也幹了,而重疊的時間就是省下來來的時間。

4.3.2 背光控制邏輯

直接調用Power.wakeu默認就會點亮背光,而觸發irq的時候並不知道auth是否成功,所以wakeup之前需要把背光攔截掉,不能點亮,只有等指紋auth success後才點亮,所以這裏需要更改點亮背光的策略.

4.3.3 Keyguard隱藏

背光提早到keyguard unlock之前亮會引入一個副作用就是會看到keyguard消退的動畫跳變過程,用戶體驗不佳,這個時候就可以藉助vivo-x9的做法了,直接設置爲透明解鎖,這樣就可以巧妙的避開這個問題,當然實際開發過程可能會有各種狀態考慮需要細細調整.

 

 

5、源碼實現

5.1 tp resume改爲異步

上面分析了tp resume是跟fb blank同步進行,那麼事實上tp resume時間大概是200ms以下,人的反應時間大概是100-200ms,人眼看到亮屏在到用手去觸摸屏幕時間需要至少200+ms以上了,所以可以嘗試改爲異步執行,這樣就可以節省掉tp resume的200ms時間了。代碼實現很簡單如下:

首先增加一個指針fb_notify_work,在probe函數中創建一個workqueue,INIT_WORK該宏的本質是創建一個新的內核線程,然後再使之與指針fb_notify_work綁定,這樣就可以操作該指針來實現啓動、關閉該線程的執行了。

 

實現workqueue的callback:

 

修改notifier通知鏈回調代碼:

入行代碼中同步調用tp resume改爲schedule_work調用,該函數本質是執行新的內核線程來實現異步執行,這樣tp resume的執行就跟fb blank不在同一個線程了,也就不會影響到亮屏的時間了.

 

5.2 中斷喚醒

分兩步,其一就是IRQ(中斷)要能觸發,其二就是system_server進程需要接受到這個IRQ信號,在指紋認證等待模式下,只要手指有觸摸指紋模塊,那麼自動就會觸發指紋IRQ信號,所以我們需要做的就是需要新建一個線程用於監聽來自指紋驅動上報的IRQ信號,然後再通過PowerManagerService執行wakeup的動作喚醒系統。

5.2.2 監聽IRQ

圖解:

(1-3)FingerprintService啓動的時候初始化本地封裝類FingerprintNative,然後通過jni調用本地函數native_init();

(4)執行路徑路由熬fp_dev_init(),調用fp_dev_open();

(5)open(“/dev/fp_drv") 設備節點,拿到句柄dev_fd,註冊異步監聽該設備來自內核的消息,fp_input_handler()是回調函數;

(6)return;

(7)傳入new FingerprintNative()對象到native層;

(8)通過pthread_create()創建監聽線程,監聽來自“/dev/fp_drv”設備的消息,該監聽是阻塞執行的。

5.2.3 IRQ上報

 

e94b76a6d547ecf09dc16ab9c6825cba.pnguploading.4e448015.gif轉存失敗重新上傳取消

圖解:

(1)觸摸指紋模塊,硬件觸發中斷,被kernel接到,回調中斷處理函數;

(2)調用fp_drv.c裏面的接口,內核空間使用kill_fasync()發送異步消息;

(3)消息通過系統調用接口傳入到用戶空間,被native層的監聽線程監聽到,通過JNI回到到java層;

(4)JNI回調Java層的onReport() 函數;

(5)調用Power的wakeUp()函數執行喚醒系統;

 

 

5.3 背光策略

5.3.1 背光攔截

默認情況下執行Power的wakeup()就會自動亮背光,但是事實上我們需要改成指紋Auth success才亮背光,所以需要在調用wakeup前攔截掉背光控制流程,可以通過改上層或者底層來實現,經過實際測試綜合考慮還是改kernel層更合適,如下內核led接口處添加攔截判斷:

kernel-3.18/drivers/leds/leds.h

 

kernel-3.18/drivers/input/fingerprint/fp_drv/fp_drv.c

get_bl_ctr_flag()函數實現在fp_drv.c中:

 

這裏會判斷傳入的led dev,如果是背光 "lcd-backlight" 則需要判斷fp_backlight_control的值是否爲1,爲1則攔截,否則不攔截,如此通過控制全局變量fp_backlight_control的值就可以實現背光的攔截操作。

5.3.2 點亮背光

這個就比較簡單了,有兩種方式實現,一種在用戶空間,另外一種在內核空間:

先看用戶空間的實現方式:

#define BL_DEV_ATTR "/sys/class/leds/lcd-backlight/brightness"

 

也可以再內核空間操作,內核空間實現方法如下:

 

所有的led設備初始化的時候都會掛到一個鏈表中,頭指針是leds_list,遍歷這個鏈表找到“lcd-backlight" 那就是背光驅動,設置需要的值即可.

5.4 keyguard隱藏

實現涉及文件:

SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java

SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java

關鍵代碼:

簡析:

LINE 2985-2986:獲取keyguard窗口的動畫值備份mScale,然後設置爲0,關閉窗口動畫;

LINE 2994:如果是狀態欄或者導航欄的view,則不納入視圖隱藏,保持不變;

LINE 3011:設置透明度爲0.0f,隱藏顯示;

LINE 2979:若是按下Power鍵亮屏,則需要恢復動畫顯示,恢復keyguard顯示;

 

5.5 Power/usb亮背光處理

5.5.1 按Power鍵處理

使用錯誤指紋觸摸模組,此刻會wakup系統,此時如果按power鍵,系統是不會亮屏的,因爲當前已經wakeup了,這明顯是引入的問題點,所以需要特別針對這樣的情況特殊處理下,主要涉及修改如下:

services/core/java/com/android/server/policy/PhoneWindowManager.java:


簡析:

LINE 1196:Power down事件會調interceptPowerKeyDown函數,這個時候判斷是否是屬於使用過錯誤指紋觸摸過模組,如果是,就直接通過isFingerprintQuickWakeup接口內部實現點亮背光,同時設置標記mFpWakeupFlag=true;

LINE 1294:Power up事件的時會調此函數,此函數主要作用是gotosleep(),如果mFpWakeupFlag=true說明是上面說的這種情況就不執行休眠,直接返回,如此就可以解決前面說的按power無法亮屏的問題.

 

5.5.2 拔插USB,來電亮屏處理

可以看到,代碼其實很簡單,就是監聽相關廣播,然後直接設置爲亮背光,不再解釋.

 

總結:

一系列優化調試後,息屏指紋解鎖亮屏整個耗時在400-500ms左右,提升一倍速率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章