Android7.0指紋服務(wù)FingerprintService實(shí)例介紹
指紋服務(wù)是Android系統(tǒng)中一個(gè)較為簡(jiǎn)單的服務(wù)(相比于AMS,WMS等),也比較獨(dú)立,功能上包括幾點(diǎn)
- 指紋的錄入與刪除
- 指紋認(rèn)證
- 指紋的安全策略(錯(cuò)誤次數(shù)判定)
和其他的system service 一樣,應(yīng)用程序通過(guò)FingerprintManager實(shí)現(xiàn)與FingerprintService的通信,除了上面所說(shuō)的功能之外,F(xiàn)ingerprintManager提供了一些別的的接口,重要的接口都會(huì)要求系統(tǒng)級(jí)別的權(quán)限,并且也不是公開(kāi)的api(指紋的錄入,刪除,重命名,重置錯(cuò)誤計(jì)數(shù)等)
/**
* Obtain the list of enrolled fingerprints templates.
* @return list of current fingerprint items
*
* @hide
*/
@RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints(int userId) {
if (mService != null) try {
return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return null;
}
/**
* @hide
*/
@RequiresPermission(allOf = {
USE_FINGERPRINT,
INTERACT_ACROSS_USERS})
public boolean hasEnrolledFingerprints(int userId) {
if (mService != null) try {
return mService.hasEnrolledFingerprints(userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return false;
}
/**
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
*/
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
try {
long deviceId = 0; /* TODO: plumb hardware id to FPMS */
return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
}
return false;
}
FingerprintService的啟動(dòng)過(guò)程
FingerprintService在system server中創(chuàng)建并初始化,當(dāng)檢測(cè)到手機(jī)支持指紋功能的時(shí)候就會(huì)啟動(dòng)這個(gè)service
...
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mSystemServiceManager.startService(FingerprintService.class);
}
...
FingerprintService在初始化后會(huì)建立和HAL層的通信,即連接到fingerprintd,拿到用于通信的IFingerprintDaemon對(duì)象(binder)
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
IFingerprintDaemon daemon = getFingerprintDaemon();
listenForUserSwitches();
}
public IFingerprintDaemon getFingerprintDaemon() {
if (mDaemon == null) {
mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
if (mDaemon != null) {
try {
mDaemon.asBinder().linkToDeath(this, 0);
mDaemon.init(mDaemonCallback);
mHalDeviceId = mDaemon.openHal();
if (mHalDeviceId != 0) {
updateActiveGroup(ActivityManager.getCurrentUser(), null);
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
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;
}
本質(zhì)上來(lái)說(shuō),除去安全相關(guān)的策略外,指紋的功能是依賴(lài)硬件實(shí)現(xiàn)的,F(xiàn)ingerprintService也只是充當(dāng)了framework java層與native層的消息傳遞者罷了,所以指紋的識(shí)別,錄入和監(jiān)聽(tīng)都是向fingerprintd發(fā)送命令和獲取相應(yīng)的結(jié)果
指紋監(jiān)聽(tīng)認(rèn)證過(guò)程
以指紋認(rèn)證為例,介紹這一過(guò)程,錄入和刪除的過(guò)程和認(rèn)證類(lèi)似,不重復(fù)描述
FingerprintManager
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
return;
} else {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
}
}
if (mService != null) try {
useHandler(handler);
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
}
}
}
可以看到,最終仍然是向FingerprintService發(fā)送消息,但是開(kāi)啟指紋認(rèn)證的函數(shù)傳入了兩個(gè)比較重要的參數(shù),一個(gè)是CancellationSignal對(duì)象,用于取消指紋認(rèn)證,另一個(gè)是指紋認(rèn)證的回調(diào)對(duì)象AuthenticationCallback
public static abstract class AuthenticationCallback {
public void onAuthenticationError(int errorCode, CharSequence errString) { }
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
public void onAuthenticationSucceeded(AuthenticationResult result) { }
public void onAuthenticationFailed() { }
public void onAuthenticationAcquired(int acquireInfo) {}
};
看函數(shù)名稱(chēng)也能知道其功能,他們分別代表了指紋認(rèn)證時(shí)的回調(diào)結(jié)果(成功,失敗,檢測(cè)到指紋,認(rèn)證異常等),參數(shù)包含了具體的信息,這些信息在FingerprintManager中都有對(duì)應(yīng)的常量定義,有興趣可以查看代碼
FingerprintService
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getCallingUserId();
final int pid = Binder.getCallingPid();
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
callingUid, pid)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
// Get performance stats object for this user.
HashMap<Integer, PerformanceStats> pmap
= (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
PerformanceStats stats = pmap.get(mCurrentUserId);
if (stats == null) {
stats = new PerformanceStats();
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
startAuthentication(token, opId, callingUserId, groupId, receiver,
flags, restricted, opPackageName);
}
});
}
前面會(huì)有對(duì)包名,userid以及應(yīng)用進(jìn)程是否在在前臺(tái)的檢查,繼續(xù)看
private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
updateActiveGroup(groupId, opPackageName);
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
@Override
public boolean handleFailedAttempt() {
mFailedAttempts++;
if (mFailedAttempts == MAX_FAILED_ATTEMPTS) {
mPerformanceStats.lockout++;
}
if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
scheduleLockoutReset();
return true;
}
return false;
}
@Override
public void resetFailedAttempts() {
FingerprintService.this.resetFailedAttempts();
}
@Override
public void notifyUserActivity() {
FingerprintService.this.userActivity();
}
@Override
public IFingerprintDaemon getFingerprintDaemon() {
return FingerprintService.this.getFingerprintDaemon();
}
};
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
// Don't bother starting the client. Just send the error message.
if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
Slog.w(TAG, "Cannot send timeout message to client");
}
return;
}
startClient(client, true /* initiatedByClient */);
}
AuthenticationClient繼承自ClientMonitor,用于處理指紋認(rèn)證相關(guān)的功能事務(wù),ClientMonitor的其他子類(lèi)如RemovalMonior,EnrollMonitor也是如此,ClientMonitor會(huì)直接與fingerprintd通信,其核心是調(diào)用其start()或stop()方法,
對(duì)于AuthenticationClient而言
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
currentClient.stop(initiatedByClient);
mPendingClient = newClient;
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
mCurrentClient = newClient;
if (DEBUG) Slog.v(TAG, "starting client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient + ")");
newClient.start();
}
}
public int start() {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "start authentication: no fingeprintd!");
return ERROR_ESRCH;
}
try {
final int result = daemon.authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
return result;
}
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
}
向底層發(fā)送認(rèn)證命令后就只需要等待認(rèn)證結(jié)果就可以了,前面我們說(shuō)到在初始化的時(shí)候會(huì)建立與fingerprintd的通信,其核心是下面這行代碼
mDaemon.init(mDaemonCallback);
mDaemonCallback是一個(gè)binder對(duì)象,接受來(lái)自底層的結(jié)果,然后通過(guò)FingerprintService和FingerManager一層層把結(jié)果發(fā)送到應(yīng)用程序中去。
8.0的一些變化
8.0上的fingerprintd變化很大,甚至都不叫fingerprintd了,當(dāng)然這是native層的東西,這里不討論,對(duì)于FingerprintService而言,一個(gè)顯著的變化是安全策略的調(diào)整
- 8.0之前,指紋只能錯(cuò)誤5次,達(dá)到5次時(shí)會(huì)禁止指紋認(rèn)證,同時(shí)開(kāi)啟30秒倒計(jì)時(shí),等待結(jié)束后重置錯(cuò)誤計(jì)數(shù),繼續(xù)認(rèn)證
- 8.0之后,依然是每錯(cuò)誤5次就會(huì)倒計(jì)時(shí)30秒,然而30秒結(jié)束后錯(cuò)誤計(jì)數(shù)并不會(huì)被清空,8.0上加入了最大20次的限制,累計(jì)錯(cuò)誤20次之后就無(wú)法使用指紋認(rèn)證功能了,只能用密碼的方式才能重置錯(cuò)誤計(jì)數(shù)
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
private int getLockoutMode() {
if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return AuthenticationClient.LOCKOUT_PERMANENT;
} else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
(mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
return AuthenticationClient.LOCKOUT_TIMED;
}
return AuthenticationClient.LOCKOUT_NONE;
}
總結(jié)
以上所述是小編給大家介紹的Android7.0指紋服務(wù)FingerprintService實(shí)例介紹,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android服務(wù)應(yīng)用ClockService實(shí)現(xiàn)鬧鐘功能
- Android 系統(tǒng)服務(wù)TelecomService啟動(dòng)過(guò)程原理分析
- Android8.0適配前臺(tái)定位服務(wù)service的示例代碼
- 淺談Android Service服務(wù)的高級(jí)技巧
- 說(shuō)說(shuō)在Android如何使用服務(wù)(Service)的方法
- Android實(shí)現(xiàn)Service在前臺(tái)運(yùn)行服務(wù)
- Android實(shí)現(xiàn)在ServiceManager中加入自定義服務(wù)的方法詳解
- Android服務(wù)Service教程
相關(guān)文章
Android的簡(jiǎn)單前后端交互(okHttp+springboot+mysql)
這篇文章主要介紹了Android的簡(jiǎn)單前后端交互(okHttp+springboot+mysql),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Kotlin中?和!!的區(qū)別詳細(xì)對(duì)比
這篇文章主要給大家介紹了關(guān)于Kotlin中?和!!區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Android實(shí)現(xiàn)登錄注冊(cè)功能封裝
Android應(yīng)用軟件基本上都會(huì)用到登錄注冊(cè)功能,本篇文章主要介紹了Android登錄注冊(cè)功能封裝功能實(shí)現(xiàn),具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
Android ListView 默認(rèn)選中某一項(xiàng)實(shí)現(xiàn)代碼
這篇文章主要介紹了Android ListView 默認(rèn)選中某一項(xiàng)實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Flutter Navigator路由傳參的實(shí)現(xiàn)
本文主要介紹了Flutter Navigator路由傳參的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android啟動(dòng)屏實(shí)現(xiàn)左右滑動(dòng)切換查看功能
這篇文章主要介紹了Android啟動(dòng)屏實(shí)現(xiàn)左右滑動(dòng)切換查看功能的相關(guān)資料,針對(duì)新功能屬性介紹和啟動(dòng)屏進(jìn)行詳細(xì)講解,感興趣的小伙伴們可以參考一下2016-01-01
Android編程調(diào)用Camera和相冊(cè)功能詳解
這篇文章主要介紹了Android編程調(diào)用Camera和相冊(cè)功能,結(jié)合實(shí)例形式分析了Android的拍照及相冊(cè)調(diào)用功能相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2017-02-02
往Android系統(tǒng)中添加服務(wù)的方法教程
最近因?yàn)槠脚_(tái)升級(jí),需要在系統(tǒng)中添加一些服務(wù),所以將整個(gè)過(guò)程總結(jié)一下,下面這篇文章主要給大家介紹了往Android系統(tǒng)中添加服務(wù)的方法教程,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-05-05
Android 可拖動(dòng)的seekbar自定義進(jìn)度值
這篇文章主要介紹了Android 可拖動(dòng)的seekbar自定義進(jìn)度值的相關(guān)資料,有需要的朋友參考下2016-04-04

