学习笔记:
人脸解锁概述
人脸解锁即用户通过注视设备的正面方便地解锁手机或平板。Android 10 为支持人脸解锁的设备在人脸认证期间添加了一个新的可以安全处理相机帧、保持隐私与安全的人脸认证栈的支持,也为安全合规地启用集成交易的应用(网上银行或其他服务)提供了一种容易实现的方式。
Android 原生的人脸认证栈在 Android 10 是一种新的实现,与 Android P 不一样了。新的实现介绍了 IBiometricsFace.hal ,IBiometricsFaceClientCallback.hal 和 types.hal 这些接口。例如:我这边的源码都是 extends 以上接口,进行了些扩展后再实现的。
底层 Face HIDL简单认识
为了实现 Face HIDL,必须在供应商 (vendor) 指定的库 (library) 里实现 IBiometricsFace.hal 的所有方法。接下来我们就来看看 hardware/interfaces/biometrics/face/1.0/ 目录下的源代码(这里的源码)。
IBiometricsFace.hal
// hardware/interfaces/biometrics/face/1.0/IBiometricsFace.hal package android.hardware.biometrics.face@1.0; import IBiometricsFaceClientCallback; /** * 用于人脸认证的 HAL 接口 */ interface IBiometricsFace { /** * 设置当前的客户端回调 */ @callflow(next={"setActiveUser"}) @entry setCallback(IBiometricsFaceClientCallback clientCallback) generates (OptionalUint64 result); /** * 设置所有随后的 HAL 操作作用于上面的活跃用户 */ @callflow(next={"authenticate", "generateChallenge", "enumerate", "remove"}) setActiveUser(int32_t userId, string storePath) generates (Status status); /** * 生成随机数,用于 token 校验 */ @callflow(next={"enroll", "revokeChallenge", "setFeature"}) generateChallenge(uint32_t challengeTimeoutSec) generates (OptionalUint64 result); /** * 录入一张用户的人脸 */ @callflow(next={"cancel", "enroll", "revokeChallenge", "remove"}) enroll(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures) generates (Status status); /** * 撤销随机数 */ @callflow(next={"authenticate", "setActiveUser", "enumerate", "remove"}) revokeChallenge() generates (Status status); setFeature(Feature feature, bool enabled, vec<uint8_t> hat, uint32_t faceId) generates(Status status); getFeature(Feature feature, uint32_t faceId) generates (OptionalBool result); /** * 返回和当前人脸集关联的标识符 (ID),认证者 ID */ @callflow(next={"authenticate"}) getAuthenticatorId() generates (OptionalUint64 result); /** * 取消当前的录入、认证、删除人脸或枚举人脸的操作 */ @callflow(next={"authenticate", "enroll", "enumerate", "remove", "setActiveUser"}) cancel() generates (Status status); /** * 枚举正在使用系统的用户的所有人脸模板 */ @callflow(next={"remove", "enroll", "authenticate", "setActiveUser"}) enumerate() generates (Status status); /** * 删除正在使用系统的用户的一个或所有人脸模板 */ @callflow(next={"enumerate", "authenticate", "cancel", "getAuthenticatorId", "setActiveUser"}) remove(uint32_t faceId) generates (Status status); /** * 认证当前用户是否登录系统的用户 */ @callflow(next={"cancel", "generateChallenge", "remove"}) authenticate(uint64_t operationId) generates (Status status); userActivity() generates (Status status); /** * 为当前用户重置禁用状态 */ resetLockout(vec<uint8_t> hat) generates (Status status); };
IBiometricsFaceClientCallback.hal
package android.hardware.biometrics.face@1.0; /** * 这个回调接口被客户端用来接收人脸 HAL 的(状态)更新 */ interface IBiometricsFaceClientCallback { /** * 当录入的步骤完成时被回调 */ oneway onEnrollResult(uint64_t deviceId, uint32_t faceId, int32_t userId, uint32_t remaining); /** * 当一张人脸被成功认证时被回调 */ oneway onAuthenticated(uint64_t deviceId, uint32_t faceId, int32_t userId, vec<uint8_t> token); /** * 当底层获得一张人脸时被回调 */ oneway onAcquired(uint64_t deviceId, int32_t userId, FaceAcquiredInfo acquiredInfo, int32_t vendorCode); /** * 当错误发生时被回调 */ oneway onError(uint64_t deviceId, int32_t userId, FaceError error, int32_t vendorCode); /** * 当人脸模板被删除时被回调 */ oneway onRemoved(uint64_t deviceId, vec<uint32_t> removed, int32_t userId); /** * 枚举所有人脸模板的回调 */ oneway onEnumerate(uint64_t deviceId, vec<uint32_t> faceIds, int32_t userId); /** * 当禁用状态改变时被回调 */ oneway onLockoutChanged(uint64_t duration); };
应商(主要是手机厂商)需要实现上述接口的方法并集成人脸识别算法,完成录入和认证等的底层实现。
types.hal
package android.hardware.biometrics.face@1.0; /* * 在这里 setActiveUser 不会被调用,所有错误消息会返回这个用户 ID */ enum UserHandle : int32_t { NONE = -1 }; /** * 状态码 */ enum Status : uint32_t { /** * 方法被成功调用 */ OK = 0, /** * 方法调用的参数之一无效 */ ILLEGAL_ARGUMENT = 1, /** * 人脸 HAL 不支持这个操作 */ OPERATION_NOT_SUPPORTED = 2, /** * HAL 遭遇内部错误,不能完成请求 */ INTERNAL_ERROR = 3, /** * 没有录入人脸 */ NOT_ENROLLED = 4 }; enum Feature : uint32_t { /** * 要求注视 */ REQUIRE_ATTENTION = 1, /** * 要求录入时姿势多样(有变化) */ REQUIRE_DIVERSITY = 2 }; /** * onError 回调的人脸错误消息 */ enum FaceError : int32_t { /** * 不能被解析的硬件错误 */ HW_UNAVAILABLE = 1, /** * 不能处理当前操作 */ UNABLE_TO_PROCESS = 2, /** * 超时 */ TIMEOUT = 3, /** * 没有足够的存储空间去完成当前的操作 */ NO_SPACE = 4, /** * 被取消 */ CANCELED = 5, /** * 无法删除 */ UNABLE_TO_REMOVE = 6, /** * 30s 禁用 */ LOCKOUT = 7, /** * 用来开启供应商指定的错误消息 */ VENDOR = 8, /** * 禁用直到使用主身份认证 */ LOCKOUT_PERMANENT = 9 }; /** * 向客户端反馈获取人脸的消息(质量),以便用户做出相应的改变 */ enum FaceAcquiredInfo : int32_t { GOOD = 0, /** * 无效人脸 */ INSUFFICIENT = 1, /** * 人脸太亮 */ TOO_BRIGHT = 2, /** * 人脸太暗 */ TOO_DARK = 3, /** * 人脸太近 */ TOO_CLOSE = 4, /** * 人脸太远 */ TOO_FAR = 5, /** * 人脸太高,只有下半部分 */ FACE_TOO_HIGH = 6, /** * 人脸太低 */ FACE_TOO_LOW = 7, /** * 人脸偏右 */ FACE_TOO_RIGHT = 8, /** * 人脸偏左 */ FACE_TOO_LEFT = 9, /** * 凝视不佳 */ POOR_GAZE = 10, /** * 未检测到人脸 */ NOT_DETECTED = 11, /** * 检测到运动过多 */ TOO_MUCH_MOTION = 12, /** * 重新校正 */ RECALIBRATE = 13, /** * 和前一帧差异太大 */ TOO_DIFFERENT = 14, /** * 和前一帧太相似 */ TOO_SIMILAR = 15, /** * 摇射角度太大,直面相机角度为 0 */ PAN_TOO_EXTREME = 16, /** * 倾斜角度太大 */ TILT_TOO_EXTREME = 17, /** * 侧倾角幅度太大 */ ROLL_TOO_EXTREME = 18, /** * 人脸被遮挡 */ FACE_OBSCURED = 19, START = 20, /** * 传感器(摄像头)脏了 */ SENSOR_DIRTY = 21, /** * 用于开启供应商指定的获取人脸的消息 */ VENDOR = 22 }; /** * 结果 */ struct OptionalUint64 { /** * 返回的状态 */ Status status; /** * 只意味着状态是 OK 的 */ uint64_t value; }; /** * 结果 */ struct OptionalBool { /** * 返回的状态 */ Status status; /** * 只意味着状态是 OK 的 */ bool value; };
人脸识别调用流程(注册监听、捕获人脸、比对)
人脸解锁的入口在Keyguard中,但要从
息屏的处理是从PowerManager开始,最终到锁屏的核心类KeyguardViewMediator,息屏处理的大致流程如下:
前面几步就跳过,直接从PhoneWindowManager开始分析。灭屏之后会调用PhoneWindowManager的startedGoingToSleep方法:
// PhoneWindowManager.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); } }
在该方法中又调用了KeyguardServiceDelegate类的onStartedGoingToSleep方法。
KeyguardServiceDelegate#onStartedGoingToSleep →KeyguardServiceWrapper#onStartedGoingToSleep → KeyguardService#onStartedGoingToSleep → KeyguardViewMediator#onStartedGoingToSleep,最终会调用到KeyguardViewMediator锁屏核心类。
// KeyguardViewMediator.java public void onStartedGoingToSleep(int why) { if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + why + ")"); synchronized (this) { mDeviceInteractive = false; mGoingToSleep = true; // 这位置的代码作用具体不知,但放在前面可以解决息屏后又立马使用指纹解锁时:出现1.2s内没反应的问题。 mUpdateMonitor.dispatchKeyguardGoingAway(false); // Lock immediately based on setting if secure (user has a pin/pattern/password). // This also "locks" the device when not secure to provide easy access to the // camera while preventing unwanted input. int currentUser = KeyguardUpdateMonitor.getCurrentUser(); final boolean lockImmediately = mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser) || !mLockPatternUtils.isSecure(currentUser); long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser()); mLockLater = false; // 省略部分代码...... //判断是否需要播放锁屏音 if (mPendingLock) { playSounds(true); } } // 使得KeyguardUpdateMonitor可以监听到GoingToSleep // KeyguardUpdateMonitor 是Keyguard更新监视器 mUpdateMonitor.dispatchStartedGoingToSleep(why); //通知开始息屏 notifyStartedGoingToSleep(); }
这里主要分析的是屏幕自己息屏,则重点关注mUpdateMonitor.dispatchStartedGoingToSleep(why)。
// KeyguardUpdateMonitor.java // 等待屏幕超时息屏,handler会发送 MSG_STARTED_GOING_TO_SLEEP public void dispatchStartedGoingToSleep(int why) { mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0)); } // 注意:如果说按电源键息屏,handler会发送 MSG_STARTED_WAKING_UP public void dispatchStartedWakingUp() { synchronized (this) { mDeviceInteractive = true; } mHandler.sendEmptyMessage(MSG_STARTED_WAKING_UP); }
屏幕超时息屏堆栈:
09-14 09:43:41.437 1468 1468 D updateFaceListeningState: java.lang.Throwable 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.updateFaceListeningState(KeyguardUpdateMonitor.java:2128) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.updateBiometricListeningState(KeyguardUpdateMonitor.java:2053) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.setKeyguardGoingAway(KeyguardUpdateMonitor.java:575) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.handleKeyguardGoingAway(KeyguardUpdateMonitor.java:1727) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.access$5000(KeyguardUpdateMonitor.java:143) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor$16.handleMessage(KeyguardUpdateMonitor.java:1872) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.os.Handler.dispatchMessage(Handler.java:106) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.os.Looper.loop(Looper.java:223) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.app.ActivityThread.main(ActivityThread.java:7945) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at java.lang.reflect.Method.invoke(Native Method) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 09-14 09:43:41.437 1468 1468 V KeyguardUpdateMonitor: at com.android.keyguard.KeyguardUpdateMonitor.updateFaceListeningState(KeyguardUpdateMonitor.java:2129)
电源键息屏堆栈:
09-14 09:43:41.437 1468 1468 D updateFaceListeningState: java.lang.Throwable 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.updateFaceListeningState(KeyguardUpdateMonitor.java:2128) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.updateBiometricListeningState(KeyguardUpdateMonitor.java:2053) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.setKeyguardGoingAway(KeyguardUpdateMonitor.java:575) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.handleKeyguardGoingAway(KeyguardUpdateMonitor.java:1727) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor.access$5000(KeyguardUpdateMonitor.java:143) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.keyguard.KeyguardUpdateMonitor$16.handleMessage(KeyguardUpdateMonitor.java:1872) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.os.Handler.dispatchMessage(Handler.java:106) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.os.Looper.loop(Looper.java:223) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at android.app.ActivityThread.main(ActivityThread.java:7945) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at java.lang.reflect.Method.invoke(Native Method) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603) 09-14 09:43:41.437 1468 1468 D updateFaceListeningState: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 09-14 09:43:41.437 1468 1468 V KeyguardUpdateMonitor: at com.android.keyguard.KeyguardUpdateMonitor.updateFaceListeningState(KeyguardUpdateMonitor.java:2129)
这里通过handler发送消息让:handleStartedGoingToSleep处理
// KeyguardUpdateMonitor.java protected void handleStartedGoingToSleep(int arg1) { checkIsHandlerThread(); 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(); }
在这篇文章里,我们只需要关注updateFaceListeningState(),更新人脸状态。
// KeyguardUpdateMonitor.java private void updateFaceListeningState() { // 如果此消息存在,我们不应再次进行身份验证 if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { return; } mHandler.removeCallbacks(mRetryFaceAuthentication); boolean shouldListenForFace = shouldListenForFace(); if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) { stopListeningForFace(); } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) { // 在这里开始监听人脸 /*重点关注*/ startListeningForFace(); } }