Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


title: Keystore、Biometric 与 Credential 登录性能 chapter: '8.5' section: '8.5' status: finalized applicable_versions: Android 6 (API 23) - Android 17 (API 37) last_verified: '2026-08-23' last_verified_against: AOSP android-17.0.0_r1, Android Developers Keystore/BiometricPrompt docs, AOSP KeyMint docs confidence: medium sources:

  • type: official path: https://developer.android.com/privacy-and-security/keystore
  • type: official path: https://developer.android.com/identity/sign-in/biometric-auth
  • type: official path: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder
  • type: aosp-doc path: https://source.android.com/docs/security/features/keystore
  • type: aosp-doc path: https://source.android.com/docs/security/features/keystore/implementer-ref
  • type: aosp-doc path: https://source.android.com/docs/security/features/keystore/features
  • type: aosp path: frameworks/base/keystore/java/android/security/KeyStore2.java
  • type: aosp path: frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
  • type: aosp path: frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
  • type: aosp path: system/security/keystore2/src/security_level.rs
  • type: aosp path: system/security/keystore2/src/operation.rs
  • type: paper path: https://arxiv.org/html/2507.07927v1
  • type: official path: https://developer.android.com/identity/credential-manager
  • type: official path: https://developer.android.com/identity/sign-in/single-tap-biometric
  • type: official path: https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt
  • type: official path: https://developer.android.com/reference/androidx/credentials/provider/BiometricPromptData
  • type: aosp-doc path: https://source.android.com/docs/security/features/authentication
  • type: aosp path: frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java
  • type: aosp path: frameworks/base/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
  • type: aosp path: frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
  • type: aosp path: frameworks/base/services/credentials/java/com/android/server/credentials/CredentialManagerService.java tags:
  • keystore
  • keymint
  • strongbox
  • biometricprompt
  • startup
  • responsiveness
  • biometric
  • credential-manager
  • passkeys related_chapters:
  • '6.1'
  • '8.2'
  • '8.3'
  • '26.1'
  • '20.10'
  • '26.6'
  • '26.8'
  • '8.7' pipeline_stage: ready-to-publish task6_state: reviewed task9_state: reviewed last_consolidated_at: '2026-08-24' consolidated_from:
  • src/part2-performance/ch08-responsiveness/09-keystore-keymint-latency.md
  • src/part2-performance/ch08-responsiveness/10-biometric-credential-login-performance.md

Keystore、Biometric 与 Credential 登录性能

登录流程中的一次签名或解密,可能依次经过 App、keystore2、KeyMint HAL(硬件抽象层),随后进入 TEE(Trusted Execution Environment,可信执行环境)或 StrongBox 安全硬件。若密钥绑定了用户认证,链路中还会出现系统认证 UI、传感器、Gatekeeper 或 biometric TA(运行在安全环境中的可信应用),以及用于证明认证结果的 Hardware Auth Token。

若把这些阶段统称为“Keystore 很慢”,既无法定位瓶颈,也可能让性能改动破坏原有安全约束。

平台源码锚点为 Android 17 / API 37 的 android-17.0.0_r1,kernel 侧固定到 android17-6.18-2026-06_r6。应用 API 以 Android Developers 文档为准,服务行为与 operation 生命周期则回到 Android 17 的 framework 和 system/security 源码核查。这里的 operation 是一次有状态的密码操作会话,从初始化到完成或中止都占用后端资源。

登录链路可能同时访问 Keystore/KeyMint、BiometricPrompt 和 Credential Manager。密钥操作、硬件认证、系统 UI 与网络请求应分段计时,避免把安全等待统一归为接口慢。

密钥生成、访问与 KeyMint 延迟

调用路径:App、Keystore2、KeyMint 与安全环境

Android Keystore 以 JCA/JCE Provider 的形式接入 Java 密码 API。JCA/JCE 是 Java 的密码体系,Provider 负责实现具体算法和密钥访问。KeyStoreKeyGeneratorKeyPairGeneratorCipherSignatureMac 对象在 App 进程中创建;需要使用受保护密钥时,调用才通过 Binder 进入 keystore2

下面的图同时展示密码操作和用户认证两条路径,说明认证结果怎样参与密钥授权;它们并不共享完全相同的执行阶段。

flowchart LR
    App["App<br/>JCA / JCE API"]
    SPI["AndroidKeyStore Provider<br/>KeyStore2 / *Spi"]
    KS["keystore2<br/>key blob + operation 管理"]
    KM["IKeyMintDevice<br/>AIDL HAL"]
    Secure["TEE KeyMint TA<br/>或 StrongBox"]
    Prompt["BiometricPrompt / 锁屏凭据"]
    Auth["Biometric / Gatekeeper TA<br/>HardwareAuthToken"]

    App --> SPI -->|Binder| KS -->|Binder| KM --> Secure
    App --> Prompt --> Auth
    Auth -. "认证 token" .-> KS
    KS -. "授权校验" .-> Secure

图中的 keystore2 保存 KeyMint 返回的加密 key blob(由安全环境封装的不透明密钥数据)和元数据。对于 hardware-backed key(硬件保护密钥),明文密钥材料只在对应安全环境内可用。待签名消息、待加密明文或密文仍会从 App 传到系统进程,再交给 KeyMint 处理,因此不能宣称业务数据“从未离开 App”。

一次操作至少有五段

阶段入口示例主要成本可观测性
密钥查找(key lookup)KeyStore.getKey()getEntry()Provider、Binder、数据库/key blob 读取App trace marker、Binder、keystore2 调度
密钥生成或导入generateKey()generateKeyPair()随机数、参数校验、安全级别、key blob、证书端到端 trace marker、错误码
operation 初始化Cipher.init()Signature.initSign()密钥查找、KeyMint begin()、slot、challengeApp trace marker、Binder、operation 错误
数据处理与结束update()doFinal()sign()数据传输、硬件计算、认证 token 校验payload 大小、端到端耗时
用户认证BiometricPrompt.authenticate()系统 UI、用户操作、传感器、认证 TAprompt 回调与认证错误

App trace marker 是应用写入时间线的自定义起止标记。认证 UI 的等待时间不属于密码算法耗时;通过网络换取会话 token、由服务端校验 attestation 的时间也不属于本地 KeyMint 耗时。指标必须沿这些边界分段记录。

init() 已经创建 KeyMint operation

Android 17 的 AndroidKeyStoreCipherSpiBase.engineInit() 会调用 ensureKeystoreOperationInitialized(),后者进入 KeyStoreSecurityLevel.createOperation()Signature.initSign()Mac.init() 也遵循相同的 operation 模型。每个 operation 会关联后端句柄并占用一个 slot(可同时存在的操作名额)。若耗时埋点只包住 doFinal()sign(),就会漏掉 begin()、key blob 读取和 slot 分配。

