Android 人脸解锁源码剖析

2023-05-16

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、人脸识别身份验证HIDL
二、人脸模块流程分析
三、人脸录入
四、人脸匹配
五、人脸解锁屏幕

一、人脸识别身份验证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);

        // 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);
        }
    

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
    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

    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) {
                    // 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();
            }
        }
    }
    

frameworks/base/services/core/java/com/android/server/biometrics/face/FaceService.java

        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 /* groupId */, 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

        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 */);
        });
    }

startClient(client, true /* 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());

        
        }
    }
    

在此将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 /* vendorCode */);
                return result;
            }
        } catch (RemoteException e) {
            Slog.e(getLogTag(), "startEnroll failed", e);
        }
        return 0; // success
    }

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 // binder call
        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

        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 /* remaining */);
                    break;
                    ...

sendEnrollResult

private void sendEnrollResult(Face face, int remaining) {
        if (mEnrollmentCallback != null) {
            mEnrollmentCallback.onEnrollmentProgress(remaining);
        }
    }

四、人脸匹配

人脸解锁的入口在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 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();
        }
    }

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) {
                    // 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();
            }
        }
    }

frameworks/base/services/core/java/com/android/server/biometrics/face/FaceService.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);
        }

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 /* 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);
        });
    }

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 /* vendorCode */)) {
                Slog.w(getTag(), "Cannot send permanent lockout message to client");
            }
            return;
        }
        /*重点关注*/
        startClient(client, true /* initiatedByClient */);
        //这里将AuthenticationClient传递进去
    }    

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());
            // 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());
            //这里继续将AuthenticationClient传递进去
        }
    }

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();
        //这里调用的是AuthenticationClient的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 /* 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
    }

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) {
                    // BiometricService will add the token to keystore
                    listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
                } else if (!isBiometricPrompt() && listener != null) {
                    KeyStore.getInstance().addAuthToken(byteToken);
                    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(getCookie(),
                                    getRequireConfirmation());
                        } else {
                            listener.onAuthenticationFailed(getHalDeviceId());
                        }
                    }
                }
                result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
            }
        } 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


    /**
     * 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)
                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 // binder call
        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 /* userId */);
                    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 {

        /**
         * Called when an unrecoverable error has been encountered and the operation is complete.
         * No further callbacks will be made on this object.
         *
         * @param errorCode An integer identifying the error message
         * @param errString A human-readable error string that can be shown in UI
         */
        public void onAuthenticationError(int errorCode, CharSequence errString) {
        }

        /**
         * Called when a recoverable error has been encountered during authentication. The help
         * string is provided to give the user guidance for what went wrong, such as
         * "Sensor dirty, please clean it."
         *
         * @param helpCode   An integer identifying the error message
         * @param helpString A human-readable string that can be shown in UI
         */
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
        }

        /**
         * Called when a face is recognized.
         *
         * @param result An object containing authentication-related data
         */
         /*重点关注*/
        public void onAuthenticationSucceeded(AuthenticationResult result) {
        }

        /**
         * Called when a face is detected but not recognized.
         */
        public void onAuthenticationFailed() {
        }

        /**
         * Called when a face image has been acquired, but wasn't processed yet.
         *
         * @param acquireInfo one of FACE_ACQUIRED_* constants
         * @hide
         */
        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);
        // 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);
            }
        }

        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
                BIOMETRIC_CONTINUE_DELAY_MS);

        // Only authenticate face once when assistant is visible
        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

   /**
    * Called when a biometric is recognized.
    * @param userId the user id for which the biometric sample was authenticated
    * @param biometricSourceType
    */
    /*重点关注*/
   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) {
        // TODO(b/62444020): remove when this bug is fixed
        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.
            mStatusBarWindowController.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 = 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 /* strongAuth */);
                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 /* 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();
                }
                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 /* authenciated */);
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to call onKeyguardExitResult()", e);
            }

            mExitSecureCallback = null;

            // after succesfully exiting securely, no need to reshow
            // the keyguard when they've released the lock
            mExternallyEnabled = true;
            mNeedToReshowWhenReenabled = false;
            updateInputRestricted();
        }
        /*重点关注*/
        handleHide();
        Trace.endSection();
    }


