CVE-2025-48531 AI-generated analysis
platform/packages/apps/Settings, security/CredentialStorage (credential install activity)
Settings' CredentialStorage now identifies the caller of its install flow with ActivityManager.getLaunchedFromPackage(getActivityToken()) instead of Activity.getCallingPackage() when deciding whether the caller is com.android.certinstaller.
Root cause
CredentialStorage.checkCallerIsCertInstallerOrSelfInProfile() used Activity.getCallingPackage() to decide whether the caller is "com.android.certinstaller", then compared that package's signature with Settings. Per the commit message, getCallingPackage() does not always return the package name of the app that actually called. As a result, the certinstaller branch of the authorization check could evaluate a package identity that was not the real caller.
How the fix works
- src/com/android/settings/security/CredentialStorage.java: adds a private helper getCallingPackageName(). It calls ActivityManager.getService().getLaunchedFromPackage(getActivityToken()) and returns null on RemoteException. The commit imports android.app.ActivityManager; RemoteException was already imported.
- CredentialStorage.checkCallerIsCertInstallerOrSelfInProfile(): reads the caller once into a local callingPackage via getCallingPackageName(). It compares that value to "com.android.certinstaller" and passes the same local to getPackageManager().checkSignatures(callingPackage, getPackageName()). Before the fix, the method called getCallingPackage() twice.
- The diff does not change the method's other branch (the launchedFromUserId-based profile check).
Completeness
- Covers the certinstaller branch of checkCallerIsCertInstallerOrSelfInProfile(). Both the package-name comparison and the signature check now use one consistently obtained value.
- The helper returns null if ActivityManager cannot be reached. TextUtils.equals("com.android.certinstaller", null) is false, so the check fails closed in that case.
- The second branch (self-in-profile via launchedFromUserId) is not touched. The material does not show its body, so the diff cannot confirm whether it already used a launched-from API.
- The material shows no other callers of getCallingPackage() in Settings; the fix is limited to this one method.
Behavior changes
- The certinstaller path in CredentialStorage is taken only when the launching package recorded by ActivityManager is com.android.certinstaller and its signature matches Settings.
- Callers that previously passed the check through the getCallingPackage() value but are not the real launcher are now rejected by this branch.
- If the ActivityManager binder call fails, the certinstaller check is denied instead of throwing.
Per major
- Android 14: same fix: commit da4efc0a0615 in android-security-14.0.0_r22 (cherry-pick of 70bd3efe0674). At android-security-14.0.0_r29, 7/8 lines of the patched region are present, and the ActivityManager import is visible in the code context.
- Android 15: same fix: commit cc1b1b5e493a in android-security-15.0.0_r10, identical hunk content with a one-line offset (@@ -332). At android-security-15.0.0_r17, 7/8 lines of the patched region are present.
- Android 16: same fix: commit 0f61e586873a in android-security-16.0.0_r1, identical hunk content at offset @@ -322. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.
- Android 17: not affected per the bulletin (Android 17 is not listed). At android-security-17.0.0_r1, 7/8 lines of the patched region are present.
Unknowns
- Beyond the commit message, the material does not show how a third-party app could cause a mismatched getCallingPackage() value.
- The material does not show the body of the launchedFromUserId branch of checkCallerIsCertInstallerOrSelfInProfile() or which API it uses.
- The material does not show which line of the 8-line patched region differs in the latest tags (7/8 present).