Android 17 源码还给出两个工程约束:

  • engineGenerateKey() 和多处 engineInit() 会调用 StrictMode.noteSlowCall(),或标记磁盘读写,表明这些路径可能阻塞调用线程。
  • Cipher reset、成功结束和部分错误都会 abort(中止)operation;finalizer 是对象回收前的兜底清理,不能依赖 GC 及时释放硬件 slot。

因此,CipherSignatureMacinit 后应尽快完成。提前数分钟创建对象再等待用户操作,会无谓地延长 operation 和 slot 的占用时间。

operation 的完整生命周期

一个 KeyMint operation 从 begin() 开始,以下事件都会结束这次会话:

  • finish() 成功;
  • update()finish() 返回错误;
  • 客户端显式 abort()
  • operation 对应的 Binder 对象被释放;
  • slot 紧张时被 Keystore2 prune,也就是选中并回收。

同一个 operation 代理还有并发保护。Android 17 的 operation.rs 在多个线程同时调用同一 operation 时可返回 OPERATION_BUSY,因此同一个 CipherSignature 实例不能并发使用。

密钥生成、签名、解密与 attestation

密钥生成(key generation)

生成密钥通常比读取已有密钥耗时更长。成本可能包括生成安全随机数、创建 key blob、检查硬件能力、持久化,以及为非对称密钥生成证书。若设置 attestation challenge(服务端提供的随机质询),还会生成带 attestation extension(密钥证明扩展)的证书链。

生成动作适合放在:

  • 首帧后的明确后台任务;
  • 用户启用支付、设备绑定或安全登录的设置流程;
  • 注册完成后、进入高频登录之前的准备阶段。

同一用途的密钥不应在每次 App 冷启动、每次登录点击或每个网络请求中重复生成。检查密钥是否存在时,还要处理密钥被删除,以及锁屏重置、biometric enrollment(生物识别模板录入状态)变化或 OTA 系统升级后失效等情况。

begin/update/finish

硬件保护的 operation 适合处理密钥、短 challenge、小 token,或执行 data key wrapping(数据密钥封装)。若把大文件、数据库页或长 JSON 直接送入 Keystore,会增加 Binder/KeyMint 往返和数据传输,并长时间占用 operation slot。

大数据加密常采用 envelope encryption(信封加密):业务数据由临时数据密钥加密,Android Keystore 密钥只负责保护这把数据密钥。

  1. 用经过安全审查的密码库生成 data encryption key(DEK,数据加密密钥)。
  2. 用 DEK 对业务数据执行 AEAD(带关联数据的认证加密),同时保证机密性和完整性。
  3. 用 Android Keystore 密钥包装或保护 DEK。
  4. 按协议保存算法版本、nonce(一次性随机数或计数值)、wrapped key(封装后的密钥)和 ciphertext(密文)。

这项设计必须经过威胁模型和密码协议审查。它可以降低安全硬件的数据吞吐压力,但应用不能自行更换算法、复用 nonce 或长期缓存明文 DEK。

密钥证明(attestation)

attestation 用证书链证明密钥的属性及其受保护环境。它应拆成三个指标:

  • 本地密钥生成;
  • 本地 attestation certificate chain(证明证书链)返回;
  • 服务端上传、链验证和策略判断。

若把后两项都放进一个登录网络 span(链路追踪区间),设备侧问题会被网络与服务端耗时掩盖。证书链、challenge 和应用标识也具有安全与隐私属性;日志只记录长度、结果类别和版本,不记录原始内容。

认证绑定密钥:per-use 与 time-based 两套流程

setUserAuthenticationRequired(true) 让 secret key(对称密钥)或 private key(私钥)的使用受到用户认证约束。API 30 及以上可用 setUserAuthenticationParameters(timeoutSeconds, authTypes) 指定授权有效期和允许的认证类型。

类型timeout推荐流程operation 何时创建
per-use(每次使用授权)0初始化密码 operation,作为 CryptoObject 交给 BiometricPromptprompt 前、临近使用时创建
time-based(限时授权)> 0先尝试初始化;遇到 UserNotAuthenticatedException 后认证,再创建一个新 operation有效授权期内创建

per-use 密钥

下面的 KeyGenParameterSpec 配置表示每次签名都需要 Class 3 biometric(Android 定义的强生物识别等级)或设备锁屏凭据授权。

val spec = KeyGenParameterSpec.Builder(
    alias,
    KeyProperties.PURPOSE_SIGN,
)
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setUserAuthenticationRequired(true)
    .setUserAuthenticationParameters(
        0,
        KeyProperties.AUTH_BIOMETRIC_STRONG or
            KeyProperties.AUTH_DEVICE_CREDENTIAL,
    )
    .build()

生成密钥后,每次使用都要新建一个 Signature 并调用 initSign(),再将它包装成 BiometricPrompt.CryptoObject。认证成功回调会返回已经获得本次授权的 operation,应用随后在同一对象上完成签名。若在回调前另建一个 Signature,新对象对应的是另一项 operation,没有绑定刚才的认证结果。

time-based 密钥

time-based 密钥的初始化可能抛出 UserNotAuthenticatedException,表示当前没有仍在有效期内的认证授权。此时应展示策略允许的生物识别或设备凭据流程;认证成功后,重新创建并初始化 CipherSignature,不再使用初始化失败的旧对象。

BiometricPrompt 等待阶段需要单独记录:

  • prompt_requestedonAuthenticationSucceeded、错误或取消回调;
  • 认证成功后的密码操作完成时间;
  • 登录网络请求及服务端响应。

用户思考或操作的时间、系统 UI 动画和传感器重试都属于 prompt 阶段。KeyMint operation 只覆盖密码操作和授权校验中的一部分,不能用它代表完整的用户认证时长。

密钥永久失效(invalidation)

关闭安全锁屏、强制重置凭据或改变 biometric enrollment,可能让密钥永久失效,并在初始化时抛出 KeyPermanentlyInvalidatedException。恢复策略应明确:

  • 哪些密钥可以删除后重建;
  • 哪些密钥失效后需要重新登录或在服务端解除设备绑定;
  • 是否允许 AUTH_DEVICE_CREDENTIAL
  • enrollment 变化后密钥是否保持有效。

把“重建密钥”当作通用重试,可能导致旧密钥保护的数据再也无法解密。重建前要确认服务端和本地恢复协议允许这样做。

StrongBox、TEE 与 security level

