Android定制化人脸识别流程的简单了解

3 阅读8分钟

人脸识别大致流程

应用层Settings进行人脸录入,SystemUI进行人脸识别等操作时,会调用FaceMnager的相关接口,进而调用到FaceService和FaceProvider,具体业务由对应的Face***Client(如FaceEnrollClient,FaceAuthenticationClient等)调用Hal接口实现,hal层会通过Sensor.HalSessionCallback(extends ISessionCallback.Stub)把结果回调给Face***Client,最终回调到应用层。

人脸大致流程.png

人脸录入

在系统设置Settings或是开机向导,会有入口跳转到人脸识别管理页面。若此时尚未设置锁屏密码,则会要求添加密码;若已设置锁屏密码,则会要求校验密码。密码验证通过后,如果此时没有已录入的人脸模板或是从开机向导那边过来的,会跳转到人脸录入页面,否则停留在当前页面。

人脸录入 Token 生成

若尚未设置锁屏密码,token会在成功设置锁屏密码后生成;若已设置锁屏密码,token会在成功验证密码后生成。两者的token生成流程类似,下面流程以在设置锁屏密码后生成token为例

人脸token生成.png

人脸录入流程

人脸录入时,会调用到FaceManager的enroll方法,通过跨进程通信,调用到FaceService的enroll方法,目前高安卓版本是通过aidl的方式与hal层进行通信,接下来的流程主要涉及到frameworks/base/services/core/java/com/android/server/biometrics/sensors/face/aidl/ 这个目录里面的代码,会调用到FaceProvider里面的scheduleEnroll方法,在FaceEnrollClient里面打开相机,获取数据后传给hal层,最终通过一系列的回调把结果返回给应用层。

E3384BD8-EEA5-449B-B8D1-5AE356CCA75C_1_201_a.jpeg

人脸识别管理

在人脸识别管理页,会显示已录入人脸名称
FaceUtils初始化时,会根据 sensorId 拼装一个文件名 fileName = "settings_face_" + sensorId + ".xml";初始化FaceUserState时,会从 fileName 文件里面读取人脸数据,人脸录入成功后也会往里面更新数据。路径类似于data/system/users/0/settings_face_4.xml。

人脸识别管理.png

人脸重命名

点击人脸数据item,可更改人脸名称。

人脸重命名.png

renameBiometric方法会根据 mBiometricId 查找匹配的 Face 数据,重置 Name 字段,把数据更新到data/system/users/0/settings_face_4.xml 里面

人脸删除

删除单个人脸

点击人脸数据item删除按钮,可删除单个人脸数据。

删除单个人脸.png

FaceRemovalClient 在执行 onRemoved 方法的过程中,会根据 faceId 删除 data/system/users/0/settings_face_4.xml 里面对应的人脸数据

删除所有人脸

移除锁屏密码时,会删除所有已录入的人脸

删除全部人脸.png

人脸补光

当打开“暗光环境下使用屏幕补光”选项,在锁屏、应用加密等页面使用人脸识别认证时,会根据当前环境亮度去决定是否要进行人脸补光,具体时机是在打开相机前进行监听,在关闭相机前解除监听。

截图 2026-01-23 09-34-55.png

注视不灭屏

开启“注释时不灭屏”选项,超时灭屏倒计时 2200毫秒时,PowerManagerService 会向某一常驻应用发送广播 com.face.pre.dim,该应用处理完一些自身逻辑后,会向 FaceService 发送广播 com.face.detect,face_state = FACE_OPEN, 开始检测人脸,若检测到人脸注视,则会取消人脸检测,重置灭屏倒计时。若超时灭屏时间只有350毫秒还没检测到人脸,PowerManagerService 会向该应用发送广播 com.face.dim.state.changed,该应用处理完自身逻辑后,会向FaceService 发送广播 com.face.detect,face_state = FACE_CLOSE,取消人脸检测。

注视不灭屏.png

解锁

加密应用/文档锁定区/图库加密相册解锁

打开应用加密解锁、文档锁定区解锁、图库加密相册解锁选项时,跳转到相应加密页面时,会增加人脸识别验证方式,对应的验证页面为ConfirmPasswordFragment

首先,会判断是否有录入人脸,从哪些页面跳转过来,并且是否有打开对应解锁类型的选项开关,来确定满不满足使用人脸识别解锁的条件。

