Android10.0 人脸解锁(上)

简介: Android10.0 人脸解锁(上)

学习笔记:

人脸解锁概述

人脸解锁即用户通过注视设备的正面方便地解锁手机或平板。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,息屏处理的大致流程如下:

image.png



前面几步就跳过,直接从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();
        }
    }

相关文章
|
3月前
|
API 开发工具 Android开发
视觉智能开放平台产品使用合集之人脸活体检测能力是否支持Android端或者iOS端直接调用
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
|
存储 传感器 安全
Android11.0 增加人脸解锁功能
Android11.0 增加人脸解锁功能
950 0
|
计算机视觉
Android10.0 人脸解锁(下)
Android10.0 人脸解锁
|
安全 Android开发 数据安全/隐私保护
Android支持Smart Lock 人脸解锁
Android支持Smart Lock 人脸解锁
303 0
|
算法 数据处理 开发工具
|
测试技术 API 数据安全/隐私保护
Android——实现人脸支付
功能实现 人脸支付 API初始化 人脸特征提取 返回支付结果 密码框输入支付 自定义密码输入框控件 初始化控件 密码匹配 尾言 效果展
371 0
|
算法 Java Android开发
Android 中使用 dlib+opencv 实现动态人脸检测
1 概述 完成 Android 相机预览功能以后,在此基础上我使用 dlib 与 opencv 库做了一个关于人脸检测的 demo。
1850 0
|
前端开发 Android开发 计算机视觉
Android FeceDetector(人脸识别)
1. FectDetector 人脸的检测方法是用双眼来检测人脸的位置,也就是说无法检测到嘴、侧脸等,双眼必须同时可见,并且眼镜会影响检测的效果。实际上,FaceDetector检测到的并不是人的全脸,而只是双眼。
1446 0
|
机器学习/深度学习 前端开发 API
Android 开发者如何通过运动视觉 API 进行机器学习 - 第一部 - 人脸检测
本文讲的是Android 开发者如何通过运动视觉 API 进行机器学习 - 第一部 - 人脸检测,在计算机科学中,机器学习是一个非常有意思的领域,它已经在我的最想学习的愿望清单中驻留已久。因为有太多来自于RxJava, Testing, Android N, Android Studio 以及其他 Android 相关的技术更新,所以我都每能花时间来学习这个。
1888 0
|
存储 前端开发 API
下一篇
无影云桌面