Android 9 / API 28 及以上设备可以提供 StrongBox KeyMint。TEE 与 Android 主系统隔离运行,StrongBox 则使用隔离程度更高的安全硬件。StrongBox 面向更高的物理攻击和侧信道风险,但通常速度更慢、资源更少、并发能力更低,因此多数应用无需默认选择。

区分设备能力与业务策略

FEATURE_STRONGBOX_KEYSTORE 只说明设备声明了 StrongBox 能力。具体的算法、key size(密钥长度)、digest(摘要算法)、padding(填充方式)或 attestation 参数组合仍可能不受支持。setIsStrongBoxBacked(true) 失败时会抛出 StrongBoxUnavailableException,平台不会自动改为生成 TEE 密钥。

应用需要预先定义三类策略:

策略StrongBox 不可用时适合场景
required(必须)终止流程并提示,或交给服务端处理高价值设备绑定、明确合规要求
preferred(优先)记录失败,经安全策略批准后重新生成非 StrongBox 密钥中风险签名、可分级设备
not requested(不要求)使用平台选择的普通 Android Keystore 安全级别普通会话和大多数 App

preferred fallback(首选方案不可用时的后备流程)需要由应用重新发起一次密钥生成。新密钥的安全级别已经改变,应用必须记录这次变化并通知服务端,不能只捕获异常后悄悄继续。

查询生成结果

Android 12 / API 31 及以上可通过 KeyInfo.getSecurityLevel() 读取:

  • SECURITY_LEVEL_STRONGBOX
  • SECURITY_LEVEL_TRUSTED_ENVIRONMENT
  • SECURITY_LEVEL_SOFTWARE
  • SECURITY_LEVEL_UNKNOWN_SECURE / UNKNOWN

埋点既要记录 strongbox_requested,也要记录密钥生成后的实际 security level 和 fallback 原因。三者齐全后,才能比较不同安全级别的延迟,并识别请求 StrongBox 后发生了多少次降级。

operation slot:没有稳定的等待队列

AOSP KeyMint AIDL 自 Android 12 / API 31 起就把 IKeyMintDevice.begin() 的最低并发上限写成 32,注释原文为 "IKeyMintDevice implementations must support 32 concurrent operations"。source.android.com/docs/security/features/keystore/implementer-ref 上仍写着 16,那是 API 31 之前 KeyMaster 与早期 KeyMint 文档的历史遗留值,不要再当作当前合同。Android 17 的 keystore2 代码本身没有固定配额常量,而是把上限交给 KeyMint HAL 的 AIDL 合同。

vold 是 Android 的卷管理守护进程,会使用 KeyMint 支撑存储加密。Keystore2 不在源码里写死为 vold 保留一个 slot;如果想给 vold 留出余量,应该在自测时直接观察 prune/返回错误,而不是套用旧文档里的“15+1”分配。

不能据 AIDL 合同认定第 32 个 App 请求会进入 FIFO(先进先出)队列等待。Android 17 的处理过程如下:

  1. IKeyMintDevice.begin() 返回 TOO_MANY_OPERATIONS
  2. Keystore2 调用 OperationDb::prune(),尝试选中并释放一项已有 operation。
  3. prune() 用 malus(修剪分)选候选:malus = 1 + sibling_count + floor(log6(age_in_seconds + 1)),其中 sibling 指同一 owner(所属客户端 UID)下的其他 operation;分值高者优先被选作修剪目标,老的、最近没动过的 sibling 最容易被回收。
  4. 没有合适候选时可能返回 BACKEND_BUSY;被 prune 的旧 operation 再次使用时会收到 invalid handle(句柄已失效)类错误。

因此,应用侧应限制并发、缩短 operation 生命周期,并按请求记录 busy、pruned 和 invalid-handle 类错误。旧资料常将策略概括为“中止全局最久未使用的 operation”,这个说法没有包含 Android 17 对 owner 和 sibling operation 数量的考虑;KeyMint AIDL 的 32 是针对整个 HAL 的下限,不等于 App 一次能开多少并发 operation。

容易耗尽 slot 的写法

  • 为即将展示的多个列表项提前各建一个 Cipher
  • per-use operation 创建后,用户长时间没有响应 BiometricPrompt。
  • 批量文件加解密为每个分片同时建立硬件 operation。
  • 多个 SDK 各自使用无界线程池访问 Keystore。
  • 超时后保留旧 Cipher,同时开始新一轮重试。

Keystore 工作线程池应限制最大线程数,通常一到少量线程即可。合适的并发值要分别在 TEE 和 StrongBox 上压测确定,不能直接照搬 CPU 核数。

冷启动和登录的线程调度

Android 官方明确建议避免在主线程使用 AndroidKeyStore。密钥查找、init、生成、签名和 doFinal 都可能等待 Binder、数据库或安全硬件;Android 17 Provider 源码也通过 StrictMode 标出了多处慢调用。在主线程执行这些操作,会直接阻塞界面绘制和输入处理。

时机表

时机可安排的工作应避开的工作
首帧前读取不依赖 Keystore 的轻量登录状态密钥生成、attestation、StrongBox operation、批量解密
首帧后检查密钥能力、预生成允许提前创建的密钥创建 per-use operation 后长期等待
登录点击后在有界后台执行器中创建本次密码 operation在主线程串行执行密码操作、JSON 处理和网络请求
prompt 阶段展示认证 UI,处理取消和错误同时启动多个备用 operation
认证成功后完成本次短签名/解密,发起网络换票解密大文件或大数据库

下面的 Kotlin 示例在专用 Dispatcher 上执行一次短签名。这个 Dispatcher 使用固定大小的线程池,trace 区间覆盖 initSignsign,因此不会漏掉 operation 初始化时间。

private val keystoreDispatcher =
    Executors.newFixedThreadPool(2).asCoroutineDispatcher()

suspend fun signChallenge(alias: String, challenge: ByteArray): ByteArray =
    withContext(keystoreDispatcher) {
        Trace.beginSection("Keystore:sign")
        try {
            val store = KeyStore.getInstance("AndroidKeyStore").apply {
                load(null)
            }
            val privateKey = store.getKey(alias, null) as PrivateKey
            Signature.getInstance("SHA256withECDSA").run {
                initSign(privateKey)
                update(challenge)
                sign()
            }
        } finally {
            Trace.endSection()
        }
    }

示例适用于无需通过 CryptoObject 单独授权的密钥。per-use 密钥应先在后台初始化 Signature,再回到主线程将它交给 BiometricPrompt;认证成功后,把回调返回的同一对象转移到后台完成签名。整个过程中不能并发使用该对象。若 Dispatcher 隶属于有明确生命周期的组件,应在组件销毁时关闭它,避免线程泄漏。

