Android系統開發小記:FingerprintManager

黃燜雞米花發表於2018-10-14

因為首次接觸指紋模組的專案已是Android O平臺,搜了下網路的資料,得知在Oreo上,google重新設計了框架。通過HIDL使framework與HAL通訊.
下面講述了 FingerprintService 和 FingerprintManager 啟動和關聯的流程,不做相關內容可以不需要了解,對於系統開發,關注指紋錄入部分。

FingerprintService啟動

frameworks\base\services\core\java\com\android\server\fingerprint\

/**
 * A service to manage multiple clients that want to access the fingerprint HAL API.
 * The service is responsible for maintaining a list of clients and dispatching all
 * fingerprint -related events.
 *
 * @hide
 */
複製程式碼

FingerprintService 是由 systemserver 開機時啟動的。
frameworks\base\services\java\com\android\server\SystemServer.java

if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
    traceBeginAndSlog("StartFingerprintSensor");
    mSystemServiceManager.startService(FingerprintService.class);
    traceEnd();
}
複製程式碼

FingerprintService.java
FingerprintService 啟動後呼叫 onStart()

    public void onStart() {
        publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
        SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
        listenForUserSwitches();
    }
複製程式碼

SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart"); 這裡實際上是執行了 FingerprintService 中的

    public synchronized IBiometricsFingerprint getFingerprintDaemon() {
        if (mDaemon == null) {
            Slog.v(TAG, "mDeamon was null, reconnect to fingerprint");
            try {
                mDaemon = IBiometricsFingerprint.getService();  //獲取遠端服務的本地物件
            } catch (java.util.NoSuchElementException e) {
                // Service doesn't exist or cannot be opened. Logged below.
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to get biometric interface", e);
            }
            if (mDaemon == null) {
                Slog.w(TAG, "fingerprint HIDL not available");
                return null;
            }

            mDaemon.asBinder().linkToDeath(this, 0);

            try {
                mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to open fingerprint HAL", e);
                mDaemon = null; // try again later!
            }

            if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
            if (mHalDeviceId != 0) {
                loadAuthenticatorIds();
                updateActiveGroup(ActivityManager.getCurrentUser(), null);
                doFingerprintCleanup(ActivityManager.getCurrentUser());
            } else {
                Slog.w(TAG, "Failed to open Fingerprint HAL!");
                MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
                mDaemon = null;
            }
        }
        return mDaemon;
    }
複製程式碼

IBiometricsFingerprint 介面位置如下

Android系統開發小記:FingerprintManager
BiometricsFingerprint.cpp 是介面 IBiometricsFingerprint 的具體實現
android.hardware.biometrics.fingerprint@2.1-service.rc 啟動fps_hal這個service

    service fps_hal /vendor/bin/hw/android.hardware.biometrics.fingerprint@2.1-service
    # "class hal" causes a race condition on some devices due to files created
    # in /data. As a workaround, postpone startup until later in boot once
    # /data is mounted.
    class late_start
    user system
    group system input
複製程式碼

BiometricsFingerprint.cpp 檔案,會在建構函式中去開啟HAL

