
Android 14 引入的憑據管理器框架,提供了一套用于管理用戶憑據的統一 API,可管理密碼、通行密鑰(FIDO2/WebAuthn)、聯合登錄令牌以及數字身份文檔。該框架用一套可插拔的系統服務取代了過去碎片化的自動填充服務與私有登錄 SDK,這套系統服務在請求應用與憑據提供方應用之間充當中間媒介。本章完整梳理整套架構,從面向客戶端的CredentialManagerAPI,到系統服務、提供方會話、選擇器 UI,再到提供方側的CredentialProviderService。全部描述均基于 AOSP 真實源碼,對應目錄:frameworks/base/services/credentials/與frameworks/base/core/java/android/credentials/。41.1 憑據管理器架構41.1.1 問題背景在憑據管理器出現之前,憑據獲取需要依靠多個互不關聯的機制:機制局限性AccountManager僅管理賬號令牌;無標準化通行密鑰支持自動填充框架(AutofillService)設計目標為視圖填充,并不適配現代憑據類型FIDO2 庫(Google Play 服務)屬于私有實現;AOSP 版本無法使用第三方密碼管理器每一款都需要單獨做集成適配應用針對密碼、通行密鑰、聯合憑據,需要調用各不相同的 API。用戶也需要分別對每一套機制進行配置。41.1.2 設計目標憑據管理器圍繞以下設計原則構建:單一 API 入口:一次調用即可獲取任意類型憑據可插拔提供方:任意應用均可注冊成為憑據提供方系統中介選擇:由系統控制憑據選擇器 UI兩階段協議:先執行 “開始” 查詢,再在用戶選擇后完成最終處理按用戶隔離:Android 每一個用戶都擁有獨立的提供方配置41.1.3 高層架構源文件位置:組件路徑CredentialManagerServiceframeworks/base/services/credentials/java/com/android/server/credentials/CredentialManagerService.javaCredentialManagerServiceImplframeworks/base/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.javaRequestSessionframeworks/base/services/credentials/java/com/android/server/credentials/RequestSession.javaProviderSessionframeworks/base/services/credentials/java/com/android/server/credentials/ProviderSession.javaRemoteCredentialServiceframeworks/base/services/credentials/java/com/android/server/credentials/RemoteCredentialService.javaCredentialManagerUiframeworks/base/services/credentials/java/com/android/server/credentials/CredentialManagerUi.javaCredentialProviderServiceframeworks/base/core/java/android/service/credentials/CredentialProviderService.javaCredentialframeworks/base/core/java/android/credentials/Credential.java41.1.4 核心抽象概念該框架引入多層抽象,讓系統可以通過統一協議處理各式各樣的憑據類型。Credential:帶類型的憑據數據容器。Credential類攜帶一個類型字符串以及存放數據的 Bundle。// 摘自 Credential.java public final class Credential implements Parcelable { public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL"; private final String mType; private final Bundle mData; }不同憑據類型由字符串常量標識:類型常量憑據種類TYPE_PASSWORD_CREDENTIAL用戶名?密碼對"androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"通行密鑰(FIDO2/WebAuthn)"com.credman.IdentityCredential"數字身份文檔自定義字符串由提供方自定義的憑據CredentialOption:指定客戶端應用請求的內容。每一個選項包含類型、取回數據(Bundle)以及候選查詢數據。CredentialProviderInfo:已安裝憑據提供方的元數據,包含組件名、能力(支持的憑據類型)、是否為系統提供方。41.1.5 兩階段協議system_server和憑據提供方之間采用兩階段通信,這是一項核心設計:階段 1:開始(查詢)階段 2:完成(Finalize)getCredential(request)客戶端發起請求onBeginGetCredential(beginRequest):系統向各個已啟用提供方發送開始獲取請求返回BeginGetCredentialResponse(憑據條目、認證動作)系統展示憑據選擇器界面用戶選中某一條目,觸發該條目綁定的 PendingIntent,拉起提供方 Activity提供方讀取完整憑據返回GetCredentialResponse(credential)應答回傳給客戶端階段 1(開始 / 查詢):系統向每一個啟用的提供方發送BeginGetCredentialRequest。提供方返回輕量化元數據:描述可用憑據的憑據條目、提供方被鎖定時的認證動作、可選遠程條目。此階段不會傳輸任何真實憑據數據。階段 2(完成):用戶在系統 UI 選中條目之后,系統觸發該條目附帶的PendingIntent。提供方的 Activity 讀取完整憑據(可能需要先生物識別校驗),并通過Activity.setResult()返回。這套兩階段設計具備重要安全特性:用戶明確選中之前,憑據原始數據不會加載進內存系統從不持有原始憑據,僅負責轉發元數據提供方可要求先完成解鎖、生物認證,才允許出示憑據數據41.1.6 服務注冊與發現憑據管理器在SystemServer啟動階段注冊成為系統服務:// 摘自 CredentialManagerService.java @Override // from SystemService public void onStart() { publishBinderService(CREDENTIAL_SERVICE, new CredentialManagerServiceStub()); }服務名稱為Context.CREDENTIAL_SERVICE,獲取方式:CredentialManager cm = context.getSystemService(CredentialManager.class);41.2 CredentialManagerService41.2.1 服務繼承層級CredentialManagerService繼承自AbstractMasterSystemService,該框架模式用于管理多用戶子服務。類繼承關系:源碼文件:frameworks/base/services/credentials/java/com/android/server/credentials/CredentialManagerService.java41.2.2 構造函數與配置解析器構造函數完成基于系統設置的提供方解析邏輯綁定:// 摘自 CredentialManagerService.java(約130行) public CredentialManagerService(@NonNull Context context) { super( context, new SecureSettingsServiceNameResolver( context, Settings.Secure.CREDENTIAL_SERVICE, /* isMultipleMode= */ true), null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER); mContext = context; }關鍵參數說明:參數作用Settings.Secure.CREDENTIAL_SERVICE存儲已啟用提供方組件名的配置鍵isMultipleMode=true支持同時啟用多個提供方,不同于自動填充的單提供方模型PACKAGE_UPDATE_POLICY_REFRESH_EAGER應用包發生變更時立刻重新構建提供方列表已啟用的提供方以冒號分隔的序列化ComponentName字符串,保存在Settings.Secure.CREDENTIAL_SERVICE。另一個配置項Settings.Secure.CREDENTIAL_SERVICE_PRIMARY記錄 “主提供方”(憑據創建時優先選用)。41.2.3 用戶可配置提供方與系統提供方服務維護兩類提供方:系統發現系統提供方的代碼片段:// 摘自 CredentialManagerService.java private ListCredentialManagerServiceImpl constructSystemServiceListLocked( int resolvedUserId) { ListCredentialProviderInfo serviceInfos = CredentialProviderInfoFactory.getAvailableSystemServices( mContext, resolvedUserId, /* disableSystemAppVerificationForTests= */ false, new HashSet()); // ... 將每一個服務包裝為 CredentialManagerServiceImpl }41.2.4 請求會話管理所有正在進行的憑據操作,以用戶為維度通過請求會話跟蹤:// 摘自 CredentialManagerService.java @GuardedBy("mLock") private final SparseArrayMapIBinder, RequestSession mRequestSessions = new SparseArray();SparseArray以用戶 ID 作為 key。同一個用戶可同時存在多個請求會話,會話使用IBinder令牌區分。請求發起時新增會話,會話完成或被取消時移除。private void addSessionLocked(int userId, RequestSession session) { synchronized (mLock) { MapIBinder, RequestSession sessions = mRequestSessions.get(userId); if (sessions == null) { sessions = new HashMap(); mRequestSessions.put(userId, sessions); } sessions.put(session.mRequestId, session); } }41.2.5 CredentialManagerServiceStub(Binder 接口)內部類CredentialManagerServiceStub實現ICredentialManager.Stub,是 Binder 調用真正入口。主要方法:方法用途executeGetCredential()讀取已有憑據(密碼、通行密鑰)executeCreateCredential()創建 / 保存新憑據executePrepareGetCredential()兩步式讀取:先準備,再拉取憑據getCandidateCredentials()供自動填充模塊獲取候選憑據clearCredentialState()清除提供方側狀態(例如賬號登出)setEnabledProviders()配置生效的提供方getCredentialProviderServices()列出可用提供方isEnabledCredentialProviderService()檢查指定提供方是否啟用registerCredentialDescription()注冊憑據描述,用于匹配查詢41.2.6 獲取憑據完整流程executeGetCredential()方法負責調度完整讀取流程:1.請求校驗,創建會話// CredentialManagerServiceStub.executeGetCredential() final GetRequestSession session = new GetRequestSession( getContext(), mSessionManager, mLock, userId, callingUid, callback, request, constructCallingAppInfo(callingPackage, userId, request.getOrigin()), getEnabledProvidersForUser(userId), CancellationSignal.fromTransport(cancelTransport), timestampBegan); addSessionLocked(userId, session);2.初始化提供方會話:遍歷全部啟用提供方,為每一個具備處理能力的提供方創建ProviderGetSessionListProviderSession providerSessions = initiateProviderSessions(session, request.getCredentialOptions() .stream().map(CredentialOption::getType).collect(Collectors.toList()));3.調用提供方:ProviderSession.invokeSession()綁定遠端提供方并調用onBeginGetCredential。4.聚合應答:每一個提供方返回結果后觸發onProviderStatusChanged()。當所有提供方都完成響應,并且至少存在一條可用憑據:// GetRequestSession.onProviderStatusChanged() if (!isAnyProviderPending()) { if (isUiInvocationNeeded()) { getProviderDataAndInitiateUi(); } else { respondToClientWithErrorAndFinish( GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available"); } }5. UI 展示與用戶選擇:系統 UI 展示聚合后的憑據列表;用戶選擇后onUiSelection()路由到對應ProviderSession。6.返回最終憑據:提供方PendingIntent解析完整憑據,經由onFinalResponseReceived()回調至客戶端。41.2.7 創建憑據流程創建憑據邏輯模式類似,使用CreateRequestSession與ProviderCreateSession。創建流程區別點:只創建一條憑據,不是從多條現有憑據里選擇返回CreateEntry列表,每一項代表愿意保存憑據的一個提供方UI 高亮主提供方,主提供方由Settings.Secure.CREDENTIAL_SERVICE_PRIMARY指定41.2.8 權限模型憑據管理器強制校驗多項權限:權限使用場景CREDENTIAL_MANAGER_SET_ORIGIN設置自定義 origin(瀏覽器發起跨源請求場景)CREDENTIAL_MANAGER_S