超时与取消

JCA Keystore API 没有统一的 per-operation deadline(单次操作截止时间)。withTimeoutFuture.get(timeout) 或业务计时器可以让上层停止等待,并让 UI 进入备用登录流程;但如果调用已经进入 Binder/KeyMint,这些计时机制不保证底层操作会立即停止。

处理超时应遵守:

  • 旧 worker(工作线程)尚未结束时,不复用同一个密码对象;
  • 将晚到结果标记为过期,不写入当前会话;
  • BiometricPrompt 取消只按其 API 取消认证 UI;
  • worker 返回后释放对象引用,让 Provider 完成 finish 或 abort;
  • 连续超时后进入退避等待或备用登录,不做没有并发上限的重试。

观测:分阶段、分安全级别、保护敏感数据

应记录的字段

字段示例用途
scenesession_restorebiometric_loginpayment_confirm对齐用户流程
phaseget_keygeneratebeginprompt_waitfinishattestation找到耗时阶段
primitiveAES_GCMHMAC_SHA256ECDSA_P256区分密码原语或算法组合
purposeencrypt、decrypt、sign、verify、agree区分授权
security_levelTEE、StrongBox、Software、Unknown比较实现
strongbox_requestedtrue / false对照策略
auth_policynone、per-use、duration、credential区分授权方式
payload_bucket0—64 B、65—512 B、513 B—4 KiB用区间记录输入大小,避免暴露精确长度
threadmain / keystore-worker暴露 UI 阻塞
duration_ms单次值,服务端计算 P50/P90/P99保留耗时分布,而非只看平均值
result_classok、not-authenticated、invalidated、unavailable、busy归类失败
device/build/app型号、Build、版本识别设备聚集

不要记录 key alias(密钥别名)、明文、密文、签名原文、认证 token、attestation challenge 或完整证书链。alias 常包含账号或业务含义,也不适合作为日志标签。

Perfetto 能看到什么

在能够稳定复现问题的设备上,采集应用 trace marker、sched(线程调度)、Binder、CPU frequency(CPU 频率)和磁盘 I/O,可以回答:

  • App 主线程是否同步等待;
  • Binder 调用发往 keystore2 的起止和调度延迟;
  • keystore2 线程是 Running、Runnable、Sleeping,还是运行过程中被其他线程抢占;
  • key 数据库访问是否伴随 I/O;
  • 多个请求是否在同一时间窗内竞争 operation。

TEE/StrongBox 内部阶段通常不会出现在普通 Perfetto trace 中。若时间线只能看到 keystore2 进入 KeyMint,无法看到安全环境内部的细分轨道,结论应写成“耗时位于 KeyMint/HAL/安全环境区间”,再结合厂商日志或 HAL instrumentation(HAL 内部插桩)继续定位。时间线上的空白不足以证明某个密码算法本身很慢。

测试组合

每个关键 primitive(密码原语或算法组合)至少覆盖以下测试条件:

  • key 首次生成与既有 key;
  • 4 KB 和更小的 payload,避免只测试空消息;
  • TEE 与 StrongBox 的实际 security level;
  • 单 operation、少量并发,以及接近 slot 上限的受控压力测试;
  • 冷机、热机、锁屏后,以及 biometric enrollment 变化后;
  • 成功、用户取消、认证失败、密钥失效和 StrongBox 不可用;
  • 设备型号、Android 版本、厂商 Build。

“StrongBox 比 TEE 慢多少毫秒”没有适用于所有设备的固定答案。测试时要保存条件和分位数,并在同一设备上使用相同密钥类型、算法、payload 和认证策略对照。

治理:性能动作不能改变安全策略

动作性能收益安全边界
首帧后预生成移出冷启动和点击路径只预生成已获同意、允许提前存在的 key
有界后台执行避免主线程卡顿线程切换不改变密钥授权条件
just-in-time init(临用时初始化)缩短 slot 占用per-use operation 要与 prompt 绑定
envelope encryption减少安全硬件处理大 payload 的时间协议、AEAD、nonce 与数据密钥生命周期需审查
StrongBox preferred fallback提高设备覆盖记录降级,并让服务端接受变化后的 security level
超时或备用登录让 UI 可以继续响应上层超时不代表 operation 已取消
能力缓存减少重复探测系统升级、锁屏和 enrollment 变化后失效

可以缓存设备能力结果、密钥是否存在的非敏感状态和服务端策略版本。解密后的 refresh token(刷新令牌)不应仅为缩短 Keystore 耗时而缓存;明文缓存扩大了数据可能暴露的位置,必须单独进行安全评审。

Passkey / Credential Manager 的边界

Credential Manager 是初始登录的统一凭据入口,也支持后续重新授权。普通 relying-party App(依赖凭据完成登录的业务应用)请求 passkey 时,私钥通常由 credential provider(凭据提供方)管理。用户创建 passkey,并不代表业务 App 自己新增了一个 AndroidKeyStore alias。

性能指标应分开:

  • Credential Manager 发出请求到 provider UI 出现;
  • 用户选择/认证;
  • provider 返回 credential(凭据);
  • App 与服务端完成 assertion(认证断言)验证;
  • App 自己用于会话缓存的 Keystore 解密。

只有应用自行生成 Android Keystore 密钥,或应用本身实现 credential provider 并管理相应密钥时,才需要把这些密钥纳入 alias、operation 与 security-level 清单。

Android 6—17 的版本边界

版本相关能力
Android 6 / API 23KeyGenParameterSpec 与认证绑定密钥的现代应用基线
Android 9 / API 28StrongBox API 与 StrongBoxUnavailableException
Android 11 / API 30setUserAuthenticationParameters() 明确认证有效期与 auth type
Android 12 / API 31Keystore2 + KeyMint 架构;KeyInfo.getSecurityLevel()
Android 13 / API 33KeyMint v2 增加 Curve25519 等能力
Android 17 / API 37本文源码锚点;Keystore2 operation、Provider 和 AIDL KeyMint 主线按固定 tag 核查

平台版本表只描述 API 和架构边界。StrongBox 支持情况、算法组合、可用 operation 数和安全环境耗时仍由具体设备实现决定。

Android 17 与 kernel 源码入口

Android 17 可从以下固定源码继续追踪:

App 到 keystore2,以及 keystore2 到 Binderized KeyMint HAL(通过 Binder 暴露的 KeyMint 服务)都经过 Binder。kernel android17-6.18-2026-06_r6 可固定查看 drivers/android/binder.c。TEE/StrongBox 的 transport(通信通道)和 driver 多由设备厂商实现,不在 common kernel 中;没有设备内核与 HAL 源码时,应明确说明这部分无法继续归因。