BiometricsFingerprint::BiometricsFingerprint() : mClientCallback(nullptr), mDevice(nullptr) {
    sInstance = this; // keep track of the most recent instance
    mDevice = openHal();
    if (!mDevice) {
        ALOGE("Can't open HAL module");
    }
}
複製程式碼
fingerprint_device_t* BiometricsFingerprint::openHal() {
    int err;
    const hw_module_t *hw_mdl = nullptr;
    ALOGD("Opening fingerprint hal library...");
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_mdl))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return nullptr;
    }

    if (hw_mdl == nullptr) {
        ALOGE("No valid fingerprint module");
        return nullptr;
    }

    fingerprint_module_t const *module =
        reinterpret_cast<const fingerprint_module_t*>(hw_mdl);
    if (module->common.methods->open == nullptr) {
        ALOGE("No valid open method");
        return nullptr;
    }

    hw_device_t *device = nullptr;

    if (0 != (err = module->common.methods->open(hw_mdl, nullptr, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return nullptr;
    }

    if (kVersion != device->version) {
        // enforce version on new devices because of HIDL@2.1 translation layer
        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
        return nullptr;
    }

    fingerprint_device_t* fp_device =
        reinterpret_cast<fingerprint_device_t*>(device);

    if (0 != (err =
            fp_device->set_notify(fp_device, BiometricsFingerprint::notify))) {
        ALOGE("Can't register fingerprint module callback, error: %d", err);
        return nullptr;
    }

    return fp_device;
}
複製程式碼

openHal() 中連線到指紋HAL層

FingerprintManager關聯

上面簡單介紹了指紋服務相關流程,對應上層應用常用到的則是 FingerprintManager 類。例如 FingerprintSettings 中的重新命名已存在指紋的方法,呼叫的就是 FingerprintManager 的 rename() 方法。
FingerprintSettings.java

    private void renameFingerPrint(int fingerId, String newName) {
            mFingerprintManager.rename(fingerId, mUserId, newName);
            if (!TextUtils.isEmpty(newName)) {
                mFingerprintsRenaming.put(fingerId, newName);
            }
            updatePreferences();
        }

複製程式碼

FingerprintManager.java

    /**
     * Renames the given fingerprint template
     * @param fpId the fingerprint id
     * @param userId the user who this fingerprint belongs to
     * @param newName the new name
     *
     * @hide
     */
    @RequiresPermission(MANAGE_FINGERPRINT)
    public void rename(int fpId, int userId, String newName) {
        // Renames the given fpId
        if (mService != null) {
            try {
                mService.rename(fpId, userId, newName);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } else {
            Log.w(TAG, "rename(): Service not connected!");
        }
    }
複製程式碼

可以看到 FingerprintManager 是Android 提供給上層應用的介面,在類檔案頭,也有這樣的說明

A class that coordinates access to the fingerprint hardware.
複製程式碼

對於上層應用,在需要呼叫 FingerprintManager 時,可由如下方式

    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
            return context.getSystemService(FingerprintManager.class);
        } else {
            return null;
        }
複製程式碼

通過 Context 的 getSystemService(@NonNull Class serviceClass) 方法

    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
        // Because subclasses may override getSystemService(String) we cannot
        // perform a lookup by class alone.  We must first map the class to its
        // service name then invoke the string-based method.
        String serviceName = getSystemServiceName(serviceClass);
        return serviceName != null ? (T)getSystemService(serviceName) : null;
    }
複製程式碼

再呼叫 ContextImpl 的 getSystemServiceName(Class<?> serviceClass) 方法獲取到對應的service 類名,若類名存在則返回 getSystemService(serviceName)

    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
複製程式碼

走到 SystemServiceRegistry 的 getSystemService(ContextImpl ctx, String name),返回 fetcher.getService(ctx)。到這裡有2個問題,SYSTEM_SERVICE_FETCHERS 是什麼?ServiceFetcher 是什麼?

    /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
複製程式碼

搜尋到在 registerService 方法裡面,分別儲存2個 HashMap,SYSTEM_SERVICE_FETCHERS 中以 serviceName 和 serviceFetcher 組成鍵值對儲存。所以現在搞清楚了 SYSTEM_SERVICE_FETCHERS 是一個 HashMap ,通過我們服務的名字 name 字串,從這個 HashMap 裡取出一個 ServiceFetcher,再 return 這個 ServiceFetcher 的 getService()。

/**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
     */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }
複製程式碼

在 SystemServiceRegistry 中的 static 程式碼塊中有一系列的 registerService 方法,包含了各種系統服務。可以看到第三引數應該是 ServiceFetcher 型別,這裡寫入的是 new CachedServiceFetcher()

    registerService(Context.FINGERPRINT_SERVICE, FingerprintManager.class,
                new CachedServiceFetcher<FingerprintManager>() {
            @Override
            public FingerprintManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                final IBinder binder;
                if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
                    binder = ServiceManager.getServiceOrThrow(Context.FINGERPRINT_SERVICE);
                } else {
                    binder = ServiceManager.getService(Context.FINGERPRINT_SERVICE);
                }
                IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
                return new FingerprintManager(ctx.getOuterContext(), service);
            }});
複製程式碼

