因為首次接觸指紋模組的專案已是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 介面位置如下
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;
}
};
複製程式碼
三種狀態對應錄入正常,異常,錯誤。簡單的訊息傳遞流程如下圖。
可以看到,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) {
……
}
}
複製程式碼