生物认证、凭据选择与登录完成

密钥可用后,认证 UI 和凭据提供方还会引入 Binder、硬件和用户交互等待。最终登录完成时间应与密钥阶段分开归因。

如果登录页只记录一个 login_cost_ms(登录总耗时),出现慢登录时几乎无法判断时间花在哪里。一次凭据登录可能包含 Credential Provider 查询、系统凭据选择器、用户操作、传感器认证、passkey assertion(通行密钥生成的认证断言)、服务端验证和会话初始化;会话内的重新授权则可能只经过 BiometricPrompt、Keystore 与业务操作。两个流程都会显示系统认证界面,但调用者、数据边界和可观测信号并不相同。

平台源码基线为 Android 17(API 37)的 android-17.0.0_r1,内核基线为 android17-6.18-2026-06_r6。应用初次登录优先使用 Credential Manager;已登录会话内确认敏感操作时,可以使用 Credential Manager 或 AndroidX BiometricPrompt。这一分工来自当前 Android 生物认证指南,也能避免应用自行实现账号选择与认证界面。

两条流程,两个责任边界

登录与重新授权可以共享同一种 flow_id(一次用户意图的短期关联 ID)规则,但应使用各自的阶段名称。下图中的实线表示 Credential Manager 登录,虚线表示会话内重新授权:

flowchart LR
    A["用户触发操作"] --> B["AndroidX Credential Manager"]
    B --> C["CredentialManagerService"]
    C --> D["Credential Provider"]
    D --> E["系统选择或认证 UI"]
    E --> F["Credential 返回应用"]
    F --> G["服务端验证"]
    G --> H["写入会话并进入业务页"]

    A -. "会话内重新授权" .-> I["AndroidX BiometricPrompt"]
    I --> J["BiometricService / AuthSession"]
    J --> K["SystemUI 与传感器服务"]
    K --> L["认证回调"]
    L --> M["可选 Keystore / KeyMint 操作"]
    M --> G

图中两条路径最终都要回到业务验证。Credential Manager 返回的 Credential 仍需由应用和服务端验证。BiometricPrompt 的成功回调只说明平台接受了本次认证;应用仍要自行推进账号会话,服务端也仍要验证 passkey assertion。

性能拆分建议使用下列口径:

阶段应用入口或完成点应用能直接观测什么不应从该阶段推断什么
凭据请求getCredential() 开始到返回或抛出异常请求类型、总等待、结果类型、异常类各 provider 的查询时长、候选总数、系统 UI 出现时间
生物认证请求authenticate() 开始到成功或终态错误请求时间、认证类型、错误码、调用方取消原因对话框精确出现时间、实际使用的指纹或人脸传感器
加密操作Cipher.initSignature.initSigndoFinalsign每次调用耗时、异常家族、密钥策略用户停留时间、服务端耗时
凭据验证Credential 返回到服务端响应网络耗时、HTTP 类别、验证或风控结果SystemUI 或传感器性能
会话就绪服务端通过到目标页面首帧本地写入、数据加载、首帧时间前面认证阶段的耗时

CredentialManager.getCredential() 的等待时间包含用户在系统 UI 上查看和选择凭据的时间。BiometricPrompt 从发起请求到收到回调的区间也包含用户操作。没有平台专用观测能力时,应用只能将这些指标命名为“请求到结果”,不能将整段时间标成“provider 查询耗时”或“传感器识别耗时”。

BiometricPrompt:从应用调用到系统回调

AndroidX BiometricPrompt 是应用侧推荐使用的封装。Android 9(API 28)及以上使用系统认证界面;AndroidX 还处理版本兼容和配置变更后的回调接续。平台文档说明,BiometricPrompt.authenticate() 会触发准备生物识别硬件、显示系统对话框和开始扫描等后续工作;该方法返回时,界面不一定已经可见。

Android 17 源码把这段工作分到多个进程和对象:

  1. Framework 的 BiometricPrompt.javaauthenticateInternal() 中调用 IBiometricService.authenticate(),并把 CancellationSignal 连接到返回的请求 ID。
  2. system_server 中的 BiometricService.java 接收 Binder 请求后,将 handleAuthenticate() 投递到服务 Handler(串行处理消息的线程队列)。请求会经过权限、前台状态、认证器可用性和注册状态检查。
  3. AuthSession.java 保存一次认证会话的状态,并通过状态栏服务请求 SystemUI 显示认证界面。
  4. SystemUI 的 AuthController.java 实现 showAuthenticationDialog(),处理界面展示、消失和结果回传。
  5. 每个生物传感器有自己的 BiometricScheduler.java 实例。它维护当前操作和待处理队列,再由具体的 sensor service(传感器系统服务)与厂商 HAL 交互。

这条调用路径跨越应用进程、system_server、SystemUI、传感器服务和厂商 HAL。应用主线程 trace 只能覆盖调用方看到的部分,无法细分这些系统组件各自的耗时。

应用没有公开的 prompt-onShown 回调

AndroidX 公开的 AuthenticationCallback 只提供成功、终态错误和本次样本未识别三类结果,没有“对话框已显示”回调。埋点名称要反映这个限制:

  • biometric_request_start:调用 authenticate() 前;
  • biometric_rejected:收到 onAuthenticationFailed()
  • biometric_terminal:收到 onAuthenticationSucceeded()onAuthenticationError();terminal 表示本次认证会话已经结束;
  • biometric_client_cancel:应用调用 cancelAuthentication() 前记录自身原因。

request_to_terminal_ms 是应用可以稳定记录的口径。只有测试环境通过录屏、UI 自动化、Perfetto 或平台内部日志取得界面实际显示时间后,prompt_show_ms 才有依据。线上若直接把调用 authenticate() 的时间当作 prompt 出现时间,就会把 Binder 调度、预检查和 SystemUI 调度一并误算为界面展示耗时。

配置变更不会要求重启认证

当前 AndroidX BiometricPrompt API 参考 明确说明,旋转等配置变更导致 Activity 或 Fragment 重建时,认证界面默认继续保留。应用应在新的 onCreate() 早期重新构造 BiometricPrompt,让新实例的 callback 接收正在进行的会话;此时无需再次调用 authenticate(),也不应调用 cancelAuthentication()

同一个 Activity 或 Fragment 创建多个 BiometricPrompt 时,只有最近创建实例的 callback 会被保存。登录页应只维护一个 prompt 实例,并用业务状态阻止重复点击。应用离开前台后,系统可能因安全策略关闭 prompt;这类终止要与旋转重建分别统计。

以下代码骨架演示怎样维持单次认证会话,并区分非终态与终态回调。LoginViewModelLoginMetrics 代表业务自己的状态与埋点接口:

class LoginActivity : FragmentActivity() {
    private val loginViewModel: LoginViewModel by viewModels()
    private lateinit var biometricPrompt: BiometricPrompt

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        biometricPrompt = BiometricPrompt(
            this,
            ContextCompat.getMainExecutor(this),
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationFailed() {
                    LoginMetrics.rejected(loginViewModel.requireAttemptId())
                }

                override fun onAuthenticationError(
                    errorCode: Int,
                    errString: CharSequence
                ) {
                    loginViewModel.finishWithError(errorCode)
                }

                override fun onAuthenticationSucceeded(
                    result: BiometricPrompt.AuthenticationResult
                ) {
                    loginViewModel.finishWithSuccess(result.authenticationType)
                }
            }
        )
    }

    fun requestReauthentication(promptInfo: BiometricPrompt.PromptInfo) {
        if (!loginViewModel.beginAttempt()) return
        LoginMetrics.requestStarted(loginViewModel.requireAttemptId())
        biometricPrompt.authenticate(promptInfo)
    }
}

重建后的 Activity 会注册新的 callback,正在显示的认证会话继续运行。beginAttempt() 负责拒绝重复点击;onAuthenticationFailed() 只记录一次样本未识别,不结束业务状态。成功或错误回调才会结束本次 attempt。

三种回调的状态含义

回调会话状态处理方式
onAuthenticationFailed()非终态;本次生物样本未识别记录一次 reject(未识别),继续等待系统允许的下一次尝试
onAuthenticationSucceeded()终态;当前会话不会再有事件读取 authenticationType,进入加密操作或业务验证
onAuthenticationError()终态;当前会话不会再有事件按 error code 分类,释放一次性业务状态

ERROR_USER_CANCELED 表示用户主动关闭认证界面;ERROR_NEGATIVE_BUTTON 表示用户点击了应用配置的负按钮;ERROR_CANCELED 多用于用户切换、设备锁定、传感器不可用或另一个请求造成的系统取消。三者的原因不同,不能合并为一个“用户取消”。ERROR_LOCKOUTERROR_LOCKOUT_PERMANENTERROR_HW_UNAVAILABLEERROR_SECURITY_UPDATE_REQUIRED 也要分别归类。厂商错误文本可能随设备和版本变化,不适合作为稳定聚合键;线上可以按标准错误码、是否存在 vendor code(厂商扩展码)及设备分组聚合。

认证类型不等于传感器形态

AuthenticationResult.getAuthenticationType() 能区分 biometric(生物识别)、device credential(设备 PIN、图案或密码)和部分旧版本上的 unknown。它不公开本次使用的是指纹、人脸还是虹膜,也不公开具体是哪一个传感器。普通应用也没有可靠 API 读取本次生物认证的 modality(传感器形态)。

实验室设备清单可以标注 UDFPS(屏下指纹传感器)、侧边指纹、后置指纹、2D face 或 3D face,用来解释设备组差异;线上事件不能声称记录到了本次使用的 sensor 类型。设备同时支持多种 modality 时,按机型猜测尤其容易失真。

调用前应把 PromptInfo 使用的认证器组合原样传给 BiometricManager.canAuthenticate()。返回值只是调用当时的能力快照,设备状态随后仍可能变化,因此不能省略终态错误处理。PromptInfo 一旦允许 DEVICE_CREDENTIAL,就不能再设置 setNegativeButtonText();设备凭据入口由系统管理。

Credential Manager:应用方和 provider 方要分开

Credential Manager 这套体系包含 Jetpack API、平台服务、系统 UI 和 Credential Provider(凭据提供方)。这些角色的发布节奏、权限和可见数据不同:

角色典型 API 或组件能掌握的数据
依赖方应用androidx.credentials.CredentialManager自己提交的 option(凭据选项)、返回的 credential 类型、异常与总等待
Jetpack 适配层AndroidX Credentials按平台版本和 provider 能力选择实现
Android 14+ 平台服务CredentialManagerService启用的 provider、request session(一次应用请求)和 provider session(面向单个 provider 的子会话)及取消信号
Credential ProviderCredentialProviderService、entry、PendingIntent自己查询到的凭据、自己生成的 entry(供系统展示的候选项)、provider 内部耗时
服务端WebAuthn / 密码 / 联合身份验证challenge、assertion、账号和风控结果

业务 App 通常属于依赖方。它调用 getCredential(),却不能直接读取系统选择器中的候选总数,也不知道每个 provider 的查询耗时。凭据提供方可以统计自己返回的候选和内部时延,但它看不到其他 provider,因而不能把自己的数量当作系统候选总数。

Android 17 的 CredentialManagerService.java 展示了平台侧结构:executeGetCredential() 创建请求级 GetRequestSession,为各 provider 准备子会话并启动查询;executePrepareGetCredential() 创建 PrepareGetRequestSession,同样准备 provider session,并返回后续请求所需的 handle(继续这次预取的句柄)。一次 API 调用可以包含多个 provider session,而依赖方应用只能收到最终 response 或 exception。

prepareGetCredential() 的适用边界

AndroidX prepareGetCredential() 需要 Android 14(API 34)及以上。它只预取凭据查询所需的信息,不显示 UI;返回的 PendingGetCredentialHandle 必须传给后续的 getCredential(),才能完成选择、授权和凭据返回。

预取可以把部分 provider 准备工作移到用户点击前,但要同时满足三个条件:

  • 请求内容已经确定,其中的 passkey challenge 仍在服务端规定的有效期内;
  • 页面退出、账号切换或请求失效时能够取消或丢弃旧 handle;
  • 完成阶段使用 Activity context,使系统 UI 依附于当前页面所在的任务栈。

Kotlin 挂起版 getCredential() 会随 coroutine scope(协程作用域)取消。需要让请求跨旋转继续时,可以使用 viewModelScope 等生命周期跨越配置变更的作用域;页面已结束或账号已切换时,应主动使请求失效。回调版则通过 CancellationSignal 传递取消信号。

预取事件可以记录 prepare_startprepare_resultresume_startget_result,从而比较准备阶段与完成阶段的耗时。即使使用预取,依赖方应用仍然拿不到 prompt 出现时间或各 provider 的独立查询时间。

Android 15 single tap:provider 侧的集成能力

Android 15(API 35)加入 Credential Manager single tap(单击登录):在单账号场景下,凭据信息可以直接显示在 Biometric Prompt 中,同时保留“更多选项”入口。一个账号即使同时拥有 passkey 和密码等多种凭据,仍可满足单账号条件;存在多个账号时,系统会回到标准选择流程。到 Android 17(API 37)为止,这个边界没有改变,详见 single tap 官方指南