private void initFace() {  
    boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(UserHandle.myUserId());  
    boolean fromAppLock = mConfirmPasswordFrom == LockPasswordUtils.PASSWORD_FROM_APP_LOCK;  
    boolean fromAppLockManager = mConfirmPasswordFrom == LockPasswordUtils.PASSWORD_FROM_APP_LOCK_MANAGER;  
    boolean fromDocument = mConfirmPasswordFrom == LockPasswordUtils.PASSWORD_FROM_DOCUMENT;  
    boolean fromGalleryLock = mConfirmPasswordFrom == LockPasswordUtils.PASSWORD_FROM_GALLERY_LOCK;  
    boolean fromNotes = mConfirmPasswordFrom ==  LockPasswordUtils.PASSWORD_FROM_NOTES; 
    boolean fromSafe = mConfirmPasswordFrom ==   LockPasswordUtils.PASSWORD_FROM_SAFE;  
    boolean isFromEnabled = ((fromAppLock || fromAppLockManager) && FaceUtil.isEnableForAppLock(getContext()))  
            || (fromDocument && FaceUtil.isEnableForDocumentLock(getContext()))  
            || (fromGalleryLock && FaceUtil.isEnableForGalleryLock(getContext())  
            || (fromNotes && FaceUtil.isEnableForNotesLock(getContext()))  
            || fromSafe);  
    if (hasEnrolled && isFromEnabled) {  
        mIsFaceRecognitionEnable = true;  
        mIFaceRecognition = new FaceRecognitionImpl(getContext(), mHandler);  
    } else {  
        mIsFaceRecognitionEnable = false;  
    }  
}

当满足上述条件时,如果当前人脸识别处于冻结状态,会显示冻结动画,否则进入人脸识别认证流程。

应用加密人脸识别认证.png

屏幕解锁

屏幕解锁和加密应用解锁等在框架层面的流程类似,在应用层的交互有差异

锁屏人脸识别.png

冻结

多次错误人脸识别导致人脸冻结

屏幕解锁和加密应用解锁等页面,累计识别到75帧错误人脸后,底层会冻结人脸,并给上层发起回调。

人脸冻结回调.png

指纹冻结导致人脸冻结

指纹是强类型生物识别,人脸是弱类型生物识别。强类型的生物识别被冻结时,弱类型的生物识别也会紧跟着被冻结(此时人脸在上层冻结,底层未冻结)

frameworks/base/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
void setTimedLockout(int userId, @Authenticators.Types int strength) {  
    final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);  
    switch (strength) {  
        case Authenticators.BIOMETRIC_STRONG:  
            authMap.get(BIOMETRIC_STRONG).mTimedLockout = true;  
            // fall through  
        case Authenticators.BIOMETRIC_WEAK:  
            authMap.get(BIOMETRIC_WEAK).mTimedLockout = true;  
            // fall through  
        case Authenticators.BIOMETRIC_CONVENIENCE:  
            authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = true;  
            return;  
        default:  
            Slog.e(TAG, "increaseLockoutTime called for invalid strength : "  + strength);  
    }  
}

按电源键亮屏,锁屏页面不会判断当前人脸是否冻结,会直接进行人脸识别认证,在框架层FaceAuthenticationClient调用start方法时会判断是否为冻结状态,如果是,则直接给抛出冻结回调,然后直接return,不会去打开相机和传数据给底层

frameworks/base/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java  
  
@Override  
public void start(@NonNull ClientMonitorCallback callback) {  
    super.start(callback);  
      
    openCameraIfNecessary();  
      
    mState = STATE_STARTED;  
}  
  
private void openCameraIfNecessary() {  
    final int lockoutMode;  
  
    LockoutTracker lockoutTracker = getLockoutTracker();  
    if (lockoutTracker != null) {  
        lockoutMode = lockoutTracker.getLockoutModeForUser(getTargetUserId());  
    } else {  
        lockoutMode = getBiometricContext().getAuthSessionCoordinator()  
                .getLockoutStateFor(getTargetUserId(), getSensorStrength());  
    }  
  
    if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {  
        Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing open camera");  
        return;  
    }  
  
    if (!SystemProperties.getBoolean(CTS_PROPERTY, false)) {  
        openCamera();  
    }  
}  
  
frameworks/base/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java  
  
@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();  
}

加密应用解锁等页面,会先判断当前人脸是否冻结,如果不是冻结状态,才会进行人脸识别认证。

人脸冻结状态获取.png

解冻结

超时自动解除人脸冻结

60秒超时,底层会自动解除人脸冻结,并回调给上层

超时解冻结.png

指纹识别解锁成功解除人脸冻结

指纹识别解除人脸冻结.png

锁屏密码解锁成功解除人脸冻结

whiteboard_exported_image (1).png