第一部分,人脸识别身份验证HIDL
借助人脸识别身份验证功能,用户只需要将自己的面孔对准设备即可将其解锁。Android 10 增加了对一种新的人脸识别身份验证堆栈的支持,这种堆栈可安全处理摄像头帧,从而在支持的硬件上进行人脸识别身份验证时保障安全和隐私。Android 10 还提供了一种简单的安全合规实现方法,以支持通过应用集成来完成交易(例如网上银行或其他服务)。
Android 人脸识别身份验证堆栈是Android 10中的新实现。该实现引入了 IBiometricsFace.hal
、IBiometricsFaceClientCallback.hal
、和type.hal
接口。
要实现Face HIDL,你必须在某个供应商专用库中实现 IBiometricsFace.hal
的所有方法
人脸识别架构
BiometricPrompt API
包括人脸识别、指纹识别和虹膜识别在内的所有生物识别身份验证方法。Face HAL
会与以下组件交互:
FaceManager
FaceManager
是一个私有接口,用于维护FaceService
的之间连接。Keyguard
通过该接口访问具有自定义界面的人脸识别身份验证硬件。应用无权访问FaceManager
,必须改为使用BiometricPrompt
。
FaceService
该框架实现用于管理对人脸识别身份验证硬件的访问权限。它包含基本的注册和身份验证状态机以及各种其他辅助程序(例如枚举程序)。处于稳定性和安全性方面的考虑,不允许在此进程中运行任何供应商代码。所有供应商代码都通过Face 1.0 HIDL
接口访问。
faced
这是一个Linux
可执行文件,用于实现供FaceService
使用的Face 1.0 HIDL
接口。它会将自身注册为 IBiometricsFace@1.0
以便FaceService
能够找到它。
第二部分,人脸模块流程分析
要实现Face HIDL,你必须在某个供应商专用库中实现 IBiometricsFace.hal
的所有方法
IBiometricsFace.hal
中主要包括以下主要方法:setCallback(); setActiveUser(); revokeChallenge(); enroll(); cancel(); enumerate(); remove(); authenticate(); userActivity; resetLockout();
其余的四个都是同步方法,应将其阻塞时间缩至最短以免拖延框架。它们分别是generateChallenge(); setFeature(); getFeature; getAuthentitorId()
人脸模块中的录入,匹配,移除是三个大部分;
一、人脸录入
人脸录入的入口在Settings
中的FaceEnrollEnrolling.java
中
在这个类中没有看到明显的录入的方法,只有一些UI的加载和一些录入动画的逻辑。
在此类中的on EnrollmentProgressChange(int steps, int remaining)
更新录入进度的方法中用通过传递进来的remaining
来获取实际的进度;当remaining = 0
时打开录入结束界面launchFinish(mToken);
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
if (DEBUG) {
Log.v(TAG, "Steps: " + steps + " Remaining: " + remaining);
}
mPreviewFragment.onEnrollmentProgressChange(steps, remaining);
showError("Steps: " + steps + " Remaining: " + remaining);
if (remaining == 0) {
launchFinish(mToken);
}
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
mAnimationDrawable.onEnrollmentProgressChange(steps, remaining);
}
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
mParticleCollection.onEnrollmentProgressChange(steps, remaining);
}
packages/apps/Settings/src/com/android/settings/biometrics/face/ParticleCollection.java
public class ParticleCollection implements BiometricEnrollSidecar.Listener {
......
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
if (remaining == 0) {
updateState(STATE_COMPLETE);
}
}
}
由此可以看出此类是实现了BiometricEnrollSidecar.Listener
从而调用onEnrollmentProgressChange
通过传入的remaining
值对录入人脸的进度条进行更新的
packages/apps/Settings/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
public abstract class BiometricEnrollSidecar extends InstrumentedFragment {
public interface Listener {
void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
void onEnrollmentError(int errMsgId, CharSequence errString);
void onEnrollmentProgressChange(int steps, int remaining);
}
onEnrollmentProgressChange
protected 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));
}
}
底层在录制人脸的时候会在FaceManager
中调用onEnrollmentProgress
方法,并将进度remainiing
返回过来,BiometricEnrollSidecar
内部写有Listener
,在使用Listener
的对象将onEnrollmentProgress
的值传递进去,使更多实现Listener
接口的类可以接收到
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
我们再回到这个类中去看看startEnrollment
录入的方法
@Override
public void startEnrollment() {
super.startEnrollment();
mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FACE_PREVIEW);
if (mPreviewFragment == null) {
mPreviewFragment = new FaceEnrollPreviewFragment();
getSupportFragmentManager().beginTransaction().add(mPreviewFragment, TAG_FACE_PREVIEW)
.commitAllowingStateLoss();
}
mPreviewFragment.setListener(mListener);
}
此方法中没有明显录入的方法,可见录入方法存在于他的父类中
packages/apps/Settings/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
public void startEnrollment() {
mSidecar = (BiometricEnrollSidecar) getSupportFragmentManager()
.findFragmentByTag(TAG_SIDECAR);
if (mSidecar == null) {
mSidecar = getSidecar();
getSupportFragmentManager().beginTransaction().add(mSidecar, TAG_SIDECAR)
.commitAllowingStateLoss();
}
mSidecar.setListener(this);
}
由此可知是通过给mSidecar
设置setListener
监听传入变化而开始录入的
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@Override
protected BiometricEnrollSidecar getSidecar() {
final int[] disabledFeatures = new int[mDisabledFeatures.size()];
for (int i = 0; i < mDisabledFeatures.size(); i++) {
disabledFeatures[i] = mDisabledFeatures.get(i);
}
return new FaceEnrollSidecar(disabledFeatures);
}
而又从FaceEnrollEnrolling.java
中可知是给mSidecar
设置的是FaceEnrollSidecar
packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
在FaceEnrollSidecar.java
中的父类中的onStart
方法去启动startEnrollment()
方法
@Override
public void startEnrollment() {
super.startEnrollment();
if (mUserId != UserHandle.USER_NULL) {
mFaceManager.setActiveUser(mUserId);
}
mFaceManager.enroll(mToken, mEnrollmentCancel,
mEnrollmentCallback, mDisabledFeatures);
}
frameworks/base/core/java/android/hardware/face/FaceManager.java
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(byte[] token, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Log.w(TAG, "enrollment already canceled");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
}
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
mService.enroll(mToken, token, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
0 ));
}
} finally {
Trace.endSection();
}
}
}
frameworks/base/services/core/java/com/android/server/biometrics/face/FaceService.java
@Override
public void enroll(final IBinder token, final byte[] cryptoToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
0 , cryptoToken, restricted, opPackageName, disabledFeatures) {
@Override
public int[] getAcquireIgnorelist() {
return mEnrollIgnoreList;
}
@Override
public int[] getAcquireVendorIgnorelist() {
return mEnrollIgnoreListVendor;
}
@Override
public boolean shouldVibrate() {
return false;
}
@Override
protected int statsModality() {
return FaceService.this.statsModality();
}
};
enrollInternal(client, mCurrentUserId);
}
frameworks/base/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
protected void enrollInternal(EnrollClientImpl client, int userId) {
if (hasReachedEnrollmentLimit(userId)) {
return;
}
if (!isCurrentUserOrProfile(userId)) {
return;
}
mHandler.post(() -> {
startClient(client, true );
});
}
startClient(client, true /* initiatedByClient */);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
} else {
currentClient.stop(initiatedByClient);
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
if (newClient instanceof AuthenticationClient) {
AuthenticationClient client = (AuthenticationClient) newClient;
if (client.isBiometricPrompt()) {
if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
mCurrentClient = newClient;
if (mBiometricService == null) {
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
try {
mBiometricService.onReadyForAuthentication(client.getCookie(),
client.getRequireConfirmation(), client.getTargetUserId());
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception", e);
}
return;
}
}
mCurrentClient = newClient;
startCurrentClient(mCurrentClient.getCookie());
}
}
在此将EnrollClient
的对象传进去
startCurrentClient(mCurrentClient.getCookie());
protected void startCurrentClient(int cookie) {
if (mCurrentClient == null) {
Slog.e(getTag(), "Trying to start null client!");
return;
}
if (DEBUG) Slog.v(getTag(), "starting client "
+ mCurrentClient.getClass().getSuperclass().getSimpleName()
+ "(" + mCurrentClient.getOwnerString() + ")"
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
notifyClientActiveCallbacks(true);
mCurrentClient.start();
}
frameworks/base/services/core/java/com/android/server/biometrics/EnrollClient.java
@Override
public int start() {
mEnrollmentStartTimeMs = System.currentTimeMillis();
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final ArrayList<Integer> disabledFeatures = new ArrayList<>();
for (int i = 0; i < mDisabledFeatures.length; i++) {
disabledFeatures.add(mDisabledFeatures[i]);
}
final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 );
return result;
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "startEnroll failed", e);
}
return 0;
}
start
方法会调用faced
,调用底层的人脸库,底层库返回结果后会调用onEnrollResult
来反馈结果receiver
,再往上层反馈。这就是人脸的录制流程。在onEnrollResult
中当remaining
等于0的时候完成录制,调用addBiometricForUser
。
FaceManager.java
中注册了IFaceServiceReceiver
,实现onEnrollResult
方法发送 MSG_ENROLL_RESULT
frameworks/base/core/java/android/hardware/face/FaceManager.java
private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
@Override
public void onEnrollResult(long deviceId, int faceId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Face(null, faceId, deviceId)).sendToTarget();
}
...
MSG_ENROLL_RESULT
@Override
public void handleMessage(android.os.Message msg) {
Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
switch (msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Face) msg.obj, msg.arg1 );
break;
...
sendEnrollResult
private void sendEnrollResult(Face face, int remaining) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
Settings
中也注册了此回调,所以会实时更新人脸录入进度
二、人脸匹配
人脸解锁的入口在Keyguard
中
系统灭屏之后会调用PhoneWindowManager
的startedGoingToSleep
方法,继而调用KeyguardDelegate.onStartedGoingToSleep
方法。
继而又会调用KeyguardServiceWrapper.java
==》onStartedGoingToSleep()
方法;再调用KeyguardService.java
==》onStartedGoingToSleep()
方法;并最终在KeyguardViewMediator.java
==》dispatchStartedGoingToSleep()
达到对GoingToSleep
事件的监听
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
public void dispatchStartedGoingToSleep(int why) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0));
}
MSG_STARTED_GOING_TO_SLEEP
case MSG_STARTED_GOING_TO_SLEEP:
handleStartedGoingToSleep(msg.arg1);
break;
handleStartedGoingToSleep(msg.arg1);
protected void handleStartedGoingToSleep(int arg1) {
clearBiometricRecognized();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onStartedGoingToSleep(arg1);
}
}
mGoingToSleep = true;
updateBiometricListeningState();
}
updateBiometricListeningState()
private void updateBiometricListeningState() {
updateFingerprintListeningState();
updateFaceListeningState();
}
updateFaceListeningState()
private void updateFaceListeningState() {
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
return;
}
mHandler.removeCallbacks(mRetryFaceAuthentication);
boolean shouldListenForFace = shouldListenForFace();
if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
stopListeningForFace();
} else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING
&& shouldListenForFace) {
startListeningForFace();
}
}
startListeningForFace()
private void startListeningForFace() {
if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
if (DEBUG) Log.v(TAG, "startListeningForFace()");
int userId = getCurrentUser();
if (isUnlockWithFacePossible(userId)) {
if (mFaceCancelSignal != null) {
mFaceCancelSignal.cancel();
}
mFaceCancelSignal = new CancellationSignal();
mFaceManager.authenticate(null, mFaceCancelSignal, 0,
mFaceAuthenticationCallback, null, userId);
setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
}
frameworks/base/core/java/android/hardware/face/FaceManager.java
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable 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;
Trace.beginSection("FaceManager#authenticate");
mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
flags, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
0 ));
}
} finally {
Trace.endSection();
}
}
}
frameworks/base/services/core/java/com/android/server/biometrics/face/FaceService.java
@Override
public void authenticate(final IBinder token, final long opId, int userId,
final IFaceServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 , opId, restricted, opPackageName,
0 , false );
authenticateInternal(client, opId, opPackageName);
}
frameworks/base/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
protected void authenticateInternal(AuthenticationClientImpl client, long opId,
String opPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
}
frameworks/base/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
protected void authenticateInternal(AuthenticationClientImpl client, long opId,
String opPackageName, int callingUid, int callingPid, int callingUserId) {
if (!canUseBiometric(opPackageName, true , callingUid, callingPid,
callingUserId)) {
if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
return;
}
mHandler.post(() -> {
mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
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;
mIsCrypto = (opId != 0);
startAuthentication(client, opPackageName);
});
}
startAuthentication(client, opPackageName);
private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
if (!client.onError(getHalDeviceId(), errorCode, 0 )) {
Slog.w(getTag(), "Cannot send permanent lockout message to client");
}
return;
}
startClient(client, true );
}
startClient(client, true /* initiatedByClient */);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient);
}
} else {
currentClient.stop(initiatedByClient);
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
if (newClient instanceof AuthenticationClient) {
AuthenticationClient client = (AuthenticationClient) newClient;
if (client.isBiometricPrompt()) {
if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
mCurrentClient = newClient;
if (mBiometricService == null) {
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
try {
mBiometricService.onReadyForAuthentication(client.getCookie(),
client.getRequireConfirmation(), client.getTargetUserId());
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception", e);
}
return;
}
}
mCurrentClient = newClient;
startCurrentClient(mCurrentClient.getCookie());
}
}
startCurrentClient(mCurrentClient.getCookie());
protected void startCurrentClient(int cookie) {
if (mCurrentClient == null) {
Slog.e(getTag(), "Trying to start null client!");
return;
}
if (DEBUG) Slog.v(getTag(), "starting client "
+ mCurrentClient.getClass().getSuperclass().getSimpleName()
+ "(" + mCurrentClient.getOwnerString() + ")"
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
notifyClientActiveCallbacks(true);
mCurrentClient.start();
}
frameworks/base/services/core/java/com/android/server/biometrics/AuthenticationClient.java
public int start() {
mStarted = true;
onStart();
try {
final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 );
return result;
}
if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
} catch (RemoteException e) {
Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0;
}
start
方法会调用faced
,调用底层的人脸库,底层库返回结果后会调用onAuthenticated
来反馈结果给receiver
,在往上层反馈
三、人脸解锁屏幕
frameworks/base/services/core/java/com/android/server/biometrics/AuthenticationClient.java
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
getTargetUserId(), isBiometricPrompt());
final BiometricServiceBase.ServiceListener listener = getListener();
mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated);
boolean result = false;
try {
if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + authenticated + ")"
+ ", ID:" + identifier.getBiometricId()
+ ", Owner: " + getOwnerString()
+ ", isBP: " + isBiometricPrompt()
+ ", listener: " + listener
+ ", requireConfirmation: " + mRequireConfirmation
+ ", user: " + getTargetUserId());
if (authenticated) {
mAlreadyDone = true;
if (listener != null) {
vibrateSuccess();
}
result = true;
if (shouldFrameworkHandleLockout()) {
resetFailedAttempts();
}
onStop();
final byte[] byteToken = new byte[token.size()];
for (int i = 0; i < token.size(); i++) {
byteToken[i] = token.get(i);
}
if (isBiometricPrompt() && listener != null) {
listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
} else if (!isBiometricPrompt() && listener != null) {
KeyStore.getInstance().addAuthToken(byteToken);
try {
if (!getIsRestricted()) {
listener.onAuthenticationSucceeded(
getHalDeviceId(), identifier, getTargetUserId());
} else {
listener.onAuthenticationSucceeded(
getHalDeviceId(), null, getTargetUserId());
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
}
} else {
Slog.w(getLogTag(), "Client not listening");
result = true;
}
} else {
if (listener != null) {
vibrateError();
}
final int lockoutMode = handleFailedAttempt();
if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ lockoutMode + ")");
stop(false);
final int errorCode = lockoutMode == LOCKOUT_TIMED
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
onError(getHalDeviceId(), errorCode, 0 );
} else {
if (listener != null) {
if (isBiometricPrompt()) {
listener.onAuthenticationFailedInternal(getCookie(),
getRequireConfirmation());
} else {
listener.onAuthenticationFailed(getHalDeviceId());
}
}
}
result = lockoutMode != LOCKOUT_NONE;
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
result = true;
}
return result;
}
frameworks/base/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
protected interface ServiceListener {
default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
default void onAuthenticationSucceeded(long deviceId,
BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailed(long deviceId) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
default void onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
default void onEnumerated(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
}
onAuthenticationSucceeded
protected abstract class BiometricServiceListener implements ServiceListener {
private IBiometricServiceReceiverInternal mWrapperReceiver;
public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
mWrapperReceiver = wrapperReceiver;
}
public IBiometricServiceReceiverInternal getWrapperReceiver() {
return mWrapperReceiver;
}
@Override
public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
}
}
@Override
public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
}
}
}
frameworks/base/core/java/android/hardware/face/FaceManager.java
private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
......
@Override
public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
}
MSG_AUTHENTICATION_SUCCEEDED
case MSG_AUTHENTICATION_SUCCEEDED:
sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 );
break;
sendAuthenticatedSucceeded
private void sendAuthenticatedSucceeded(Face face, int userId) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
new AuthenticationResult(mCryptoObject, face, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
AuthenticationCallback
是Fm
的一个内部回调接口
public abstract static class AuthenticationCallback
extends BiometricAuthenticator.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) {
}
}
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
AuthenticationCallback
接口在KeyguardUpdateMonitor.java
中实现,用于监听FaceService
中人脸的解锁状态
@VisibleForTesting
FaceManager.AuthenticationCallback mFaceAuthenticationCallback
= new FaceManager.AuthenticationCallback() {
@Override
public void onAuthenticationFailed() {
handleFaceAuthFailed();
}
@Override
public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
handleFaceAuthenticated(result.getUserId());
Trace.endSection();
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
handleFaceHelp(helpMsgId, helpString.toString());
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
handleFaceError(errMsgId, errString.toString());
}
@Override
public void onAuthenticationAcquired(int acquireInfo) {
handleFaceAcquired(acquireInfo);
}
};
handleFaceAuthenticated
private void handleFaceAuthenticated(int authUserId) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
final int userId;
try {
userId = ActivityManager.getService().getCurrentUser().id;
} catch (RemoteException e) {
Log.e(TAG, "Failed to get current user id: ", e);
return;
}
if (userId != authUserId) {
Log.d(TAG, "Face authenticated for wrong user: " + authUserId);
return;
}
if (isFaceDisabled(userId)) {
Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
return;
}
onFaceAuthenticated(userId);
} finally {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
onFaceAuthenticated(userId)
protected void onFaceAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
mUserFaceAuthenticated.put(userId, true);
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
}
mFaceCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
BiometricSourceType.FACE);
}
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
BIOMETRIC_CONTINUE_DELAY_MS);
mAssistantVisible = false;
Trace.endSection();
}
这里开始调用接口将解锁成功消息层层传递直至keyguard
解锁,与指纹解锁逻辑一致
可以看到在onFaceAuthenticated(userId)
方法中调用了KeyguardUpdateMonitorCallback
这个抽象类的onBiometricAuthenticated()
抽象方法,而BiometricUnlockController extends KeyguardUpdateMonitorCallback
,并注册了回调mUpdateMonitor.registerCallback(this)
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticatedUserId = userId;
mPendingAuthenticatedBioSourceType = biometricSourceType;
Trace.endSection();
return;
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
startWakeAndUnlock(calculateMode(biometricSourceType));
}
startWakeAndUnlock(calculateMode(biometricSourceType));
public void startWakeAndUnlock(int mode) {
Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = mode;
mHasScreenTurnedOnSinceAuthenticating = false;
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
mStatusBarWindowController.setForceDozeBrightness(true);
}
boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn();
boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled && mWakeUpDelay > 0;
Runnable wakeUp = ()-> {
if (!wasDeviceInteractive) {
if (DEBUG_BIO_WAKELOCK) {
Log.i(TAG, "bio wakelock: Authenticated, waking up...");
}
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"android.policy:BIOMETRIC");
}
if (delayWakeUp) {
mKeyguardViewMediator.onWakeAndUnlocking();
}
Trace.beginSection("release wake-and-unlock");
releaseBiometricWakeLock();
Trace.endSection();
};
if (!delayWakeUp) {
wakeUp.run();
}
switch (mMode) {
case MODE_DISMISS_BOUNCER:
Trace.beginSection("MODE_DISMISS");
mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
false );
Trace.endSection();
break;
case MODE_UNLOCK:
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
showBouncer();
}
Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
case MODE_WAKE_AND_UNLOCK_PULSING:
case MODE_WAKE_AND_UNLOCK:
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
mMediaManager.updateMediaMetaData(false ,
true );
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
mUpdateMonitor.awakenFromDream();
}
mStatusBarWindowController.setStatusBarFocusable(false);
if (delayWakeUp) {
mHandler.postDelayed(wakeUp, mWakeUpDelay);
} else {
mKeyguardViewMediator.onWakeAndUnlocking();
}
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
Trace.endSection();
break;
case MODE_ONLY_WAKE:
case MODE_NONE:
break;
}
mStatusBar.notifyBiometricAuthModeChanged();
Trace.endSection();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public void onWakeAndUnlocking() {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
keyguardDone();
Trace.endSection();
}
keyguardDone();
public void keyguardDone() {
Trace.beginSection("KeyguardViewMediator#keyguardDone");
if (DEBUG) Log.d(TAG, "keyguardDone()");
userActivity();
EventLog.writeEvent(70000, 2);
Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
mHandler.sendMessage(msg);
Trace.endSection();
}
KEYGUARD_DONE
case KEYGUARD_DONE:
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
handleKeyguardDone();
Trace.endSection();
break;
handleKeyguardDone();
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
mUiOffloadThread.submit(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
});
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
}
mUpdateMonitor.clearBiometricRecognized();
if (mGoingToSleep) {
Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
return;
}
if (mExitSecureCallback != null) {
try {
mExitSecureCallback.onKeyguardExitResult(true );
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onKeyguardExitResult()", e);
}
mExitSecureCallback = null;
mExternallyEnabled = true;
mNeedToReshowWhenReenabled = false;
updateInputRestricted();
}
handleHide();
Trace.endSection();
}
handleHide();
private void handleHide() {
Trace.beginSection("KeyguardViewMediator#handleHide");
if (mAodShowing) {
PowerManager pm = mContext.getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:BOUNCER_DOZING");
}
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
if (mustNotUnlockCurrentUser()) {
if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
return;
}
mHiding = true;
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration());
}
}
Trace.endSection();
}
handleStartKeyguardExitAnimation
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
if (!mHiding) {
setShowingLocked(mShowing, mAodShowing, true );
return;
}
mHiding = false;
if (mWakeAndUnlocking && mDrawnCallback != null) {
mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();
notifyDrawn(mDrawnCallback);
mDrawnCallback = null;
}
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
playSounds(false);
}
mWakeAndUnlocking = false;
setShowingLocked(false, mAodShowing);
mDismissCallbackRegistry.notifyDismissSucceeded();
mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
sendUserPresentBroadcast();
}
Trace.endSection();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
mKeyguardMonitor.notifyKeyguardState(
mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
launchPendingWakeupAction();
if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
}
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
if (mStatusBar.isInLaunchTransition() ) {
mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
mStatusBarWindowController.setKeyguardShowing(false);
mStatusBarWindowController.setKeyguardFadingAway(true);
hideBouncer(true );
updateStates();
}
}, new Runnable() {
@Override
public void run() {
mStatusBar.hideKeyguard();
mStatusBarWindowController.setKeyguardFadingAway(false);
mViewMediatorCallback.keyguardGone();
executeAfterKeyguardGoneAction();
}
});
} else {
executeAfterKeyguardGoneAction();
boolean wakeUnlockPulsing =
mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
}
mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true );
if (wakeUnlockPulsing) {
mStatusBar.fadeKeyguardWhilePulsing();
wakeAndUnlockDejank();
} else {
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
mStatusBar.updateScrimController();
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
}
}
updateStates();
mStatusBarWindowController.setKeyguardShowing(false);
mViewMediatorCallback.keyguardGone();
}
StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
}
hideBouncer
private void hideBouncer(boolean destroyView) {
if (mBouncer == null) {
return;
}
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
public void hide(boolean destroyView) {
if (isShowing()) {
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
mDismissCallbackRegistry.notifyDismissCancelled();
}
mIsScrimmed = false;
mFalsingManager.onBouncerHidden();
mCallback.onBouncerVisiblityChanged(false );
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
mIsAnimatingAway = false;
if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
if (destroyView) {
mHandler.postDelayed(mRemoveViewRunnable, 50);
}
}
}
mRemoveViewRunnable
private final Runnable mRemoveViewRunnable = this::removeView;
removeView;
protected void removeView() {
if (mRoot != null && mRoot.getParent() == mContainer) {
mContainer.removeView(mRoot);
mRoot = null;
}
}
至此锁屏界面移除的逻辑基本clear
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)