BiometricPromptData 是 AndroidX Credentials 1.5.0 增加的 provider 侧 API。凭据提供方把它放入 CreateEntryPublicKeyCredentialEntry 后,系统才获得内嵌认证所需的信息。普通依赖方应用不构造这个对象,也拿不到 provider 侧的 biometricPromptResult

provider 集成时有四条硬约束:

  1. 显式设置 allowedAuthenticators。当前 single tap 指南的创建部分与 BiometricPromptData API 参考 对默认值的描述不一致,代码不应依赖默认值。
  2. cryptoObject 非空时,allowedAuthenticators 必须精确设置为 BIOMETRIC_STRONG;若传入组合值,会触发 IllegalArgumentException
  3. 设备配置要求使用 PIN、图案或密码时,系统采用标准 Credential Manager 流程,provider 收到的 biometricPromptResultnull
  4. provider 必须处理 result 为成功、错误和 null 三种情况;null 表示这次没有走 provider 内嵌的生物认证结果,不能记成生物认证失败。

因此,依赖方应用只能稳定记录本次返回的 credential 类型、总耗时和 exception 类型。只有平台或 provider 提供了直接信号时,才能上报 single_tap_used;仅因总耗时较短就推测使用了 single tap,会让指标失真。

CryptoObject:相似名字下有两套约束

旧实现常把“允许 device credential 时不能传 CryptoObject”写成适用于所有场景的规则。当前 API 要结合密钥类型、Android 版本和调用方角色判断:

形态认证器配置CryptoObject 用法版本或角色边界
AndroidX BiometricPrompt,只确认用户BIOMETRIC_STRONGBIOMETRIC_WEAKDEVICE_CREDENTIAL 或支持的组合不传调用前用同一组合执行 canAuthenticate()
AndroidX BiometricPrompt,auth-per-use key(每次使用均需授权的密钥)BIOMETRIC_STRONG,Android 11+ 也可按密钥策略允许 DEVICE_CREDENTIAL初始化操作后,把同一个对象传给 authenticate()Android 10 及以下不支持 crypto 与 device credential 的组合
time-based key(认证后限时可用的密钥)密钥设置认证有效期,可允许 strong biometric 或 device credentialprompt 不带对象;认证后在有效期内新建加密操作超出有效期时会遇到 UserNotAuthenticatedException
provider single tap 的 BiometricPromptData有对象时必须精确为 BIOMETRIC_STRONGprovider 把对象放入 entry metadataAndroidX Credentials provider API,不能套用普通 App 的组合规则

当前 Android 生物认证指南的 auth-per-use 章节 给出的密钥策略允许 AUTH_BIOMETRIC_STRONG | AUTH_DEVICE_CREDENTIAL;AndroidX BiometricPrompt.authenticate(info, crypto) 参考文档还说明了 Android 11 之前的兼容限制。指南中“不带 CryptoObject”的说明位于 time-based key 流程,不能套用到 Android 11+ 的 auth-per-use key。

密钥授权条件还要与 prompt 策略一致。若密钥只允许 strong biometric,即使 prompt 同时提供 device credential,PIN 也不能授权使用这把密钥。密钥允许两种认证器时,调用方仍需处理密钥失效、认证 token 不匹配和安全级别差异。完整的初始化顺序、KeyMint operation 与异常分类见 §8.5 Keystore/KeyMint 调用链延迟

性能埋点至少拆成这些时间段:

字段起止点解释
crypto_init_msCipher.init / Signature.initSign 调用可能已经进入 Keystore、keystore2 与 KeyMint
auth_request_to_terminal_msauthenticate() 到成功或终态错误包含系统调度、UI、用户操作与 sensor 工作
crypto_finish_msdoFinal / sign 调用反映授权后的 KeyMint 操作,不含网络
credential_request_msgetCredential() 到 response / exception包含 provider 查询、系统 UI 与用户操作
server_verify_ms发出验证请求到服务端响应反映网络、后端验证与风控

onAuthenticationSucceeded() 的时间和 sign() 返回时间要分别记录。若把两者合称为“指纹耗时”,StrongBox、TEE 或 KeyMint 队列延迟会被错误归因给传感器。

取消、失败与回退

是否切换到其他登录方式,应由终态决定。onAuthenticationFailed() 只表示本次样本未识别,不能每收到一次就再弹一个 prompt。一个稳定的状态流如下:

stateDiagram-v2
    [*] --> Preflight
    Preflight --> Prompting: canAuthenticate 通过
    Preflight --> Alternative: 不可用或未注册
    Prompting --> Prompting: onAuthenticationFailed
    Prompting --> Verifying: onAuthenticationSucceeded
    Prompting --> Alternative: 可回退的终态错误
    Prompting --> Stopped: 用户或页面取消
    Verifying --> SignedIn: 服务端通过
    Verifying --> Recovery: Key 或服务端拒绝
    Alternative --> SignedIn: 其他凭据通过
    SignedIn --> [*]
    Stopped --> [*]
    Recovery --> [*]

这张状态图将样本未识别、终态错误和业务验证失败放在不同状态,便于避免重复弹窗和错误归因。

信号含义合适的动作
onAuthenticationFailed()当前样本未识别,会话仍在运行留在系统 prompt,按区间记录 reject 次数
ERROR_USER_CANCELED / negative button用户选择离开当前认证方式回到当前页面,展示密码或其他凭据入口
ERROR_LOCKOUT临时锁定告知等待时间,允许 device credential 或其他登录方式
ERROR_LOCKOUT_PERMANENT需要设备凭据解除引导设备凭据,不自动循环生物认证
GetCredentialCancellationException用户退出 Credential Manager保持登录页可操作,不转成“无凭据”
NoCredentialException请求范围内没有可用凭据提供注册、密码或联合身份入口
key invalid / auth required密钥状态或授权不满足进入密钥恢复流程,参见 §20.3
assertion 或风控拒绝服务端不接受本次凭据进入账号安全流程,不归到平台认证性能

应用主动取消时,要在调用取消 API 前记录 cancel_source,例如页面关闭、账号切换、请求过期或新请求替换旧请求。系统返回的 error code 只能描述平台终态,无法还原应用为何发起取消。

一套不越权的线上事件

建议为一次用户意图生成 flow_id,每次重试再生成新的 attempt_id(单次尝试 ID)。事件只记录应用能够直接观测的时间点:

事件时间点推荐字段
credential_request_start调用 Credential Manager 前flow_idattempt_idapi_leveloption_typesprepared
credential_request_endresponse 或 exceptioncredential_typeexception_familyduration_ms
biometric_request_start调用 authenticate()authenticator_policycrypto_modecan_authenticate_result
biometric_rejectedonAuthenticationFailed()reject_count_bucket
biometric_terminalsuccess 或 errorresultauthentication_typeerror_codeduration_ms
crypto_operationinit 或 finish 返回operation_phasesecurity_levelduration_mserror_family
server_verify_end服务端响应duration_msnetwork_classhttp_familydecision_family
login_ready目标页面首帧final_methodfallback_reasontotal_ms

依赖方应用不应上报 candidate_countprovider_query_msprompt_shown_ms 或 biometric modality,除非它获得了相应的直接信号。设备型号可以在满足合规要求的前提下映射为受控设备分组,避免自由文本产生大量不同取值,也就是高基数字段。

认证数据的采集边界更严格。不要上传生物样本、PIN、图案、密码、passkey assertion 原文、challenge、credential ID、AAGUID(认证器型号标识)原值、RP ID(依赖方域名标识)或明文账号。关联排查使用短期 flow_id、凭据类型、错误家族和账号匿名分组。任何能够长期识别账号或凭据的摘要都需要安全与隐私评审;哈希处理后的值仍可能被关联,不能自动视为匿名数据。

OEM 差异如何验证

SystemUI 和 Framework API 为应用提供一致接口,但 sensor HAL、屏幕协作、锁定行为和错误分布仍会因设备而异。UDFPS 需要屏幕局部高亮、触控和指纹传感器协作;被动人脸认证还受光线、摄像头启动和是否要求用户确认等因素影响。setConfirmationRequired(false) 只是应用向系统表达“不要求额外确认”的偏好,系统可以根据设备设置忽略它。

实验室测试至少覆盖:

  • Android 版本、厂商系统版本和升级路径;
  • strong / weak biometric(强/弱生物识别)与 device credential 策略;
  • 冷启动后的首次请求、会话内再次授权、刚解锁设备、熄屏恢复;
  • 旋转重建、切到后台、锁屏、用户切换和重复点击;
  • 正常识别、连续 reject、临时锁定、永久锁定、硬件不可用;
  • 单账号、多账号、多 provider、无可用凭据和 passkey challenge 过期。

实验室可以根据设备硬件清单解释 UDFPS 或人脸认证的差异;线上则按设备组和公开 error code 统计 P50、P90、P99。若固定规定“超过 800 ms 就是 sensor 异常”,用户操作时间和 SystemUI 调度也会被算入传感器问题。观察耗时分位数时,还要同时看切换到其他登录方式后的完成率。

Android 17 源码与内核锚点

源码排查按责任范围进入:

层级Android 17 固定入口能回答的问题
Framework APIBiometricPrompt.java参数校验、Binder 请求、取消和 callback 分发
生物认证仲裁BiometricService.java预检查、请求 ID、Handler 调度和会话生命周期
认证会话AuthSession.javaSystemUI 显示请求、sensor 状态、成功与错误转换
Sensor 调度BiometricScheduler.java每个 sensor 当前执行的操作与待处理队列
SystemUIAuthController.java认证对话框显示、关闭和前台状态
Credential 平台服务CredentialManagerService.javaget / prepare session、provider session 与取消传递
GKI Binderdrivers/android/binder.cBC_TRANSACTIONbinder_transaction() 的跨进程传输

内核锚点只覆盖 Binder IPC。生物传感器驱动和厂商实现通常位于设备专用或 vendor 代码中,认证策略与会话状态则位于 Framework、系统服务和 HAL。common kernel 的 binder.c 只能帮助验证跨进程等待和 Binder 调度是否异常,不能用来推断指纹采集耗时。

普通线上应用进程没有权限读取完整系统 trace。实验室可以结合 Perfetto、SystemUI 日志、system_server 轨迹和厂商 HAL 日志定位;线上能够收集哪些证据、需要什么权限,见 §26.6 版本化诊断

与稳定性和观测章节的分工

§20.10 Keystore 配额与登录稳定性 负责密钥生命周期、配额、失效与恢复。这里仅将这些结果视为登录阶段的一类终态。

§26.6 版本化诊断 负责系统 trace、ApplicationExitInfoProfilingManager 与诊断权限。§26.8 Android Vitals 与 Play Console 负责 ANR、Crash、LMK、启动和功耗等外部质量口径。Vitals 没有“生物识别登录慢”专用指标,内部 flow_id、版本与页面信息要能和发布批次对应。

版本与实现边界

版本相关变化或限制
Android 9 / API 28平台 BiometricPrompt 引入;AndroidX 在该版本使用系统认证界面
Android 10 / API 29 及以下DEVICE_CREDENTIALBIOMETRIC_STRONG | DEVICE_CREDENTIAL 的部分组合不受支持;密码操作与 device credential 的组合也受限
Android 11 / API 30AndroidX crypto-based authentication(绑定密码操作的认证)可按密钥策略使用 device credential;生物认证要参与 Keystore 操作,仍需 strong biometric
Android 14 / API 34平台 Credential Manager 服务与 prepareGetCredential() handle(预取句柄)流程可用
Android 15 / API 35provider 可接入 passkey single tap,登录限定为单账号场景
Android 17 / API 37平台源码基线为 android-17.0.0_r1,上述职责边界继续适用

Jetpack 库版本和平台 API level 要分别记录。BiometricPromptData 来自 AndroidX Credentials 1.5.0;设备即使运行 Android 15,provider 也不一定已经采用该 API。

小结

Keystore 延迟要按密钥查找、生成、operation 初始化、update/finish、认证 UI 等待、attestation 和网络验证分段。Cipher.init() 已经可能占用 KeyMint slot;per-use 密钥必须把同一个 CryptoObject 交给认证流程;time-based 密钥在认证成功后创建新的 operation。StrongBox 是安全策略选择,不能为了性能静默回退到较低安全级别。

Credential Manager 登录和 BiometricPrompt 重新授权是两条不同的调用路径。依赖方应用可以稳定观测请求、回调、异常、加密操作、服务端响应和页面就绪;prompt 出现时间、provider 查询时间、候选总数和具体 biometric modality 则需要额外权限或 provider 侧信号。

Android 17 源码把生物认证请求分配给 BiometricServiceAuthSession、SystemUI 和每个 sensor 的 BiometricScheduler;Credential Manager 则用 request session 协调多个 provider。性能指标要沿这些责任边界命名。遇到 device credential 与 CryptoObject 时,还要区分普通 AndroidX prompt、time-based key、auth-per-use key 和 provider BiometricPromptData,避免把某一种 API 的限制套用到所有登录流程。

参考资料