handleHide();

    private void handleHide() {
        Trace.beginSection("KeyguardViewMediator#handleHide");

        // It's possible that the device was unlocked in a dream state. It's time to wake up.
        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()) {
                // In split system user mode, we never unlock system user. The end user has to
                // switch to another user.
                // TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
                // still completes and makes the screen blank.
                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();
    }

handleHide();

    private void handleHide() {
        Trace.beginSection("KeyguardViewMediator#handleHide");

        // It's possible that the device was unlocked in a dream state. It's time to wake up.
        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()) {
                // In split system user mode, we never unlock system user. The end user has to
                // switch to another user.
                // TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
                // still completes and makes the screen blank.
                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) {
                // Tell ActivityManager that we canceled the keyguardExitAnimation.
                setShowingLocked(mShowing, mAodShowing, true /* force */);
                return;
            }
            mHiding = false;

            if (mWakeAndUnlocking && mDrawnCallback != null) {

                // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
                // the next draw from here so we don't have to wait for window manager to signal
                // this to our ViewRootImpl.
                mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();
                notifyDrawn(mDrawnCallback);
                mDrawnCallback = null;
            }

            // only play "unlock" noises if not on a call (since the incall UI
            // disables the keyguard)
            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

    /**
     * Hides the keyguard view
     */
    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 /* destroyView */);
                    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 /* destroyView */);
            if (wakeUnlockPulsing) {
                mStatusBar.fadeKeyguardWhilePulsing();
                wakeAndUnlockDejank();
            } else {
                boolean staying = mStatusBar.hideKeyguard();
                if (!staying) {
                    mStatusBarWindowController.setKeyguardFadingAway(true);
                    // hide() will happen asynchronously and might arrive after the scrims
                    // were already hidden, this means that the transition callback won't
                    // be triggered anymore and StatusBarWindowController will be forever in
                    // the fadingAway state.
                    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 /* shown */);
        cancelShowRunnable();
        if (mKeyguardView != null) {
            mKeyguardView.cancelDismissAction();
            mKeyguardView.cleanUp();
        }
        mIsAnimatingAway = false;
        if (mRoot != null) {
            mRoot.setVisibility(View.INVISIBLE);
            if (destroyView) {

                // We have a ViewFlipper that unregisters a broadcast when being detached, which may
                // be slow because of AM lock contention during unlocking. We can delay it a bit.
                /*重点关注*/
                mHandler.postDelayed(mRemoveViewRunnable, 50);
            }
        }
    }

mRemoveViewRunnable

private final Runnable mRemoveViewRunnable = this::removeView;
    protected void removeView() {
        if (mRoot != null && mRoot.getParent() == mContainer) {
            /*重点关注*/
            mContainer.removeView(mRoot);
            mRoot = null;
        }
    }

至此锁屏界面移除的逻辑基本clear
原文链接:https://blog.csdn.net/Easyhood/article/details/104353983

友情推荐:

Android 开发干货集锦

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

