1 人脸解锁相关类介绍
FaceManager
FaceManager是一个私有接口,用于维护FaceService的之间连接。Keyguard通过该接口访问具有自定义界面的人脸识别身份验证硬件。应用无权访问FaceManager,必须改为使用BiometricPrompt。
FaceService
该框架实现用于管理对人脸识别身份验证硬件的访问权限。它包含基本的注册和身份验证状态机以及各种其他辅助程序(例如枚举程序)。处于稳定性和安全性方面的考虑,不允许在此进程中运行任何供应商代码。所有供应商代码都通过Face 1.0 HIDL接口访问。
faced
这是一个Linux可执行文件,用于实现供FaceService使用的Face 1.0 HIDL 接口。它会将自身注册为 IBiometricsFace@1.0以便FaceService能够找到它。
下面我们从应用到底层依次来看。
2 人脸录入
人脸录入的入口在Settings中,其重要负责人脸录入一些UI的加载和一些录入动画的逻辑。
2.1 人脸录入
先来看录入的入口类FaceEnrollEnrolling
。
java
public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling
- public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
- public abstract class BiometricEnrollBase extends InstrumentedActivity
- public abstract class InstrumentedActivity extends ObservableActivity
- public class ObservableActivity extends FragmentActivity
- androidx.fragment.app.FragmentActivity;
从继承关系可以看到,
是一个activity。
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.face_enroll_enrolling);
setHeaderText(R.string.security_settings_face_enroll_repeat_title);
mErrorText = findViewById(R.id.error_text);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
this, android.R.interpolator.linear_out_slow_in);
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
new FooterButton.Builder(this)
.setText(R.string.security_settings_face_enroll_enrolling_skip)
.setListener(this::onSkipButtonClick)
.setButtonType(FooterButton.ButtonType.SKIP)
.setTheme(R.style.SudGlifButton_Secondary)
.build()
);
....
startEnrollment();
}
先来看布局文件face_enroll_enrolling.xml
xml
<com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="?attr/face_layout_theme">
<LinearLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipToPadding="false"
android:clipChildren="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<com.android.settings.biometrics.face.FaceSquareFrameLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_weight="1">
<com.android.settings.biometrics.face.FaceSquareTextureView android:id="@+id/texture_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null" />
<ImageView
android:id="@+id/circle_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.settings.biometrics.face.FaceSquareFrameLayout>
<TextView
style="@style/TextAppearance.ErrorText"
android:id="@+id/error_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:accessibilityLiveRegion="polite"
android:gravity="center"
android:visibility="invisible"/>
</LinearLayout>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>
它的布局问题主要包含了:id/texture_view
:相机预览图,id/circle_view
: 进度动画以及错误提示语。
我们再回到这个类中去看看startEnrollment录入的方法
java
@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);
}
private ParticleCollection.Listener mListener = new ParticleCollection.Listener() {
@Override
public void onEnrolled() {
FaceEnrollEnrolling.this.launchFinish(mToken);
}
};
startEnrollment
里面创建了一个FaceEnrollPreviewFragment,然后设置了人脸录入完成的监听。此方法中没有明显录入的方法,可见录入方法存在于他的父类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);
}
这里创建了一个BiometricEnrollSidecar,通过给mSidecar设置setListener监听传入变化而开始录入的。
java
/**
* @return an instance of the biometric enroll sidecar
*/
protected abstract BiometricEnrollSidecar getSidecar();
我们这里是人脸解锁,所以BiometricEnrollSidecar就是FaceEnrollSidecar
/packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
java
@Override
public void startEnrollment() {
super.startEnrollment();
mFaceManager.enroll(mUserId, mToken, mEnrollmentCancel,
mEnrollmentCallback, mDisabledFeatures);
}
这样就调到了frameworks/base/core/java/android/hardware/face/FaceManager.java的enroll来录入了,到这里就正式进入系统流程了。
java
/**
* Request face authentication enrollment.
* This call operates the face authentication hardware
* and starts capturing images. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
* {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
* {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0,
* at which point the object is no longer valid.
* The operation can be canceled by using the
* provided cancel object.
*
* @param token a unique token provided by a recent creation
* or verification of device credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
* @param flags optional flags
* @param userId the user to whom this face will belong to
* @param callback an object to receive enrollment events
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, 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(userId, mToken, token, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
// Though this may not be a hardware issue,
// it will cause apps to give up or try again later.
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, getErrorString(
mContext, FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
}
} finally {
Trace.endSection();
}
}
}
这里直接调用的FaceService#enroll
,继续往下看。
java
/**
* Receives the incoming binder calls from FaceManager.
*/
private final class FaceServiceWrapper extends IFaceService.Stub {
@Override // Binder call
public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
final IFaceServiceReceiver receiver,
final String opPackageName, final int[] disabledFeatures) {
checkPermission(MANAGE_BIOMETRIC);
updateActiveGroup(userId, opPackageName);
mHandler.post(() -> {
mNotificationManager.cancelAsUser(
NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT);
});
final boolean restricted = isRestricted();
final EnrollClientImpl client =
new EnrollClientImpl(getContext(), mDaemonWrapper,
mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, cryptoToken, restricted,
opPackageName, disabledFeatures, ENROLL_TIMEOUT_SEC) {
@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);
}
}
这里先检查有没有MANAGE_BIOMETRIC的权限,然后向IBiometricsFace设置setActiveUser,这里重点是enrollInternal。
java
/**
* Calls from the Manager. These are still on the calling binder's thread.
*/
protected void enrollInternal(EnrollClientImpl client, int userId) {
if (hasReachedEnrollmentLimit(userId)) {
return;
}
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default biometrics for the user.
if (!isCurrentUserOrProfile(userId)) {
return;
}
mHandler.post(() -> {
startClient(client, true /* initiatedByClient */);
});
}
enrollInternal首先检查人脸录入的数量是否达到限制。
java
@Override
protected boolean hasReachedEnrollmentLimit(int userId) {
final int limit = getContext().getResources().getInteger(
com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many faces registered, user: " + userId);
return true;
}
return false;
}
对比的方法是将res配置的数量和已经录入的进行对比。
xml
<!-- Limit for the number of face templates per user -->
<integer name="config_faceMaxTemplatesPerUser">1</integer>
从res可以看到,人脸最大配置个数为1,也就是最大只能录入一个人脸数据。what?不给女朋友机会还是保护女朋友隐私?
最后从binder线程,切换到主线程中执行startClient。
java
/**
* Calls the HAL to switch states to the new task. If there's already a current task,
* it calls cancel() and sets mPendingClient to begin when the current task finishes
* ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
*
* @param newClient the new client that wants to connect
* @param initiatedByClient true for authenticate, remove and enroll
*/
@VisibleForTesting
void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
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);
// Only post the reset runnable for non-cleanup clients. Cleanup clients should
// never be forcibly stopped since they ensure synchronization between HAL and
// framework. Thus, we should instead just start the pending client once cleanup
// finishes instead of using the reset runnable.
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
// For BiometricPrompt clients, do not start until
// <Biometric>Service#startPreparedClient is called. BiometricService waits until all
// modalities are ready before initiating authentication.
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;
}
}
// We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
startCurrentClient(mCurrentClient.getCookie());
}
}
这里如果已经有task,就先cancel调当前的,然后将newClient设置到mPendingClient,并设置一个3s的延迟消息来延迟执行mPendingClient,最后执行startCurrentClient。
java
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() + ")"
+ " targetUserId: " + mCurrentClient.getTargetUserId()
+ " currentUserId: " + mCurrentUserId
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
int status = mCurrentClient.start();
if (status == 0) {
notifyClientActiveCallbacks(true);
} else {
mCurrentClient.onError(getHalDeviceId(), BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
removeClient(mCurrentClient);
}
}
还是把mCurrentClient的对象传进去了。然后是 mCurrentClient.start()。
java
//frameworks/base/services/core/java/com/android/server/biometrics/EnrollClient.java
@Override
public int start() {
mEnrollmentStartTimeMs = System.currentTimeMillis();
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(), mTimeoutSec,disabledFeatures);
if (result != 0) {
Slog.w(getLogTag(), "startEnroll failed, result=" + result);
mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
onError(getHalDeviceId(),
BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
return result;
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "startEnroll failed", e);
}
return 0; // success
}
EnrollClient#start方法会通过getDaemonWrapper().enroll调用faced,调用底层的人脸库,底层库返回结果后会调用onEnrollResult来反馈结果receiver,再往上层反馈。这就是人脸的录制流程。
在onEnrollResult中当remaining等于0的时候完成录制,调用addBiometricForUser。
FaceManager.java中注册了IFaceServiceReceiver,实现onEnrollResult方法发送 MSG_ENROLL_RESULT
java
//frameworks/base/core/java/android/hardware/face/FaceManager.java
private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
@Override // binder call
public void onEnrollResult(long deviceId, int faceId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Face(null, faceId, deviceId)).sendToTarget();
}
}
private class MyHandler extends Handler {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
break;
}
}
}
private void sendEnrollResult(Face face, int remaining) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
2.2 录入进度
在前面FaceEnrollEnrolling类中的onEnrollmentProgressChange(int steps, int remaining)更新录入进度的方法中用通过传递进来的remaining来获取实际的进度;当remaining = 0 时打开录入结束界launchFinish(mToken)
java
//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);
// TODO: Update the actual animation
showError("Steps: " + steps + " Remaining: " + remaining);
// TODO: Have this match any animations that UX comes up with
if (remaining == 0) {
launchFinish(mToken);
}
这里调用FaceEnrollPreviewFragment#onEnrollmentProgressChange
java
//packages/apps/Settings/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
mAnimationDrawable.onEnrollmentProgressChange(steps, remaining);
}
这里继续调用FaceEnrollAnimationDrawable#onEnrollmentProgressChange
java
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
/*重点关注*/
mParticleCollection.onEnrollmentProgressChange(steps, remaining);
}
继续
java
/*重点关注*/
public class ParticleCollection implements BiometricEnrollSidecar.Listener {
......
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
if (remaining == 0) {
updateState(STATE_COMPLETE);
}
}
}
可以看出,ParticleCollection实现了BiometricEnrollSidecar.Listener,从而调用onEnrollmentProgressChange通过传入的remaining值对录入人脸的进度条进行更新的。
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);
}
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接口的类可以接收到。
3 人脸解锁
3.1 人脸监听
在灭屏之后,PowerManagerService的Notifier线程会调用PhoneWindowManager#startedGoingToSleep通知startedGoingToSleep。
java
// Called on the PowerManager's Notifier thread.
@Override
public void startedGoingToSleep(int why) {
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Started going to sleep... (why="
+ WindowManagerPolicyConstants.offReasonToString(why) + ")");
}
mGoingToSleep = true;
mRequestedOrGoingToSleep = true;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(why);
}
}
调用KeyguardDelegate.onStartedGoingToSleep
方法,继而又会调用KeyguardServiceWrapper.java#onStartedGoingToSleep
方法,再调用KeyguardService#onStartedGoingToSleep
方法;并最终到达了KeyguardViewMediator#onStartedGoingToSleep
java
/**
* Called to let us know the screen was turned off.
* @param why either {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_USER} or
* {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
*/
public void onStartedGoingToSleep(int why) {
if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")");
...
mUpdateMonitor.dispatchStartedGoingToSleep(why);
notifyStartedGoingToSleep();
}
然后到KeyguardUpdateMonitor#dispatchStartedGoingToSleep
java
public void dispatchStartedGoingToSleep(int why) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0));
}
protected void handleStartedGoingToSleep(int arg1) {
Assert.isMainThread();
mLockIconPressed = false;
clearBiometricRecognized();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onStartedGoingToSleep(arg1);
}
}
mGoingToSleep = true;
updateBiometricListeningState();
}
private void updateBiometricListeningState() {
updateFingerprintListeningState();
updateFaceListeningState();
}
private void updateFaceListeningState() {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
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();
}
}
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);
}
}
这里先创建了一个CancellationSignal对象,这个类主要是给用户提供了一个cancel的接口来取消或者监听取消操作。
然后调用FaceManager#authenticate
来开始人脸解锁监听请求。
java
/**
* Request authentication of a crypto object.
* This call operates the face recognition hardware
* and starts capturing images. It terminates when
* {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
* {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
* is called, at which point the object is no longer valid.
* The operation can be canceled by using the
* provided cancel object.
*
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
* @param flags optional flags; should be 0
* @param callback an object to receive authentication events
* @param handler an optional handler to handle callback events
* @param userId userId to authenticate for
* @throws IllegalArgumentException if the crypto operation is not supported
* or is not backed by Android Keystore facility
* @throws IllegalStateException if the crypto primitive is not initialized.
* @hide
*/
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) {
// Though this may not be a hardware issue, it will cause apps to give up or
// try again later.
callback.onAuthenticationError(
FACE_ERROR_HW_UNAVAILABLE,getErrorString(
mContext, FACE_ERROR_HW_UNAVAILABLE,0 /* vendorCode */));
}
} finally {
Trace.endSection();
}
}
}
然后调用FaceService#authenticate
java
@Override // Binder call
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 /* groupId */, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
继续到BiometricServiceBase#authenticateInternal
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);
}
protected void authenticateInternal(AuthenticationClientImpl client, long opId,
String opPackageName, int callingUid,
int callingPid, int callingUserId) {
if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid,
callingPid, callingUserId)) {
if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
return;
}
mHandler.post(() -> {
mMetricsLogger.histogram(getConstants().tagAuthToken(), 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;
mIsCrypto = (opId != 0);
startAuthentication(client, opPackageName);
});
}
首先判断是否可以进行人脸解锁
java
/**
* Helper methods.
*/
/**
* @param opPackageName name of package for caller
* @param requireForeground only allow this call while app is in the foreground
* @return true if caller can use the biometric API
*/
protected boolean canUseBiometric(String opPackageName, boolean requireForeground,
int uid, int pid, int userId) {
checkUseBiometricPermission();
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
return true; // System process (BiometricService, etc) is always allowed
}
if (isKeyguard(opPackageName)) {
return true; // Keyguard is always allowed
}
if (!isCurrentUserOrProfile(userId)) {
Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
return false;
}
if (!checkAppOps(uid, opPackageName)) {
Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
return false;
}
if (requireForeground && !(Utils.isForeground(uid, pid) || isCurrentClient(
opPackageName))) {
Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
return false;
}
return true;
}
可以看到,能进行人脸解锁的条件:系统用户、在锁屏界面、当前用户匹配、有OP_USE_BIOMETRIC权限、处于前台。
然后调用startAuthentication。
java
// Should be done on a handler thread - not on the Binder's thread.
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 /* vendorCode */)) {
Slog.w(getTag(), "Cannot send permanent lockout message to client");
}
return;
}
startClient(client, true /* initiatedByClient */);
}
继续走startClient
java
void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof InternalEnumerateClient
|| currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
// remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
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);
// Only post the reset runnable for non-cleanup clients. Cleanup clients should
// never be forcibly stopped since they ensure synchronization between HAL and
// framework. Thus, we should instead just start the pending client once cleanup
// finishes instead of using the reset runnable.
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
}
mPendingClient = newClient;
} else if (newClient != null) {
// For BiometricPrompt clients, do not start until
// <Biometric>Service#startPreparedClient is called. BiometricService waits until all
// modalities are ready before initiating authentication.
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;
}
}
// We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
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() + ")"
+ " targetUserId: " + mCurrentClient.getTargetUserId()
+ " currentUserId: " + mCurrentUserId
+ " cookie: " + cookie + "/" + mCurrentClient.getCookie());
if (cookie != mCurrentClient.getCookie()) {
Slog.e(getTag(), "Mismatched cookie");
return;
}
int status = mCurrentClient.start();
if (status == 0) {
notifyClientActiveCallbacks(true);
} else {
mCurrentClient.onError(getHalDeviceId(), BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
removeClient(mCurrentClient);
}
}
上面这些流程,在人脸录入的时候都走过,这里区别是在mCurrentClient.start()开始的,也就是ClientMonitor的子类不一样。人脸录入的时候走的是EnrollClient#start(),而这里是AuthenticationClient#start()。这点需要注意。
进入到AuthenticationClient#start
java
/**
* Start authentication
*/
@Override
public int start() {
mStarted = true;
onStart();
try {
mStartTimeMs = System.currentTimeMillis();
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 /* vendorCode */);
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; // success
}
同样的,这里调到了getDaemonWrapper().authenticate(mOpId, getGroupId());
start方法会调用faced,调用底层的人脸库,底层库返回结果后会调用onAuthenticated来反馈结果给receiver,在往上层反馈。
不过从Android T开始,这块的实现由区别了。
java
/**
* Start authentication
*/
@Override
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
final @LockoutTracker.LockoutMode int lockoutMode;
if (mShouldUseLockoutTracker) {
lockoutMode = mLockoutTracker.getLockoutModeForUser(getTargetUserId());
} else {
lockoutMode = getBiometricContext().getAuthSessionCoordinator()
.getLockoutStateFor(getTargetUserId(), mSensorStrength);
}
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
Slog.v(TAG, "In lockout mode(" + lockoutMode
+ ") ; disallowing authentication");
int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
onError(errorCode, 0 /* vendorCode */);
return;
}
if (mTaskStackListener != null) {
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
}
Slog.d(TAG, "Requesting auth for " + getOwnerString());
mStartTimeMs = System.currentTimeMillis();
mAuthAttempted = true;
startHalOperation();
}
3.2 人脸认证
底层库返回结果后会调用onAuthenticated来反馈结果给receiver,在往上层反馈。
我们从AuthenticationClient#onAuthenticated开始分析。
java
@Override
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());
// Ensure authentication only succeeds if the client activity is on top or is keyguard.
boolean isBackgroundAuth = false;
if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
try {
final List<ActivityManager.RunningTaskInfo> tasks =
ActivityTaskManager.getService().getTasks(1);
if (tasks == null || tasks.isEmpty()) {
Slog.e(TAG, "No running tasks reported");
isBackgroundAuth = true;
} else {
final ComponentName topActivity = tasks.get(0).topActivity;
if (topActivity == null) {
Slog.e(TAG, "Unable to get top activity");
isBackgroundAuth = true;
} else {
final String topPackage = topActivity.getPackageName();
if (!topPackage.contentEquals(getOwnerString())) {
Slog.e(TAG, "Background authentication detected, top: " + topPackage
+ ", client: " + this);
isBackgroundAuth = true;
}
}
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to get running tasks", e);
isBackgroundAuth = true;
}
}
// Fail authentication if we can't confirm the client activity is on top.
if (isBackgroundAuth) {
Slog.e(TAG, "Failing possible background authentication");
authenticated = false;
// SafetyNet logging for exploitation attempts of b/159249069.
final ApplicationInfo appInfo = getContext().getApplicationInfo();
EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
"Attempted background authentication");
}
if (authenticated) {
// SafetyNet logging for b/159249069 if constraint is violated.
if (isBackgroundAuth) {
final ApplicationInfo appInfo = getContext().getApplicationInfo();
EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
"Successful background authentication!");
}
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) {
// BiometricService will add the token to keystore
listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken,
isStrongBiometric());
} else if (!isBiometricPrompt() && listener != null) {
if (isStrongBiometric()) {
KeyStore.getInstance().addAuthToken(byteToken);
} else {
Slog.d(getLogTag(), "Skipping addAuthToken");
}
try {
// Explicitly have if/else here to make it super obvious in case the code is
// touched in the future.
if (!getIsRestricted()) {
listener.onAuthenticationSucceeded(
getHalDeviceId(), identifier, getTargetUserId());
} else {
listener.onAuthenticationSucceeded(
getHalDeviceId(), null, getTargetUserId());
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
}
} else {
// Client not listening
Slog.w(getLogTag(), "Client not listening");
result = true;
}
} else {
if (listener != null) {
vibrateError();
}
// Allow system-defined limit of number of attempts before giving up
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 /* vendorCode */);
} else {
// Don't send onAuthenticationFailed if we're in lockout, it causes a
// janky UI on Keyguard/BiometricPrompt since "authentication failed"
// will show briefly and be replaced by "device locked out" message.
if (listener != null) {
if (isBiometricPrompt()) {
listener.onAuthenticationFailedInternal();
} else {
listener.onAuthenticationFailed(getHalDeviceId());
}
}
}
result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
if(result) { // locked out
mAlreadyDone = true;
}
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
result = true;
}
return result;
}
如果认证结果返回但是当前不是锁屏界面,并且判断是isBackgroundAuth的时候,就会将认证解锁authenticated设置为false,这时会进行认错误震动,如果是BiometricPrompt,就回调onAuthenticationFailedInternal,否则回调onAuthenticationFailed。
如果authenticated设置为true,会进行认证成功震动,并且重置错误次数resetFailedAttempts,如果是BiometricPrompt就回调onAuthenticationSucceededInternal,否则如果是isStrongBiometric,会向KeyStore中加入认证成功的记录byteToken,最后回调onAuthenticationSucceeded。
再来看listener
java
final BiometricServiceBase.ServiceListener listener = getListener();
这里是BiometricServiceBase.ServiceListener,getListener而调用的是父类的getListener。
java
public final BiometricServiceBase.ServiceListener getListener() {
return mListener;
}
它是在ClientMonitor构造函数里面赋值的。我们知道ClientMonitor是在BiometricServiceBase中赋值的,因此,我们来看看BiometricServiceBase中的流程。
java
/**
* Wraps the callback interface from Service -> Manager
*/
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, boolean isStrongBiometric) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailed(long deviceId) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationFailedInternal()
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 {};
}
/**
* Wraps the callback interface from Service -> BiometricPrompt
*/
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, boolean isStrongBiometric) throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationSucceeded(
requireConfirmation, token, isStrongBiometric);
}
}
@Override
public void onAuthenticationFailedInternal()
throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationFailed();
}
}
}
继续到FaceManager中
java
/**
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* the FaceManager.
*/
private class ServiceListenerImpl implements ServiceListener {
private IFaceServiceReceiver mFaceServiceReceiver;
public ServiceListenerImpl(IFaceServiceReceiver receiver) {
mFaceServiceReceiver = receiver;
}
@Override
public void onAuthenticationSucceeded(long deviceId,
BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
if (mFaceServiceReceiver != null) {
if (biometric == null || biometric instanceof Face) {
mFaceServiceReceiver.onAuthenticationSucceeded(deviceId,
(Face) biometric, userId, isStrongBiometric());
} else {
Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
}
}
}
}
mFaceServiceReceiver是一个IFaceServiceReceiver对象,它是在ServiceListenerImpl的构造函数里面传过来的。它的创建是在前面介绍的FaceManager#authenticate。
java
@Override // Binder call
public void authenticate(final IBinder token, final long opId, int userId,
final IFaceServiceReceiver receiver, final int flags,
final String opPackageName) {
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */);
}
可以看到,这个receiver就是传递进去的IFaceServiceReceiver,而receiver是从客户端传入的,也就是前面SystemUI里面。
java
//framework/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
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);
}
}
3.3 人脸解锁
因此,再来看SystemUI的KeyguardUpdateMonitor中的mFaceAuthenticationCallback。
java
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(), result.isStrongBiometric());
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
java
private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
if (mGoingToSleep) {
Log.d(TAG, "Aborted successful auth because device is going to sleep.");
return;
}
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;
}
if (DEBUG_FACE) Log.d(TAG, "Face auth succeeded for user " + userId);
onFaceAuthenticated(userId, isStrongBiometric);
} finally {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
继续走onFaceAuthenticated(userId, isStrongBiometric);
java
protected void onFaceAuthenticated(int userId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
Assert.isMainThread();
mUserFaceAuthenticated.put(userId,
new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
BiometricSourceType.FACE,
isStrongBiometric);
}
}
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
// Report unlock with strong or non-strong biometric
reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
Trace.endSection();
}
里开始调用接口将解锁成功消息层层传递直至keyguard解锁,与指纹解锁逻辑一致
可以看到在onFaceAuthenticated(userId)方法中调用了KeyguardUpdateMonitorCallback这个抽象类的onBiometricAuthenticated()抽象方法,而BiometricUnlockController extends KeyguardUpdateMonitorCallback,并注册了回调mUpdateMonitor.registerCallback(this)
java
@Override
public void onBiometricAuthenticated(int userId,
BiometricSourceType biometricSourceType, boolean isStrongBiometric) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticated = new PendingAuthenticated(
userId, biometricSourceType, isStrongBiometric);
Trace.endSection();
return;
}
mBiometricType = biometricSourceType;
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH).setType(
MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.SUCCESS_EVENT_BY_SOURCE_TYPE.get(
biometricSourceType)).ifPresent(UI_EVENT_LOGGER::log);
boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated(
biometricSourceType, isStrongBiometric);
if (unlockAllowed) {
mKeyguardViewMediator.userActivity();
startWakeAndUnlock(biometricSourceType, isStrongBiometric);
} else {
Log.d(TAG, "onBiometricAuthenticated aborted by bypass controller");
}
}
直接看startWakeAndUnlock。
java
public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
startWakeAndUnlock(calculateMode(biometricSourceType, isStrongBiometric));
}
public void startWakeAndUnlock(@WakeAndUnlockMode int mode) {
Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = mode;
mHasScreenTurnedOnSinceAuthenticating = false;
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
// If we are waking the device up while we are pulsing the clock and the
// notifications would light up first, creating an unpleasant animation.
// Defer changing the screen brightness by forcing doze brightness on our window
// until the clock and the notifications are faded out.
mNotificationShadeWindowController.setForceDozeBrightness(true);
}
// During wake and unlock, we need to draw black before waking up to avoid abrupt
// brightness changes due to display state transitions.
boolean alwaysOnEnabled = mDozeParameters.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 && mMode != MODE_NONE) {
wakeUp.run();
}
switch (mMode) {
case MODE_DISMISS_BOUNCER:
case MODE_UNLOCK_FADING:
Trace.beginSection("MODE_DISMISS_BOUNCER or MODE_UNLOCK_FADING");
mKeyguardViewController.notifyKeyguardAuthenticated(
false /* strongAuth */);
Trace.endSection();
break;
case MODE_UNLOCK_COLLAPSING:
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_UNLOCK_COLLAPSING 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 /* metaDataChanged */,
true /* allowEnterAnimation */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
mUpdateMonitor.awakenFromDream();
}
mNotificationShadeWindowController.setNotificationShadeFocusable(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();
}
如果当前是在或者要进入Bouncer界面,就走mKeyguardViewController.notifyKeyguardAuthenticated。
java
public void notifyKeyguardAuthenticated(boolean strongAuth) {
ensureView();
mKeyguardView.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
}
@Override
public void finish(boolean strongAuth, int targetUserId) {
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
if (mDismissAction != null) {
deferKeyguardDone = mDismissAction.onDismiss();
mDismissAction = null;
mCancelAction = null;
}
if (mViewMediatorCallback != null) {
if (deferKeyguardDone) {
mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
} else {
mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
}
}
}
private void tryKeyguardDone() {
if (DEBUG) {
Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+ mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
}
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
.startPreHideAnimation(mHideAnimationFinishedRunnable);
}
}
如果是MODE_WAKE_AND_UNLOCK,就走mKeyguardViewMediator.onWakeAndUnlocking()
java
public void onWakeAndUnlocking() {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
keyguardDone();
Trace.endSection();
}
后面解锁流程就不看了,本文主要是介绍人脸相关的流程。