看下 CachedServiceFetcher ,可以看出這個抽象類繼承自 ServiceFetcher ,實現了 getService(ContextImpl ctx) 並返回了所要的 service。

    /**
     * Override this class when the system service constructor needs a
     * ContextImpl and should be cached and retained by that context.
     */
    static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
        private final int mCacheIndex;

        public CachedServiceFetcher() {
            mCacheIndex = sServiceCacheSize++;
        }

        @Override
        @SuppressWarnings("unchecked")
        public final T getService(ContextImpl ctx) {
            final Object[] cache = ctx.mServiceCache;
            synchronized (cache) {
                // Fetch or create the service.
                Object service = cache[mCacheIndex];
                if (service == null) {
                    try {
                        service = createService(ctx);
                        cache[mCacheIndex] = service;
                    } catch (ServiceNotFoundException e) {
                        onServiceNotFound(e);
                    }
                }
                return (T)service;
            }
        }

        public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
    }
複製程式碼

對於上層應用呼叫 context.getSystemService(FingerprintManager.class) 後,
最終返回的是 return new FingerprintManager(ctx.getOuterContext(), service); 在 FingerprintManager 的構造方法中,繫結了傳入的 service 即 FingerprintService。 再回頭看,但上層應用呼叫 rename 方法時,最終走到了 FingerprintService 裡面

    @Override // Binder call
        public void rename(final int fingerId, final int groupId, final String name) {
            checkPermission(MANAGE_FINGERPRINT);
            if (!isCurrentUserOrProfile(groupId)) {
                return;
            }
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mFingerprintUtils.renameFingerprintForUser(mContext, fingerId, groupId, name);
                }
            });
        }
複製程式碼

錄入

對於系統開發,廠商提供 vendor 部分程式碼合入系統後,需要完成指紋錄入方面的除錯和修改,也就是 settigns 指紋相關內容。 結合上一篇跟蹤到 settings 錄入指紋獲取指紋狀態主要在 FingerprintEnrollSidecar

mFingerprintManager.enroll(mToken, mEnrollmentCancel, 0 /* flags */, mUserId, mEnrollmentCallback);
複製程式碼

其中 mEnrollmentCallback 用來接收指紋錄入過程中的狀態回撥

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

        @Override
        public void onEnrollmentProgress(int remaining) {
            if (mEnrollmentSteps == -1) {
                mEnrollmentSteps = remaining;
            }
            mEnrollmentRemaining = remaining;
            mDone = remaining == 0;
            if (mListener != null) {
                mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
            } else {
                mQueuedEvents.add(new QueuedEnrollmentProgress(mEnrollmentSteps, remaining));
            }
        }

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

        @Override
        public void onEnrollmentError(int errMsgId, CharSequence errString) {
            if (mListener != null) {
                mListener.onEnrollmentError(errMsgId, errString);
            } else {
                mQueuedEvents.add(new QueuedEnrollmentError(errMsgId, errString));
            }
            mEnrolling = false;
        }
    };
複製程式碼

三種狀態對應錄入正常,異常,錯誤。簡單的訊息傳遞流程如下圖。

Android系統開發小記:FingerprintManager
可以看到,FingerprintEnrollSidecar 中的 mEnrollmentCallback 註冊到了 FingerprintManager 中的 mEnrollmentCallback。 在 FingerprintManager 中有個 mServiceReceiver ,在接收到各種 binder call 後,通過 handler 傳送對應訊息,呼叫 mEnrollmentCallback 對應的響應方法。
至於這個 mServiceReceiver 是如何與 HAL 通訊的以後再分析。

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, int vendorCode) {
            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, 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, int vendorCode) {
            mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
        }

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

        @Override // binder call
        public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
            // TODO: propagate remaining
            mHandler.obtainMessage(MSG_ENUMERATED, 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;
    ……
}
複製程式碼
private void sendEnrollResult(Fingerprint fp, int remaining) {
        if (mEnrollmentCallback != null) {
            mEnrollmentCallback.onEnrollmentProgress(remaining);
        }
}
複製程式碼

到這裡差不多搞清楚了,settings 通過以下獲取指錄入狀態的回撥。

private FingerprintManager.EnrollmentCallback mEnrollmentCallback
            = new FingerprintManager.EnrollmentCallback() {
            public void onEnrollmentProgress(int remaining) {
            ……
            }
            
            public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
            ……
            }
            
            public void onEnrollmentError(int errMsgId, CharSequence errString) {
            ……
            }
    }
複製程式碼

相關文章