点击阅读原文,为大佬点赞!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 人脸解锁源码剖析 的相关文章

  • 任务“:app:checkReleaseDuplicateClasses”执行失败

    我的 React Native Android 构建中突然出现构建问题 令人惊讶的是 它是早上建好的 没有做任何改变 但突然就失败了 这就是我得到的错误 知道为什么会发生这种情况吗 在 stack 和 GitHub 中也看到了一些类似的问题
  • 如何使用 (a)smack 在 Android 上保持 XMPP 连接稳定?

    我使用适用于 Android 的 asmack android 7 beem 库 我有一个后台服务正在运行 例如我的应用程序保持活动状态 但 XMPP 连接迟早会在没有任何通知的情况下消失 服务器表示客户端仍然在线 但没有发送或接收数据包
  • Android 上通过 JSCH 的基本 SSH 连接

    作为来自此的用户question https stackoverflow com questions 14323661 simple ssh connect with jsch和这个tutorial http eridem net andr
  • 用于代码生成的 ANTLR 工具版本 4.7.1 与当前运行时版本 4.5.3 不匹配

    我正在开发一个 Android 应用程序 当前使用 DSL 和一些库 突然构建给了我这个错误 任务 app kaptDebugKotlin 失败 用于代码生成的 ANTLR 工具版本 4 7 1 与当前运行时版本 4 5 3 不匹配 用于解
  • 将 ArrayList 保存在捆绑包 savingInstanceState 中

    ArrayList 是在类级别定义的 这些是我保存的实例方法 Override protected void onSaveInstanceState Bundle outState super onSaveInstanceState out
  • 如何在Firebase Android应用程序中分离两个不同的用户?

    我有一个应用程序 有两种不同类型的用户 一种是教师 第二种是普通用户 如果普通会员登录 他会去normal memberActivity如果他是教师会员 他会去Teacher memberActivity 我如何在登录活动中执行此操作 我的
  • 如何在不改变的情况下将字符串转换为字节?

    我需要一个解决方案将字符串转换为字节数组而不需要像这样进行更改 Input String s Test Output String s Test byte b Test 当我使用 s getBytes 那么回复是 B 428b76b8 但我
  • ImageButton 拉伸背景图像

    我正在尝试创建一个没有边框的 ImageButton 但遇到了图像按钮大小的问题 我使用 Eclipse ADT 将 ImageButton 拖到布局中并选择背景图像 图像按钮显示如下 正如您所看到的 背景图像和图像按钮周边之间有一个边框
  • 改造Android基本且简单的问题

    我的服务器返回简单的 Json 结果 如下所示 message Upload Success 我正在尝试将结果放入改造模型类中 public class MyResponse SerializedName message String me
  • EditText 的高度不会扩展到其父级的高度

    我在滚动视图中放置了编辑文本 高度 match parent并期望它的高度等于滚动视图 但事实并非如此 它的高度就像wrap content这意味着如果 EditText 中没有文本 我必须将光标指向要弹出的软键盘的第一 行 我想要的是我可
  • MPAndroidChart:组合图表

    我在用MPAndroidChart 库 https github com PhilJay MPAndroidChart 我想用CombinedChart创建这样的图表 那可能吗 我尝试了一下 但似乎不起作用 因为 这些条目没有按我的预期工作
  • onTouch 给出奇怪的触摸点 Android

    我正在做的事情非常简单 我以前做过 但现在它没有按我的预期运行 无论如何 让我简要解释一下我正在尝试做什么以及我得到了什么 设想 我有一个RelativeLayout其中一个ImageView已放置 现在我设置touchlistener像这
  • 如何使用asynctask显示倒计时的进度条?

    在我的应用程序中 我希望用户按下按钮 然后等待 5 分钟 我知道这听起来很糟糕 但就这样吧 5 分钟等待期间的剩余时间应显示在进度条中 我使用带有文本视图的 CountDownTimer 来倒计时 但我的老板想要看起来更好的东西 这就是进度
  • Java中如何限制文件大小

    我正在我的应用程序中创建一个文件 并继续向该文件中写入一些内容 但是当我的文件达到一定大小 比如说 100 行 后 我想删除第一行并将新行写入底部 要求是我的文件应该受到限制 但它应该保留我写入文件的最新内容 请告诉我在Java中是否可行
  • Exif 方向标签返回 0

    我正在开发一个自定义相机应用程序 我面临以下问题 当我尝试使用检索方向时ExifInterface 它总是返回 0 ORIENTATION UNDEFINED 这使我无法将图像旋转到正确的状态 从而无法正确显示 我使用示例代码来设置相机旋转
  • 在android中的日期选择器对话框中显示当前日期

    我多次尝试在日期选择器对话框中显示当前日期 但失败了 它显示 1 1 1990 我已经遵循了堆栈溢出的一些答案 但不幸的是这些对我不起作用 谁能解释一下在日期选择器对话框中显示当前日期的代码 谢谢 It may help you publi
  • RecyclerView元素更新+异步网络调用

    我有一个按预期工作的回收视图 我的布局中有一个按钮可以填充列表 该按钮应该进行异步调用 根据结果 我更改按钮的外观 这一切都发生得很好 但是 当我单击按钮并快速向下滚动列表时 异步调用的结果会更新新视图的按钮 代替旧视图的视图 我该如何处理
  • SambaFileInputStream 和 FileInputStream 有什么不同?

    我需要从 samba 服务器流式传输视频 并且我使用 nanohttpd 在我的项目中创建简单的服务器 当我使用本地文件中的 fileinputstream 时 视频视图可以按设置播放视频 http localhost 8080 publi
  • android-如何在谷歌地图上将标记的位置显示为地址

    我已经尝试过 commonsware googlemapsv2 教程 特别是在地图上拖动标记 但现在另一个问题困扰着我 问题是如何将标记的当前位置显示为地图下方或上方的地址 字符串 这是我使用的代码 public class MainAct
  • 将 firebase 消息传递添加到 flutter android 项目时出现依赖错误

    我已将 firebase 消息传递添加到我的 Flutter 项目中 在 iOS 上运行良好 在 Android 上运行时出现错误 Android dependency androidx localbroadcastmanager loca

