1. 程式人生 > >android開發中Fingerprint模組淺析

android開發中Fingerprint模組淺析

一、Fingerprint上層總體架構

Fingerprint模組架構圖如下,這裡分為application,framework,fingerprintd和FingerprintHal這幾個部分,不涉及指紋的IC庫和驅動這部分,這部分邏輯由指紋廠商來實現,目前瞭解的並不多。
image

二、Fingerprint framework初始化流程

在系統開機的時候,會啟動各種Service,包括FingerprintService。從下圖的開機log(sys_log.boot)中可以看出:
image
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);
        }
        ......

這裡會通過PackageManager來判斷是否支援指紋功能,這個判斷是N新加的,如果支援的話,需要在framework/native/data/ect/目錄下新增android.hardware.fingerprint.xml來支援該功能,這樣才能啟動FingerprintService。
image

這裡啟動的時候,會將FingerprintService新增到ServiceManager中去,如下圖:
image
將FingerprintService新增到ServiceManager中後,在SystemServiceRegistry.java中靜態程式碼塊中註冊服務的時候,可以從ServiceManager中獲取FingerprintService的Binder物件,從而可以構造出FingerprintManager物件,這樣app端就可以通過Context來獲取FingerprintManager物件。
image

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

在FingerprintService的getFingerprintDaemon方法中有如下步驟:

//①獲取fingerprintd

//②向fingerprintd註冊回撥函式mDaemonCallback

//③呼叫獲取fingerprintd的openhal函式

//④建立fingerprint檔案系統節點,設定節點訪問許可權,呼叫fingerprintd的setActiveGroup,將路徑傳下去。此路徑一半用來儲存指紋模板的圖片等

public IFingerprintDaemon getFingerprintDaemon() {
        if (mDaemon == null) {
             //①獲取fingerprintd
            mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
            if (mDaemon != null) {
                try {
                    mDaemon.asBinder().linkToDeath(this, 0);
                    //②向fingerprintd註冊回撥函式mDaemonCallback
                    mDaemon.init(mDaemonCallback);
                    //③呼叫獲取fingerprintd的openhal函式
                    mHalDeviceId = mDaemon.openHal();
                    /*④建立fingerprint檔案系統節點,設定節點訪問許可權,
                    呼叫fingerprintd的setActiveGroup,
                    將路徑傳下去。此路徑一半用來儲存指紋模板的圖片等*/
                    if (mHalDeviceId != 0) {
                        updateActiveGroup(ActivityManager.getCurrentUser());
                    } else {
                        Slog.w(TAG, "Failed to open Fingerprint HAL!");
                        mDaemon = null;
                    }
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to open fingeprintd HAL", e);
                    mDaemon = null; // try again later!
                }
            } else {
                Slog.w(TAG, "fingerprint service not available");
            }
        }
        return mDaemon;
    }
1 FingerprintService在framework模組負責指紋的大部分邏輯,FingerprintService會在開機的時候初始化;
2 application呼叫framework通過FingerprintManager介面即可實現;
3 framework中FingerManager和FingerprintService的通訊使用Binder機制實現,表現即使用aidl這個介面定義語言實現
4 framework調往fingerprintd的同樣屬於Binder通訊,兩者分屬於不同的程序。不過這部分跟java層Binder處理有點不一樣,是java層往native層的呼叫。

三、fingerprintd 部分的初始化

在system/core/fingerprintd目錄下,有如下檔案:
image

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層"
fingerprintd在init.rc有相應的開機啟動指令碼,所以一開機就會跑它的main函式。fingerprintd作為一個獨立的程序執行,負責將Framework和Hal層的通訊連線起來。

image

fingerprintd 的main函式就是將fingerprintd新增到servicemanager中管理。然後開了一個執行緒,等待binder訊息。

四、接下來簡單介紹下IFingerprintDaemon是如何跟framework通訊的。

來看下IFingerprintDaemon.h檔案:

17#ifndef IFINGERPRINT_DAEMON_H_
18#define IFINGERPRINT_DAEMON_H_
19
20#include <binder/IInterface.h>
21#include <binder/Parcel.h>
22
23namespace android {
24
25class IFingerprintDaemonCallback;
26
27/*
28* Abstract base class for native implementation of FingerprintService.
29*
30* Note: This must be kept manually in sync with IFingerprintDaemon.aidl
31*/
32class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient {
33    public:
34        enum {
35           AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0,
36           CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1,
37           ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2,
38           CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3,
39           PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4,
40           REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5,
41           GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6,
42           SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7,
43           OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
44           CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
45           INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
46           POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
47           ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
48        };
49
50        IFingerprintDaemon() { }
51        virtual ~IFingerprintDaemon() { }
52        virtual const android::String16& getInterfaceDescriptor() const;
53
54        // Binder interface methods
55        virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0;
56        virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
57                int32_t timeout) = 0;
58        virtual uint64_t preEnroll() = 0;
59        virtual int32_t postEnroll() = 0;
60        virtual int32_t stopEnrollment() = 0;
61        virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
62        virtual int32_t stopAuthentication() = 0;
63        virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
64        virtual int32_t enumerate() = 0;
65        virtual uint64_t getAuthenticatorId() = 0;
66        virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
67        virtual int64_t openHal() = 0;
68        virtual int32_t closeHal() = 0;
69
70        // DECLARE_META_INTERFACE - C++ client interface not needed
71        static const android::String16 descriptor;
72        static void hal_notify_callback(const fingerprint_msg_t *msg);
73};
74
75// ----------------------------------------------------------------------------
76
77class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> {
78    public:
79       virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
80               uint32_t flags = 0);
81    private:
82       bool checkPermission(const String16& permission);
83};
84
85} // namespace android
86
87#endif // IFINGERPRINT_DAEMON_H_

java層到fingerprintd的通訊這裡同樣是採用binder方式,注意到上面IFingerprintDaemon.h第30行的NOTE,需要手動保證IFingerprintDaemon.h檔案與IFingerprintDaemon.aidl檔案一致,由於java層aidl檔案編譯時會自動編譯成IFingerprintDaemon.java檔案。

當新增介面來呼叫指紋底層暴露的介面,在IFingerprintDaemon.h檔案中新增類似上面35行到68行的列舉,列舉的值需要與java層aidl自動生成的java檔案中的列舉保持一致。另外還需要在上面68行處加上描述這些介面的純虛擬函式(c++中的純虛擬函式類似java的抽象方法,用於定義介面的規範,在C++中,一個具有純虛擬函式的基類被稱為抽象類)。

如下面截圖對比,我們發現IFingerprintDaemon.cpp和java層aidl生成的IFingerprintDaemon.java在onTransact是基本一致的。這樣我們也就明白了為什麼上面說需要手動和IFingerprintDaemon.aidl保持同步了,這樣方式類似我們平時在三方應用使用aidl檔案,需要保持client端和server端aidl檔案一致。

可以看到onTransact有四個引數
code , data ,replay , flags
code 是一個整形的唯一標識,用於區分執行哪個方法,客戶端會傳遞此引數,告訴服務端執行哪個方法
data客戶端傳遞過來的引數
replay伺服器返回回去的值
flags標明是否有返回值,0為有(雙向),1為沒有(單向)

image

IFingerprintDaemon.aidl檔案生成的IFingerprintDaemon.java檔案
image

接著介紹下fingerprintd程序是如何和Fingerprint Hal層是如何傳遞資料的。

說到Hal層,即硬體抽象層,Android系統為HAL層中的模組介面定義了規範,所有工作於HAL的模組必須按照這個規範來編寫模組介面,否則將無法正常訪問硬體。
image

指紋的HAL層規範fingerprint.h在/hardware/libhardware/include/hardware/下可以看到。

我們注意到在fingerprint.h中定義了兩個結構體,分別是fingerprint_device_t和fingerprint_module_t,如下圖。
image

fingerprint_device_t結構體,用於描述指紋硬體裝置;fingerprint_module_t結構體,用於描述指紋硬體模組。在FingerprintDaemonProxy.cpp就是通過拿到fingerprint_device_t這個結構體來和Fingerprint HAL層通訊的。

當需要新增介面呼叫指紋底層時,在這個fingerprint.h中同樣需要新增函式指標,然後通過FingerprintDaemonProxy.cpp中拿到這個fingerprint_device_t來呼叫fingerprint.h中定義的函式指標,也就相當於呼叫指紋HAL層。

我們重點看一下它的openHal()函式。

image

openHal的方法這裡主要看上面三個部分:

  • ①根據名稱獲取指紋hal層模組。hw_module這個一般由指紋晶片廠商根據 fingerprint.h實現,hw_get_module是由HAL框架提供的一個公用的函式,這個函式的主要功能是根據模組ID(module_id)去查詢註冊在當前系統中與id對應的硬體物件,然後載入(load)其相應的HAL層驅動模組的*so檔案。
  • ②呼叫fingerprint_module_t的open函式
  • ③向hal層註冊訊息回撥函式,主要回調 註冊指紋進度,識別結果,錯誤資訊等等
  • ④判斷向hal層註冊訊息回撥是否註冊成功

目前關於指紋的上層流程大致就到這兒,指紋底層就不怎麼介紹了,術業有專攻,和專業做指紋的還是有不少差距。

關注我的公眾號 ,不定期會有優質技術文章推送 。

微信掃一掃下方二維碼即可關注
在這裡插入圖片描述