随机推荐

  • Java 多线程编程 实验题

    Java 多线程编程 实验二 1 创建键盘操作练习2 双线程猜数字3 月亮围绕地球 1 创建键盘操作练习 题目描述 xff1a 编写一个Java应用程序 xff0c 在主线程中再创建两个线程 xff0c 一个线程负责给出键盘上字母键上的字母
  • 【测试沉思录】15. 性能测试中的系统资源分析之二:内存

    作者 xff1a 马海琴 编辑 xff1a 毕小烦 二 内存 内存又称主存 xff0c 是 CPU 能直接寻址的存储空间 xff08 由半导体器件制成 xff09 内存的特点是存取速率快 xff0c 断电一般不保存数据 xff08 非持久化
  • 【测试沉思录】16. 性能测试中的系统资源分析之三:磁盘

    作者 xff1a 马海琴 编辑 xff1a 毕小烦 三 磁盘 磁盘是可以持久化存储的设备 xff0c 根据存储介质的不同 xff0c 常见磁盘可以分为两类 xff1a 机械磁盘和固态磁盘 磁盘就像人的大脑皮层 xff0c 负责数据的储存 记
  • 【测试沉思录】17. 性能测试中的系统资源分析之四:网络

    作者 xff1a 马海琴 编辑 xff1a 毕小烦 计算机网络 xff0c 就是通过光缆 电缆 电话线或无线通讯将两台以上的计算机互连起来的集合 xff0c 包括广域网 城域网 局域网和无线网 计算机网络是传输信息的媒介 我们常说的千兆网
  • 【测试沉思录】18.如何测试微信小程序?

    作者 xff1a 雷远缘 编辑 xff1a 毕小烦 一 先知道小程序是什么 啥是小程序 xff1f 小程序是一种不需要下载安装即可使用的应用 xff0c 它实现了应用 触手可及 的梦想 xff0c 用户扫一扫或者搜一下即可打开应用 也体现了
  • 【测试沉思录】19. 如何设置 JMeter 线程组?

    作者 xff1a 宋赟 编辑 xff1a 毕小烦 最近有不少测试同学问我 JMeter 线程组如何设置并发的问题 xff0c 发现很多人对线程组里的参数不是很清楚 xff0c 今天就科普一下 JMeter 线程组的信息 xff0c 也简单介
  • 【测试沉思录】20. 如何做好测试需求分析?

    作者 xff1a 刘亚茹 编辑 xff1a 毕小烦 我们都知道测试用例是软件测试中保障质量的必要手段 xff0c 而测试需求作为用例编写的主要依据却往往被很多人忽视 到底什么是测试需求 xff1f 又如何做好测试需求分析呢 xff1f 本文
  • 【测试沉思录】21. 如何用 JMeter 编写性能测试脚本?

    作者 xff1a 宋赟 编辑 xff1a 毕小烦 Apache JMeter 应该是应用最广泛的性能测试工具 怎么用 JMeter 编写性能测试脚本 xff1f 1 编写 HTTP 性能测试脚本 STEP 1 添加 HTTP 请求 STEP
  • 【测试沉思录】21. 如何用 JMeter 编写性能测试脚本?

    作者 xff1a 宋赟 编辑 xff1a 毕小烦 Apache JMeter 应该是应用最广泛的性能测试工具 怎么用 JMeter 编写性能测试脚本 xff1f 1 编写 HTTP 性能测试脚本 STEP 1 添加 HTTP 请求 STEP
  • 【测试沉思录】22. 前端性能测试怎么做?

    作者 xff1a 张丹青 编辑 xff1a 毕小烦 普通用户如何评价一个网站的体验好不好呢 xff1f 除了满足他的功能需求以外 xff0c 用得爽不爽可能是最大的评估因素 这个爽不爽可以简单理解为快不快 xff0c 好不好看 xff0c
  • 【测试沉思录】23. 如何实现基于场景的接口自动化测试用例?

    作者 xff1a 陈爱娇 编辑 xff1a 毕小烦 自动化本身是为了提高工作效率 xff0c 不论选择何种框架 xff0c 何种开发语言 xff0c 我们最终想实现的效果 xff0c 就是让大家用最少的代码 xff0c 最小的投入 xff0
  • 搭建linux服务器详细教程

    Linux服务器的部署 xff0c 配置 xff0c 搭建步骤 xff1a 1 准备 xff1a 1 1 jdk1 8 xff1a jdk 8u11 linux x64 tar gz tomcat xff1a apache tomcat 8
  • 使用Word2013写论文的时候,波浪号(~)一直在一行的上面,无法上下居中 的解决方案

    这里提供四种方法给大家 xff0c 不用谢 xff01 1 搜狗输入法 xff0c 直接打 blh xff0c 即可获得波浪号 xff08 这个方法大多数地方都可以用 xff0c 比如打摄氏度符号 xff08 xff09 的时候 xff09
  • centos7.4安装图形界面并远程桌面连接

    1 系统版本 CentOS release 6 2 Final 以下安装需要用root权限操作 2 安装x windows yum groupinstall y X Window System 注意有引号 3 安装图形界面软件 GNOME
  • linux服务器更改网络配置

    文章目录 前言一 更改vmware的虚拟网络配置二 修改window的网络配置三 修改虚拟机内部的配置四 映射 选做 修改hostname修改hosts修改windows的配置验证 前言 linux服务器更改网络配置 xff0c 是为让它的
  • FastBoot 刷机教程

    本篇文章主要介绍 Android 开发中的 FastBoot 部分知识点 xff0c 通过阅读本篇文章 xff0c 您将收获以下内容 一 Fastboot 简介 欢迎关注微信公众号 程序员Android 微信公众号 xff1a Progra
  • Google GMS Crash 优化方案

    极力推荐文章 xff1a 欢迎收藏 Android 干货分享 阅读五分钟 xff0c 每日十点 xff0c 和您一起终身学习 xff0c 这里是程序员Android GMS GoogleMobile Service 包是出口国外手机中 Go
  • FastBoot 刷机使用方法

    和你一起终身学习 xff0c 这里是程序员Android 经典好文推荐 xff0c 通过阅读本文 xff0c 您将收获以下知识点 一 Fastboot 简介 二 Fastboot 刷机准备 三 Fastboot 刷机命令 四 其他刷机工具
  • Android 系统奔溃触发WatchDog分析

    和你一起终身学习 xff0c 这里是程序员Android 经典好文推荐 xff0c 通过阅读本文 xff0c 您将收获以下知识点 一 前言 二 场景介绍 三 分析trace文件 一 前言 作为一个Android开发者 xff0c 不管是Ap
  • Android 人脸解锁源码剖析

    和你一起终身学习 xff0c 这里是程序员Android 经典好文推荐 xff0c 通过阅读本文 xff0c 您将收获以下知识点 一 人脸识别身份验证HIDL 二 人脸模块流程分析 三 人脸录入 四 人脸匹配 五 人脸解锁屏幕 一 人脸识别