{
 "month": "2025-12",
 "generated": "2026-10-06T12:05+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 37,
    "certain": 36,
    "probable": 1,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2023-40130",
     "osv_id": "ASB-A-289809991",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds an onBindingDied override to Telecom's CallRedirectionServiceConnection that calls finishCallRedirection() under mTelecomLock, so the CallRedirectionService is unbound when its binding dies.",
      "confidence": "medium",
      "backport_notes": [
       "The onBindingDied override is a self-contained 14-line addition to CallRedirectionServiceConnection. It follows the existing onNullBinding and onServiceDisconnected pattern: Log.startSession/endSession around taking mTelecomLock and calling finishCallRedirection().",
       "The 15/16 notifyTimeout try/catch for IllegalArgumentException is already present in the 14 r29 code. Check whether your tree has it."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "18299e2d9062fc23ad8a9f53b0495cd33c782d14",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "2536d3735135f9ccc8cd460a334f189476b498e0",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 756,
     "first_commit_before_announcement_days": 789,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-22420",
     "osv_id": "ASB-A-337775777",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a Uri read-permission check (UriGrantsManagerInternal.checkGrantUriPermission) on a content:// sound Uri when a new notification channel is created in PreferencesHelper.createNotificationChannel, and moves the check into a new PermissionHelper.grantUriPermission helper. On Android 14 a first commit made channel creation throw SecurityException; a second commit falls back to the default notification sound instead.",
      "confidence": "high",
      "backport_notes": [
       "Prefer the fallback variant (ccc01c147ead / 6c7c10e6234b) over the r19 throwing variant. The PreferencesHelper constructor change requires passing mUgmInternal from NotificationManagerService and updating every PreferencesHelper/TestPreferencesHelper constructor call in PreferencesHelperTest.",
       "Keep the branch's existing NotificationRecord.visitGrantableUri semantics (Android 14 has a SYSTEM_UID exemption and lacked the mGrantableUris dedupe; 15/16 differ). Make sure UriGrantsManagerInternal is available through LocalServices in tests, as the Android 14 r19 NotificationRecordTest changes do."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r22",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "04a5d5dd09104732850f3bc504d44b54bc9cc891",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "ccc01c147ead7c1f1a17042b9528c5c5ae9270c1",
       "tag": "android-security-14.0.0_r22",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 90,
     "first_commit_before_announcement_days": 238,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22432",
     "osv_id": "ASB-A-376461726",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes CallRedirectionProcessor.notifyTimeout() unbind the CallRedirectionServiceConnection and clear mConnection/mService when a call redirection service times out, so Telecom releases the bound service instead of keeping it bound.",
      "confidence": "high",
      "backport_notes": [
       "Apply both changes in order: first the unbind in notifyTimeout() (Change-Id I76a65c3993f5107652f98bbe0283043ecff27a7c), then the try/catch (IllegalArgumentException) around mContext.unbindService(mConnection) from a43a880beaa6. Without the second change, an IllegalArgumentException from unbindService() is not caught in notifyTimeout().",
       "The added test assertions use verify(mContext, ...).unbindService(...), so they need mContext to be a mock as in the existing CallRedirectionProcessorTest."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "284cf771ae8fcb8b215df055ce7ddbdbdcddb4a4",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 238,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-32328",
     "osv_id": "ASB-A-327137311",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The Autofill system UI (FillUi, DialogFillUi, SaveUi) now inflates service-supplied RemoteViews (and in SaveUi its whole UI) with a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user 0 context. Session also fetches the service's custom icon and label resources under a cleared Binder calling identity.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. Helper.getUserContext must land together with the FillUi, DialogFillUi and SaveUi changes because all three call it. The Session clearCallingIdentity hunks do not depend on Helper.",
       "The 14 and 15 versions of the patch differ only in context: line offsets, and in the FillUi constructor the assignment comes after mMaxInputLengthForAutofill on 15."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f08b9104a6e9609ad30205d00ff8c88b45c9e87a",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-32329",
     "osv_id": "ASB-A-326926596",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Autofill system UI (FillUi, DialogFillUi, SaveUi) now applies the fill service's RemoteViews presentations with a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user-0 context, and Session's custom icon/label PackageManager lookups run under a cleared Binder calling identity.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. Helper.getUserContext uses Context.createContextAsUser and needs INTERACT_ACROSS_USERS, which system_server holds (general platform knowledge). The @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") annotations only silence the errorprone lint (general platform knowledge).",
       "The bulletin lists Android 13 as affected, but the material has no Android 13 commit or code."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f08b9104a6e9609ad30205d00ff8c88b45c9e87a",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref e030442861f4",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48525",
     "osv_id": "ASB-A-391895151",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "When a package's last companion association for a user is removed, the companion device service now revokes notification listener (NLS) access for all of that package's NotificationListenerService components.",
      "confidence": "high",
      "backport_notes": [
       "For Android 14-style trees, the fix is a self-contained block in the CHANGE_TYPE_REMOVED branch of CompanionDeviceManagerService. It needs AssociationStore.getAssociationsForPackage(userId, packageName), NotificationManager.setNotificationListenerAccessGranted, and the Intent/ResolveInfo/NotificationListenerService imports.",
       "For 15/16-style trees with DisassociationProcessor, the constructor gains a NotificationManager parameter, so any code or tests that construct DisassociationProcessor must be updated."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r22",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r22",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9473c3649017d3b636be7bcce44d19a9077226e9",
       "tag": "android-security-14.0.0_r22",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 90,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48536",
     "osv_id": "BUL-CVE-2025-48536",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SettingsSliceProvider.grantAllowlistedPackagePermissions now combines the existing slice allowlist (slice_allowlist_package_names) with a new debug-only array (slice_allowlist_package_names_for_dev) whose entries are granted only when Build.IS_DEBUGGABLE is true, so packages listed only in the dev array no longer get Settings slice permissions on non-debuggable builds.",
      "confidence": "medium",
      "backport_notes": [
       "The production change is the same on 14, 15 and 16 (resource addition plus the grantAllowlistedPackagePermissions rewrite), but hunk offsets and surrounding imports differ between branches. Forks that populate slice_allowlist_package_names in their overlays should check whether any debug-only entries should move to slice_allowlist_package_names_for_dev.",
       "For the robolectric test, the 14 commit uses ReflectionHelpers.setStaticField(Build.class, \"IS_DEBUGGABLE\", ...) and the 15/16 commits use ShadowBuild.setDebuggable(...)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "b1fdab0d00655b8b8347522368cf1b0444e53629",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48555",
     "osv_id": "BUL-CVE-2025-48555",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' NotificationStation (notification log/history screen) now works out in onResume which profiles of the current user are secure, currently locked and have LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS disabled. For history entries from those profiles it hides the title, the text and the notification/ranking extras.",
      "confidence": "high",
      "backport_notes": [
       "This is a single-file, single-commit change, and 14, 15 and 16 received the same hunks. It relies on com.android.internal.widget.LockPatternUtils, UserManager.getProfiles, KeyguardManager.isDeviceLocked(int) and the LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS constant (general platform knowledge).",
       "Downstream forks with extra callers of the HistoricalNotificationPreference constructor or updatePreference must update them for the new boolean parameter."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "06a1643e44a6508993a5aa67500be0524ba91f2e",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48572",
     "osv_id": "ASB-A-385736540",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaButtonReceiverHolder.send no longer attaches BroadcastOptions that grant background-activity-start and FGS temp-allowlist privileges to the media button receiver; it instead calls MediaSessionService.tempAllowlistTargetPkgIfPossible for the target package, with caller identity.",
      "confidence": "high",
      "backport_notes": [
       "The diff does not add MediaSessionService.tempAllowlistTargetPkgIfPossible(int, String, int, int, String, String), so it must already exist in the target branch. Any other callers of MediaButtonReceiverHolder.send must be updated to the new 10-argument form. The bulletin lists Android 13 as affected, but no 13 commit is in the material."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7dcb4043f5d77962ece1d15b62eb139b70be0888",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48573",
     "osv_id": "ASB-A-339637822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the temporary allowlist grant (tempAllowlistTargetPkgIfPossible) that system_server made to the media session owner whenever a controller invoked MediaController.sendCommand, so this call no longer propagates while-in-use / background-FGS-launch privileges.",
      "confidence": "high",
      "backport_notes": [
       "The change is a pure 3-line deletion inside the sendCommand wrapper of MediaSessionRecord.java. It applies to any branch that has the tempAllowlistTargetPkgIfPossible call there. The only differences across majors are cosmetic: Log.e on 14 versus Slog.e on 15/16, and the class signature extending MediaSessionRecordImpl on 15/16."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "21481e44fa92ca441c39d255d0de48ecb11c9245",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48575",
     "osv_id": "ASB-A-417463103",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller now trusts a \"called by Settings\" referrer for the CA certificate installation path only if the package named com.android.settings resolves to the system uid. Before, it relied on the referrer package-name string alone.",
      "confidence": "high",
      "backport_notes": [
       "The fix is a single commit touching two files in packages/apps/CertInstaller. The new code uses CredentialHelper.getReferrer(), UserHandle.isSameApp and Process.SYSTEM_UID. The CertInstaller.java hunks are identical across 14, 15 and 16. The CredentialHelper.java base blob differs between 14 (eab58f5) and 15/16 (bc1a141), so check context on customized trees."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "17c124efb1f18643a9133c58d5d75aac1194463c",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2025-48576",
     "osv_id": "ASB-A-426205822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the unused, AIDL-only Binder method updateNotificationChannelGroupFromPrivilegedListener from INotificationManager and its NotificationManagerService implementation, eliminating a system_server entry point that created and persisted a notification channel group on behalf of a listener.",
      "confidence": "medium",
      "backport_notes": [
       "Pure deletion with no dependencies; apply the AIDL and service removals together so the build stays consistent. Grep the tree (including vendor/OEM code) for updateNotificationChannelGroupFromPrivilegedListener before backporting, since removal also shifts AIDL transaction codes for later INotificationManager methods (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7b28c9436fffed2a3e41657f7d7bf2a532c87b04",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48580",
     "osv_id": "ASB-A-393582077",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaBrowser no longer always binds to a MediaBrowserService with Context.BIND_INCLUDE_CAPABILITIES; the flag is dropped when the root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing key BrowserRoot.EXTRA_RECENT.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained two-file change. It depends only on the existing mRootHints field in MediaBrowser and the BrowserRoot inner class. Anchor the MediaBrowserService.java hunk on the EXTRA_SUGGESTED constant, because its line number differs between 14 (872) and 15/16 (608).",
       "A platform client that must not pass on capabilities but does not use EXTRA_RECENT needs a separate change to put BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES in its root hints. This commit includes no such client change."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "874eaecb47b8aca60be6250895320834152db1e8",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48583",
     "osv_id": "ASB-A-381885240",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Once a bundle's lazy values have all been consumed, or the bundle is cleared, BaseBundle no longer puts its backing Parcel back into the Parcel pool with recycle(). Instead it calls Parcel.destroy(), which is now exposed as a public @hide method, so these Parcel instances are never reused.",
      "confidence": "medium",
      "backport_notes": [
       "Make destroy() in Parcel.java public (@hide) before or together with the BaseBundle change, because BaseBundle now calls parcel.destroy(). On Android 14, also bring in the 'Parcel parcel = mWeakParcelledData.get(); if (mLazyValues == 0 && parcel != null)' restructuring, and remove recycleParcel() together with all of its callers.",
       "The material does not explain the partial line-match figures (4/8 and 6/8). To confirm the patched state, check that BaseBundle has no recycleParcel() and calls parcel.destroy() in both unwrapLazyValueFromMapLocked and clear()."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "401cc81d7d948e5c59bf6a5a83e9400c1ebc56ac",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48588",
     "osv_id": "ASB-A-303408193",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Vpn.startAlwaysOnVpn() no longer resets the always-on VPN configuration (setAlwaysOnPackage(null, false, null)) when the always-on package is deemed unsupported while the device is in safe mode; per the code comment, the always-on VPN can then start after rebooting to normal mode.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, self-contained change in Vpn.java:startAlwaysOnVpn() (frameworks/base services/core); only the line offset differs between 14 (~1252) and 15 (~1109). The A15 VpnTest addition depends on existing test mocks (mPackageManager, mVpnProfileStore) and the setAndVerifyAlwaysOnPackage helper, whose definitions are not shown; it can be omitted as Google did for A14."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "da768ae60459112ad3815fc3edd8ac1b48a41df4",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48589",
     "osv_id": "ASB-A-362492829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's HeaderPrivacyIconsController now filters the PermissionGroupUsage list it sends to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS, EXTRA_PERMISSION_USAGES). Only usages belonging to the current user and its profiles, plus phone-call usages, are included. Before the fix, the unfiltered list from PermissionManager.getIndicatorAppOpUsageData() was sent.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. Every manual instantiation of the controller (for example in tests) must add the UserTracker argument. The test helper createPermUsage calls a PermissionGroupUsage constructor with 9 arguments on 14 and 10 on 15/16, so match the target branch's constructor."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ec11eca039748f1a7099a9e1faf1867ddf5170a1",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48590",
     "osv_id": "ASB-A-417987184",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the Q- app compat exemption in AppOpsService attribution tag validation. An attribution tag not declared by the package (or whose package is not found) is no longer forced to valid when SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE is disabled for the package or calling UID.",
      "confidence": "medium",
      "backport_notes": [
       "Single-file change with no dependencies: delete the mPlatformCompat/SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE try/catch after the msg assignment, keep Slog.e(TAG, msg), and drop the static import if nothing else references the constant. Line offsets (3937 / 4692 / 5017) and the import-block context differ per branch; the method-body context is the same."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0c46a5ec453bf8dd6c98136753e3aa4e5991d147",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48591",
     "osv_id": "ASB-A-305710469",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a callingUser parameter to the IMms binder interface and makes MmsServiceBroker fill it from Binder.getCallingUserHandle(), so MmsService can check that the client-supplied contentUri belongs to the originating user.",
      "confidence": "medium",
      "backport_notes": [
       "The AIDL signature change has to land together with the matching change in the IMms implementer (MmsService) that performs the user-ID check. The commit message says AIDL does not support method overloading, so there is no flag-guarded compatibility path. Google landed the same content on 14 and 15; only the line offsets in MmsServiceBroker.java differ."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1dd5a8353b998f04e9dabc128e0894d84282ab3f",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 3df02a7df848",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/services/Mms"
     ]
    },
    {
     "cve": "CVE-2025-48594",
     "osv_id": "ASB-A-427206637",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The CompanionDeviceManager UID callback that drains revoked/pending associations now resolves every package sharing a UID via PackageManager.getPackagesForUid() and processes pending revoked associations for each of them, instead of only the single package name previously resolved for the UID.",
      "confidence": "high",
      "backport_notes": [
       "On 14 the fix is applied in CompanionDeviceManagerService itself; it needs the new mPackageManager field from context.getPackageManager() and the nested loop, and leaves getPackageNameByUid untouched. PackageManager.getPackagesForUid() is a long-standing public API, so no new dependency is required (general platform knowledge).",
       "No fork comparison was provided in the material, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6b8c659a8cb351b5638afaccb0ea2ce66ef9c6bf",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48596",
     "osv_id": "ASB-A-438098181",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Parcel::appendFrom in libbinder now sets mDataSize to mDataPos only when the new position exceeds the current size, instead of unconditionally adding len to mDataSize.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained one-line change in Parcel::appendFrom with no dependencies; it applies directly where the `mDataPos += len; mDataSize += len;` pair follows the memcpy."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "aa67ec623b7db8c90bc8f80443f503d142983b95",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48597",
     "osv_id": "ASB-A-436270922",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Moves the grant of the TRUSTED_OVERLAY surface flag for a PiP task out of WindowManager (where it was set as soon as the activity was marked pinned) into WM Shell's PipTransition.onFinishResize, so it is only set once Shell finishes the enter-PiP transition with a valid leash, while WM keeps only the revocation when the task leaves pinned mode.",
      "confidence": "high",
      "backport_notes": [
       "Land both hunks together: the Shell side (libs/WindowManager/Shell) and the WM side (services/core) split grant and revoke, and the split only works when both are present.",
       "On trees where PipTransition.onFinishResize differs (14: no leashOffset param, 16: mMoveToBackTransition in the finish condition), the adaptation is moving the existing leash/hasValidLeash locals above the enteringPip block."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3972f55646f420b18ce58214e16e340f0d714263",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48599",
     "osv_id": "ASB-A-299633613",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' WifiScanModeActivity now finishes immediately, without showing the \"allow scanning when Wi-Fi is off\" dialog, when the current user has the DISALLOW_CONFIG_LOCATION user restriction.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, self-contained change. Only the block added in createDialog() and the isWifiScanModeConfigAllowed() helper change behavior. The other hunks are reformatting plus the isGuestUser() null-guard removal, and those can be dropped. UserManager.DISALLOW_CONFIG_LOCATION and hasUserRestriction() are long-standing public APIs (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 11,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "b263af44716ef8e72985c475301639531847e288",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48601",
     "osv_id": "ASB-A-426207912",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating (and letting callers mutate) PackagePreferences for packages that do not exist, by making getOrCreatePackagePreferencesLocked reject Process.INVALID_UID, making pure getters non-creating, and early-exiting channel lookups on an invalid uid.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. The core is the Preconditions.checkArgument in getOrCreatePackagePreferencesLocked plus the rename of the multi-arg overload; on trees with a different set of getters, each getter calling getOrCreatePackagePreferencesLocked should be converted to getPackagePreferencesLocked with a default, and tests relying on canShowBadge to create entries switched to a setter.",
       "Replace all UNKNOWN_UID uses (XML parsing, dump, Delegate.isAllowed, PackagePreferences.uid default) with Process.INVALID_UID as the commit does."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "30b11a3523d6ff3b08232f5f31f9ec9a1d470516",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48603",
     "osv_id": "ASB-A-416259832",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 200 KB size limit on an input method's metadata XML resource (InputMethod.SERVICE_META_DATA) in InputMethodInfo. An oversized resource now makes the code throw XmlPullParserException before the XML is parsed, so the system process (IMMS) cannot be driven out of memory while constructing InputMethodInfo.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching one file. Its only context dependency: pm.getResourcesForApplication must move above si.loadXmlMetaData, and the imports android.os.Bundle and java.io.InputStream must be added. Hunk offsets differ between 14 and 15/16, but the content is identical."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ea309e799b2100bce5e74b82979f04cf0a7e3355",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48604",
     "osv_id": "ASB-A-305710989",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MmsService now records the calling user for each MMS download request and refuses to write the downloaded PDU to the caller-supplied content URI when the URI's embedded user ID does not match the calling user.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, no dependency. The Android 14 DownloadRequest constructor does not have the trailing TelephonyManager parameter that 15 and 16 have, so insert the new callingUser argument before callingPkg in whatever constructor shape the target branch has. Every RequestManager implementation and test mock must also be updated for the new writePduToContentUri signature.",
       "callingUser must already be available in the MmsService download handler. In all three branches shown, the diffs use it without adding it. If a target branch lacks it, that plumbing has to be added first."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Mms",
       "sha": "ed4355456181087ec76f53339afa33872093d33b",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Mms"
     ]
    },
    {
     "cve": "CVE-2025-48614",
     "osv_id": "ASB-A-430568718",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "RecoverySystem.rebootWipeUserData now throws a SecurityException when DynamicSystemManager.isInUse() reports that the device is running a Dynamic System Update (DSU) image, so a factory reset can no longer be started while in DSU mode.",
      "confidence": "high",
      "backport_notes": [
       "The fix is one self-contained change: an import plus two added statements in rebootWipeUserData. The hunk context differs between 14 (no keepMemtagMode) and 15/16, so other branches may need the context adjusted. The bulletin lists Android 13 as affected, but no 13 commit is in the material."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ed10409eb6dea732b36573306e0bb9839211d650",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48618",
     "osv_id": "ASB-A-404254549",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a KeyguardManager.isDeviceLocked() check to CommandParamsFactory.processLaunchBrowser so that a SIM Toolkit LAUNCH BROWSER proactive command is rejected with a ResultException(ResultCode.LAUNCH_BROWSER_ERROR) while the device is locked. It also relaxes ResultException so this result code can be thrown with only a text explanation.",
      "confidence": "high",
      "backport_notes": [
       "Take both files together. The new throw uses ResultException(ResultCode, String), which calls the single-argument constructor. Without the ResultException.java hunk that removes LAUNCH_BROWSER_ERROR from the assert switch, the new check would raise an AssertionError.",
       "On 14, 15 and 16 the private constructor of CommandParamsFactory already receives a Context; the patch only stores it in mContext. Hunks differ only in context lines between 14 and 15/16. Android 13 code is not shown in the material."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/opt/telephony",
       "sha": "cc7d88a59cbd2954f9facc270d1818e9ed1a8c7e",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/opt/telephony"
     ]
    },
    {
     "cve": "CVE-2025-48620",
     "osv_id": "ASB-A-352024705",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes an early return in VoiceInteractionManagerService's package-change handler so that, when a voice interaction service is set, the code that resets the current voice recognition service on package removal is still reached.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit, three-line change with no visible dependencies. The same hunk was applied on 14, 15 and 16 with only line-offset differences."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "66a76238a130f711eeee03e6a76c7ce8fb4dc1fc",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48621",
     "osv_id": "ASB-A-266433089",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Two linked changes under bug 266433089: InputDispatcher stops delivering motion events to windows that use TouchOcclusionMode::USE_OPACITY while their alpha is below 0.5, and the WM Shell DefaultTransitionHandler lowers MAX_ANIMATION_DURATION from 3000 to 1500. The commit message frames this as reducing tapjacking risk during animations.",
      "confidence": "medium",
      "backport_notes": [
       "The fix spans two projects. Take both the frameworks/native InputDispatcher change, which uses WindowInfo::touchOcclusionMode, WindowInfo::alpha and TouchOcclusionMode::USE_OPACITY (all present in the Android 14 context), and the frameworks/base DefaultTransitionHandler constant change. The material includes no fork comparison.",
       "The native commit message cites inputflinger_tests, but the material does not include a test diff."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "848e575f07ffc9fad38fdb77914356ccfeb171dc",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/native",
       "sha": "0433b19e7d1c39ce1e5412631fab2a4d8f26012f",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48622",
     "osv_id": "ASB-A-412662901",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the Android copy of Adobe's DNG SDK (platform/external/dng_sdk) with an import of version 1.7.1 that has JXL and XMP support stubbed out. It also adapts the two in-tree consumers, Skia's SkRawCodec and the CTS dng-validate JNI helper, to the new SDK API.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the commits as a set in one build: the dng_sdk import first, then the skia SkRawCodec and Android.bp change and the cts dng-validate-jni change. The new dng_area_task, dng_host and dng_image_writer signatures break compilation of the old consumers. Also adapt any other module that linked libdng_sdk, since the new Android.bp removes vendor_available.",
       "The import compiles 1.7.1 with JXL stubbed (source/jxl/color_encoding.h, [[maybe_unused]] in dng_jxl.h) and qDNGUseXMP=0. A backport needs to keep these stubs or provide the missing dependencies."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/cts",
       "sha": "4ff6e58c770b353d8a228fe6c490770672e16b32",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/dng_sdk",
       "sha": "56e64aaa5b59e1758933713a1852f6a881f21bc9",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/skia",
       "sha": "9a77caf186a08b11af44e29b1d2b4ab90f0d43c5",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "projects": [
      "platform/cts",
      "platform/external/dng_sdk",
      "platform/external/skia"
     ]
    },
    {
     "cve": "CVE-2025-48626",
     "osv_id": "ASB-A-381339822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Launcher3's OverviewCommandHelper no longer launches the home activity directly via startActivity for the TYPE_HOME command; it asks SystemUI (new ISystemUiProxy.onKeyEvent) to send a KEYCODE_HOME key event so the press goes through the system's key handling.",
      "confidence": "medium",
      "backport_notes": [
       "Order and dependency: the frameworks/base AIDL change (ISystemUiProxy.onKeyEvent, id 60) must land before or together with the Launcher3 change, because Launcher3's SystemUiProxy implements ISystemUiProxy and calls onKeyEvent. Keep transaction id 60. The A14 variant has no displayId parameter, so do not mix the A16 signature with A14/A15 Launcher3 code.",
       "The change is unconditional (no com.android.launcher3.home_button_uses_keycode_home flag check in the diff). A forked Launcher3 with its own home-button handling should check that its TYPE_HOME handling uses mSystemUiProxy.onKeyEvent(KEYCODE_HOME)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a8a441b0eb62a7aa8c965b22253b4592306959c7",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "9f5688403e254a8d8d15304e7fb98c3d207fba5a",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2025-48627",
     "osv_id": "ASB-A-326571066",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In ActivityTaskManagerService.startNextMatchingActivity, the fix saves the real Binder caller (uid/pid) before the calling identity is cleared. It then passes them to the ActivityStarter as realCallingUid/realCallingPid instead of r.launchedFromUid / -1, so the background activity launch (BAL) decision is made against the app that actually calls the method. It also removes the earlier setAvoidMoveToFront() workaround from b/230492947.",
      "confidence": "high",
      "backport_notes": [
       "The change is contained in startNextMatchingActivity. When applying it, remove options.getOptions(r).setAvoidMoveToFront() and make sure Binder.getCallingUid()/getCallingPid() are read before Binder.clearCallingIdentity(). At android-security-14.0.0_r29 the starter chain is assigned to a local variable, so a hunk may need re-anchoring on setRealCallingPid/setRealCallingUid rather than being applied verbatim."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "07561c7d685db2898d91ee3ef4ba408044fd9d6c",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref d34ae40f870d",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48628",
     "osv_id": "ASB-A-376462130",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id from the URI authority via ContentProvider.getUserIdFromAuthority (defaulting to the resolved calling user) and always enforces the current-user-group check for URI-type icons, instead of relying on Uri.getEncodedUserInfo and skipping the check when it returned null.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, single-commit change; the only new dependency is the android.content.ContentProvider import. The diff is identical on 14, 15 and 16. Android 13 is listed as affected by the bulletin, but no 13 commit is in the material."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0d6bfc8a1f50767eb5efad9d662678ba13b740b0",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48629",
     "osv_id": "ASB-A-352518318",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "When the preferred recognizer package is not found, VoiceInteractionManagerService no longer falls back to the first available RecognitionService. It now auto-selects only a service whose application has ApplicationInfo.FLAG_SYSTEM, and returns null if there is none.",
      "confidence": "high",
      "backport_notes": [
       "Single hunk; the diff adds no import. Only the line offset differs between 14 (about 852) and 15/16 (about 933). When backporting, check that callers of the selection method can handle a null ComponentName, which it can now return even when services are available."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6c030f1f30b4d7ceae9a2fd67c29e688123a4b6b",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48632",
     "osv_id": "ASB-A-443742829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a shared validateDisplayName() length check (DISPLAY_NAME_LENGTH_LIMIT = 1024) to the private AssociationRequest constructor and the @hide setDisplayName() in core/java/android/companion/AssociationRequest.java. Before this, the display-name limit was enforced only in Builder.setDisplayName().",
      "confidence": "high",
      "backport_notes": [
       "Single-file, single-commit change with no dependencies. It only requires that DISPLAY_NAME_LENGTH_LIMIT and the inline check in Builder.setDisplayName() already exist; they do in all three fixed branches. If a fork's constructor signature differs (as on 16 with skipRoleGrant/deviceIcon), insert validateDisplayName(displayName) as the first statement of the explicit-argument constructor that stores mDisplayName."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a100e23a3fda9f6391db9da037e35f78b1c45444",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48633",
     "osv_id": "ASB-A-417988098",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() is changed to query AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of iterating users and calling AccountManager.getAccounts(). Per the commit title, this makes it count all accounts regardless of visibility.",
      "confidence": "medium",
      "backport_notes": [
       "Single-commit fix. The target branch must provide com.android.server.accounts.AccountManagerService.getSingleton() and getAllAccountsForSystemProcess(). Android 13 is listed as affected, but the material includes no Android 13 commit."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "527b25ca541ff11a37280b19faf5217db2749e39",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48639",
     "osv_id": "ASB-A-301952571",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Halves the maximum animation duration enforced by WM Shell's DefaultTransitionHandler (MAX_ANIMATION_DURATION 3000 -> 1500 ms) to shrink the window in which a running transition animation can be used for tap-jacking.",
      "confidence": "medium",
      "backport_notes": [
       "Single constant change in DefaultTransitionHandler.java. The changed line is the same on 14, 15 and 16; only the surrounding hunk context differs. WM Shell is a frameworks/base library built into SystemUI, so the fix ships with the platform image (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "848e575f07ffc9fad38fdb77914356ccfeb171dc",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 6d1697c96c5c mentions bug 266433089, carried by 848e575f07ff (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r24"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "441823943"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "5776485a9378fb38fb6e08e7e76868a3e660739b",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "bb4f07b645738eb630c8d52f0740853efd898bca",
       "subject": "Restricting UserBuild from presistent carrierConfig Override",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "641000b0783ca1731ad9aebeda7e02e0896e85d9",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r24"
     ],
     "tag": "android-security-14.0.0_r24"
    },
    {
     "bug_ids": [
      "395640609",
      "419095873"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0f4a71b50895283b6de0be0e68cd263b3a0a7976",
       "subject": "Remove all pending keyguard exits on SHOW",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r24"
     ],
     "tag": "android-security-14.0.0_r24"
    },
    {
     "bug_ids": [
      "443970347"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "04363cb99c117a78a144b6827aecf762d37e18cb",
       "subject": "Add each blur region as a separate request",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r24"
     ],
     "tag": "android-security-14.0.0_r24"
    },
    {
     "bug_ids": [
      "236134583",
      "236134775"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "6c94029bed7a548c9590c74674b6de977a856528",
       "subject": "Import translations. DO NOT MERGE ANYWHERE",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": [
        "restrict_automerge"
       ]
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "fcdea1bc033f73a8c030e7a2bd630ea1d3d1a925",
       "subject": "Backport BT pairing dialog changes",
       "tag": "android-security-14.0.0_r24",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r24"
     ],
     "tag": "android-security-14.0.0_r24"
    }
   ],
   "coverage": {
    "n_ingested_tags": 30,
    "first_ingested_tag": "android-security-14.0.0_r1",
    "latest_ingested_tag": "android-security-14.0.0_r30",
    "window_base_tag": "android-14.0.0_r1",
    "window_start": "2023-10-04",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2023-40130": {
     "cve": "CVE-2023-40130",
     "summary": "Adds an onBindingDied override to Telecom's CallRedirectionServiceConnection that calls finishCallRedirection() under mTelecomLock, so the CallRedirectionService is unbound when its binding dies.",
     "component": "platform/packages/services/Telecomm, call redirection (src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java)",
     "weakness": "Missing cleanup on service binding death (ServiceConnection.onBindingDied not handled)",
     "root_cause": "CallRedirectionProcessor.CallRedirectionServiceConnection implemented onServiceConnected, onServiceDisconnected and onNullBinding (the last two call finishCallRedirection()), but did not override onBindingDied. When the CallRedirectionService binding died, Telecom did nothing. Per the commit message, an app implementing CallRedirectionService could, in onPlaceCall, schedule an activity start from a separate process and then kill itself, and the activity could still start after the service died. The bulletin rates this as High severity EoP.",
     "fix_analysis": [
      "CallRedirectionProcessor.java:CallRedirectionServiceConnection.onBindingDied (new override, Android 14 commits 18299e2d9062 and 2536d3735135): starts log session \"CRSC.oBD\" and, under synchronized (mTelecomLock), calls finishCallRedirection(). This mirrors the existing onServiceDisconnected and onNullBinding handlers. Per the commit message, this unbinds the service when the binding dies. The body of finishCallRedirection() is not shown.",
      "CallRedirectionProcessor.java:notifyTimeout (Android 15 commit 3623312367a5 and Android 16 commit a43a880beaa6, \"Catch IllegalArgumentException when unbinding CallRedirectionServiceConnection\"): wraps mContext.unbindService(mConnection) in a try/catch for IllegalArgumentException and logs the error. The same try/catch appears in the Android 14 code at android-security-14.0.0_r29 (lines 139-144). The material does not show how this commit relates to the onBindingDied change."
     ],
     "completeness": [
      "Three ServiceConnection callbacks in CallRedirectionServiceConnection now call finishCallRedirection(): onServiceDisconnected, onNullBinding and onBindingDied. Separately, notifyTimeout unbinds on timeout.",
      "The material does not show whether finishCallRedirection() has the same IllegalArgumentException protection that the 15/16 commits add to notifyTimeout.",
      "Only CallRedirectionProcessor is changed. The material does not show whether other Telecom ServiceConnection implementations handle onBindingDied."
     ],
     "behavior_changes": [
      "When a bound CallRedirectionService's binding dies, Telecom now calls finishCallRedirection(), which per the commit message unbinds the service.",
      "A new Telecom log session tag \"CRSC.oBD\" is emitted when the binding dies.",
      "On Android 15 and 16 (and in the 14 r29 code), an IllegalArgumentException thrown by unbindService in notifyTimeout is caught and logged instead of propagating."
     ],
     "preconditions": [
      "An app that implements CallRedirectionService and that Telecom binds to as a user-defined or carrier call redirection service.",
      "An outgoing call placed through Telecom, causing CallRedirectionProcessor to bind the service. One example is TelecomManager.placeCall called from Dialer TelecomUtil.placeCall (per index search).",
      "The service's binding dies while it is held."
     ],
     "per_major": {
      "14": "Same fix: onBindingDied override added in android-security-14.0.0_r1 (commit 18299e2d9062; identical commit 2536d3735135 in r2; Change-Id I065d361b83700474a1efab2a75928427ee0a14ba). At android-security-14.0.0_r29 the patched region is present (2/2 lines), and notifyTimeout also has the try/catch for IllegalArgumentException around unbindService.",
      "15": "Resolved by change_id in android-security-15.0.0_r12. The linked commit 3623312367a5 only wraps unbindService in notifyTimeout with a try/catch for IllegalArgumentException. The patched region is reported present at android-security-15.0.0_r17 (7/7 lines). The material does not show which commit introduced onBindingDied on 15.",
      "16": "Resolved by change_id in android-security-16.0.0_r3 via commit a43a880beaa6, which has the same notifyTimeout try/catch diff as 15. The patched region is present at android-security-16.0.0_r8 (7/7 lines). The commit that introduced onBindingDied on 16 is not shown.",
      "17": "Not listed by the bulletin. The patched region is present (2/2 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The onBindingDied override is a self-contained 14-line addition to CallRedirectionServiceConnection. It follows the existing onNullBinding and onServiceDisconnected pattern: Log.startSession/endSession around taking mTelecomLock and calling finishCallRedirection().",
      "The 15/16 notifyTimeout try/catch for IllegalArgumentException is already present in the 14 r29 code. Check whether your tree has it."
     ],
     "confidence": "medium",
     "unknowns": [
      "The body of finishCallRedirection() is not shown. The material only shows the unbind sequence in notifyTimeout.",
      "Why the Android 15/16 resolver matched the IllegalArgumentException-catch commits (3623312367a5, a43a880beaa6) by Change-Id, whether they are needed for this fix, and which commit introduced onBindingDied on those branches.",
      "The platform mechanism by which the binding that was not released allowed the activity start. The commit message describes only the outcome."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22420": {
     "cve": "CVE-2025-22420",
     "summary": "Adds a Uri read-permission check (UriGrantsManagerInternal.checkGrantUriPermission) on a content:// sound Uri when a new notification channel is created in PreferencesHelper.createNotificationChannel, and moves the check into a new PermissionHelper.grantUriPermission helper. On Android 14 a first commit made channel creation throw SecurityException; a second commit falls back to the default notification sound instead.",
     "component": "platform/frameworks/base, NotificationManagerService / PreferencesHelper (notification channels)",
     "weakness": "Missing permission check (Uri grant not verified when a notification channel's sound Uri is set at channel creation)",
     "root_cause": "PreferencesHelper.createNotificationChannel accepted and stored any sound Uri supplied for a new channel without checking that the uid could read or grant that Uri. Per the commit message, Uri permissions for the channel sound were only checked later, by NotificationRecord (visitGrantableUri) when a notification was posted. For privileged listener updates a check also existed in NotificationManagerService. So the stored channel sound Uri was not validated when it entered the channel preferences.",
     "fix_analysis": [
      "PermissionHelper.java: new static grantUriPermission(UriGrantsManagerInternal ugmInternal, Uri uri, int sourceUid). It returns immediately when uri is null or its scheme is not ContentResolver.SCHEME_CONTENT. Otherwise, under Binder.withCleanCallingIdentity, it calls ugmInternal.checkGrantUriPermission(sourceUid, null, ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))), which throws SecurityException if sourceUid cannot grant read access.",
      "PreferencesHelper.java: the constructor gains a UriGrantsManagerInternal ugmInternal parameter, stored in mUgmInternal. In createNotificationChannel, in the new-channel branch right after clearLockedFieldsLocked(channel), the fix calls PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid). Commit 04a5d5dd0910 (android-security-14.0.0_r19) lets the SecurityException propagate, so the channel is not created (testCreateChannel_noSoundUriPermission_contentSchemeVerified asserts the channel is null). Commit ccc01c147ead (android-security-14.0.0_r22) wraps the call in try/catch(SecurityException); on failure it calls channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT), so the channel is created with the default sound (testCreateChannel_noSoundUriPermission_replaceWithDefaultSound).",
      "NotificationManagerService.java: passes mUgmInternal into the PreferencesHelper constructor. In the privileged-listener channel update path (around line 5876), the inline Binder.withCleanCallingIdentity/checkGrantUriPermission block on the new soundUri is replaced by PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid). The check still runs only when soundUri is non-null and differs from the original channel's sound (test ..._oldSoundNoUriPerm_newSoundHasUriPerm).",
      "NotificationRecord.java visitGrantableUri: replaces the inline clearCallingIdentity/checkGrantUriPermission with PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid). On Android 14 the fix also adds an early return when mGrantableUris already contains the uri. Commit 04a5d5dd0910 dropped the uri == null early return; ccc01c147ead restores 'if (uri == null) return;'. The SYSTEM_UID exemption is kept on Android 14. The existing catch(SecurityException) handling (replace the sound with DEFAULT_NOTIFICATION_URI, or log/throw depending on target SDK) is unchanged.",
      "Tests: PreferencesHelperTest gains content, file and android.resource scheme cases, and all constructor call sites are updated. NotificationManagerServiceTest gains the oldSoundNoUriPerm_newSoundHasUriPerm case. NotificationRecordTest (first commit only) switches to a UriGrantsManagerInternal mock provided through LocalServices."
     ],
     "completeness": [
      "The new check is applied only in the new-channel branch of PreferencesHelper.createNotificationChannel. The existing-channel update branch of the same function and PreferencesHelper.updateNotificationChannel are not touched by the diff. The code comment says regular apps can only set the sound before creating a channel, citing NotificationChannel#setSound, which is not in the material.",
      "Only content:// Uris are checked. file:// and android.resource:// sounds are accepted without a check (the tests explicitly call these 'ignored').",
      "The check in createNotificationChannel passes the `uid` variable to the helper. Whether this differs from the caller's uid in delegated creation is not shown.",
      "The diff does not touch the readXml or restore paths, so channel sounds loaded from persisted XML are not re-verified by this fix.",
      "In the privileged listener update path, the check still runs only when the new sound differs from the original channel sound.",
      "NotificationRecord.visitGrantableUri on Android 14 still exempts Process.SYSTEM_UID. The Android 15/16 versions of the same function have no such exemption and already had the mGrantableUris dedupe before this fix."
     ],
     "behavior_changes": [
      "Android 14 r19 (04a5d5dd0910): createNotificationChannel throws SecurityException to the caller when a content:// sound Uri cannot be read or granted by the checked uid, and the channel is not created.",
      "Android 14 r22 and Android 15/16 (ccc01c147ead, 6c7c10e6234b, fb8f76eca907): instead of throwing, the channel is created with Settings.System.DEFAULT_NOTIFICATION_URI and Notification.AUDIO_ATTRIBUTES_DEFAULT. The requested sound and audio attributes are silently replaced.",
      "Privileged listener channel updates with a non-content sound Uri no longer call checkGrantUriPermission (the helper returns early). Before the fix the call was made for any scheme.",
      "NotificationRecord.visitGrantableUri now adds non-content Uris to mGrantableUris (the helper returns without throwing for them). Before the fix such Uris returned early and were never added.",
      "The PreferencesHelper constructor signature changes (new UriGrantsManagerInternal parameter)."
     ],
     "preconditions": [
      "An app creates a new notification channel whose sound is a content:// Uri it cannot read or grant (for the NotificationRecord path, the uid must not be SYSTEM_UID).",
      "Reached through NotificationManagerService channel creation into PreferencesHelper.createNotificationChannel (new-channel branch). The listener-update path requires a privileged notification listener (a CDM association in the tests) changing the channel sound."
     ],
     "per_major": {
      "14": "Two commits linked by bug 337775777. 04a5d5dd0910 in android-security-14.0.0_r19 throws SecurityException on creation. ccc01c147ead in android-security-14.0.0_r22 does the same refactor but falls back to the default sound and restores the uri == null check. The r29 code shown has the ccc01c147ead form of NotificationRecord.visitGrantableUri. The per-major facts report 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "Adapted fix 6c7c10e6234b in android-security-15.0.0_r10 (fallback-to-default variant). The PreferencesHelper constructor already has PermissionManager, UserProfiles and Clock parameters, and ugmInternal is inserted before showReviewPermissionsNotification. NotificationRecord.visitGrantableUri already had the mGrantableUris dedupe and no SYSTEM_UID exemption. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix fb8f76eca907 in android-security-16.0.0_r3. Same as 15, plus the TestPreferencesHelper subclass constructor in PreferencesHelperTest is updated. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). For information: 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Prefer the fallback variant (ccc01c147ead / 6c7c10e6234b) over the r19 throwing variant. The PreferencesHelper constructor change requires passing mUgmInternal from NotificationManagerService and updating every PreferencesHelper/TestPreferencesHelper constructor call in PreferencesHelperTest.",
      "Keep the branch's existing NotificationRecord.visitGrantableUri semantics (Android 14 has a SYSTEM_UID exemption and lacked the mGrantableUris dedupe; 15/16 differ). Make sure UriGrantsManagerInternal is available through LocalServices in tests, as the Android 14 r19 NotificationRecordTest changes do."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which consumer of the stored channel sound Uri made the missing check an EoP. Only the commit message statement that checks previously happened at post time is available.",
      "Whether the r19 throwing commit was reverted on Android 14 before r22 or simply overwritten is not shown; both diffs apply against the same base.",
      "Which variant the 8/8 line match at android-security-14.0.0_r29 corresponds to for PreferencesHelper.createNotificationChannel is not shown.",
      "The createNotificationChannel signature, and whether `uid` is the owning package uid or the caller uid, is not shown.",
      "Whether mUgmInternal is initialised before PreferencesHelper is constructed in NotificationManagerService is not shown.",
      "Why Android 17 is not affected, and what the 2 of 8 lines that differ at android-security-17.0.0_r1 are, is not shown.",
      "Whether NotificationChannel#setSound actually prevents apps from changing the sound of an existing channel (the stated reason for checking only new channels) is not in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22432": {
     "cve": "CVE-2025-22432",
     "summary": "Makes CallRedirectionProcessor.notifyTimeout() unbind the CallRedirectionServiceConnection and clear mConnection/mService when a call redirection service times out, so Telecom releases the bound service instead of keeping it bound.",
     "component": "platform/packages/services/Telecomm, call redirection (com.android.server.telecom.callredirection.CallRedirectionProcessor)",
     "weakness": "Missing release of a bound service connection on the timeout path (resource not released after its effective lifetime)",
     "root_cause": "In CallRedirectionProcessor, the normal completion path finishCallRedirection() calls mContext.unbindService(mConnection) and clears mConnection and mService. The timeout path notifyTimeout() only passed the timeout to the remote service via mService.notifyTimeout() and then returned. It never unbound the ServiceConnection. So a call redirection service that did not respond within the redirection timeout stayed bound to Telecom after the timeout. The bulletin rates this as a High EoP.",
     "fix_analysis": [
      "Commit 284cf771ae8f (android-security-14.0.0_r19): in src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java:notifyTimeout(), after the mService.notifyTimeout() attempt, the fix adds a log line and a cleanup step: if mConnection != null, it calls mContext.unbindService(mConnection) and sets mConnection = null; then it sets mService = null. This matches the cleanup that finishCallRedirection() already does.",
      "Same commit: tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java adds verify(mContext, times(1)).unbindService(any(ServiceConnection.class)) at four verification points in the timeout tests, so the tests check that the timeout path unbinds exactly once.",
      "Follow-up commit a43a880beaa6 (android-security-16.0.0_r3, 'Catch IllegalArgumentException when unbinding CallRedirectionServiceConnection'): wraps the new mContext.unbindService(mConnection) call in notifyTimeout() in try/catch (IllegalArgumentException), logs 'Error unbinding the connection', and still sets mConnection = null. The Android 14 code at android-security-14.0.0_r29 also contains this try/catch."
     ],
     "completeness": [
      "The fix covers the timeout path: notifyTimeout() now unbinds and clears state. finishCallRedirection() already unbound on normal completion, so both paths now release the connection.",
      "The unbind in notifyTimeout() does not check mIsCarrierRedirectionPending or mIsUserDefinedRedirectionPending the way finishCallRedirection() does. If mConnection was already cleared, the null check on mConnection prevents a second unbind.",
      "The first commit called unbindService() without a guard, so an IllegalArgumentException from it was not caught inside notifyTimeout(). The follow-up commit adds the try/catch.",
      "Other lifecycle paths of CallRedirectionServiceConnection (onServiceDisconnected, binding died, bind failure) are not shown in the material and are not changed by this fix."
     ],
     "behavior_changes": [
      "On call redirection timeout, Telecom now calls Context.unbindService() on the redirection service connection and sets mConnection and mService to null.",
      "A new Log.i line 'notifyTimeout: call redirection has timed out so unbinding the connection' is written on timeout.",
      "With the follow-up commit, an IllegalArgumentException from unbindService() is caught and logged instead of propagating."
     ],
     "preconditions": [
      "An outgoing call goes through CallRedirectionProcessor.performCallRedirection(), and Telecom binds a user-defined or carrier call redirection service (bindServiceAsUser).",
      "The bound service does not complete redirection before the carrier or user-defined redirection timeout, and notifyTimeout() runs.",
      "Tests that exercise this path include testCarrierServiceTimeoutNoUserDefinedService and testUserDefinedServiceTimeoutNoCarrierService in CallRedirectionProcessorTest."
     ],
     "per_major": {
      "14": "Same fix: 284cf771ae8f in android-security-14.0.0_r19 (unbind in notifyTimeout plus tests). The code at android-security-14.0.0_r29 also contains the try/catch (IllegalArgumentException) follow-up; 8/8 patched lines present.",
      "15": "Same fix: 1b081682e2ad in android-security-15.0.0_r7, with an identical source diff. 8/8 patched lines present at android-security-15.0.0_r17. The material does not show whether the IllegalArgumentException follow-up is present.",
      "16": "Base fix already in the branch (the diff base index 15b8aa942 is the post-fix file). The change resolved via change_id in android-security-16.0.0_r3 is a43a880beaa6, the follow-up that wraps unbindService in try/catch (IllegalArgumentException). 7/7 lines present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply both changes in order: first the unbind in notifyTimeout() (Change-Id I76a65c3993f5107652f98bbe0283043ecff27a7c), then the try/catch (IllegalArgumentException) around mContext.unbindService(mConnection) from a43a880beaa6. Without the second change, an IllegalArgumentException from unbindService() is not caught in notifyTimeout().",
      "The added test assertions use verify(mContext, ...).unbindService(...), so they need mContext to be a mock as in the existing CallRedirectionProcessorTest."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the concrete EoP consequence of leaving the redirection service bound; the bulletin only gives High / EoP.",
      "Whether the IllegalArgumentException follow-up was applied on Android 15 is not shown.",
      "How other connection lifecycle callbacks (onServiceDisconnected, onBindingDied) are handled in CallRedirectionServiceConnection is not shown.",
      "The caller of notifyTimeout() and how it would handle an exception thrown by notifyTimeout() are not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32328": {
     "cve": "CVE-2025-32328",
     "summary": "The Autofill system UI (FillUi, DialogFillUi, SaveUi) now inflates service-supplied RemoteViews (and in SaveUi its whole UI) with a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user 0 context. Session also fetches the service's custom icon and label resources under a cleared Binder calling identity.",
     "component": "platform/frameworks/base, services/autofill (FillUi, DialogFillUi, SaveUi, Session, Helper)",
     "weakness": "Incorrect user context for untrusted RemoteViews inflation in system_server (inflation as user 0 instead of the foreground user)",
     "root_cause": "FillUi, DialogFillUi and SaveUi built their Context as new ContextThemeWrapper(context, mThemeId) from the system_server context. FillUi and DialogFillUi passed it to RemoteViews.applyWithTheme for the presentations supplied by the autofill service in the FillResponse (main presentation, header, footer, per-dataset views, dialog presentations). SaveUi used it as the base context for its UI. system_server runs as user 0, so these views were inflated as user 0 rather than as the current foreground user. In FillUi a RemoteView URI permission check (a Helper method documented as checking against the current userId; failure throws 'Permission error accessing RemoteView') already existed, but the inflation that followed still used the user 0 context. Session.getServiceIcon and the service label lookup called PackageManager.getDrawable/getText on the master context without clearing the Binder calling identity.",
     "fix_analysis": [
      "Helper.java: adds Helper.getUserContext(Context), annotated @RequiresPermission(INTERACT_ACROSS_USERS). It reads ActivityManager.getCurrentUser() and returns context.createContextAsUser(UserHandle.of(userId), 0), with a debug Slog when sDebug is set.",
      "FillUi.java, FillUi constructor: adds the field mUserContext = Helper.getUserContext(mContext) and uses it in the four RemoteViews.applyWithTheme calls (main response.getPresentation(), headerPresentation, footerPresentation, and the per-dataset presentation). Decor inflation via LayoutInflater.from(mContext) is unchanged. dump() prints mUserContext. The constructor gets @SuppressWarnings(\"AndroidFrameworkRequiresPermission\").",
      "DialogFillUi.java, DialogFillUi constructor: adds mUserContext = Helper.getUserContext(mContext) and uses it in the three applyWithTheme calls (two presentation paths and the per-dataset presentation loop). Decor inflation still uses mContext. The same @SuppressWarnings is added.",
      "SaveUi.java, SaveUi constructor: the themed context is now new ContextThemeWrapper(Helper.getUserContext(context), mThemeId) { ... }. Everything the constructor inflates from that context, including R.layout.autofill_save via LayoutInflater.from(context), now uses the foreground user context. The same @SuppressWarnings is added.",
      "Session.java, getServiceIcon and the service label lookup: PackageManager.getDrawable(servicePackageName, iconResourceId, null) and getText(servicePackageName, customServiceNameId, null) are wrapped in Binder.clearCallingIdentity()/restoreCallingIdentity(token). The context used is still mService.getMaster().getContext()."
     ],
     "completeness": [
      "All RemoteViews.applyWithTheme call sites shown in FillUi (4) and DialogFillUi (3) now use mUserContext. SaveUi switches its entire base context.",
      "The user id comes from ActivityManager.getCurrentUser() (the foreground user), not from the session's own user id.",
      "Session icon/label lookups: only the calling identity is cleared. The PackageManager lookup still uses the master service context. The material does not explain whether that is intended.",
      "FillUi and DialogFillUi keep the system context for their decor layout inflation. In SaveUi the decor layout is inflated with the user context.",
      "No other autofill UI surfaces appear in the diff. The material does not show whether other paths that render service-supplied views exist outside these three classes."
     ],
     "behavior_changes": [
      "Service-supplied RemoteViews in the fill and dialog-fill UIs, and the whole save UI, are inflated with a Context for the current foreground user instead of user 0.",
      "Helper.getUserContext is annotated as requiring INTERACT_ACROSS_USERS. The callers suppress the AndroidFrameworkRequiresPermission lint with the comment 'System has all permissions'.",
      "New sDebug log line in Helper.getUserContext and a new 'mUserContext' line in FillUi.dump().",
      "FillResponse custom icon/label resource fetches in Session now run with the Binder calling identity cleared, and the identity is restored in a finally block."
     ],
     "preconditions": [
      "An autofill service is enabled and returns a FillResponse (or a save request) whose views the system renders in FillUi, DialogFillUi or SaveUi.",
      "A user other than user 0 is in the foreground (the commit's test runs CtsAutoFillServiceTestCases with --user-type secondary_user)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base f08b9104a6e9 in android-security-14.0.0_r24 (RESTRICT AUTOMERGE cherry-pick, Change-Id I8a2c96259976e61d297dec914d10d86e585a6d6f). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 3ca035e4cdf3 in android-security-15.0.0_r12. Same content, except that in FillUi mUserContext is assigned after mMaxInputLengthForAutofill. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). The patched region (8/8 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. Helper.getUserContext must land together with the FillUi, DialogFillUi and SaveUi changes because all three call it. The Session clearCallingIdentity hunks do not depend on Helper.",
      "The 14 and 15 versions of the patch differ only in context: line offsets, and in the FillUi constructor the assignment comes after mMaxInputLengthForAutofill on 15."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact impact of inflating as user 0 (which resources or URIs could be reached) is not shown. The bulletin only rates it High EoP.",
      "Why the Session icon/label lookups needed clearCallingIdentity (which caller identity was present) is not explained in the material.",
      "Whether Android 13, which the bulletin lists as affected, received a corresponding commit is not shown in the material.",
      "Which RemoteViews SaveUi inflates (for example custom descriptions) is not shown in the diff.",
      "Whether inline-suggestion rendering or other autofill UI code outside these three classes inflates service-supplied views is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32329": {
     "cve": "CVE-2025-32329",
     "summary": "Autofill system UI (FillUi, DialogFillUi, SaveUi) now applies the fill service's RemoteViews presentations with a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user-0 context, and Session's custom icon/label PackageManager lookups run under a cleared Binder calling identity.",
     "component": "platform/frameworks/base, services/autofill (FillUi, DialogFillUi, SaveUi, Session, Helper)",
     "weakness": "Wrong user identity when processing RemoteViews: system_server (user 0) context used to inflate content supplied by the autofill service",
     "root_cause": "FillUi, DialogFillUi and SaveUi run inside system_server and inflate RemoteViews presentations that come from the autofill service's FillResponse. Before the fix they called presentation.applyWithTheme(mContext, ...), and SaveUi built its ContextThemeWrapper over the plain system context, so this inflation ran as user 0 (system_server's user). The commit message says: \"Autofill will inflate views as the current foreground user instead of the system_server user (0)\". The bulletin rates this High, EoP.",
     "fix_analysis": [
      "Helper.java: adds public static Context getUserContext(Context), annotated @RequiresPermission(INTERACT_ACROSS_USERS). It reads ActivityManager.getCurrentUser() and returns context.createContextAsUser(UserHandle.of(userId), 0). When sDebug is set it logs the user id and the content resolver's user id.",
      "FillUi.java, FillUi(...) constructor: new field mUserContext = Helper.getUserContext(mContext). Four RemoteViews inflation sites now pass mUserContext to applyWithTheme: response.getPresentation(), headerPresentation (mHeader), footerPresentation (mFooter) and each dataset presentation in the loop. The decor layout is still inflated through LayoutInflater.from(mContext). dump() prints mUserContext. The constructor gets @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") with the comment 'System has all permissions, see b/228957088'.",
      "DialogFillUi.java, DialogFillUi(...) constructor: mUserContext = Helper.getUserContext(mContext). Three presentation.applyWithTheme call sites now use mUserContext: two that add content to a container, and the per-dataset remote views in the loop. The autofill_fill_dialog layout is still inflated from mContext.",
      "SaveUi.java, SaveUi(...) constructor: the anonymous ContextThemeWrapper is now built over Helper.getUserContext(context) instead of context, so the LayoutInflater that inflates autofill_save uses the foreground-user context.",
      "Session.java, getServiceIcon(FillResponse) and the matching custom service label block: the PackageManager getDrawable(...) and getText(...) calls for response.getIconResourceId() and getServiceDisplayNameResourceId() are wrapped in Binder.clearCallingIdentity()/restoreCallingIdentity(token)."
     ],
     "completeness": [
      "The fix switches every RemoteViews application site shown in the diff to the foreground-user context: 4 in FillUi, 3 in DialogFillUi, and SaveUi's whole wrapped context.",
      "The user comes from ActivityManager.getCurrentUser() (the global foreground user), not from the autofill session's own user id. The material does not show how this behaves when the two differ.",
      "The Session.java changes only clear the calling identity around the icon/label fetch. They do not change which context resolves the resources (still mService.getMaster().getContext()).",
      "FillUi and DialogFillUi still inflate their own layouts with the system context. SaveUi's own autofill_save layout is now inflated with the foreground-user context."
     ],
     "behavior_changes": [
      "Service-supplied RemoteViews in the fill, fill-dialog and save UIs are applied with a Context for the current foreground user.",
      "SaveUi's whole Context, including its startActivity override and LayoutInflater, now wraps the foreground-user context.",
      "Helper.getUserContext adds a debug log line when sDebug is set, and FillUi.dump prints mUserContext.",
      "Session's custom service icon/label resource fetch now runs with cleared Binder calling identity."
     ],
     "preconditions": [
      "An autofill service is enabled and returns a FillResponse (or SaveInfo) whose RemoteViews presentations the system shows through FillUi, DialogFillUi or SaveUi.",
      "The fix matters when the foreground user is not user 0. It was tested with 'atest CtsAutoFillServiceTestCases --user-type secondary_user'."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base f08b9104a6e9 in android-security-14.0.0_r24 (RESTRICT AUTOMERGE cherry-pick, Change-Id I8a2c9625...). Patched region fully present at android-security-14.0.0_r29 (8/8 lines).",
      "15": "same fix: 3ca035e4cdf3 in android-security-15.0.0_r12, with the same content. In FillUi the mUserContext assignment comes after the mMaxInputLengthForAutofill assignment. Patched region fully present at android-security-15.0.0_r17 (8/8 lines).",
      "16": "not affected per the bulletin (not listed). Patched-region lines present at android-security-16.0.0_r8 (8/8).",
      "17": "not affected per the bulletin (not listed). Patched-region lines present at android-security-17.0.0_r1 (8/8)."
     },
     "backport_notes": [
      "Single commit. Helper.getUserContext uses Context.createContextAsUser and needs INTERACT_ACROSS_USERS, which system_server holds (general platform knowledge). The @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") annotations only silence the errorprone lint (general platform knowledge).",
      "The bulletin lists Android 13 as affected, but the material has no Android 13 commit or code."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which resource or content resolution inside the RemoteViews was reachable as user 0, and what that exposed. The material says only that inflation happened as the system_server user.",
      "Why the Binder identity clearing in Session's icon/label fetch was needed, and how it changes outcomes.",
      "Whether an Android 13 commit exists, what it contains, and whether the Android 14 diff applies to 13.",
      "Behavior when the autofill session's user differs from ActivityManager.getCurrentUser()."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48525": {
     "cve": "CVE-2025-48525",
     "summary": "When a package's last companion association for a user is removed, the companion device service now revokes notification listener (NLS) access for all of that package's NotificationListenerService components.",
     "component": "platform/frameworks/base, services/companion (CompanionDeviceManagerService / DisassociationProcessor)",
     "weakness": "Incomplete cleanup / missing revocation of a granted privilege: notification listener access was not removed when a package's companion associations were removed",
     "root_cause": "When an association was removed, nothing revoked notification listener access. On Android 14 the CHANGE_TYPE_REMOVED branch in CompanionDeviceManagerService only called markIdAsPreviouslyUsedForPackage. On 15/16, DisassociationProcessor.disassociate() removed the association and revoked the device-profile role, but not NLS access. The 15/16 diffs delete a comment \"// TODO: also revoke notification access\" above disassociate(), so the gap was already known. A package could therefore keep NLS access after it no longer had any companion association.",
     "fix_analysis": [
      "Android 14, services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java, in the branch for AssociationStore.CHANGE_TYPE_REMOVED (just before markIdAsPreviouslyUsedForPackage): inside Binder.withCleanCallingIdentity, if mAssociationStore.getAssociationsForPackage(userId, packageName).isEmpty(), it calls queryIntentServicesAsUser(new Intent(NotificationListenerService.SERVICE_INTERFACE), 0, userId). For every ResolveInfo whose component package equals packageName, it calls NotificationManager.setNotificationListenerAccessGranted(componentName, false).",
      "Android 15/16, DisassociationProcessor.java: the same logic is added to disassociate() right after mAssociationStore.removeAssociation(...) and before the role revocation. It uses mAssociationStore.getAssociationsByPackage(userId, packageName), mContext.getPackageManager() and a new injected mNotificationManager field. The constructor gains a NotificationManager parameter.",
      "Android 15/16, CompanionDeviceManagerService.java constructor: gets NotificationManager via context.getSystemService and passes it to the DisassociationProcessor constructor."
     ],
     "completeness": [
      "Revocation happens only when the package has zero remaining associations for that userId. While any association remains, NLS access is left alone.",
      "Every NLS component of the package returned by the query for that user is revoked, whoever granted the access. The code does not track who granted it.",
      "Android 14 hooks the revocation into the handling of AssociationStore.CHANGE_TYPE_REMOVED (enclosing method not shown). Android 15/16 add it only in DisassociationProcessor.disassociate(), so any removal path that bypasses disassociate() is not covered by the diff shown.",
      "The grant path is unchanged; the fix only adds cleanup on removal."
     ],
     "behavior_changes": [
      "After a package's last association is removed, setNotificationListenerAccessGranted(component, false) is called for each of the package's NotificationListenerService components returned by the query in that user. This includes access the user may have granted independently.",
      "The revocation runs with cleared calling identity.",
      "Android 15/16: the DisassociationProcessor constructor signature changes (extra NotificationManager argument)."
     ],
     "preconditions": [
      "A package holds notification listener access and has at least one companion association for a user, and that association (the package's last one for that user) is removed.",
      "The vulnerable state is the NLS access kept after disassociation."
     ],
     "per_major": {
      "14": "Same fix (single file, in the CHANGE_TYPE_REMOVED branch of CompanionDeviceManagerService). Landed in android-security-14.0.0_r22; 8/8 patched-region lines present at android-security-14.0.0_r29.",
      "15": "Adapted fix: logic moved into DisassociationProcessor.disassociate(int id), with NotificationManager injected from the CompanionDeviceManagerService constructor; uses getAssociationsByPackage. Landed in android-security-15.0.0_r10; 7/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "Adapted fix, same as 15 except it uses disassociate(int id, String reason) and removeAssociation(id, reason). Landed in android-security-16.0.0_r3; 7/8 lines present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin); 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "For Android 14-style trees, the fix is a self-contained block in the CHANGE_TYPE_REMOVED branch of CompanionDeviceManagerService. It needs AssociationStore.getAssociationsForPackage(userId, packageName), NotificationManager.setNotificationListenerAccessGranted, and the Intent/ResolveInfo/NotificationListenerService imports.",
      "For 15/16-style trees with DisassociationProcessor, the constructor gains a NotificationManager parameter, so any code or tests that construct DisassociationProcessor must be updated."
     ],
     "confidence": "high",
     "unknowns": [
      "How companion apps get NLS access in the first place is not shown in the material.",
      "The enclosing method of the Android 14 hunk is not shown in the diff header.",
      "Whether 15/16 have association removal paths that bypass DisassociationProcessor.disassociate().",
      "Whether querying with flags 0 leaves out any of the package's NLS components (for example disabled ones).",
      "Which line differs in the 7/8 patched-region comparison on 15/16/17."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48536": {
     "cve": "CVE-2025-48536",
     "summary": "SettingsSliceProvider.grantAllowlistedPackagePermissions now combines the existing slice allowlist (slice_allowlist_package_names) with a new debug-only array (slice_allowlist_package_names_for_dev) whose entries are granted only when Build.IS_DEBUGGABLE is true, so packages listed only in the dev array no longer get Settings slice permissions on non-debuggable builds.",
     "component": "platform/packages/apps/Settings, Settings Slices (SettingsSliceProvider)",
     "weakness": "Over-broad permission grant / improper access control: slice URI permissions granted by package name to allowlisted packages regardless of build type",
     "root_cause": "In src/com/android/settings/slices/SettingsSliceProvider.java, grantAllowlistedPackagePermissions(Context, List<Uri>) read a single resource array R.array.slice_allowlist_package_names and, for every descendant slice Uri, called SliceManager.grantSlicePermission(toPackage, descendant) for every listed package name. It made no distinction between packages needed in production and packages only used for debugging, so every package name in the allowlist (the AOSP default is empty; entries come from overlays) was granted access to the Settings slice URIs on all build types. Per index search, SliceManagerService.grantSlicePermission checks the caller and the URI owner, then records the grant for toPkg by name via mPermissions.grantSliceAccess. The material does not show any check that toPkg is installed or trusted. The commit title describes the problem as the provider granting access to \"unused packages\".",
     "fix_analysis": [
      "res/values/config.xml: adds a new empty string-array resource slice_allowlist_package_names_for_dev, documented as the allowlist for slice uri access 'for debugging purpose', next to the existing slice_allowlist_package_names.",
      "src/com/android/settings/slices/SettingsSliceProvider.java:grantAllowlistedPackagePermissions: builds a List<String> allowlist from slice_allowlist_package_names (added only if the array is non-null). Only if Build.IS_DEBUGGABLE is true does it append the entries of slice_allowlist_package_names_for_dev (also null-checked). If the combined list is empty it logs and returns. Otherwise it loops over descendants x allowlist and calls sliceManager.grantSlicePermission(toPackage, descendant) as before. Adds the android.os.Build import.",
      "tests/robotests/res/values-mcc998/config.xml and values-mcc999/config.xml: add the new array (empty for mcc998; one entry, com.android.settings.slice_allowlist_package_dev, for mcc999) so the robolectric tests can exercise both paths.",
      "tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java: the existing test grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant now forces IS_DEBUGGABLE=false and verifies the dev package is never granted. The new test grantAllowlistedPackagePermissions_hasPackageAllowlistAndDebuggable_shouldGrant forces IS_DEBUGGABLE=true and verifies the dev package is granted. On Android 14 this uses ReflectionHelpers.setStaticField(Build.class, \"IS_DEBUGGABLE\", ...); on 15/16 it uses ShadowBuild.setDebuggable(...)."
     ],
     "completeness": [
      "The commit adds the gating mechanism only. In AOSP both arrays are empty and the commit contains no overlay change. Whether overlays that populate slice_allowlist_package_names had to move entries into the dev array is not shown.",
      "Packages that stay in slice_allowlist_package_names are still granted access to every descendant slice Uri on all build types. The fix adds no check that an allowlisted package is installed and no validation of the target package's identity.",
      "On debuggable builds the dev packages are granted in addition to the production list. The gating depends only on Build.IS_DEBUGGABLE.",
      "The diff does not revoke grants made before the update. The material does not show whether SliceManagerService grants persist across the update.",
      "Only grantAllowlistedPackagePermissions is changed; the diff touches no other grant path in SettingsSliceProvider."
     ],
     "behavior_changes": [
      "New resource string-array slice_allowlist_package_names_for_dev, available to device overlays.",
      "On non-debuggable builds, package names listed only in slice_allowlist_package_names_for_dev are never granted slice permissions. On debuggable builds they are granted in addition to slice_allowlist_package_names.",
      "The 'Allowlisting %d uris to %d pkgs.' log now reports the combined list size, and the 'No packages to allowlist' early return triggers only when the combined list is empty."
     ],
     "preconditions": [
      "grantAllowlistedPackagePermissions(Context, List<Uri>), a static method on SettingsSliceProvider, must run with the slice descendants. The runtime trigger is not shown in the material.",
      "The slice_allowlist_package_names resource must contain package names; the AOSP default array is empty.",
      "Grants go through SliceManager.grantSlicePermission to SliceManagerService, which (per index search) records access for toPkg by package name after checking the calling package and the URI owner."
     ],
     "per_major": {
      "14": "same fix: commit b1fdab0d0065 in android-security-14.0.0_r24; the test uses ReflectionHelpers.setStaticField on Build.IS_DEBUGGABLE; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix (11b11215aa18 in android-security-15.0.0_r12); identical production code change, test adapted to ShadowBuild.setDebuggable; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix (586f8dedd8e0 in android-security-16.0.0_r3); identical production code change, test uses ShadowBuild.setDebuggable; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The production change is the same on 14, 15 and 16 (resource addition plus the grantAllowlistedPackagePermissions rewrite), but hunk offsets and surrounding imports differ between branches. Forks that populate slice_allowlist_package_names in their overlays should check whether any debug-only entries should move to slice_allowlist_package_names_for_dev.",
      "For the robolectric test, the 14 commit uses ReflectionHelpers.setStaticField(Build.class, \"IS_DEBUGGABLE\", ...) and the 15/16 commits use ShadowBuild.setDebuggable(...)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which package names were in device overlays of slice_allowlist_package_names and counted as 'unused'; no overlay changes are in the material.",
      "Whether existing slice grants persist across the update and need separate revocation.",
      "The exact runtime caller and timing of grantAllowlistedPackagePermissions.",
      "Why Android 17 is not listed by the bulletin; the material does not say."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48555": {
     "cve": "CVE-2025-48555",
     "summary": "Settings' NotificationStation (notification log/history screen) now works out in onResume which profiles of the current user are secure, currently locked and have LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS disabled. For history entries from those profiles it hides the title, the text and the notification/ranking extras.",
     "component": "platform/packages/apps/Settings, notification history (NotificationStation fragment)",
     "weakness": "Missing lock-state / visibility check before showing sensitive notification content from a locked profile (information disclosure)",
     "root_cause": "NotificationStation builds a HistoricalNotificationInfo from every StatusBarNotification that reaches its NotificationListenerService, which is registered as a system service for ActivityManager.getCurrentUser(). HistoricalNotificationPreference.updatePreference always rendered info.title, info.text, mInfo.notificationExtra and mInfo.rankingExtra. Nothing checked whether the notification's user (info.user) was a profile locked with a secure credential and set not to show private notification content while locked. As a result, a locked profile's notification content could be shown in the Settings UI.",
     "fix_analysis": [
      "NotificationStation.java:onResume: the new field mContentRestrictedUsers (ArrayList<Integer>) is cleared and rebuilt on every resume. For each UserInfo in UserManager.getProfiles(mContext.getUserId()), the user id is added to the set when three things hold: Settings.Secure LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS for that user is 0 (read with a default of 0), LockPatternUtils.isSecure(user.id) is true, and KeyguardManager.isDeviceLocked(user.id) is true.",
      "NotificationStation.java: every HistoricalNotificationPreference construction and updatePreference call in the diff now passes mContentRestrictedUsers.contains(<info>.user) as a new boolean. These call sites are the full-list rebuild loop, the removal path, the posted-update and addFirst branches, and the path that calls updateFromRanking.",
      "NotificationStation.java:HistoricalNotificationPreference: the constructor takes a new restrictContent parameter and stores it in mRestrictContent. The view-binding method calls updatePreference(mInfo, mRestrictContent).",
      "NotificationStation.java:HistoricalNotificationPreference.updatePreference(info, restrictContent): when restrictContent is true, R.id.title and R.id.text are set to View.GONE (the same branch as the empty-string case) and their text is not set. R.id.notification_extra and R.id.ranking_extra are also set to View.GONE and do not receive mInfo.notificationExtra / mInfo.rankingExtra. Package name, icon and timestamp are still shown.",
      "Two hunks in createFromSbn only add whitespace and change nothing functionally."
     ],
     "completeness": [
      "The fix covers all four content fields that updatePreference renders (title, text, notification_extra, ranking_extra), and every call site changed in the diff passes the new flag.",
      "The restricted set is computed only in onResume. If a profile becomes locked while the fragment stays resumed, it is not re-evaluated until the next resume.",
      "In the non-restricted branch for notification_extra / ranking_extra, visibility is never set back to View.VISIBLE. Once a preference has been rendered restricted, those two views can stay GONE.",
      "Only the UI rendering is gated. HistoricalNotificationInfo still holds title, text and extras in memory for restricted users.",
      "Restriction depends only on LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, isSecure and isDeviceLocked for profiles of mContext.getUserId(). Notifications with sbn.getUserId()==USER_ALL are mapped to USER_SYSTEM, so they are restricted only if the system user meets those conditions. Other redaction inputs, such as the DevicePolicyManager keyguard DISABLE_UNREDACTED_NOTIFICATIONS feature, are not consulted in this code.",
      "Icon, package name and timestamp of restricted-profile notifications stay visible."
     ],
     "behavior_changes": [
      "In the Settings notification log, entries from a locked, secure profile that disallows private notifications on the lock screen show only package name, icon and timestamp. Title, text and the expandable notification/ranking extras are hidden.",
      "onResume now does extra per-profile lookups (a Settings.Secure read, LockPatternUtils.isSecure, KeyguardManager.isDeviceLocked) on every resume.",
      "The HistoricalNotificationPreference constructor and updatePreference each take an extra boolean parameter."
     ],
     "preconditions": [
      "The Settings NotificationStation (notification log/history) screen is displayed in the parent user.",
      "A profile of the current user has a secure lock (LockPatternUtils.isSecure), is currently locked (KeyguardManager.isDeviceLocked) and has Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS == 0.",
      "Notifications from that profile are rendered as HistoricalNotificationPreference rows."
     ],
     "per_major": {
      "13": "The bulletin lists 13 as affected, but the material has no Android 13 commit or code context, so the status of the fix on 13 is unknown.",
      "14": "Same fix: commit 06a1643e44a6 in android-security-14.0.0_r24 (cherry-pick of b1bfda5a03c6, Change-Id Ie7705a1ceea54203585c3b3737d0bada68aa1469). The patched region is fully present (8/8) at android-security-14.0.0_r29.",
      "15": "Same fix: commit 0db1143041a6 in android-security-15.0.0_r12, with the same hunks as the 14 patch (file index e79a4ac58c6..4315c6dbfaa, which differs from 14's 3b27e1947d2..278f6ec198c). 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 596c7b9911f2 in android-security-16.0.0_r3, with a diff identical to 15 (same file indexes). 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. The patched region is present (8/8) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a single-file, single-commit change, and 14, 15 and 16 received the same hunks. It relies on com.android.internal.widget.LockPatternUtils, UserManager.getProfiles, KeyguardManager.isDeviceLocked(int) and the LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS constant (general platform knowledge).",
      "Downstream forks with extra callers of the HistoricalNotificationPreference constructor or updatePreference must update them for the new boolean parameter."
     ],
     "confidence": "high",
     "unknowns": [
      "No Android 13 commit is provided, even though 13 is listed as affected, so whether the patch applies cleanly there is unknown.",
      "The disclosure scenario reported in bug 396666065 is not shown.",
      "It is not shown whether a profile lock-state change while the fragment is resumed is handled some other way.",
      "The enclosing method names of the changed call sites, and whether other call sites exist in the full file, are not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48572": {
     "cve": "CVE-2025-48572",
     "summary": "MediaButtonReceiverHolder.send no longer attaches BroadcastOptions that grant background-activity-start and FGS temp-allowlist privileges to the media button receiver; it instead calls MediaSessionService.tempAllowlistTargetPkgIfPossible for the target package, with caller identity.",
     "component": "platform/frameworks/base, services/core media (MediaSessionService / MediaButtonReceiverHolder)",
     "weakness": "Improper privilege propagation: system_server unconditionally granted background activity start (BAL) and FGS allowlist capabilities to the media button receiver via BroadcastOptions.",
     "root_cause": "In services/core/java/com/android/server/media/MediaButtonReceiverHolder.java, send() built BroadcastOptions with setTemporaryAppAllowlist(fgsAllowlistDurationMs, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_MEDIA_BUTTON) and setBackgroundActivityStartsAllowed(true). It passed options.toBundle() both to mPendingIntent.send(...) and to context.sendBroadcastAsUser(...) in the legacy default path. These options gave the media button receiver (the package in the holder) background activity start and FGS allowlist capabilities without regard to the dispatching caller's identity. The commit message says this accidentally propagated BAL abilities to the receiver.",
     "fix_analysis": [
      "MediaButtonReceiverHolder.java:send() - the signature now takes the MediaSessionService instance, callingPackageName, callingPid, callingUid and a separate reportedPackageName. The fgsAllowlistDurationMs parameter is removed.",
      "MediaButtonReceiverHolder.java:send() - the BroadcastOptions construction (setTemporaryAppAllowlist + setBackgroundActivityStartsAllowed(true)) is deleted. Instead, the target UID is resolved via context.getPackageManager().getPackageUidAsUser(mPackageName, mUserId), and mediaSessionService.tempAllowlistTargetPkgIfPossible(targetUid, mPackageName, callingPid, callingUid, callingPackageName, TAG) is called. NameNotFoundException is caught and only logged under DEBUG_KEY_EVENT.",
      "MediaButtonReceiverHolder.java:send() - mPendingIntent.send(...) now uses the 5-argument overload (context, resultCode, mediaButtonIntent, onFinishedListener, handler), with no requiredPermission or options bundle. The legacy default branch now calls context.sendBroadcastAsUser(mediaButtonIntent, userHandle) with no options bundle.",
      "MediaButtonReceiverHolder.java:send() - Intent.EXTRA_PACKAGE_NAME is now filled from reportedPackageName, which is the value previously passed as callingPackageName. The package name the receiver sees is unchanged.",
      "MediaSessionService.java (media key dispatch, mediaButtonReceiverHolder != null branch) - the local variable is renamed to reportedPackageName = asSystemService ? mContext.getPackageName() : packageName. The call to mediaButtonReceiverHolder.send now passes MediaSessionService.this, the raw packageName, pid, uid, reportedPackageName, resultCode, mKeyEventReceiver and mHandler. It no longer passes MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs()."
     ],
     "completeness": [
      "Both send paths in MediaButtonReceiverHolder.send that carried the options bundle are changed: the PendingIntent path and the legacy default sendBroadcastAsUser path. The options variable is removed, so no BAL grant from it remains in this method.",
      "FGS allowlisting now goes through MediaSessionService.tempAllowlistTargetPkgIfPossible. Its body is not in the diff, so the material does not show which conditions it applies.",
      "The session.sendMediaButton path in MediaSessionService is not touched by this fix. The material does not show whether it attaches comparable options. The non-default cases of the restored-intent switch are not changed by the diff and cannot reference the removed options variable.",
      "If getPackageUidAsUser throws NameNotFoundException, the send still goes ahead without any allowlisting."
     ],
     "behavior_changes": [
      "Media button receivers no longer get a background-activity-start grant when MediaButtonReceiverHolder delivers the key event via PendingIntent or broadcast.",
      "FGS temporary allowlisting is no longer applied unconditionally via BroadcastOptions with the MediaSessionDeviceConfig duration. It is now delegated to MediaSessionService.tempAllowlistTargetPkgIfPossible with the caller's pid/uid/package, so the result depends on that method's checks.",
      "PendingIntent.send is called without the requiredPermission/options overload, and the legacy broadcast is sent without an options bundle.",
      "A debug log is emitted (under DEBUG_KEY_EVENT) when the receiver package cannot be resolved for the holder's user.",
      "MediaButtonReceiverHolder.send has a new, incompatible signature."
     ],
     "preconditions": [
      "A media key event is dispatched through MediaSessionService and no MediaSessionRecord handles it. The code then falls through to a MediaButtonReceiverHolder (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder, or a PendingIntent from mCustomMediaKeyDispatcher.getMediaButtonReceiver), per the code context around MediaSessionService lines 2270-2317."
     ],
     "per_major": {
      "14": "same fix: commit 7dcb4043f5d7 in android-security-14.0.0_r24, a manual cherry-pick of ag/33211293 (Change-Id I374a2f3a4acaac03e90ac9f9bbfccd47ffc1f4cf). The MediaSessionService hunk is at line ~2311.",
      "15": "same fix: commit 739ece18a63a in android-security-15.0.0_r12, identical diff content. The MediaSessionService hunk is at line ~2640.",
      "16": "same fix: commit e707f6600330 in android-security-16.0.0_r3, identical diff content. The MediaSessionService hunk is at line ~2677.",
      "17": "not affected (the bulletin does not list Android 17). At android-security-17.0.0_r1, 5/8 lines of the patched region are present. The material has no 17 commit."
     },
     "backport_notes": [
      "The diff does not add MediaSessionService.tempAllowlistTargetPkgIfPossible(int, String, int, int, String, String), so it must already exist in the target branch. Any other callers of MediaButtonReceiverHolder.send must be updated to the new 10-argument form. The bulletin lists Android 13 as affected, but no 13 commit is in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The implementation of MediaSessionService.tempAllowlistTargetPkgIfPossible and what conditions it checks before allowlisting.",
      "The Android 13 fix commit (the bulletin lists 13 as affected, but no diff is provided).",
      "Whether MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs is still used elsewhere after this change.",
      "Whether the session.sendMediaButton path or the non-default restored-intent cases use any other BroadcastOptions."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48573": {
     "cve": "CVE-2025-48573",
     "summary": "Removes the temporary allowlist grant (tempAllowlistTargetPkgIfPossible) that system_server made to the media session owner whenever a controller invoked MediaController.sendCommand, so this call no longer propagates while-in-use / background-FGS-launch privileges.",
     "component": "platform/frameworks/base, MediaSessionService (services/core/java/com/android/server/media/MediaSessionRecord.java)",
     "weakness": "Improper privilege propagation: an IPC call meant only for exchanging information (sendCommand) triggered a temporary allowlist grant (WIU/BFSL) to another app (CWE-269 style improper privilege management)",
     "root_cause": "In MediaSessionRecord's session callback wrapper, sendCommand(packageName, pid, uid, command, args, cb) called mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), pid, uid, packageName, reason) before forwarding the command to the session's callback (mCb.onCommand). This temporarily allowlists the session-owning package (getUid()/getPackageName()) on behalf of the calling controller (pid/uid/packageName). Per the commit message, this propagates while-in-use (WIU) permissions and background foreground-service-launch (BFSL) capability. The commit states that sendCommand is only meant for exchanging technical information and should not be a way to grant another app these privileges. The same removed lines appear in the Android 14, 15 and 16 diffs.",
     "fix_analysis": [
      "services/core/java/com/android/server/media/MediaSessionRecord.java : sendCommand (session callback wrapper inside MediaSessionRecord): deletes the three lines that build the reason string (TAG + \":\" + command) and call mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), pid, uid, packageName, reason). The method now only forwards to mCb.onCommand(packageName, pid, uid, command, args, cb) inside the existing try/catch for RemoteException.",
      "Single-commit fix. It adds no new checks; it removes the privilege grant from this path."
     ],
     "completeness": [
      "Covers exactly one entry point: the sendCommand path of the session callback wrapper. Forwarding of the command itself is unchanged, so the session app still receives the command; the only difference is the missing allowlist grant.",
      "Sibling methods in the same class still call mService.tempAllowlistTargetPkgIfPossible: sendMediaButton (only when KeyEvent.isMediaSessionKey is true), sendCustomAction (reason TAG + \":custom-\" + action) and prepare (reason TAG + \":prepare\"), per the code context at android-security-14.0.0_r29. The commit message does not mention these methods, and this fix does not change them.",
      "The internal eligibility logic of tempAllowlistTargetPkgIfPossible is not shown in the material and is not modified."
     ],
     "behavior_changes": [
      "A session-owning app no longer receives a temporary allowlist entry as a side effect of a controller calling MediaController.sendCommand. Per the commit message, this entry covered while-in-use permissions and permission to start a foreground service. Apps that depended on this grant may lose the ability to start a foreground service from the background in this flow; the exact effect depends on tempAllowlistTargetPkgIfPossible, which is not shown.",
      "No new exception, error code or API surface change. The command is still delivered to the session callback."
     ],
     "preconditions": [
      "A caller holds a MediaController bound to a target MediaSession and invokes MediaController.sendCommand. Per index search, frameworks/base/media/java/android/media/session/MediaController.java calls mSessionBinder.sendCommand, which reaches the MediaSessionRecord sendCommand wrapper in system_server.",
      "The allowlist target is the session owner (getUid()/getPackageName()), and the grant is made on behalf of the calling pid/uid/packageName. Whatever conditions tempAllowlistTargetPkgIfPossible imposes on the caller are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 21481e44fa92 in android-security-14.0.0_r24 removes the 3-line allowlist call from sendCommand (the catch block uses Log.e). At android-security-14.0.0_r29 the code context shows sendCommand without the call (7/8 lines of the patched region present).",
      "15": "same fix: commit c88cb354b7fa in android-security-15.0.0_r12, the same 3-line removal (the class extends MediaSessionRecordImpl and the catch block uses Slog.e). 7/8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "same fix: commit 039030a6b0e7 in android-security-16.0.0_r3, the same 3-line removal. 7/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 6/8 lines of the patched region are present. The material does not show the exact code."
     },
     "backport_notes": [
      "The change is a pure 3-line deletion inside the sendCommand wrapper of MediaSessionRecord.java. It applies to any branch that has the tempAllowlistTargetPkgIfPossible call there. The only differences across majors are cosmetic: Log.e on 14 versus Slog.e on 15/16, and the class signature extending MediaSessionRecordImpl on 15/16."
     ],
     "confidence": "high",
     "unknowns": [
      "The implementation of MediaSessionService.tempAllowlistTargetPkgIfPossible (what caller state it requires and exactly what it grants) is not in the material.",
      "Whether Android 13 (listed as affected by the bulletin) received a corresponding commit is not shown.",
      "The material does not say whether Google intentionally kept the allowlist grant in sendCustomAction, prepare and sendMediaButton."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48575": {
     "cve": "CVE-2025-48575",
     "summary": "CertInstaller now trusts a \"called by Settings\" referrer for the CA certificate installation path only if the package named com.android.settings resolves to the system uid. Before, it relied on the referrer package-name string alone.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA certificate installation flow)",
     "weakness": "Improper authorization based on an unverified package name (missing caller identity / uid check)",
     "root_cause": "CredentialHelper.calledBySettings() returned true whenever mReferrer was string-equal to \"com.android.settings\". CertInstaller used this in two places to choose the privileged InstallOthersAction / installCertificateOrShowNameDialog() path. Nothing checked that the package with that name was really the system Settings app. Per the commit comment, some systems have no preinstalled com.android.settings, so an impostor package could take that name, be treated as Settings, and install CA certificates through CertInstaller.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java: adds a private isCalledBySettings(). It first requires SETTINGS_PACKAGE.equals(mCredentials.getReferrer()). It then reads getPackageManager().getApplicationInfo(\"com.android.settings\", 0).uid and returns UserHandle.isSameApp(settingsUid, Process.SYSTEM_UID). If the package is not found (NameNotFoundException), it logs an error and returns false.",
      "src/com/android/certinstaller/CertInstaller.java: the two call sites that used mCredentials.calledBySettings() now call isCalledBySettings(). These are the branch that runs new InstallOthersAction().run(this), and the success branch (after removeDialog(PROGRESS_BAR_DIALOG) / removeDialog(PKCS12_PASSWORD_DIALOG)) that calls installCertificateOrShowNameDialog() when validCertificateSelected() is true.",
      "src/com/android/certinstaller/CredentialHelper.java: removes the SETTINGS_PACKAGE constant and the calledBySettings() method, so the name-only check no longer exists."
     ],
     "completeness": [
      "Both uses of the Settings-trust decision in CertInstaller.java are converted, and the old name-only helper is deleted from CredentialHelper.java.",
      "The new check verifies the uid of whatever package is installed under the name com.android.settings (it must share the system app id). It does not verify the uid of the actual calling process. It closes the case the commit comment describes: no system Settings is installed and an impostor holds that package name. The material does not show whether a caller can set the referrer string (mCredentials.getReferrer()) independently of its real package identity, so that case cannot be judged from the code provided.",
      "If com.android.settings is absent, the privileged path is now unreachable (the check returns false) instead of being decided by name alone."
     ],
     "behavior_changes": [
      "On devices where no package named com.android.settings exists, or where it does not have the system uid, CertInstaller no longer takes the Settings-only install path. It falls to the non-Settings branch instead.",
      "A new error log \"Unable to find info for settings package\" is written when the Settings package cannot be resolved.",
      "Each decision now does one extra PackageManager.getApplicationInfo lookup."
     ],
     "preconditions": [
      "The CertInstaller activity is started with credentials whose referrer is the string \"com.android.settings\". How mReferrer is populated is not shown in the material.",
      "Per the commit comment, the vulnerable condition is a system where com.android.settings is not preinstalled under the system uid, so another package can hold that name.",
      "The flow reaches either the branch that runs InstallOthersAction or the post-extraction success branch that calls installCertificateOrShowNameDialog()."
     ],
     "per_major": {
      "14": "same fix: 17c124efb1f1 in android-security-14.0.0_r24 (cherry-pick, Change-Id I3779b0d38dafee2333716113f6d5f4de78529380); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: b24bef75f325 in android-security-15.0.0_r12. The CertInstaller.java hunks are identical to Android 14 (570c400..883c897), while the CredentialHelper.java base blob differs (bc1a141); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: d688ebdbfd40 in android-security-16.0.0_r3, with hunks identical to Android 15; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix is a single commit touching two files in packages/apps/CertInstaller. The new code uses CredentialHelper.getReferrer(), UserHandle.isSameApp and Process.SYSTEM_UID. The CertInstaller.java hunks are identical across 14, 15 and 16. The CredentialHelper.java base blob differs between 14 (eab58f5) and 15/16 (bc1a141), so check context on customized trees."
     ],
     "confidence": "high",
     "unknowns": [
      "How CredentialHelper.mReferrer is populated (for example from the launching intent or from the activity's referrer) is not shown in the material.",
      "What the non-Settings fallback branch does when isCalledBySettings() is false is not shown.",
      "Whether Android 13 (listed as affected by the bulletin) received the same change is not covered by the per-major facts.",
      "Whether a clean apply works on trees other than the listed security tags is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48576": {
     "cve": "CVE-2025-48576",
     "summary": "Removes the unused, AIDL-only Binder method updateNotificationChannelGroupFromPrivilegedListener from INotificationManager and its NotificationManagerService implementation, eliminating a system_server entry point that created and persisted a notification channel group on behalf of a listener.",
     "component": "platform/frameworks/base, NotificationManagerService (notification listener privileged channel APIs)",
     "weakness": "unknown",
     "root_cause": "NotificationManagerService exposed updateNotificationChannelGroupFromPrivilegedListener(token, pkg, user, group) over INotificationManager. The implementation did Objects.requireNonNull(user), verifyPrivilegedListener(token, user, false), then called createNotificationChannelGroup(pkg, getUidForPackageAndUser(pkg, user), group, false, true) and handleSavePolicyFile(). Per the commit message the method was unpublished (AIDL only) and never used. The adjacent privileged-listener methods null-check pkg (and channel), and updateNotificationChannelFromPrivilegedListener also calls verifyPrivilegedListenerUriPermission; the removed method did neither. The material does not show which specific behavior produced the bulletin's DoS classification.",
     "fix_analysis": [
      "core/java/android/app/INotificationManager.aidl: the declaration of updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group) is deleted, so the Binder method no longer exists in the interface.",
      "services/core/java/com/android/server/notification/NotificationManagerService.java: the @Override implementation of updateNotificationChannelGroupFromPrivilegedListener (requireNonNull(user), verifyPrivilegedListener, createNotificationChannelGroup(..., false, true), handleSavePolicyFile()) is deleted from the binder service implementation inside NotificationManagerService.",
      "No validation is added elsewhere; the fix is pure removal of the unused entry point (commit: 'Flag: EXEMPT Code deletion')."
     ],
     "completeness": [
      "The entire method is removed from both the interface and the service, so there is no residual path through this entry point.",
      "The sibling methods shown in context (updateNotificationChannelFromPrivilegedListener, getNotificationChannelsFromPrivilegedListener, getNotificationChannelGroupsFromPrivilegedListener) are untouched; they requireNonNull pkg (and channel) and call verifyPrivilegedListener.",
      "Other callers of createNotificationChannelGroup are not shown in the material; the fix does not change createNotificationChannelGroup itself."
     ],
     "behavior_changes": [
      "INotificationManager no longer has updateNotificationChannelGroupFromPrivilegedListener; the method cannot be invoked through the interface.",
      "Removing an AIDL method renumbers the transaction codes of all methods declared after it in INotificationManager.aidl, so out-of-tree code using raw transaction codes against this interface would be affected (general platform knowledge).",
      "No change to NotificationListenerService public API, since the method was never used from NLS per the commit message."
     ],
     "preconditions": [
      "Caller must invoke the INotificationManager Binder interface directly (the method was AIDL only, per commit message).",
      "Caller must supply an INotificationListener token that passes verifyPrivilegedListener(token, user, false); which listeners qualify is not shown.",
      "A non-null UserHandle must be passed; pkg and group were not null-checked by the removed method."
     ],
     "per_major": {
      "14": "same fix: commit 7b28c9436fff in android-security-14.0.0_r24 deletes the AIDL line and the service method; at r29 5/8 lines of the patched region are present, consistent with the deleted lines being gone.",
      "15": "same fix: commit 345b6f2a6900 in android-security-15.0.0_r12, identical deletion (AIDL context differs by setInterruptionFilter having a fromUser parameter); 5/8 region lines present at r17.",
      "16": "same fix: commit b812baa1463c in android-security-16.0.0_r3, identical deletion (AIDL context additionally contains createConversationNotificationChannelForPackageFromPrivilegedListener above the removed line); 5/8 region lines present at r8.",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1 5/8 lines of the patched region are present, the same state as the fixed branches."
     },
     "backport_notes": [
      "Pure deletion with no dependencies; apply the AIDL and service removals together so the build stays consistent. Grep the tree (including vendor/OEM code) for updateNotificationChannelGroupFromPrivilegedListener before backporting, since removal also shifts AIDL transaction codes for later INotificationManager methods (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which behavior of the removed method produced the DoS is not shown; the DoS classification comes only from the bulletin. Missing pkg/group validation is a possibility, not shown.",
      "The exact failure point inside createNotificationChannelGroup / getUidForPackageAndUser for invalid arguments is not shown.",
      "Semantics of verifyPrivilegedListener's boolean argument and which listeners qualify are not shown in the material.",
      "How a stale caller using the old transaction code behaves after removal is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48580": {
     "cve": "CVE-2025-48580",
     "summary": "MediaBrowser no longer always binds to a MediaBrowserService with Context.BIND_INCLUDE_CAPABILITIES; the flag is dropped when the root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing key BrowserRoot.EXTRA_RECENT.",
     "component": "platform/frameworks/base, media framework (android.media.browse.MediaBrowser / android.service.media.MediaBrowserService)",
     "weakness": "Incorrect privilege assignment: capability propagation (BIND_INCLUDE_CAPABILITIES) from the MediaBrowser client process to the bound MediaBrowserService on every connection",
     "root_cause": "In media/java/android/media/browse/MediaBrowser.java the bind made during connection was hard-coded as mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES). BIND_INCLUDE_CAPABILITIES makes the bound service inherit the binding process's capabilities (general platform knowledge). Because the flag was always set, every MediaBrowser connection passed the client's capabilities to the targeted MediaBrowserService, and the client had no way to opt out. This included the one-off EXTRA_RECENT query of recent playback described in the commit message.",
     "fix_analysis": [
      "media/java/android/service/media/MediaBrowserService.java, class MediaBrowserService.BrowserRoot: adds the @hide constant EXTRA_EXCLUDE_CAPABILITIES = \"android.service.media.extra.EXCLUDE_CAPABILITIES\". It is a root-hint key meaning the bind connection should not include capabilities. Its Javadoc states the key is assumed to be set whenever EXTRA_RECENT is used.",
      "media/java/android/media/browse/MediaBrowser.java, bindService call inside class MediaBrowser (the enclosing method is not shown in the hunk): the fixed flag set is replaced by a computed int, bindServiceFlags = Context.BIND_AUTO_CREATE. BIND_INCLUDE_CAPABILITIES is added only if mRootHints == null, or if mRootHints contains neither BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES nor BrowserRoot.EXTRA_RECENT. The commit also adds the import android.service.media.MediaBrowserService.BrowserRoot.",
      "Net effect: connections using EXTRA_RECENT, and connections that explicitly carry EXTRA_EXCLUDE_CAPABILITIES, bind without capability propagation. All other connections behave as before."
     ],
     "completeness": [
      "Covers only the single bindService call in MediaBrowser.java. The fix is an opt-out: the default case (null root hints, or hints without either key) still binds with BIND_INCLUDE_CAPABILITIES.",
      "EXTRA_RECENT is the only existing hint that now disables capability propagation implicitly. The EXTRA_OFFLINE and EXTRA_SUGGESTED root hints, defined in the same BrowserRoot class, do not exclude capabilities.",
      "The check uses containsKey only and does not read the value stored under EXTRA_EXCLUDE_CAPABILITIES or EXTRA_RECENT. A key present with the value false still disables the flag.",
      "EXTRA_EXCLUDE_CAPABILITIES is @hide. The material does not show any platform client being updated to pass it, so clients that do not use EXTRA_RECENT are protected only if they adopt the key.",
      "Nothing changes on the MediaBrowserService side. The restriction is entirely client-side."
     ],
     "behavior_changes": [
      "MediaBrowser connections whose root hints contain EXTRA_RECENT now bind with BIND_AUTO_CREATE only, so the MediaBrowserService reached that way no longer inherits the client's capabilities.",
      "The new hidden root-hint key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES lets a client explicitly ask for a bind without BIND_INCLUDE_CAPABILITIES.",
      "No new error paths or exceptions; the only change is how the bind flags are computed."
     ],
     "preconditions": [
      "A process creates a MediaBrowser for a MediaBrowserService component (mServiceComponent) and connects, so the bindService path in MediaBrowser.java runs.",
      "Before the fix, every such connection passed on the client's capabilities. The commit message names connections that use the EXTRA_RECENT root hint (recent-playback query) as the case that should always be a short-lived request without propagation."
     ],
     "per_major": {
      "14": "same fix: commit 874eaecb47b8 in android-security-14.0.0_r24 (manual cherry-pick of ag/33253388, Change-Id Idc9b428f2ff53cb490ee596da35d24e1e7fca681). All 8/8 patched lines are present at android-security-14.0.0_r29. The MediaBrowserService.java hunk lands near line 872.",
      "15": "same fix: commit 2588609ffab9 in android-security-15.0.0_r12, with the same diff content. The MediaBrowserService.java hunk sits near line 608 because the file layout differs. All 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix: commit eb19b27ed8ab in android-security-16.0.0_r3, with the same diff content as 15. All 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Self-contained two-file change. It depends only on the existing mRootHints field in MediaBrowser and the BrowserRoot inner class. Anchor the MediaBrowserService.java hunk on the EXTRA_SUGGESTED constant, because its line number differs between 14 (872) and 15/16 (608).",
      "A platform client that must not pass on capabilities but does not use EXTRA_RECENT needs a separate change to put BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES in its root hints. This commit includes no such client change."
     ],
     "confidence": "high",
     "unknowns": [
      "Which client and which capabilities were involved in the reported issue (bug 393582077 is not included in the material).",
      "Whether companion changes adopting EXTRA_EXCLUDE_CAPABILITIES (for example in SystemUI or other MediaBrowser users) shipped in the same release.",
      "The exact enclosing method of the modified bindService call; the hunk header shows only the class MediaBrowser.",
      "How the target service component is chosen, and which services can be reached this way."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48583": {
     "cve": "CVE-2025-48583",
     "summary": "Once a bundle's lazy values have all been consumed, or the bundle is cleared, BaseBundle no longer puts its backing Parcel back into the Parcel pool with recycle(). Instead it calls Parcel.destroy(), which is now exposed as a public @hide method, so these Parcel instances are never reused.",
     "component": "platform/frameworks/base, core/java/android/os (Bundle / Parcel lazy-value handling)",
     "weakness": "Use-after-free through object pool reuse. A Parcel instance that had backed lazy values was recycled into the Parcel pool, which the commit message says may lead to reuse of these instances and to several UAF issues.",
     "root_cause": "In core/java/android/os/BaseBundle.java, if a bundle owned its lazy values (mOwnsLazyValues), BaseBundle called recycleParcel(), which calls Parcel.recycle(), in two places: when the lazy-value refcount mLazyValues reached zero in unwrapLazyValueFromMapLocked, and in clear(). recycle() puts the Parcel object back into the static sOwnedPool/sHolderPool, so a later obtain() can hand the same instance out again. The bundle only held the parcel through a WeakReference (mWeakParcelledData). According to the commit message, recycling the parcel once all lazy values are consumed may lead to several UAF issues. The material does not show which remaining references see the reused instance. On Android 14, the code also asserted with Preconditions.checkState that the weak reference was still non-null when the refcount hit zero (\"Parcel recycled earlier than expected\").",
     "fix_analysis": [
      "core/java/android/os/Parcel.java: destroy() goes from private to public (@hide) so that BaseBundle can call it. The visible part of destroy() calls resetSqaushingState() and then handles the native pointer depending on mOwnsNativeParcelObject. According to the commit message, BaseBundle now destroys the dangling parcel and never calls recycle on it, so these instances cannot be reused.",
      "core/java/android/os/Parcel.java: the line 'mClassCookies = null' moves out of recycle() into the buffer-freeing helper that calls nativeFreeBuffer and resets mReadWriteHelper. That helper is presumably freeBuffer(), which recycle() still calls.",
      "core/java/android/os/BaseBundle.java:unwrapLazyValueFromMapLocked: when mOwnsLazyValues is set and mLazyValues reaches 0, the code reads 'Parcel parcel = mWeakParcelledData.get()'. Only if parcel is non-null does it call parcel.destroy() and set mWeakParcelledData to null. On Android 14 this change also removes the Preconditions.checkState(mWeakParcelledData.get() != null, ...) assertion. On 15 and 16 the null-check form is already present as context.",
      "core/java/android/os/BaseBundle.java:initializeFromParcelLocked: when the bundle owns the parcel and numLazyValues[0] == 0, it now calls parcelledData.recycle() directly, with the comment 'No lazy value, we can directly recycle this parcel'. The empty-parcel case already returns early at the top of the method.",
      "core/java/android/os/BaseBundle.java:clear(): if mOwnsLazyValues is set and mWeakParcelledData is non-null, the parcel is destroy()ed (if still reachable) instead of recycled.",
      "core/java/android/os/BaseBundle.java: the private static helper recycleParcel(Parcel) is deleted.",
      "core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java: the lazy-value tests now count destroy() calls instead of recycle() calls. The no-lazy test bundleClear_whenUnparcelledWithoutLazy_recyclesParcelOnce still checks recycle()."
     ],
     "completeness": [
      "The fix covers both BaseBundle paths that used to recycle a parcel after lazy values existed: the refcount reaching zero in unwrapLazyValueFromMapLocked, and clear(). After the fix, those paths destroy the parcel instead of putting it back in the pool.",
      "The no-lazy-values path in initializeFromParcelLocked still recycles the parcel. The commit treats this as safe ('No lazy value, we can directly recycle this parcel').",
      "Shared or copied bundles (mOwnsLazyValues false) still neither destroy nor recycle the parcel, as the *_doesNotDestroyParcel tests check.",
      "The material does not show whether code outside BaseBundle recycles Parcels that lazy values still refer to."
     ],
     "behavior_changes": [
      "Parcels that backed bundles with lazy values are no longer returned to the Parcel pool. They are destroyed when the last lazy value is consumed or the bundle is cleared.",
      "Parcel.destroy() becomes a public @hide method.",
      "mClassCookies is now cleared in the buffer-freeing helper instead of directly in recycle(). recycle() still reaches it through freeBuffer().",
      "Android 14 only: the Preconditions.checkState assertion (\"Parcel recycled earlier than expected\") at refcount zero is gone. A weak reference that has already been cleared is now skipped."
     ],
     "preconditions": [
      "A Bundle is initialized from a Parcel it owns (ownsParcel true in initializeFromParcelLocked) and holds at least one lazily-unparcelled value, so mWeakParcelledData is set. The recycle and destroy paths in unwrapLazyValueFromMapLocked and clear() additionally require mOwnsLazyValues to be true.",
      "Then either all lazy values are unwrapped (mLazyValues reaches 0 in unwrapLazyValueFromMapLocked via getValueAt), or clear() is called."
     ],
     "per_major": {
      "14": "Adapted fix: commit 401cc81d7d94 in android-security-14.0.0_r24. Besides the shared changes, it replaces the Preconditions.checkState null assertion in unwrapLazyValueFromMapLocked with a plain null check, a form already present on 15 and 16. The per-major facts report 4/8 patched-region lines present at android-security-14.0.0_r29.",
      "15": "Same fix: commit 26f25bbc2ae8 in android-security-15.0.0_r12, with the same BaseBundle, Parcel and test changes. The null-check form was already present. 6/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 02751bc65824 in android-security-16.0.0_r3, with the same changes. On this branch BaseBundle implements Parcel.ClassLoaderProvider, which only shows up as diff context. 6/8 lines present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. The per-major facts show 4/8 patched-region lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Make destroy() in Parcel.java public (@hide) before or together with the BaseBundle change, because BaseBundle now calls parcel.destroy(). On Android 14, also bring in the 'Parcel parcel = mWeakParcelledData.get(); if (mLazyValues == 0 && parcel != null)' restructuring, and remove recycleParcel() together with all of its callers.",
      "The material does not explain the partial line-match figures (4/8 and 6/8). To confirm the patched state, check that BaseBundle has no recycleParcel() and calls parcel.destroy() in both unwrapLazyValueFromMapLocked and clear()."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the concrete UAF scenario(s) behind bugs 377704076 and 381885240, that is, which other holder of the pooled Parcel saw it reused.",
      "The material does not show the attack surface, for example whether the triggering bundle content can come from another app over IPC.",
      "The full body of Parcel.destroy() is not shown, including whether it calls freeBuffer() and so also clears mClassCookies.",
      "The material does not show whether any code outside BaseBundle recycles Parcels while lazy-value closures still refer to them.",
      "The material does not explain why Android 17 is not affected or what the code looks like there, beyond the 4/8 line-match figure."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48588": {
     "cve": "CVE-2025-48588",
     "summary": "Vpn.startAlwaysOnVpn() no longer resets the always-on VPN configuration (setAlwaysOnPackage(null, false, null)) when the always-on package is deemed unsupported while the device is in safe mode; per the code comment, the always-on VPN can then start after rebooting to normal mode.",
     "component": "platform/frameworks/base, system_server connectivity (services/core/java/com/android/server/connectivity/Vpn.java, always-on VPN)",
     "weakness": "Improper handling of an exceptional state (safe mode) causing a security setting to be reset; logic error, not a memory-safety issue",
     "root_cause": "In Vpn.startAlwaysOnVpn(), when isAlwaysOnPackageSupported(alwaysOnPackage) returned false the code unconditionally called setAlwaysOnPackage(null, false, null), removing the always-on VPN package (with lockdown false). The commit message states that in safe mode third-party VPN apps have limited functionality and can be deemed unsupported for always-on use, so booting into safe mode caused the always-on VPN configuration to be removed rather than merely not started for that boot. The Android 15 test (testStartAlwaysOnVpnOnSafeMode) models this by making the VPN profile store and queryIntentServicesAsUser return null while isSafeMode() returns true, and asserts the always-on package survives.",
     "fix_analysis": [
      "services/core/java/com/android/server/connectivity/Vpn.java:startAlwaysOnVpn(): inside the existing `if (!isAlwaysOnPackageSupported(alwaysOnPackage))` branch, the call setAlwaysOnPackage(null, false, null) is now wrapped in `if (!mContext.getPackageManager().isSafeMode())`. In safe mode the setting is left intact; the method still returns false so no VPN is started. Outside safe mode behavior is unchanged.",
      "Android 15 commit fccd4c6bc347 additionally adds services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java:testStartAlwaysOnVpnOnSafeMode, which sets an always-on package, simulates safe mode (isSafeMode() true, profile store and queryIntentServicesAsUser returning null), calls startAlwaysOnVpn() and asserts it returns false while getAlwaysOnPackage() still equals TEST_VPN_PKG. The Android 14 commit da768ae60459 contains only the Vpn.java change (commit message says Test: manual, VpnTest)."
     ],
     "completeness": [
      "Only the startAlwaysOnVpn() path is guarded. The fix does not change isAlwaysOnPackageSupported() itself; in safe mode the package is still reported unsupported and the always-on VPN is not started for that boot.",
      "Any other code path that resets the always-on VPN configuration is not touched by this diff; the material shows no such path, so whether others exist is unknown.",
      "The guard relies on PackageManager.isSafeMode(); no other restricted-boot condition is considered."
     ],
     "behavior_changes": [
      "In safe mode, an always-on VPN package that fails isAlwaysOnPackageSupported() no longer causes setAlwaysOnPackage(null, false, null) to be called; startAlwaysOnVpn() still returns false (VPN not started).",
      "getAlwaysOnPackage() still returns the configured package afterwards (as asserted by the A15 test), so per the code comment the always-on VPN can start after rebooting to normal mode.",
      "No change to behavior outside safe mode."
     ],
     "preconditions": [
      "An always-on VPN package is configured for the user (getAlwaysOnPackage() non-null in startAlwaysOnVpn()).",
      "The device is running in safe mode (PackageManager.isSafeMode() true), where the configured third-party VPN package is deemed unsupported by isAlwaysOnPackageSupported().",
      "Vpn.startAlwaysOnVpn() is invoked in that state; the caller is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit da768ae60459 in android-security-14.0.0_r24 (Vpn.java only, no test file); at android-security-14.0.0_r29 the patched region is fully present (4/4 lines).",
      "15": "same fix plus test: commit fccd4c6bc347 in android-security-15.0.0_r12 changes Vpn.java identically (hunk at ~1109) and adds VpnTest.testStartAlwaysOnVpnOnSafeMode; at android-security-15.0.0_r17 7/7 lines present.",
      "16": "not affected per the bulletin (not listed); at android-security-16.0.0_r8 the patched region is present (4/4 lines).",
      "17": "not affected per the bulletin (not listed); at android-security-17.0.0_r1 the patched region is present (4/4 lines)."
     },
     "backport_notes": [
      "Single-file, self-contained change in Vpn.java:startAlwaysOnVpn() (frameworks/base services/core); only the line offset differs between 14 (~1252) and 15 (~1109). The A15 VpnTest addition depends on existing test mocks (mPackageManager, mVpnProfileStore) and the setAndVerifyAlwaysOnPackage helper, whose definitions are not shown; it can be omitted as Google did for A14."
     ],
     "confidence": "high",
     "unknowns": [
      "Who calls startAlwaysOnVpn() and how the device ends up in safe mode in the EoP scenario; the material does not show the trigger.",
      "How setAlwaysOnPackage(null, false, null) persists the reset (e.g. which Settings keys are written); not shown.",
      "Whether other paths exist that reset the always-on VPN setting under restricted conditions.",
      "Why the bulletin lists 16-qpr2-next as affected while the per-major facts treat Android 16 as not listed; the code at android-security-16.0.0_r8 already contains the fix."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48589": {
     "cve": "CVE-2025-48589",
     "summary": "SystemUI's HeaderPrivacyIconsController now filters the PermissionGroupUsage list it sends to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS, EXTRA_PERMISSION_USAGES). Only usages belonging to the current user and its profiles, plus phone-call usages, are included. Before the fix, the unfiltered list from PermissionManager.getIndicatorAppOpUsageData() was sent.",
     "component": "platform/frameworks/base, SystemUI quick settings privacy indicator (packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt)",
     "weakness": "Missing user boundary check: usage data was passed to another component without filtering by the current user's profile group",
     "root_cause": "In HeaderPrivacyIconsController.kt, permGroupUsage() returned permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) unchanged. showSafetyCenter() then put that whole list into the ACTION_VIEW_SAFETY_CENTER_QS intent as EXTRA_PERMISSION_USAGES. Nothing compared UserHandle.getUserId(usage.uid) with the current user's profiles. As the commit message and the added tests imply, usages belonging to users outside the current profile group could therefore be sent to Safety Center when the mic/camera indicator was clicked.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt: the constructor gains an injected `userTracker: UserTracker` parameter (com.android.systemui.settings.UserTracker).",
      "HeaderPrivacyIconsController.permGroupUsage(): builds an ArrayList from permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) and reads userTracker.userProfiles once. For each usage it computes UserHandle.getUserId(usage.uid). A usage is kept if usage.isPhoneCall is true or its userId matches the id of any entry in userProfiles. Otherwise it goes into invalidUserUsages, which is removed with usages.removeAll(invalidUserUsages). The return type changes from List to ArrayList, and the function changes from private to public (the new tests call it).",
      "HeaderPrivacyIconsController.showSafetyCenter(): uses the filtered ArrayList directly (`val usage = permGroupUsage()`) both for privacyLogger.logUnfilteredPermGroupUsage(usage) and for the EXTRA_PERMISSION_USAGES extra.",
      "packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt: adds a mocked UserTracker to the constructor call and three tests: testPermGroupUsage_filtersOutInactiveUsers, testPermGroupUsage_alwaysReturnPhoneCallUsage and testPermGroupUsage_returnsProfileUsages."
     ],
     "completeness": [
      "Filtering is applied in permGroupUsage(), before showSafetyCenter() sends the data out of SystemUI. Per index search, no other callers of showSafetyCenter() or the constructor were found, but that search may be incomplete.",
      "Phone-call usages (usage.isPhoneCall) are deliberately exempt from the user filter and are forwarded whichever user owns them. testPermGroupUsage_alwaysReturnPhoneCallUsage asserts this.",
      "The filter uses userTracker.userProfiles as read when permGroupUsage() runs. The diff adds no handling for a user switch that happens after that read.",
      "The PermissionManager.getIndicatorAppOpUsageData() API itself is not changed. The fix only filters on the consumer side, in SystemUI.",
      "privacyLogger.logUnfilteredPermGroupUsage() now receives the filtered list, so its name no longer matches its input."
     ],
     "behavior_changes": [
      "Safety Center launched from the QS header privacy chip now receives only usages for the current user and its profiles, plus any phone-call usage. Usages of other users are no longer in EXTRA_PERMISSION_USAGES.",
      "The privacy log entry written by logUnfilteredPermGroupUsage now shows the filtered list.",
      "HeaderPrivacyIconsController requires an injected UserTracker. permGroupUsage() is now public and returns ArrayList<PermissionGroupUsage>."
     ],
     "preconditions": [
      "A multi-user device where getIndicatorAppOpUsageData() reports a mic/camera usage for a user outside the current user's profile group.",
      "The current user taps the mic/camera privacy indicator in the QS/shade header, which reaches HeaderPrivacyIconsController.showSafetyCenter(). The exact condition that selects the Safety Center path is not shown in the diff."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base ec11eca03974 in android-security-14.0.0_r24, a RESTRICT AUTOMERGE cherry-pick with Change-Id I4c7ec006c839f331cd8bd6b7ac366382cf2c2f4d. The constructor has no featureFlags parameter, and the tests are under packages/SystemUI/tests. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: e086bd9b0fa8 in android-security-15.0.0_r12 (RESTRICT AUTOMERGE). The permGroupUsage()/showSafetyCenter() logic is identical. userTracker is added after the existing featureFlags constructor parameter, and the test's PermissionGroupUsage constructor call takes an extra trailing String argument. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Adapted fix: 2aeba76a58c1 in android-security-16.0.0_r3. Same logic; userTracker is added after shadeDialogContextInteractor in the constructor, and the tests are in packages/SystemUI/multivalentTests. 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Not affected (the bulletin does not list Android 17). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. Every manual instantiation of the controller (for example in tests) must add the UserTracker argument. The test helper createPermUsage calls a PermissionGroupUsage constructor with 9 arguments on 14 and 10 on 15/16, so match the target branch's constructor."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact condition that selects showSafetyCenter() over the privacy dialog path (not in the diff).",
      "Whether getIndicatorAppOpUsageData() returns usages for all users on the device or only some of them.",
      "Whether other SystemUI consumers of PermissionManager.getIndicatorAppOpUsageData() exist on these branches, and whether they filter by user.",
      "What Safety Center does with usages for other users on the receiving side (packages/modules/Permission code is not provided)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48590": {
     "cve": "CVE-2025-48590",
     "summary": "Removes the Q- app compat exemption in AppOpsService attribution tag validation. An attribution tag not declared by the package (or whose package is not found) is no longer forced to valid when SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE is disabled for the package or calling UID.",
     "component": "platform/frameworks/base, services/core AppOps (AppOpsService)",
     "weakness": "Validation bypass through a compatibility exemption: invalid attribution tags were treated as valid for callers with the compat change disabled. The bulletin classifies the impact as DoS, and the test named is AppOpMemoryUsageTest.",
     "root_cause": "In services/core/java/com/android/server/appop/AppOpsService.java, the attribution tag validation path builds an error message when the tag is not declared by the package or the package is not found. Before the fix it then queried mPlatformCompat.isChangeEnabledByPackageName(SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName, userId) and isChangeEnabledByUid(..., callingUid). If either returned false (the 'Q- apps' exemption named in the commit title), it set isAttributionTagValid = true and only logged an error. Invalid tags from such callers were therefore treated as valid.",
     "fix_analysis": [
      "services/core/java/com/android/server/appop/AppOpsService.java, attribution tag validation method (hunk at ~line 3937 on 14; method name not shown in the hunk header): the try/catch block that consulted mPlatformCompat for SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE and set isAttributionTagValid = true is deleted, and only Slog.e(TAG, msg) remains. isAttributionTagValid is therefore no longer overridden to true when the tag is undeclared or the package is not found.",
      "The now-unused static import of android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE is removed from AppOpsService.java. No other file is touched."
     ],
     "completeness": [
      "The fix covers the single place in AppOpsService.java where the compat exemption overrode the validation result.",
      "The diff does not show how callers consume isAttributionTagValid (whether an invalid tag is replaced, dropped, or rejected), so the material cannot confirm downstream handling; it only shows that the validation result is no longer forced to true.",
      "Any other users of SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE outside this file are not touched by this commit; the material does not show whether any exist."
     ],
     "behavior_changes": [
      "Apps for which SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE is disabled (by package name or UID) now have undeclared attribution tags, or tags for a package that is not found, treated as invalid; previously they were forced to valid.",
      "The Slog.e error log is still emitted in the same cases; the compat query and its RemoteException catch are gone.",
      "The hunk itself adds no new exception or error code; the effect on callers of an invalid result is not shown in the material."
     ],
     "preconditions": [
      "The caller must be an app for which the SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE compat change is disabled for its package or UID (the commit describes these as Q- apps).",
      "The caller supplies an attribution tag that reaches this AppOpsService validation path, and the tag is not declared by the package (or the package lookup fails).",
      "The exact AppOpsService entry points that lead to this method are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 0c46a5ec453b in android-security-14.0.0_r24 (import removal + deletion of the compat try/catch) at ~line 3937; 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: commit 529f24023e15 in android-security-15.0.0_r12, same change at ~line 4692; 7/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit 848f94492175 in android-security-16.0.0_r3, same change at ~line 5017; 7/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file change with no dependencies: delete the mPlatformCompat/SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE try/catch after the msg assignment, keep Slog.e(TAG, msg), and drop the static import if nothing else references the constant. Line offsets (3937 / 4692 / 5017) and the import-block context differ per branch; the method-body context is the same."
     ],
     "confidence": "medium",
     "unknowns": [
      "The name of the enclosing validation method and the public AppOpsService entry points that call it are not shown.",
      "How callers react to isAttributionTagValid == false is not shown, so the precise pre- and post-fix handling of an invalid tag is unknown.",
      "The resource exhaustion mechanism is only implied by the bulletin type (DoS) and the test name AppOpMemoryUsageTest; the affected data structure is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48591": {
     "cve": "CVE-2025-48591",
     "summary": "Adds a callingUser parameter to the IMms binder interface and makes MmsServiceBroker fill it from Binder.getCallingUserHandle(), so MmsService can check that the client-supplied contentUri belongs to the originating user.",
     "component": "platform/frameworks/base, MMS system service broker (services/core MmsServiceBroker, mms/java IMms.aidl and MmsManager)",
     "weakness": "Missing propagation of the caller's user ID, which is needed to validate a caller-supplied content URI across nested binder calls (cross-user data access, bulletin type ID)",
     "root_cause": "MmsServiceBroker in system_server takes sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft calls from apps and forwards them over IMms to MmsService. The forwarded call carried subId, callingPkg and the app-supplied contentUri but not the originating caller's user ID. MmsService therefore had nothing to check contentUri against for the originating user. The commit message says the goal is to 'Convey the originating user ID across the nested binder calls to MMSService to validate the contentURI is coming from the same userId'.",
     "fix_analysis": [
      "mms/java/com/android/internal/telephony/IMms.aidl: adds an 'in int callingUser' parameter to sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft, documented as 'user id of the calling app'. Other IMms methods are unchanged.",
      "services/core/java/com/android/server/MmsServiceBroker.java (BinderService): the four proxied methods accept the new callingUser argument but do not use it. When forwarding to getServiceGuarded(), they pass getCallingUserId() instead. This is a new private helper that returns Binder.getCallingUserHandle().getIdentifier(). The user ID therefore comes from the binder identity of the actual caller, so a client cannot supply a different one.",
      "services/core/java/com/android/server/MmsServiceBroker.java (mServiceStubForFailure): the four stub methods get the new signatures. Their behaviour is unchanged (they return an error PendingIntent or null).",
      "mms/java/android/telephony/MmsManager.java: sendMessage and downloadMessage pass UserHandle.USER_NULL as a placeholder callingUser. The broker overwrites it.",
      "The actual contentUri-vs-user check is done on the MmsService side (the IMms implementer), which is not part of this commit. This change only passes along the information that check needs."
     ],
     "completeness": [
      "Covers four IMms methods that take a caller-supplied contentUri: sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft.",
      "The broker takes the user ID from Binder.getCallingUserHandle(), so the placeholder value the client provides cannot influence it.",
      "Other IMms methods are not changed. deleteStoredMessage (callingPkg, messageUri) is shown taking a URI and gets no callingUser. The material does not show whether it or the other methods needed one.",
      "The enforcement itself (comparing callingUser with the owner of contentUri) happens in MmsService, which the material does not show. This commit alone adds no validation."
     ],
     "behavior_changes": [
      "The IMms binder transaction layout changes for four methods (one new int parameter), so the IMms clients (MmsManager and the broker) and the implementer (MmsService) must be updated together.",
      "MmsService receives the originating user ID for these four calls. Any rejection based on it would show up through the usual MMS failure paths. This commit introduces no new error codes."
     ],
     "preconditions": [
      "The caller reaches the broker through sendMessage or downloadMessage, where MmsServiceBroker enforces SEND_SMS or RECEIVE_MMS, or through importMultimediaMessage or addMultimediaMessageDraft. For those two, if the OP_WRITE_SMS app-op is not allowed (per the code comment, the caller is not the default SMS app), the broker returns a fake URI without forwarding.",
      "Multi-user device: per the commit message, the issue concerns a contentUri that does not come from the caller's user."
     ],
     "per_major": {
      "13": "Listed as affected by the bulletin. The material includes no Android 13 commit, so its status is unknown.",
      "14": "Same fix: platform/frameworks/base 1dd5a8353b99 in android-security-14.0.0_r24 (Change-Id I9beb19f1a94aae76e6cd48a72feeb6c1dc47cf5a). 7 of 8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/frameworks/base eba8dce96baa in android-security-15.0.0_r12. The diff is identical apart from line offsets in MmsServiceBroker.java. 7 of 8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected (not listed by the bulletin). 7 of 8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). 7 of 8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The AIDL signature change has to land together with the matching change in the IMms implementer (MmsService) that performs the user-ID check. The commit message says AIDL does not support method overloading, so there is no flag-guarded compatibility path. Google landed the same content on 14 and 15; only the line offsets in MmsServiceBroker.java differ."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not include the MmsService-side change that uses callingUser to validate contentUri, so the exact check and how it fails are unknown.",
      "MmsService's project location and how it is versioned against frameworks/base are not shown.",
      "The bulletin lists Android 13 as affected, but no Android 13 commit is provided.",
      "The material does not show whether other IMms methods that carry URIs (for example deleteStoredMessage) needed similar treatment."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48594": {
     "cve": "CVE-2025-48594",
     "summary": "The CompanionDeviceManager UID callback that drains revoked/pending associations now resolves every package sharing a UID via PackageManager.getPackagesForUid() and processes pending revoked associations for each of them, instead of only the single package name previously resolved for the UID.",
     "component": "platform/frameworks/base, services/companion (CompanionDeviceManagerService on 14; association/DisassociationProcessor on 15/16)",
     "weakness": "Incomplete revocation / cleanup: a UID shared by several packages (android:sharedUserId) was resolved to a single package name, so revoked associations of other packages in the same UID were not processed in that callback (role holder removal on 14, disassociation on 15/16).",
     "root_cause": "In the UID callback of CompanionDeviceManagerService (Android 14, hunk around line 1562) the code called getPackageNameByUid(uid) and then iterated getPendingRoleHolderRemovalAssociationsForUser(userId) looking only for associations whose getPackageName() equalled that single name. On 15/16 the same logic in DisassociationProcessor used mPackageManagerInternal.getNameForUid(uid) and only disassociated mAssociationStore.getRevokedAssociations(userId, packageName) for that one name. For a UID shared by several packages, revoked associations belonging to packages other than the resolved name were not matched and stayed pending.",
     "fix_analysis": [
      "services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java (Android 14): adds a PackageManager field mPackageManager initialised from context.getPackageManager() in the constructor.",
      "Same file, UID callback: replaces getPackageNameByUid(uid) with mPackageManager.getPackagesForUid(uid); returns early only if the array is null or empty.",
      "Same callback: wraps the existing loop over getPendingRoleHolderRemovalAssociationsForUser(userId) in an outer loop over all packageNames, so for each package the matching associations go through maybeRemoveRoleHolderForAssociation(association) and removeFromPendingRoleHolderRemoval(association); needToPersistStateForUser is set if any removal happened, as before.",
      "services/companion/java/com/android/server/companion/association/DisassociationProcessor.java (Android 15 and 16): same pattern, adds mPackageManager, replaces mPackageManagerInternal.getNameForUid(uid) with mPackageManager.getPackagesForUid(uid), and loops over all packages calling disassociate(association.getId()) (15) or disassociate(association.getId(), REASON_REVOKED) (16) for mAssociationStore.getRevokedAssociations(userId, packageName)."
     ],
     "completeness": [
      "The fix covers only the UID callback that drains revoked/pending associations; all packages of the UID are now processed in that path.",
      "The 14 variant keeps the existing behaviour that an association whose role holder could not be removed (maybeRemoveRoleHolderForAssociation returns false) stays pending for a later attempt.",
      "The diff does not touch other places that may map a UID to a single package name (the getPackageNameByUid helper is not removed or changed in the diff); the material does not show whether such other call sites exist.",
      "On 15/16 the trailing check mAssociationStore.getRevokedAssociations().isEmpty() is unchanged and now runs after all packages of the UID have been processed."
     ],
     "behavior_changes": [
      "The callback now uses PackageManager.getPackagesForUid() instead of a single-name lookup; a UID with no packages still returns early.",
      "For shared-UID apps, revoked associations of every package in the UID are now processed in one pass; on 14 this may trigger role holder removal and state persistence for more packages than before, on 15/16 more disassociate() calls.",
      "No new error codes or API surface; additional field mPackageManager in the service/processor."
     ],
     "preconditions": [
      "An app package that owns a companion association which has been revoked and is pending role holder removal (14) or recorded as revoked in the association store (15/16).",
      "That package shares its UID with at least one other package via android:sharedUserId, such that the single-name UID lookup did not match it.",
      "The UID callback must fire (per the surrounding context comment, removal happens once the package is no longer visible after revocation)."
     ],
     "per_major": {
      "14": "same fix: CompanionDeviceManagerService.java in android-security-14.0.0_r24 (cherry-picked from another change); 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "adapted fix: same logic applied to association/DisassociationProcessor.java (getNameForUid replaced by getPackagesForUid, loop calls disassociate(association.getId())) in android-security-15.0.0_r12; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: same as 15 with disassociate(association.getId(), REASON_REVOKED) in android-security-16.0.0_r3; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 6/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "On 14 the fix is applied in CompanionDeviceManagerService itself; it needs the new mPackageManager field from context.getPackageManager() and the nested loop, and leaves getPackageNameByUid untouched. PackageManager.getPackagesForUid() is a long-standing public API, so no new dependency is required (general platform knowledge).",
      "No fork comparison was provided in the material, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact enclosing method name of the patched callback on 14 is not shown in the diff hunk header.",
      "The implementation of getPackageNameByUid / getNameForUid for shared UIDs, and whether other call sites in the companion service still resolve a UID to a single package name.",
      "What the 6/8 line match at android-security-17.0.0_r1 means for the code on 17."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48596": {
     "cve": "CVE-2025-48596",
     "summary": "Parcel::appendFrom in libbinder now sets mDataSize to mDataPos only when the new position exceeds the current size, instead of unconditionally adding len to mDataSize.",
     "component": "platform/frameworks/native, libbinder (libs/binder/Parcel.cpp)",
     "weakness": "Incorrect size accounting leading to a data-size field that can exceed the actually written/allocated region (improper bounds tracking)",
     "root_cause": "In libs/binder/Parcel.cpp, Parcel::appendFrom copied len bytes into the destination at mDataPos, advanced mDataPos by len and then ran `mDataSize += len` unconditionally. If the destination's mDataPos was below mDataSize at call time, the write overlapped existing data and added fewer than len new bytes, but mDataSize still grew by the full len. The capacity check before the copy only compares mDataPos + len against mDataCapacity, so the inflated mDataSize could exceed both the valid data and the capacity checked for, leaving the Parcel with a size field that does not match its buffer.",
     "fix_analysis": [
      "libs/binder/Parcel.cpp : Parcel::appendFrom - the line `mDataSize += len;` after the memcpy and `mDataPos += len;` is replaced with `if (mDataPos > mDataSize) mDataSize = mDataPos;`, so mDataSize becomes max(old mDataSize, new mDataPos). mDataSize therefore cannot pass mDataPos + len unless it was already larger, and mDataPos + len is the value the preceding capacity check compares against.",
      "Single one-line commit; no other files, no change to the range checks against the source parcel, validateReadData, growData or the kernel-object (flat_binder_object) copying that follows."
     ],
     "completeness": [
      "In the part of appendFrom shown in the material, this is the only update to mDataSize. The new expression is correct for both the pure-append case (mDataPos == mDataSize before the call, same result as the old code) and the overwrite-in-the-middle case.",
      "The object-copying block after the fix (kernelFields / mObjects handling) is untouched by this commit, and the material shows only part of it. The material does not show whether it depends on the old mDataSize value.",
      "Other Parcel write paths (writeInplace, setDataSize, etc.) are not part of this change; the material only shows appendFrom."
     ],
     "behavior_changes": [
      "After appendFrom into a position below the current data size, dataSize() now reflects the real end of data (max of old size and new position) rather than an inflated value.",
      "No new error codes; return values of appendFrom are unchanged."
     ],
     "preconditions": [
      "Caller invokes Parcel::appendFrom on a destination Parcel whose mDataPos is less than its mDataSize (e.g. after setDataPosition to an earlier offset) with a non-zero len.",
      "appendFrom is reachable from Java via android_os_Parcel_appendFrom in frameworks/base/core/jni/android_os_Parcel.cpp and from the NDK via AParcel_appendFrom in frameworks/native/libs/binder/ndk/parcel.cpp, per index search."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/native aa67ec623b7d in android-security-14.0.0_r24, one-line change in Parcel::appendFrom; at android-security-14.0.0_r29, 2/3 lines of the patched region are present, including the fixed line.",
      "15": "same fix: platform/frameworks/native de36dac70667 in android-security-15.0.0_r12, identical one-line change (different line offset); at android-security-15.0.0_r17, 2/3 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/native 6ffdde944d4e in android-security-16.0.0_r3, identical one-line change; at android-security-16.0.0_r8, 2/3 lines of the patched region are present.",
      "17": "not affected per the bulletin (not listed); at android-security-17.0.0_r1, 2/3 lines of the patched region are present."
     },
     "backport_notes": [
      "Self-contained one-line change in Parcel::appendFrom with no dependencies; it applies directly where the `mDataPos += len; mDataSize += len;` pair follows the memcpy."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which downstream read/write path uses the inflated mDataSize to produce the EoP impact the bulletin rates.",
      "Whether the binder_parcel_fuzzer or unit tests were extended in a separate commit is not shown.",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 commit.",
      "The rest of appendFrom after the object-count growth checks is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48597": {
     "cve": "CVE-2025-48597",
     "summary": "Moves the grant of the TRUSTED_OVERLAY surface flag for a PiP task out of WindowManager (where it was set as soon as the activity was marked pinned) into WM Shell's PipTransition.onFinishResize, so it is only set once Shell finishes the enter-PiP transition with a valid leash, while WM keeps only the revocation when the task leaves pinned mode.",
     "component": "platform/frameworks/base, WindowManager (services/core RootWindowContainer) and WM Shell PiP (libs/WindowManager/Shell PipTransition)",
     "weakness": "Logic error: a trust flag (TRUSTED_OVERLAY) was granted to a task surface before the condition it depends on (Shell successfully completing the enter-PiP transition) was established.",
     "root_cause": "In RootWindowContainer.notifyActivityPipModeChanged, system_server applied mWmService.mTransactionFactory.get().setTrustedOverlay(task.getSurfaceControl(), inPip).apply() on every PiP mode change. When an activity entered PiP this marked the task surface as a trusted overlay immediately, at notification time, regardless of whether WM Shell actually received and completed the entering-PiP transition (the commit message says the flag must only be granted after WMShell successfully handled the transition). On Android 14 the call passed task.getSurfaceControl() without a null check (15/16 already had one). Surfaces flagged TRUSTED_OVERLAY are not counted as obscuring windows in input touch-obscured checks (general platform knowledge).",
     "fix_analysis": [
      "libs/WindowManager/Shell/.../pip/PipTransition.java:onFinishResize: the leash/hasValidLeash computation (mPipOrganizer.getSurfaceControl(), non-null and isValid()) is moved to the top of the method; inside the existing `if (enteringPip)` block (after mPipTransitionState.setTransitionState(ENTERED_PIP)) the fix adds `if (hasValidLeash) tx.setTrustedOverlay(leash, true)` plus a ProtoLog WM_SHELL_PICTURE_IN_PICTURE debug line. The grant therefore happens only for an in-PiP direction with a valid PiP leash, and is written to the Shell transaction `tx` passed to onFinishResize.",
      "services/core/java/com/android/server/wm/RootWindowContainer.java:notifyActivityPipModeChanged: the unconditional setTrustedOverlay(task.getSurfaceControl(), inPip) after setPipVisibilityLw is removed. In its place, only in the `else` branch (r == null, activity unpinned) after notifyActivityUnpinned(), WM applies setTrustedOverlay(task.getSurfaceControl(), false), guarded by task.getSurfaceControl() != null, as a blanket revocation.",
      "Net effect: the grant moves to Shell and is gated on transition completion; the revoke stays in WM and runs on every unpin. Both files must change together: with only the WM change nothing grants the flag, and with only the Shell change WM still grants it early."
     ],
     "completeness": [
      "The grant is now only emitted from PipTransition.onFinishResize when isInPipDirection(direction) and the PiP leash is valid; within the changed code, a pin that does not reach this Shell callback with a valid leash does not get TRUSTED_OVERLAY (fail-closed).",
      "Revocation in RootWindowContainer.notifyActivityPipModeChanged runs on every unpin notification whether or not the grant happened, and is skipped only when task.getSurfaceControl() is null.",
      "The diff adds no separate apply for `tx` in the enter direction; how `tx` gets applied for entering PiP is not shown in the material (the context only shows mFinishTransaction.merge(tx) in the out-PiP branch, inside a conditional block).",
      "The fix does not change who may enter PiP or when; it only changes when the flag is set, and which process sets it (Shell instead of WM)."
     ],
     "behavior_changes": [
      "TRUSTED_OVERLAY is set on the PiP task leash later than before: at Shell onFinishResize for the enter direction, not at WM's pinned notification.",
      "If the Shell has no valid leash at onFinishResize, no TRUSTED_OVERLAY is granted for that PiP session.",
      "The flag is now set through the WM Shell transaction instead of system_server's transaction factory.",
      "On unpin, WM always clears TRUSTED_OVERLAY on the task surface (if non-null) and applies the transaction immediately.",
      "New ProtoLog debug message 'Set TRUSTED_OVERLAY for Task#%d' in the WM_SHELL_PICTURE_IN_PICTURE group.",
      "Android 14 only: the WM call site gains a null check on task.getSurfaceControl() that 15/16 already had."
     ],
     "preconditions": [
      "A task moves into pinned (PiP) mode so that RootWindowContainer.notifyActivityPipModeChanged(task, r) is invoked with a non-null ActivityRecord; the context shows it called as notifyActivityPipModeChanged(r.getTask(), r) at the end of a RootWindowContainer method, after requestStartTransition/resumeFocusedTasksTopActivities.",
      "Post-fix, the PipTransition path must reach onFinishResize with an in-PiP direction and a valid mPipOrganizer leash for the grant to occur.",
      "The exact pre-fix condition in which WM had granted the flag but Shell had not completed the enter transition is not described in the material beyond the commit message."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 3972f55646f4 in android-security-14.0.0_r24 (cherry-pick of b90e634cc244, Change-Id Id849d93bc92209fc296ee91ef70b0ffe2a09550e). Removes the unconditional setTrustedOverlay(inPip) (which on 14 had no null check) and adds the Shell grant + WM revoke. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: cfcef3f38c2f in android-security-15.0.0_r12. Identical Shell hunk; the WM hunk replaces an already null-checked unconditional setTrustedOverlay(inPip) block. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix, trivially adapted: 68170bad5225 in android-security-16.0.0_r3. onFinishResize there has an extra leashOffset parameter and its finish condition includes mMoveToBackTransition; the moved leash/hasValidLeash lines are removed from that block. WM hunk identical to 15. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; the patched region (8/8 lines) is already present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Land both hunks together: the Shell side (libs/WindowManager/Shell) and the WM side (services/core) split grant and revoke, and the split only works when both are present.",
      "On trees where PipTransition.onFinishResize differs (14: no leashOffset param, 16: mMoveToBackTransition in the finish condition), the adaptation is moving the existing leash/hasValidLeash locals above the enteringPip block."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete scenario in which the early WM grant was abusable (e.g. aborted or merged enter transitions, or a task holding the flag while not in a completed PiP state) is not shown; only the commit message's statement that the grant must follow successful Shell handling.",
      "How the Shell transaction `tx` is applied in the enter-PiP direction is not shown in the material.",
      "Whether any non-Shell-transitions (legacy) PiP path exists on Android 14 that previously relied on the WM grant; the material only shows PipTransition.",
      "Whether PinnedStackTests changes referenced in the Test: line were part of the same change."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48599": {
     "cve": "CVE-2025-48599",
     "summary": "Settings' WifiScanModeActivity now finishes immediately, without showing the \"allow scanning when Wi-Fi is off\" dialog, when the current user has the DISALLOW_CONFIG_LOCATION user restriction.",
     "component": "platform/packages/apps/Settings, Wi-Fi (WifiScanModeActivity, handler for WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE)",
     "weakness": "Missing authorization check (user restriction not enforced before offering a configuration change)",
     "root_cause": "WifiScanModeActivity.createDialog() built and showed the AlertDialogFragment, which lets the user approve \"scan always available\" for a requesting app, after only its existing early-return checks. Of those checks, only a trailing 'return;' is visible in the diff. The method never called UserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_LOCATION), so a user under that restriction could still see the scan-mode dialog and accept it.",
     "fix_analysis": [
      "src/com/android/settings/wifi/WifiScanModeActivity.java:createDialog(): after the existing early-return checks and before the 'if (mDialog == null)' dialog creation, the fix adds 'if (!isWifiScanModeConfigAllowed(getApplicationContext())) { Log.e(...); finish(); return; }'. onCreate() calls createDialog() after both the fresh-intent branch and the savedInstanceState restore branch, so both paths reach the new gate.",
      "src/com/android/settings/wifi/WifiScanModeActivity.java: adds a new private static helper isWifiScanModeConfigAllowed(Context). It gets UserManager from the context and returns !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_LOCATION). If UserManager is unavailable it returns true (allow).",
      "src/com/android/settings/wifi/WifiScanModeActivity.java:isGuestUser(): removes the 'if (context == null) return false;' guard and keeps the UserManager null guard. The rest of the diff (onCreate, AlertDialogFragment.onCreateDialog, field declarations) only changes formatting, with no logic change."
     ],
     "completeness": [
      "The fix gates this activity's dialog path in createDialog(), which onCreate() reaches from both branches. The material does not show the whole file.",
      "Only DISALLOW_CONFIG_LOCATION is checked. No other user restriction is checked here, and the material does not show whether that is intended.",
      "The new helper fails open: if context.getSystemService(UserManager.class) returns null, the activity treats the user as allowed. isGuestUser() behaves the same way, since it returns false (not a guest) when UserManager is null.",
      "The check is a UI-side gate in Settings. The material does not show whether the action taken in doPositiveClick() enforces the restriction on its own."
     ],
     "behavior_changes": [
      "When the user has DISALLOW_CONFIG_LOCATION, launching WifiScanModeActivity now logs 'This user is not allowed to configure Wi-Fi Scan Mode!' at error level and finishes without showing the dialog. The new block calls no setResult, so the requesting app gets no positive result.",
      "No change for users without the restriction. Apart from formatting, the dialog and its allow/deny handling are unchanged.",
      "isGuestUser() would now throw a NullPointerException if passed a null context, where it used to return false. The material does not show its callers."
     ],
     "preconditions": [
      "An app launches WifiScanModeActivity with the intent action WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE. With any other action or a null intent, onCreate() finishes the activity.",
      "The user has the DISALLOW_CONFIG_LOCATION user restriction set.",
      "Before the fix, the user would also have had to tap the allow button in the dialog for the scan-mode setting to change."
     ],
     "per_major": {
      "14": "same fix: commit b263af44716e cherry-picked into android-security-14.0.0_r24 (Change-Id If8cfb7047c0131da451a7af0d2b5108080876b85). All 8/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "not affected per the bulletin. The patched region (8/8 lines) is present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin's per-major listing, although the bulletin's affected versions include '16-qpr2-next'. The patched region (8/8 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, self-contained change. Only the block added in createDialog() and the isWifiScanModeConfigAllowed() helper change behavior. The other hunks are reformatting plus the isGuestUser() null-guard removal, and those can be dropped. UserManager.DISALLOW_CONFIG_LOCATION and hasUserRestriction() are long-standing public APIs (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "What the early-return checks before the new block in createDialog() contain (the diff context shows only the trailing 'return;').",
      "Whether the source changes (7a792e0b8f68 / a42e722b0775) included tests or other files that this cherry-pick does not carry.",
      "Whether the action taken by doPositiveClick() enforces DISALLOW_CONFIG_LOCATION on the service side.",
      "Which user's restriction getApplicationContext() checks in multi-user setups.",
      "Why the bulletin lists Android 13 as affected when the material includes no Android 13 fix commit."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 11,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48601": {
     "cve": "CVE-2025-48601",
     "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating (and letting callers mutate) PackagePreferences for packages that do not exist, by making getOrCreatePackagePreferencesLocked reject Process.INVALID_UID, making pure getters non-creating, and early-exiting channel lookups on an invalid uid.",
     "component": "platform/frameworks/base, services/core notification (NotificationManagerService, PreferencesHelper)",
     "weakness": "Missing validation of a package/uid resolution result (invalid uid sentinel accepted as a valid key), leading to unintended creation/modification of per-package notification settings",
     "root_cause": "PreferencesHelper.getOrCreatePackagePreferencesLocked(pkg, uid) lazily created and stored a PackagePreferences entry for any pkg/uid pair. It only special-cased its own sentinel UNKNOWN_UID (UserHandle.USER_NULL), while NotificationManagerService used a different sentinel INVALID_UID (-1), which per the commit message is what NMS passes along from getUidForPackageAndUser for a missing package. Per the commit message, most API paths were protected by other checks (e.g. canNotifyAsPackage), but the \"fromPrivilegedListener\" methods (tests cover getNotificationChannelsFromPrivilegedListener, getNotificationChannelGroupsFromPrivilegedListener, updateNotificationChannelFromPrivilegedListener) had no similar enforcement, so PackagePreferences could be created for a non-existing package. Many read-only getters (canShowBadge, getBubblePreference, isImportanceLocked, ...) also had creation as a side effect, and the RoleObserver passed Pair(pkg, -1) entries to updateDefaultApps when a role holder package was missing.",
     "fix_analysis": [
      "PreferencesHelper.java:getOrCreatePackagePreferencesLocked(String, int): now requires non-null pkg and Preconditions.checkArgument(uid != INVALID_UID, \"Valid uid required to get settings of %s\"), throwing IllegalArgumentException for an invalid uid. Setters such as createNotificationChannelGroup, createNotificationChannel, both updateNotificationChannel overloads and setAppImportanceLocked go through this method; their now-dead 'if (r == null) throw IllegalArgumentException(\"Invalid package\")' checks were removed.",
      "PreferencesHelper.java: the 8-argument overload is renamed getOrCreatePackagePreferencesSupportingInvalidUidLocked and documented as the only path that may receive INVALID_UID (storing into mRestoredWithoutUids); it is called from the readXml package-parsing path and from the validated 2-arg method.",
      "PreferencesHelper.java: UNKNOWN_UID (UserHandle.USER_NULL) is removed and every use (uid/delegate parsing in the XML read path, pkgInstalled for restore, createDefaultChannelIfNeededLocked, dump output, the permission migration loop, PackagePreferences.uid default, Delegate.isAllowed) switched to android.os.Process.INVALID_UID, so the helper and NMS agree on the missing-package sentinel.",
      "PreferencesHelper.java: getters getBubblePreference, getAppLockedFields, canShowBadge, isInInvalidMsgState, hasUserDemotedInvalidMsgApp, hasSentInvalidMsg, hasSentValidMsg, didUserEverDemoteInvalidMsgApp, hasSentValidBubble, isImportanceLocked, the group-blocked check (groupId lookup), getPackagePriority and getPackageVisibility now use getPackagePreferencesLocked (nullable, non-creating) and return the DEFAULT_* value / false when no entry exists.",
      "PreferencesHelper.java: getNotificationChannel and getConversationNotificationChannel return null and getNotificationChannels returns ParceledListSlice.emptyList() when uid == INVALID_UID, before any getOrCreate call. Unused getNotificationChannelsByConversationId was deleted.",
      "NotificationManagerService.java: local 'static final int INVALID_UID = -1' replaced by import of android.os.Process.INVALID_UID; getUidForPackageAndUser returns mPackageManager.getPackageUid directly; the two canNotifyAsPackage paths initialise targetUid = INVALID_UID; getUidForPackage returns INVALID_UID.",
      "NotificationManagerService.java RoleObserver: init() and onRoleHoldersChanged() skip role-holder packages whose uid resolves to INVALID_UID (logging Slog.e) instead of passing Pair(pkg, -1) to mPreferencesHelper.updateDefaultApps; the browser-role trampoline update path compares against INVALID_UID.",
      "Tests: NotificationManagerServiceTest adds MISSING_PACKAGE cases for the three fromPrivilegedListener methods (channels empty, groups empty, update throws IllegalArgumentException with the new message); PreferencesHelperTest replaces canShowBadge-as-creator with setShowBadge and mocks missing packages via NameNotFoundException; RoleObserverTest adds testInit_withInvalidPackages_invalidIgnored."
     ],
     "completeness": [
      "The guard is centralised in the 2-arg getOrCreatePackagePreferencesLocked, so every setter in PreferencesHelper that resolves preferences through it is covered, not just the fromPrivilegedListener entry points.",
      "Only the sentinel INVALID_UID is rejected; no positive validation that the uid belongs to pkg is added in PreferencesHelper, which remains the caller's responsibility.",
      "The readXml path (getOrCreatePackagePreferencesSupportingInvalidUidLocked) intentionally still accepts INVALID_UID and stores into mRestoredWithoutUids, as the restore-before-install behaviour.",
      "Since the converted getters no longer create entries, lazy entry creation (including default-channel setup via createDefaultChannelIfNeededLocked) appears to happen only through the remaining getOrCreate callers (setters and getNotificationChannel/getConversationNotificationChannel with a valid uid)."
     ],
     "behavior_changes": [
      "updateNotificationChannelFromPrivilegedListener (and other setters reached with a missing package) now throws IllegalArgumentException(\"Valid uid required to get settings of <pkg>\") instead of creating a PackagePreferences entry.",
      "getNotificationChannelsFromPrivilegedListener / getNotificationChannels return an empty ParceledListSlice and getNotificationChannel / getConversationNotificationChannel return null for an INVALID_UID.",
      "Read-only getters (canShowBadge, getBubblePreference, isImportanceLocked, getPackagePriority, getPackageVisibility, ...) return defaults without storing a new entry.",
      "Missing role-holder packages are skipped with a Slog.e in RoleObserver init/onRoleHoldersChanged instead of being added with uid -1.",
      "The uid sentinel for restored-without-uid entries changes from UserHandle.USER_NULL to Process.INVALID_UID; dump output prints \"INVALID_UID\" instead of \"UNKNOWN_UID\"."
     ],
     "preconditions": [
      "Caller reaches a fromPrivilegedListener method as a listener passing checkServiceTokenLocked with a companion device association (the tests stub mListeners.checkServiceTokenLocked and mCompanionMgr.getAssociations).",
      "The target package is not installed for the given user, so getPackageUid yields INVALID_UID (as mocked in the tests)."
     ],
     "per_major": {
      "14": "same fix: commit 30b11a3523d6 in android-security-14.0.0_r24 (cherry-pick of 236cff6879b2); patched region fully present at android-security-14.0.0_r29.",
      "15": "adapted fix: commit ed9af8192664 in android-security-15.0.0_r12; same logic on code that uses mLock instead of synchronized(mPackagePreferences), the Flags.persistIncompleteRestoreData/creationTime variant of the 9-arg overload, and keeps setAppImportanceLocked public with @VisibleForTesting; patched region fully present at android-security-15.0.0_r17.",
      "16": "adapted fix: commit 93629e575f69 in android-security-16.0.0_r3; additionally adds an INVALID_UID early return in a bundle-type channel getter, wraps the bundle-type reserved-channel path (getOrCreate + addReservedChannelLocked) in synchronized(mLock), handles getNotificationChannels(..., includeBundles), uses @VisibleForTesting(otherwise = NONE), and removes an INVALID_UID early return after getUidForPackageAndUser in an NMS caller; patched region fully present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. The core is the Preconditions.checkArgument in getOrCreatePackagePreferencesLocked plus the rename of the multi-arg overload; on trees with a different set of getters, each getter calling getOrCreatePackagePreferencesLocked should be converted to getPackagePreferencesLocked with a default, and tests relying on canShowBadge to create entries switched to a setter.",
      "Replace all UNKNOWN_UID uses (XML parsing, dump, Delegate.isAllowed, PackagePreferences.uid default) with Process.INVALID_UID as the commit does."
     ],
     "confidence": "high",
     "unknowns": [
      "Concrete security impact of a stray PackagePreferences entry for a non-existing package is not shown; the bulletin only rates it High EoP.",
      "How previously persisted XML containing the old UNKNOWN_UID value (USER_NULL) is treated after the sentinel change.",
      "Whether the Android 14 NMS code has an equivalent of the INVALID_UID early return that the Android 16 commit removes is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48603": {
     "cve": "CVE-2025-48603",
     "summary": "Adds a 200 KB size limit on an input method's metadata XML resource (InputMethod.SERVICE_META_DATA) in InputMethodInfo. An oversized resource now makes the code throw XmlPullParserException before the XML is parsed, so the system process (IMMS) cannot be driven out of memory while constructing InputMethodInfo.",
     "component": "platform/frameworks/base, input method framework (core/java/android/view/inputmethod/InputMethodInfo.java, consumed by InputMethodManagerService)",
     "weakness": "Missing size/bounds check on attacker-controlled input leading to uncontrolled memory consumption (OOM DoS in the system process)",
     "root_cause": "The InputMethodInfo constructor that takes a ServiceInfo called si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA) and parsed the resulting XmlResourceParser with no limit on the size of the referenced XML resource. Per the commit message, a malicious IME could load an extremely large metadata XML, and IMMS ran out of memory while constructing InputMethodInfo from it.",
     "fix_analysis": [
      "core/java/android/view/inputmethod/InputMethodInfo.java, constructor: the call pm.getResourcesForApplication(si.applicationInfo) moves ahead of si.loadXmlMetaData(...), and a new call validateXmlMetaData(si, res) is inserted before loadXmlMetaData. The size check therefore runs before any XML parsing starts.",
      "core/java/android/view/inputmethod/InputMethodInfo.java: new constant MAX_METADATA_SIZE_BYTES = 200 * 1024.",
      "core/java/android/view/inputmethod/InputMethodInfo.java, new private static validateXmlMetaData(ServiceInfo si, Resources res): returns early if si.metaData is null or metaData.getInt(InputMethod.SERVICE_META_DATA) is 0. Otherwise it opens the resource with res.openRawResource(resourceId) in a try-with-resources block and calls is.skip() in a loop until MAX_METADATA_SIZE_BYTES bytes are skipped or the stream ends. If exactly MAX_METADATA_SIZE_BYTES bytes were skipped and is.read() != -1, it throws XmlPullParserException(\"Input method metadata exceeds maximum allowed limit of 200KB for <pkg>. InputMethod will not be loaded. \").",
      "The exception is thrown inside the constructor's try block, before loadXmlMetaData runs. Its message says the input method will not be loaded."
     ],
     "completeness": [
      "Covers only the InputMethod.SERVICE_META_DATA resource referenced from the IME ServiceInfo metaData, and only in the InputMethodInfo constructor path that calls si.loadXmlMetaData.",
      "The check measures the raw resource byte size, not the in-memory size of the parsed structures, so the bound on memory use is indirect.",
      "If metaData is null or metaData.getInt(InputMethod.SERVICE_META_DATA) returns 0, validation is skipped.",
      "The patch does not touch any other metadata-XML consumers in frameworks/base. The material does not show whether sibling parsers have equivalent limits."
     ],
     "behavior_changes": [
      "An IME whose metadata resource exceeds 200 KB (204800 bytes) now causes an XmlPullParserException ('InputMethod will not be loaded'). Before the fix it was parsed regardless of size.",
      "pm.getResourcesForApplication(si.applicationInfo) is now called before si.loadXmlMetaData, so a NameNotFoundException for missing application resources is raised earlier, before the null-parser check.",
      "Each InputMethodInfo construction now does an extra openRawResource and skips up to 200 KB of the metadata resource."
     ],
     "preconditions": [
      "A package installed on the device declares an input method service whose InputMethod.SERVICE_META_DATA meta-data points to an XML resource larger than 200 KB (commit message: \"Malicious IMEs can load an extremely large metaData\").",
      "The system process constructs an InputMethodInfo for that service through the constructor that takes the ServiceInfo and PackageManager (the commit message names IMMS)."
     ],
     "per_major": {
      "14": "same fix: commit ea309e799b21 in android-security-14.0.0_r24 (cherry-pick of 5e31d9c067f0, Change-Id Ic90e1bd6615f5954577ed29c3daf7b44596fb137). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit e7824e85ad70 in android-security-15.0.0_r12. The hunks are identical apart from line offsets (the file has an extra java.util.Collections import and an mIsVirtualDeviceOnly field). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit b4c6786312a2 in android-security-16.0.0_r3, with hunks identical to Android 15. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching one file. Its only context dependency: pm.getResourcesForApplication must move above si.loadXmlMetaData, and the imports android.os.Bundle and java.io.InputStream must be added. Hunk offsets differ between 14 and 15/16, but the content is identical."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the exact caller in InputMethodManagerService or when the vulnerable constructor runs (package scan, IME list refresh).",
      "The material does not show the constructor's catch/handling of XmlPullParserException or how callers react, so how the rejection is surfaced (log, skipped IME) is not confirmed.",
      "The material does not show whether the original OOM came from the parser itself or from the subtype list built from the XML. The fix bounds only the raw resource size.",
      "The material does not show what loadXmlMetaData does when the meta-data value is not a resource reference."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48604": {
     "cve": "CVE-2025-48604",
     "summary": "MmsService now records the calling user for each MMS download request and refuses to write the downloaded PDU to the caller-supplied content URI when the URI's embedded user ID does not match the calling user.",
     "component": "platform/packages/services/Mms, MmsService download path (DownloadRequest / RequestManager.writePduToContentUri)",
     "weakness": "Missing authorization check on cross-user content URI (caller-supplied URI not validated against calling user before write)",
     "root_cause": "In the MmsService download handler, the caller supplies a contentUri, and the downloaded MMS PDU is written to it. DownloadRequest did not keep the calling user. MmsService.writePduToContentUri(Uri, byte[]) checked only for null arguments and never compared the URI's user component with the user of the app that requested the download, so a content URI carrying a different user's ID was not rejected. In the code context, the sibling read path (RequestManager.readPduFromContentUri) already takes a callingUser parameter. Before this fix, the write path used for downloads did not.",
     "fix_analysis": [
      "src/com/android/mms/service/MmsService.java (download handler that builds DownloadRequest): passes the existing callingUser variable into the DownloadRequest constructor, as a new argument placed before callingPkg.",
      "src/com/android/mms/service/DownloadRequest.java: adds a final field mCallingUser, set from the new constructor parameter. DownloadRequest.transferResponse now calls mRequestManager.writePduToContentUri(mContentUri, response, mCallingUser).",
      "src/com/android/mms/service/MmsRequest.java:RequestManager.writePduToContentUri: the interface signature gains an int callingUser parameter.",
      "src/com/android/mms/service/MmsService.java:writePduToContentUri: after the existing null checks, computes contentUriUserID = ContentProvider.getUserIdFromUri(contentUri, UserHandle.myUserId()). If callingUser != contentUriUserID, it logs with LogUtil.e and returns false. Only when the IDs match does it go on to the existing copyDownloadedPduToOutput callable.",
      "The Android 15 and 16 commits also update tests/robotests/src/com/android/mms/service/MmsRequestRoboTest.java (downloadRequest_noSatellite_downloadSuccessful) to pass CALLING_USER to the new constructor. The Android 14 commit has no test change."
     ],
     "completeness": [
      "Covers the download write path. The only caller of RequestManager.writePduToContentUri visible in the diff is DownloadRequest.transferResponse, and that call now enforces calling user == URI user. Per index search, no other callers were found, but that search may be incomplete.",
      "A URI with no explicit user component resolves to UserHandle.myUserId() (the user MmsService runs as). Such a URI is accepted when the calling user equals that user. A URI with an explicit, different user is rejected.",
      "The check runs in transferResponse/writePduToContentUri. The request is still created and enqueued (addSimRequest). The diff does not change the carrier-app path (request.tryDownloadingByCarrierApp), and the material does not show whether that path reaches transferResponse.",
      "In the code context, the read path readPduFromContentUri already takes a callingUser parameter. Its body is not in the material, so it cannot be confirmed whether it performs an equivalent check."
     ],
     "behavior_changes": [
      "writePduToContentUri returns false, so the download fails at transferResponse, when the content URI's user ID differs from the calling user. It also logs an error line starting 'Uri belongs to a different user'.",
      "The DownloadRequest constructor and RequestManager.writePduToContentUri signatures change (new int callingUser parameter). This affects every implementer or test double of RequestManager."
     ],
     "preconditions": [
      "A caller reaches the MmsService download handler with a contentUri whose explicit user component differs from the calling user (multi-user device).",
      "MMS must be enabled for the resolved subId, and the request must reach transferResponse, for the new check to run."
     ],
     "per_major": {
      "14": "same fix: ed4355456181 in android-security-14.0.0_r24 (cherry-pick of 0b8c94aa0a37), touching three source files with no test change. 7/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix, adapted: ad9bf77e4577 in android-security-15.0.0_r12. Same logic; the DownloadRequest constructor here also has a trailing TelephonyManager parameter, and the commit updates MmsRequestRoboTest. 7/8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix, adapted: c60a828b9fa1 in android-security-16.0.0_r3, the same change as Android 15 including the robotest update. 7/8 lines are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). 5/8 lines of the patched region are present at android-security-17.0.0_r1, and the material provides no commit for this branch."
     },
     "backport_notes": [
      "Single commit, no dependency. The Android 14 DownloadRequest constructor does not have the trailing TelephonyManager parameter that 15 and 16 have, so insert the new callingUser argument before callingPkg in whatever constructor shape the target branch has. Every RequestManager implementation and test mock must also be updated for the new writePduToContentUri signature.",
      "callingUser must already be available in the MmsService download handler. In all three branches shown, the diffs use it without adding it. If a target branch lacks it, that plumbing has to be added first."
     ],
     "confidence": "medium",
     "unknowns": [
      "The name of the MmsService download handler method, and how callingUser is derived in it (not shown in the diff or context).",
      "Whether the carrier-app download path (tryDownloadingByCarrierApp) ends in transferResponse and so gets the check.",
      "The order of doHttp and transferResponse in the request flow.",
      "The body of readPduFromContentUri and whether it performs an equivalent user check.",
      "What the copyDownloadedPduToOutput callable does when opening the URI (which identity it uses).",
      "Why only 5/8 patched lines are present at android-security-17.0.0_r1."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48614": {
     "cve": "CVE-2025-48614",
     "summary": "RecoverySystem.rebootWipeUserData now throws a SecurityException when DynamicSystemManager.isInUse() reports that the device is running a Dynamic System Update (DSU) image, so a factory reset can no longer be started while in DSU mode.",
     "component": "platform/frameworks/base, core/java/android/os/RecoverySystem.java (factory reset / recovery system)",
     "weakness": "Missing state check before a destructive operation (factory reset allowed while in DSU mode). The bulletin classifies the result as DoS.",
     "root_cause": "Before sending the MASTER_CLEAR_NOTIFICATION broadcast and starting the wipe, RecoverySystem.rebootWipeUserData only checked the UserManager.DISALLOW_FACTORY_RESET user restriction, and only when force was false. It did not check whether the device was booted into a DSU image, so a user-data wipe could be started in that state. The bulletin rates the consequence as a High severity DoS. The material does not explain how wiping in DSU mode leads to the DoS.",
     "fix_analysis": [
      "core/java/android/os/RecoverySystem.java: adds the import android.os.image.DynamicSystemManager.",
      "core/java/android/os/RecoverySystem.java:rebootWipeUserData: gets DynamicSystemManager via context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE), next to the existing UserManager lookup.",
      "core/java/android/os/RecoverySystem.java:rebootWipeUserData: after the existing DISALLOW_FACTORY_RESET check, adds `if (dsuManager.isInUse()) throw new SecurityException(\"Wiping data is not allowed while in DSU mode.\")`. The `force` parameter does not gate this check, so forced wipes are also refused in DSU mode. The throw happens before the ConditionVariable is created and before the MASTER_CLEAR_NOTIFICATION broadcast, so no wipe side effects start."
     ],
     "completeness": [
      "The fix covers only RecoverySystem.rebootWipeUserData. Every caller that goes through this method, including forced wipes, hits the new check.",
      "Unlike the DISALLOW_FACTORY_RESET check, the new check does not depend on `force`, so callers cannot skip it through that flag.",
      "This diff does not touch any other RecoverySystem entry point. The material does not show whether other wipe or recovery paths exist that would need the same guard.",
      "The result of getSystemService(Context.DYNAMIC_SYSTEM_SERVICE) is used without a null check. If the service were unavailable, the method would fail with a NullPointerException instead of a SecurityException."
     ],
     "behavior_changes": [
      "rebootWipeUserData throws SecurityException(\"Wiping data is not allowed while in DSU mode.\") whenever DynamicSystemManager.isInUse() is true, whatever the force flag and user restrictions are.",
      "Callers that could previously factory reset while in DSU mode now get a SecurityException from rebootWipeUserData. Per index search, DeviceLockController's DevicePolicyControllerImpl.wipeDevice catches SecurityException from DevicePolicyManager.wipeDevice and returns false. Whether the new exception reaches it through DPMS is not shown.",
      "Every call now does one extra getSystemService lookup."
     ],
     "preconditions": [
      "The device is booted into a Dynamic System Update image, so DynamicSystemManager.isInUse() returns true.",
      "Something calls RecoverySystem.rebootWipeUserData. Per index search, DevicePolicyManagerService test code uses an injector method recoverySystemRebootWipeUserData. The production callers are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: ed10409eb6de in android-security-14.0.0_r24, applied to the 5-parameter rebootWipeUserData(context, shutdown, reason, force, wipeEuicc). All 8 patched lines are present at android-security-14.0.0_r29.",
      "15": "same fix: aa4b01792c25 in android-security-15.0.0_r12, applied to the 6-parameter signature that includes keepMemtagMode. All 8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix: ec0c32ea736b in android-security-16.0.0_r3, same content as on 15. All 8 lines are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix is one self-contained change: an import plus two added statements in rebootWipeUserData. The hunk context differs between 14 (no keepMemtagMode) and 15/16, so other branches may need the context adjusted. The bulletin lists Android 13 as affected, but no 13 commit is in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe how a factory reset in DSU mode leads to the DoS.",
      "The material does not show whether other RecoverySystem entry points or wipe paths need the same DSU guard.",
      "The Android 13 fix commit is not in the material.",
      "Whether DynamicSystemManager can be null on any configuration (the fix does not null-check it).",
      "The production call paths into RecoverySystem.rebootWipeUserData (for example from DevicePolicyManagerService) are not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48618": {
     "cve": "CVE-2025-48618",
     "summary": "Adds a KeyguardManager.isDeviceLocked() check to CommandParamsFactory.processLaunchBrowser so that a SIM Toolkit LAUNCH BROWSER proactive command is rejected with a ResultException(ResultCode.LAUNCH_BROWSER_ERROR) while the device is locked. It also relaxes ResultException so this result code can be thrown with only a text explanation.",
     "component": "platform/frameworks/opt/telephony, CAT/SIM Toolkit proactive command decoding (com.android.internal.telephony.cat)",
     "weakness": "Missing authorization / lock-state check (lockscreen bypass): a proactive command from the modem could trigger a browser launch without checking that the device was unlocked",
     "root_cause": "CommandParamsFactory.processLaunchBrowser decoded every LAUNCH BROWSER proactive command it received, whether or not the device was locked. The resulting params went on to the STK app, which (per index search, StkAppService.launchBrowser in packages/apps/Stk) builds an ACTION_VIEW intent for the supplied URL or a default URL. Nothing in the decode path checked the keyguard state. In addition, ResultException's single-argument constructor listed LAUNCH_BROWSER_ERROR (0x26) as a code that requires additional info and threw an AssertionError without it. Throwing that code with only an explanation string would therefore have asserted.",
     "fix_analysis": [
      "src/java/com/android/internal/telephony/cat/CommandParamsFactory.java: imports android.app.KeyguardManager and adds a private Context mContext field. The private CommandParamsFactory(RilMessageDecoder, IccFileHandler, Context) constructor stores its existing context parameter in this field.",
      "CommandParamsFactory.processLaunchBrowser: at the start of the method, before any TLV parsing, gets KeyguardManager via mContext.getSystemService(KeyguardManager.class). If it is non-null and isDeviceLocked() returns true, the method logs and throws new ResultException(ResultCode.LAUNCH_BROWSER_ERROR, \"The device is locked, unable to process the command.\"), so decoding of the command stops.",
      "src/java/com/android/internal/telephony/cat/ResultException.java: ResultException(ResultCode) drops case LAUNCH_BROWSER_ERROR (0x26) from the switch that throws AssertionError when no additional info is given, and updates the ETSI TS 102 223 8.12 comment to match. This change is required because the two-argument constructor used by the new check calls this(result) before it sets mExplanation."
     ],
     "completeness": [
      "Only processLaunchBrowser gets the check. This commit changes no other proactive command handler in CommandParamsFactory.",
      "The lock state is checked at decode time in telephony, not when StkAppService.launchBrowser actually sends the ACTION_VIEW intent (per index search). The STK app side is unchanged.",
      "If getSystemService returns null for KeyguardManager, the check is skipped and the command is processed as before.",
      "KeyguardManager.isDeviceLocked() returns true only for a secured, locked device. A device with no secure lock set is not treated as locked by this API (general platform knowledge)."
     ],
     "behavior_changes": [
      "While the device is locked, decoding of LAUNCH BROWSER proactive commands now fails with a ResultException carrying ResultCode.LAUNCH_BROWSER_ERROR (0x26). The command does not continue to processing.",
      "New CatLog debug line: 'The device is locked, cannot launch the Browser'.",
      "ResultException can now be built with LAUNCH_BROWSER_ERROR and no numeric additional info without triggering an AssertionError."
     ],
     "preconditions": [
      "A LAUNCH BROWSER proactive command must reach CommandParamsFactory.processLaunchBrowser for decoding.",
      "Before the fix, the device could be locked and the command was still passed on to the STK app, which launches the browser (per index search, StkAppService.launchBrowser).",
      "The material does not show how the proactive command gets issued (SIM, baseband or test harness). The commit only says it was verified manually with a test APK."
     ],
     "per_major": {
      "14": "same fix: commit cc7d88a59cbd in android-security-14.0.0_r24 (Change-Id I9f651c7f1c5674df5774f5a7609f6d3749b5c50c, cherry-picked from internal commit dcf5c112a93d). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 8e7b0146d56b in android-security-15.0.0_r12. Same hunks; only the surrounding context differs (the UUID and NPE_WHEN_CALLED_SEND_CMD_PARAMS_ERROR_MSG constants come before mContext). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit fee68bcdcf02 in android-security-16.0.0_r3, identical to the Android 15 diff. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Take both files together. The new throw uses ResultException(ResultCode, String), which calls the single-argument constructor. Without the ResultException.java hunk that removes LAUNCH_BROWSER_ERROR from the assert switch, the new check would raise an AssertionError.",
      "On 14, 15 and 16 the private constructor of CommandParamsFactory already receives a Context; the patch only stores it in mContext. Hunks differ only in context lines between 14 and 15/16. Android 13 code is not shown in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the attacker model (who can inject the proactive command) or the concrete impact beyond the bulletin's High/EoP rating.",
      "How the thrown ResultException is handled downstream, including what terminal response is sent to the SIM, is not shown.",
      "The material does not show whether the STK app's existing user-confirmation handling for LAUNCH BROWSER played a part.",
      "The Android 13 fix is not shown.",
      "No automated test is included; the commit says it was verified manually with a test APK only."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48620": {
     "cve": "CVE-2025-48620",
     "summary": "Removes an early return in VoiceInteractionManagerService's package-change handler so that, when a voice interaction service is set, the code that resets the current voice recognition service on package removal is still reached.",
     "component": "platform/frameworks/base, services/voiceinteraction (VoiceInteractionManagerService)",
     "weakness": "Control-flow error: an early return skips state cleanup on package removal, leaving a stale voice_recognition_service setting that points at an uninstalled package (incomplete cleanup)",
     "root_cause": "In VoiceInteractionManagerService.java, in the package-change handler (onSomePackagesChanged per the commit message), the block that handles a non-null curInteractor ended with an unconditional `return;`. Whenever a voice interaction service was set, the handler therefore exited before the later logic that, per the commit message, resets the current voice recognition service when its package is uninstalled. The commit's test step (check `settings get secure voice_recognition_service` after uninstalling the recognizer) indicates the setting was left pointing at the removed package.",
     "fix_analysis": [
      "services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java, package-change handler (onSomePackagesChanged per the commit message): the `return;` at the end of the curInteractor block is deleted, so execution continues to the rest of the method, which per the commit message includes the recognizer reset logic.",
      "Same file and function: the next `if (curAssistant != null)` is changed to `if (curInteractor == null && curAssistant != null)`, with a comment saying the interactor case was already checked above. The removed return used to ensure this block only ran when curInteractor was null, so the new guard keeps the assistant check as it was. Per the commit message, the recognizer reset logic later in the method is now reached in both cases; that code is not shown in the diff.",
      "Verification in the commit: manual. Uninstall the voice recognition app and check `adb shell settings get secure voice_recognition_service`."
     ],
     "completeness": [
      "The fix only changes control flow in the package-change handler so that the existing recognizer reset logic is reached when an interactor is set. It adds no new validation of the recognizer setting itself.",
      "The curAssistant check stays equivalent to the old behavior (it is only evaluated when curInteractor == null). The fix does not change what happens to the assistant when an interactor is present.",
      "Only the hunk around the removed return is shown. Whether the curInteractor block has other early exits before the removed return is not visible in the material.",
      "handleUserStop (shown in the context) only switches the implementation when the interactor package stops. It does not touch the recognizer setting and is not modified by this fix."
     ],
     "behavior_changes": [
      "When a voice interaction service is set and a package change occurs, the handler now continues past the interactor handling to the recognizer reset logic. Per the commit message, uninstalling the voice recognition package now resets voice_recognition_service instead of leaving the stale value.",
      "No change when no interactor is set: the assistant check runs exactly as before.",
      "No new error codes or API changes."
     ],
     "preconditions": [
      "A voice interaction service is set on the device (curInteractor != null for the user).",
      "The package providing the current voice recognition service is uninstalled, which triggers the package-change handler in VoiceInteractionManagerService (per the commit message).",
      "How the stale setting leads to the bulletin's EoP rating is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 66a76238a130 in android-security-14.0.0_r24. At android-security-14.0.0_r29, 4 of 5 lines of the patched region are present.",
      "15": "same fix: commit 0925826a6ad8 in android-security-15.0.0_r12, same diff at a different line offset (~2669). At android-security-15.0.0_r17, 4 of 5 lines of the patched region are present.",
      "16": "same fix: commit db86972777c8 in android-security-16.0.0_r3, same diff at line offset ~2676. At android-security-16.0.0_r8, 4 of 5 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 4 of 5 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-commit, three-line change with no visible dependencies. The same hunk was applied on 14, 15 and 16 with only line-offset differences."
     ],
     "confidence": "high",
     "unknowns": [
      "The recognizer reset code that now becomes reachable is not shown in the diff or context. Its exact conditions are known only from the commit message.",
      "How a stale voice_recognition_service setting leads to the bulletin's EoP rating is not shown.",
      "Whether the method has other early exits that could still skip the reset in some cases is not visible.",
      "Android 13 is listed as affected, but the material has no Android 13 fix commit or code."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48621": {
     "cve": "CVE-2025-48621",
     "summary": "Two linked changes under bug 266433089: InputDispatcher stops delivering motion events to windows that use TouchOcclusionMode::USE_OPACITY while their alpha is below 0.5, and the WM Shell DefaultTransitionHandler lowers MAX_ANIMATION_DURATION from 3000 to 1500. The commit message frames this as reducing tapjacking risk during animations.",
     "component": "platform/frameworks/native (inputflinger InputDispatcher) and platform/frameworks/base (libs/WindowManager/Shell transition, DefaultTransitionHandler)",
     "weakness": "Improper restriction of rendered UI layers (tapjacking): touch input was delivered to a mostly transparent window during an animation, and the maximum animation duration was long",
     "root_cause": "InputDispatcher::canWindowReceiveMotionLocked checked targeted injection validity, PAUSE_DISPATCHING, NO_INPUT_CHANNEL, connection presence and responsiveness, but it never considered the window's alpha. A window declared opaque (touchOcclusionMode USE_OPACITY) therefore kept receiving touches even when its alpha was low, for example during a fade-out animation. Separately, DefaultTransitionHandler defined MAX_ANIMATION_DURATION as 3000. The commit message calls this \"way too long, especially for tap jacking exploitation during animation\".",
     "fix_analysis": [
      "services/inputflinger/dispatcher/InputDispatcher.cpp:InputDispatcher::canWindowReceiveMotionLocked (frameworks/native 0433b19e7d1c): adds `if (info.touchOcclusionMode == TouchOcclusionMode::USE_OPACITY && info.alpha < 0.5f)` as the first check. When it matches, the function logs at INFO and returns false, so the window cannot receive the motion event.",
      "libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java (frameworks/base 848e575f07ff): changes the constant MAX_ANIMATION_DURATION from 3000 to 1500. Per the commit message, this cuts the maximum duration of the default transition.",
      "Both commits are RESTRICT AUTOMERGE cherry-picks (Merged-In I4134b2512cf1d0c7bff595db1e011a3357ff130a and I5afd5c942b8f96347398a9b0aaafda28dc83d789) in separate projects. The material does not state a required order between them."
     ],
     "completeness": [
      "The alpha check applies only to windows whose touchOcclusionMode is USE_OPACITY. canWindowReceiveMotionLocked does not filter windows in other occlusion modes by alpha.",
      "The 0.5f threshold is hardcoded. A window with alpha at or above 0.5 is not blocked by this check.",
      "The duration cap is changed only in DefaultTransitionHandler. The material does not show how MAX_ANIMATION_DURATION is applied, or whether other transition handlers have their own duration limits. On Android 16, the separate SIZE_CHANGE_ANIMATION_DURATION (400) constant is unchanged.",
      "The code context at android-security-14.0.0_r29 shows both changes: the alpha check at InputDispatcher.cpp line 4921 and MAX_ANIMATION_DURATION = 1500 at DefaultTransitionHandler.java line 121."
     ],
     "behavior_changes": [
      "Motion events are no longer dispatched to a USE_OPACITY window whose alpha is below 0.5. In that case an INFO log line 'Not sending motion to <window>, window opacity=<alpha> is below the threshold' is emitted.",
      "The DefaultTransitionHandler maximum animation duration constant is now 1500 instead of 3000. The material does not show exactly how the limit is enforced."
     ],
     "preconditions": [
      "For the native change: a window registered with the input dispatcher using TouchOcclusionMode::USE_OPACITY whose WindowInfo alpha is below 0.5 when a motion event is being targeted (InputDispatcher::canWindowReceiveMotionLocked).",
      "For the duration change: a transition handled by the WM Shell DefaultTransitionHandler."
     ],
     "per_major": {
      "14": "Same fix: frameworks/base 848e575f07ff and frameworks/native 0433b19e7d1c in android-security-14.0.0_r24. Both changes are visible in the android-security-14.0.0_r29 context (resolver: 4/5 lines of the patched region present).",
      "15": "Same fix for frameworks/base: 06e697583c87 in android-security-15.0.0_r12, the identical one-line constant change (3000 -> 1500). The matching frameworks/native commit for 15 is not shown in the material (resolver: resolved, 4/5 lines present at 15.0.0_r17).",
      "16": "Same fix for frameworks/base: 6d1697c96c5c in android-security-16.0.0_r3, the identical constant change next to an unchanged SIZE_CHANGE_ANIMATION_DURATION = 400 line. The frameworks/native commit for 16 is not shown (resolver: resolved, 4/5 lines present at 16.0.0_r8).",
      "17": "Not affected: the bulletin does not list Android 17. The resolver reports 4/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix spans two projects. Take both the frameworks/native InputDispatcher change, which uses WindowInfo::touchOcclusionMode, WindowInfo::alpha and TouchOcclusionMode::USE_OPACITY (all present in the Android 14 context), and the frameworks/base DefaultTransitionHandler constant change. The material includes no fork comparison.",
      "The native commit message cites inputflinger_tests, but the material does not include a test diff."
     ],
     "confidence": "medium",
     "unknowns": [
      "How MAX_ANIMATION_DURATION is used inside DefaultTransitionHandler (the clamping site is not in the material).",
      "The frameworks/native commits for Android 15 and 16, and whether their diffs match the Android 14 one.",
      "Whether the two commits depend on each other.",
      "Which specific windows or animations were involved in the reported issue. The material only gives the tapjacking rationale from the commit messages.",
      "The test changes referenced by 'Test: inputflinger_tests', which are not included."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48622": {
     "cve": "CVE-2025-48622",
     "summary": "Replaces the Android copy of Adobe's DNG SDK (platform/external/dng_sdk) with an import of version 1.7.1 that has JXL and XMP support stubbed out. It also adapts the two in-tree consumers, Skia's SkRawCodec and the CTS dng-validate JNI helper, to the new SDK API.",
     "component": "platform/external/dng_sdk (libdng_sdk, DNG/RAW image parsing), with companion changes in platform/external/skia (src/codec/SkRawCodec.cpp) and platform/cts (tests/camera/libctscamera2jni/dng-validate-jni.cpp)",
     "weakness": "unknown. The bulletin type is ID (information disclosure), but the diff is a wholesale library version upgrade and does not isolate a single flaw.",
     "root_cause": "Not identifiable from the diff. The security fix is the commit \"Crude DNG SDK 1.7.1 upgrade\". It rewrites 159 files (about 61k insertions, 16k deletions) and moves external/dng_sdk from the 1.4-era sources ($Id //mondo/dng_sdk_1_4 headers) to DNG SDK 1.7.1 (kDNGSDK_VersionString \"1.7.1\", build 2502), with JXL and XMP support crudely removed to avoid new dependencies. The material names no specific vulnerable function. The only concrete defect it describes is in the Skia companion commit: SkDngStream used the dng_stream default offsetInOriginalFile of invalid (-1), which with the new SDK \"results in dng_negative hitting a bug path that causes a crash due to unsigned overflow\". That is an adaptation to the new SDK, not a demonstrated description of this CVE. The dng_sdk commit also lists \"atest CVE_2025_22423\" as a test; its relation to this fix is not shown.",
     "fix_analysis": [
      "platform/external/dng_sdk 56e64aaa5b59 (Android.bp, source/*): imports the whole DNG SDK 1.7.1 library.",
      "New files are added to the tree: dng_big_table, dng_bmff, dng_local_string, dng_lossless_jpeg_shared, dng_jxl, dng_update_meta, dng_semantic_mask.h, dng_simd_type.h and dng_deprecated_flags.h. Of these, dng_big_table.cpp, dng_bmff.cpp, dng_local_string.cpp and dng_lossless_jpeg_shared.cpp are added to the Android.bp srcs.",
      "JXL and XMP are stubbed: the stub header source/jxl/color_encoding.h lets the library compile without libjxl, the dng_jxl.h functions get [[maybe_unused]] (per the commit message), and qDNGUseXMP stays 0.",
      "Build changes:\n- -DqDNGUsingSanitizer=1 is added.\n- -DqAndroid=1 moves to the android target, and -DqLinux=1 is added for host_linux.\n- -Wno-implicit-fallthrough is added.\n- libdng_sdk gets sdk_version \"current\" and stl \"c++_shared\", and vendor_available is removed.\n- The windows host build is disabled.\n- dng_validate_host is removed, and dng_validate now links libdng_sdk statically.",
      "Representative API changes visible in the platform/external/dng_sdk diff:\n- dng_1d_table now takes a table count. DNG_REQUIRE checks that it is at least kMinTableSize (512) and a power of two, and table entries are converted with ConvertDoubleToFloat.\n- dng_1d_table::Interpolate keeps its ThrowBadFormat range check, and UnsafeInterpolate is added.\n- dng_area_task::Start gains a dstArea parameter. ProcessOnThread and Perform gain a dng_area_task_progress parameter. Tile iteration now goes through MakeTileIterator.\n- dng_abort_sniffer gains SupportsPriorityWait and a wider dng_priority enum.\n- dng_bad_pixels gains divide-by-zero guards (count = Max_uint32(count, 1)) and uses snprintf instead of sprintf.",
      "platform/external/skia 9a77caf186a0, src/codec/SkRawCodec.cpp:\n- SkDngHost::PerformAreaTask takes the new dng_area_task_progress* parameter, passes area to task.Start and passes nullptr progress to task.ProcessOnThread.\n- The SkDngStream constructor explicitly calls dng_stream((dng_abort_sniffer*) nullptr, dng_stream::kDefaultBufferSize, 0), so offsetInOriginalFile is 0 instead of the invalid -1 default.\n- Android.bp: libdng_sdk moves from the common shared_libs to the android shared_libs, and to static_libs for host_linux and darwin.",
      "platform/cts 4ff6e58c770b, tests/camera/libctscamera2jni/dng-validate-jni.cpp (test-only):\n- In dng_validate, RawJPEGImage() is replaced by RawLossyCompressedImage(), and imagePreview->fImage.Reset is replaced by imagePreview->SetImage(host, ...).\n- WriteDNG now uses dngVersion_SaveDefault.\n- WriteTIFF now takes &negative->Metadata() and render.FinalSpace(profilePtr). For non-monochrome negatives, profilePtr comes from GetProfileByID.\n- The XMP_NS_CRD and XMP_NS_CRLCP properties are also removed.\n- validateDngNative now closes pipeFds[0] instead of pipeFds[1], and fails on any stderr output (lineCount > 0) instead of on fewer than 3 lines."
     ],
     "completeness": [
      "The fix replaces the entire libdng_sdk source tree, so every parser and renderer path in the library now runs the imported 1.7.1 code. The material does not let any single check be identified as the CVE fix.",
      "JXL and XMP are deliberately stubbed out in this import (stub jxl/color_encoding.h, qDNGUseXMP=0), so those 1.7.1 code paths are not active on Android. The commit message calls the result 'crude' and says it is a cherry-pick of ag/34978264.",
      "Only two in-tree consumers are adapted: Skia SkRawCodec and the CTS dng-validate helper. The library's vendor_available flag is dropped, and these commits do not cover any other consumer of the old libdng_sdk API.",
      "The bulletin lists Android 13 as affected, but no Android 13 commit appears in the material."
     ],
     "behavior_changes": [
      "The libdng_sdk API and ABI change. Affected signatures include dng_area_task::Start/ProcessOnThread/Perform, dng_host::PerformAreaTask, the dng_1d_table constructor, dng_negative accessors (RawLossyCompressedImage, Metadata()), dng_image_writer::WriteTIFF/WriteDNG and dng_render::FinalSpace. Consumers must be adapted and rebuilt.",
      "libdng_sdk is no longer vendor_available and is built with sdk_version \"current\" and stl \"c++_shared\". Its windows host variant and the dng_validate_host binary are removed.",
      "Skia's SkDngStream reports a stream offset of 0 instead of the invalid -1 default. Per the commit comment, this avoids an unsigned-overflow crash path in dng_negative.",
      "Constructing a dng_1d_table now throws via DNG_REQUIRE if the count is too small or not a power of two.",
      "The CTS DngCreatorTest native validation now treats any stderr output from dng_validate as a failure."
     ],
     "preconditions": [
      "A DNG/RAW image is decoded through libdng_sdk. In-tree this happens through Skia's SkRawCodec (SkDngHost and SkDngStream in src/codec/SkRawCodec.cpp). The dng_sdk commit was tested with android.graphics.cts.BitmapFactoryTest.",
      "The material does not show the file contents or tags needed to trigger the information disclosure."
     ],
     "per_major": {
      "13": "Listed as affected by the bulletin. No Android 13 fix commit appears in the material, so its status is unknown.",
      "14": "Same fix: platform/external/dng_sdk 56e64aaa5b59 (Crude DNG SDK 1.7.1 upgrade) in android-security-14.0.0_r24, with companion commits skia 9a77caf186a0 and cts 4ff6e58c770b. 8/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/external/dng_sdk c210ca6441c5 (Crude DNG SDK 1.7.1 upgrade) in android-security-15.0.0_r12. The visible part of the diff matches Android 14. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Same fix: platform/external/dng_sdk de700ad461e3 (Crude DNG SDK 1.7.1 upgrade) in android-security-16.0.0_r3. The visible part of the diff matches Android 14. 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin. 6/8 lines of the patched region are present at android-security-17.0.0_r1. The material does not show which DNG SDK variant Android 17 carries."
     },
     "backport_notes": [
      "Apply the commits as a set in one build: the dng_sdk import first, then the skia SkRawCodec and Android.bp change and the cts dng-validate-jni change. The new dng_area_task, dng_host and dng_image_writer signatures break compilation of the old consumers. Also adapt any other module that linked libdng_sdk, since the new Android.bp removes vendor_available.",
      "The import compiles 1.7.1 with JXL stubbed (source/jxl/color_encoding.h, [[maybe_unused]] in dng_jxl.h) and qDNGUseXMP=0. A backport needs to keep these stubs or provide the missing dependencies."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which function or parsing path in the old dng_sdk 1.4 sources caused the information disclosure. The fix is a full version upgrade and the material names no specific flaw.",
      "Whether the SkDngStream offset change (the unsigned-overflow path in dng_negative) is related to the CVE or is only an adaptation to the new SDK.",
      "How the CVE_2025_22423 test listed in the dng_sdk commit relates to this fix.",
      "Whether Android 13 received the upgrade, and which DNG SDK variant Android 17 carries.",
      "Whether other platform or vendor components link libdng_sdk besides Skia and the CTS helper."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48626": {
     "cve": "CVE-2025-48626",
     "summary": "Launcher3's OverviewCommandHelper no longer launches the home activity directly via startActivity for the TYPE_HOME command; it asks SystemUI (new ISystemUiProxy.onKeyEvent) to send a KEYCODE_HOME key event so the press goes through the system's key handling.",
     "component": "platform/packages/apps/Launcher3 (quickstep OverviewCommandHelper / SystemUiProxy) and platform/frameworks/base (SystemUI OverviewProxyService, ISystemUiProxy AIDL)",
     "weakness": "Improper handling of a privileged navigation action: the home button handled by Launcher3 started the home activity directly instead of going through a KEYCODE_HOME key-event path. The exact restriction bypassed is not shown in the material.",
     "root_cause": "In quickstep/src/com/android/quickstep/OverviewCommandHelper.java, executeCommand handled cmd.type == TYPE_HOME by calling mService.startActivity(mOverviewComponentObserver.getHomeIntent()) on TouchInteractionService (the launcher process). This launched the home intent directly from Launcher3 instead of delivering a home key press to the system. Back, by contrast, was already routed through SystemUI: ISystemUiProxy.onBackPressed in OverviewProxyService calls sendEvent for ACTION_DOWN and ACTION_UP of KEYCODE_BACK. The fix suggests that the system's KEYCODE_HOME handling was meant to apply to home presses from the launcher-handled home button and was skipped.",
     "fix_analysis": [
      "Commit 1 (frameworks/base a8a441b0eb62, A14): packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl adds `oneway void onKeyEvent(int keycode) = 60;` (Next id = 61) and rewords the onBackPressed doc to say it sends KEYCODE_BACK ACTION_DOWN followed by ACTION_UP.",
      "Commit 1: packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java implements onKeyEvent(int keycode). It wraps the work in verifyCallerAndClearCallingIdentityPostMain(\"onKeyEvent \" + keyCodeToString(keycode), ...) and calls the existing sendEvent(KeyEvent.ACTION_DOWN, keycode) and then sendEvent(KeyEvent.ACTION_UP, keycode). onBackPressed() now just calls onKeyEvent(KEYCODE_BACK), so back and home share one path.",
      "Commit 2 (Launcher3 9f5688403e25, A14): quickstep/src/com/android/quickstep/SystemUiProxy.java adds a client-side onKeyEvent(int keycode) wrapper. When the SystemUI binder is non-null, it forwards to mSystemUiProxy.onKeyEvent(keycode) and logs any RemoteException.",
      "Commit 2: quickstep/src/com/android/quickstep/OverviewCommandHelper.java gains a SystemUiProxy constructor parameter and field (mSystemUiProxy). In executeCommand, the TYPE_HOME branch changes from mService.startActivity(mOverviewComponentObserver.getHomeIntent()) to mSystemUiProxy.onKeyEvent(KeyEvent.KEYCODE_HOME).",
      "Commit 2: quickstep/src/com/android/quickstep/TouchInteractionService.java onUserUnlocked passes SystemUiProxy.INSTANCE.get(this) to the new OverviewCommandHelper constructor.",
      "Net effect: a launcher-side home press becomes a KEYCODE_HOME down/up pair sent by SystemUI's sendEvent, instead of Launcher3 starting the home activity itself."
     ],
     "completeness": [
      "Covers only the TYPE_HOME command in OverviewCommandHelper.executeCommand, the single startActivity(getHomeIntent()) site touched by the diff. The material does not show whether other Launcher3 call sites start the home intent directly.",
      "The SystemUI-side onKeyEvent accepts any keycode from the proxy client, not just KEYCODE_HOME or KEYCODE_BACK. It is wrapped in the same verifyCallerAndClearCallingIdentityPostMain call as onBackPressed. What that check verifies is not shown.",
      "The A14 onKeyEvent has no displayId parameter. The displayId mentioned in the commit message exists only in the A16 variant: onKeyEvent(int keycode, int displayId), onBackEvent(KeyEvent, int displayId) and sendEvent(action, code, displayId).",
      "The commit message cites flag com.android.launcher3.home_button_uses_keycode_home, but the A14 and A15 Launcher3 diffs apply the change unconditionally, with no flag check in the patched code."
     ],
     "behavior_changes": [
      "Home presses routed through OverviewCommandHelper TYPE_HOME are now delivered as a KEYCODE_HOME down/up pair via SystemUI instead of a direct startActivity of the home intent.",
      "SystemUI gets a new ISystemUiProxy binder method, onKeyEvent (transaction id 60). On A14, ids 52 to 59 are skipped. Id 60 matches the A16 variant.",
      "If the SystemUI proxy is not connected (mSystemUiProxy == null in Launcher3 SystemUiProxy), the TYPE_HOME command now does nothing instead of launching home. A RemoteException is logged and swallowed.",
      "onBackPressed in SystemUI now goes through the shared onKeyEvent path, with the log label \"onKeyEvent KEYCODE_BACK\". Its sendEvent calls are unchanged."
     ],
     "preconditions": [
      "A TYPE_HOME command must reach OverviewCommandHelper, i.e. the home button is handled by Launcher3 quickstep (commit title: \"Send KEYCODE_HOME for home button\"). OverviewCommandHelper is created in TouchInteractionService.onUserUnlocked, so the user must be unlocked.",
      "Per index search, OverviewCommandHelper.addCommand is called from TaskbarNavButtonController, TaskbarUIController and TISBinder (onOverviewToggle, onOverviewShown, onOverviewHidden) for other command types. The material does not show what produces TYPE_HOME.",
      "The fixed path requires the Launcher3 to SystemUI ISystemUiProxy connection to be established."
     ],
     "per_major": {
      "14": "same fix (two commits): frameworks/base a8a441b0eb62 adds ISystemUiProxy.onKeyEvent(int keycode)=60 and OverviewProxyService.onKeyEvent; Launcher3 9f5688403e25 switches TYPE_HOME to mSystemUiProxy.onKeyEvent(KEYCODE_HOME). Landed in android-security-14.0.0_r24; 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "adapted fix: Launcher3 db8146473259 in android-security-15.0.0_r12 makes the same TYPE_HOME change inside a switch statement (with ActiveGestureLog logging), plus SystemUiProxy.onKeyEvent and the TouchInteractionService constructor change. 8/8 lines present at android-security-15.0.0_r17. The frameworks/base counterpart for 15 is not shown in the material.",
      "16": "adapted fix: frameworks/base 9fb37191609f in android-security-16.0.0_r3 adds onKeyEvent(int keycode, int displayId)=60 and adds displayId to onBackEvent(KeyEvent, int) and to sendEvent in LauncherProxyService. Only 5/8 lines of the patched region are present at android-security-16.0.0_r8. The Launcher3 commit for 16 is not shown.",
      "17": "not affected (not listed by the bulletin); 6/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Order and dependency: the frameworks/base AIDL change (ISystemUiProxy.onKeyEvent, id 60) must land before or together with the Launcher3 change, because Launcher3's SystemUiProxy implements ISystemUiProxy and calls onKeyEvent. Keep transaction id 60. The A14 variant has no displayId parameter, so do not mix the A16 signature with A14/A15 Launcher3 code.",
      "The change is unconditional (no com.android.launcher3.home_button_uses_keycode_home flag check in the diff). A forked Launcher3 with its own home-button handling should check that its TYPE_HOME handling uses mSystemUiProxy.onKeyEvent(KEYCODE_HOME)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which system-level restriction on KEYCODE_HOME the direct startActivity bypassed (the material does not show the privilege gained).",
      "Where TYPE_HOME commands are produced (the home-button caller is not in the diff or the index results).",
      "The body of sendEvent and of verifyCallerAndClearCallingIdentityPostMain on A14 (which display the event targets, how it is injected, what the caller check enforces).",
      "The Android 13 fix commits, the frameworks/base commit for Android 15, and the Launcher3 commit for Android 16.",
      "Why only 5/8 (A16) and 6/8 (A17) lines of the patched region are present in the latest tags."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48627": {
     "cve": "CVE-2025-48627",
     "summary": "In ActivityTaskManagerService.startNextMatchingActivity, the fix saves the real Binder caller (uid/pid) before the calling identity is cleared. It then passes them to the ActivityStarter as realCallingUid/realCallingPid instead of r.launchedFromUid / -1, so the background activity launch (BAL) decision is made against the app that actually calls the method. It also removes the earlier setAvoidMoveToFront() workaround from b/230492947.",
     "component": "platform/frameworks/base, WindowManager / ActivityTaskManagerService (activity start, background activity launch checks)",
     "weakness": "Wrong caller identity used for an authorization decision: the background activity launch check was evaluated against the launching app's uid rather than the real Binder caller.",
     "root_cause": "startNextMatchingActivity built the ActivityStarter with setRealCallingUid(r.launchedFromUid) and setRealCallingPid(-1). launchedFromUid is the uid of the app that launched the calling activity (for example the Launcher), and that app may hold BAL privileges. Per the commit message, using this identity as the real caller lets the next matching activity be launched with the launching app's BAL standing instead of the calling app's. An earlier mitigation (b/230492947) forced setAvoidMoveToFront() on the options. The commit message says this broke the legitimate ability to start a new task via startNextMatchingActivity and did not address the identity problem.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/ActivityTaskManagerService.java:startNextMatchingActivity: reads Binder.getCallingUid() and Binder.getCallingPid() into origCallingUid/origCallingPid before Binder.clearCallingIdentity(), which preserves the identity of the process that invoked the Binder method.",
      "Same function: the ActivityStarter chain now uses .setRealCallingPid(origCallingPid) and .setRealCallingUid(origCallingUid) instead of .setRealCallingPid(-1) and .setRealCallingUid(r.launchedFromUid). Per the in-code comment, ActivityStarter ignores callingUid/callingPid when setCaller(r.app.getThread()) is set, so both the effective callingUid and realCallingUid now resolve to the calling app.",
      "Same function: the line options.getOptions(r).setAvoidMoveToFront() (the b/230492947 workaround) is removed. The options object is still created via SafeActivityOptions(ActivityOptions.makeBasic()) when it is null."
     ],
     "completeness": [
      "Covers the single call site in startNextMatchingActivity that passed r.launchedFromUid as the real caller. realCallingUid/realCallingPid now reflect the actual Binder caller.",
      "setCallingUid(r.launchedFromUid), setCallingPackage(r.launchedFromPackage) and setCallingFeatureId(r.launchedFromFeatureId) are unchanged. The commit relies on ActivityStarter ignoring callingUid/callingPid when a caller is set. callingPackage and featureId still carry the launching app's values, and the diff does not touch them.",
      "The fix removes the previous setAvoidMoveToFront() restriction, so the BAL check with the corrected identity is what governs this launch path.",
      "The commit modifies no other ActivityTaskManagerService entry points."
     ],
     "behavior_changes": [
      "BAL checks for startNextMatchingActivity now use the uid/pid of the app calling the method rather than the uid that launched its activity.",
      "setAvoidMoveToFront() is no longer forced on the options, so startNextMatchingActivity can again start a new task, which is the behavior the commit title says it restores.",
      "realCallingPid is now a real pid instead of -1. The TODO(b/64750076) comment about -1 remains for callingPid."
     ],
     "preconditions": [
      "The caller must own an activity with a process attached (r.app.getThread() is used) and must invoke startNextMatchingActivity on it.",
      "A next matching activity must be resolved (aInfo != null). Otherwise the function returns false before reaching the patched code.",
      "The calling activity's launchedFromUid must belong to an app with BAL privileges (the commit message cites the Launcher app) for the behavior before and after the fix to differ."
     ],
     "per_major": {
      "14": "same fix: commit 07561c7d685d (RESTRICT AUTOMERGE cherry-pick of d34ae40f870d, Change-Id Ia8c6123e8e9325d17d2bfab9589b11eb087da97a) in android-security-14.0.0_r24. At android-security-14.0.0_r29, 6/8 lines of the patched region are present. The origCallingUid/origCallingPid capture and setRealCallingPid(origCallingPid) are visible, but the starter is assigned to a local `starter` variable and setRealCallingPid directly follows setCallingPid(-1).",
      "15": "not affected (not listed by the bulletin). At android-security-15.0.0_r17, 6/8 lines of the patched region are present.",
      "16": "not affected (Android 16 is not listed by the bulletin; the bulletin does list 16-qpr2-next). At android-security-16.0.0_r8, 6/8 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 5/8 lines of the patched region are present."
     },
     "backport_notes": [
      "The change is contained in startNextMatchingActivity. When applying it, remove options.getOptions(r).setAvoidMoveToFront() and make sure Binder.getCallingUid()/getCallingPid() are read before Binder.clearCallingIdentity(). At android-security-14.0.0_r29 the starter chain is assigned to a local variable, so a hunk may need re-anchoring on setRealCallingPid/setRealCallingUid rather than being applied verbatim."
     ],
     "confidence": "high",
     "unknowns": [
      "Why the bulletin lists 16-qpr2-next as affected while android-security-16.0.0_r8 already shows most of the patched region. The material does not include a diff for the 16 branch.",
      "Whether the 15/16/17 code carries the corrected identity handling. Only partial line-match counts are given.",
      "Whether setCallingUid still uses r.launchedFromUid in the android-security-14.0.0_r29 code. The context excerpt stops after setRealCallingPid.",
      "Whether the Android 13 fix (13 is listed as affected by the bulletin) is identical. No Android 13 commit is included in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48628": {
     "cve": "CVE-2025-48628",
     "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id from the URI authority via ContentProvider.getUserIdFromAuthority (defaulting to the resolved calling user) and always enforces the current-user-group check for URI-type icons, instead of relying on Uri.getEncodedUserInfo and skipping the check when it returned null.",
     "component": "platform/frameworks/base, print service (services/print, PrintManagerService)",
     "weakness": "Improper input validation / bypassable cross-user authorization check (user id parsed from URI with an unreliable method; check skipped when parse returned null)",
     "root_cause": "In services/print/java/com/android/server/print/PrintManagerService.java, validateIconUserBoundary(Icon) got the user id of a TYPE_URI / TYPE_URI_ADAPTIVE_BITMAP icon from icon.getUri().getEncodedUserInfo(). The commit message says a specially crafted URI could make Uri.getEncodedUserInfo return an incorrect value. The old code ran the \"current group members only\" check (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) only when encodedUser was non-null. When it was null, the method skipped the check and assumed the URI pointed into the caller's own user space. The old code also called Integer.parseInt(encodedUser) directly and resolved the parsed user via resolveCallingUserEnforcingPermissions.",
     "fix_analysis": [
      "PrintManagerService.java: the binder method that obtains the icon via userState.getCustomPrinterIcon(printerId) now passes its resolved calling user (resolvedUserId) to validateIconUserBoundary(icon, resolvedUserId).",
      "PrintManagerService.java: validateIconUserBoundary(Icon, int resolvedCallingId) replaces getEncodedUserInfo + Integer.parseInt + resolveCallingUserEnforcingPermissions with ContentProvider.getUserIdFromAuthority(icon.getUri().getAuthority(), resolvedCallingId). The user id now comes from the authority component, with resolvedCallingId passed as the default.",
      "PrintManagerService.java: the synchronized(mLock) check resolveCallingProfileParentLocked(iconUserId) != getCurrentUserId() -> return null now runs unconditionally for URI-type icons; it is no longer nested under 'if (encodedUser != null)'.",
      "Adds import android.content.ContentProvider."
     ],
     "completeness": [
      "Covers the call path shown in the diff (userState.getCustomPrinterIcon -> validateIconUserBoundary) for icons of type Icon.TYPE_URI and Icon.TYPE_URI_ADAPTIVE_BITMAP.",
      "Per the unchanged type condition, icons of other types are not subject to this check; the diff does not change that.",
      "The fix changes how the user id is extracted; it adds no further validation of other URI components. Whether other print service paths return Icons or URIs to callers is not shown in the material."
     ],
     "behavior_changes": [
      "URI-type custom printer icons whose authority carries no user prefix are now checked against the resolved calling user instead of being returned without any check.",
      "User id extraction moved from Uri.getEncodedUserInfo to ContentProvider.getUserIdFromAuthority; the explicit Integer.parseInt on the user-info string is removed from this method.",
      "resolveCallingUserEnforcingPermissions is no longer invoked on the user id embedded in the icon URI; the caller's resolved user is passed in as the default instead.",
      "For URI-type icons, the method returns null (icon withheld) whenever the icon's user id does not resolve to the current user's profile group."
     ],
     "preconditions": [
      "A caller reaches the print manager binder method that calls userState.getCustomPrinterIcon(printerId), and the user state returns a TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP Icon for that printerId.",
      "The icon URI must be such that Uri.getEncodedUserInfo returns an incorrect value (per commit message); the material does not give the exact URI shape."
     ],
     "per_major": {
      "14": "same fix: commit 0d6bfc8a1f50 in android-security-14.0.0_r24 (cherry-pick of 8d40513846ee); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 3723e843b659 in android-security-15.0.0_r12, identical diff; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 9489a5dcd3cd in android-security-16.0.0_r3, identical diff; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); patched region present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, single-commit change; the only new dependency is the android.content.ContentProvider import. The diff is identical on 14, 15 and 16. Android 13 is listed as affected by the bulletin, but no 13 commit is in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact URI construction that makes Uri.getEncodedUserInfo return an incorrect value is not shown (commit message only).",
      "What data a leaked cross-user icon URI could expose is not shown; the bulletin classifies it as ID (information disclosure), High.",
      "Whether an Android 13 fix commit exists is not in the material.",
      "The name of the enclosing binder method and the implementations of Icon#getUriString and userState.getCustomPrinterIcon (how the icon URI is sourced) are not in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48629": {
     "cve": "CVE-2025-48629",
     "summary": "When the preferred recognizer package is not found, VoiceInteractionManagerService no longer falls back to the first available RecognitionService. It now auto-selects only a service whose application has ApplicationInfo.FLAG_SYSTEM, and returns null if there is none.",
     "component": "platform/frameworks/base, services/voiceinteraction (VoiceInteractionManagerService, default voice recognizer selection)",
     "weakness": "Missing trust check on an automatically selected default component (insecure default selection): any app's RecognitionService could be chosen as the device's voice recognizer without user selection",
     "root_cause": "In services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java, the recognizer-selection logic tries to match a preferred package (prefPackage, which per the code comment is the current or default recognizer) among the available recognition services. If there was no match, the code unconditionally returned available.get(0), and only logged a warning when numAvailable > 1. Per the commit message, when no default is configured via config_systemSpeechRecognizer, this let any third-party app that declares a recognition service become the default recognizer automatically. There was no check that the app is a system app, and no user selection.",
     "fix_analysis": [
      "VoiceInteractionManagerService.java (recognizer selection block; hunk at about line 852 on 14 and about line 933 on 15 and 16): the unconditional available.get(0) fallback is removed.",
      "A new loop goes through available.get(i).getServiceInfo() for i in [0, numAvailable). It skips any service where serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM is 0, and returns the first service with FLAG_SYSTEM set as new ComponentName(serviceInfo.packageName, serviceInfo.name).",
      "If no system-app service is found, the method logs 'no auto selectable voice recognition services found for user <userHandle>' and returns null instead of a ComponentName.",
      "The earlier 'more than one voice recognition service found, picking first' warning is removed."
     ],
     "completeness": [
      "The fix changes only the fallback path, taken when prefPackage is not matched among the available services. The path where prefPackage is found is not modified by the diff.",
      "The only trust check is FLAG_SYSTEM. If there are several system-app recognizers, the first one in the 'available' list is still picked without user input.",
      "The method can now return null in cases where it used to return a ComponentName (numAvailable > 0). The material does not show the callers, so the diff cannot confirm that every caller handles a null return."
     ],
     "behavior_changes": [
      "On devices with no matching current or default recognizer and only third-party recognition services installed, no recognizer is auto-selected any more. The selection returns null and a warning is logged.",
      "When several recognizers exist and the preferred one is absent, a system-app recognizer is now chosen even if a third-party one comes earlier in the list.",
      "Log text changes: the 'more than one voice recognition service found, picking first' warning is removed, and a new 'no auto selectable voice recognition services found for user ...' warning is added."
     ],
     "preconditions": [
      "The recognizer-selection code runs with a prefPackage (current or default recognizer) that matches none of the available recognition services. Per the commit message, this happens when config_systemSpeechRecognizer provides no default.",
      "At least one app on the device declares a recognition service (numAvailable > 0). Before the fix, a non-system app in that list was enough to be auto-selected."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 6c030f1f30b4 in android-security-14.0.0_r24, hunk at about line 852 of VoiceInteractionManagerService.java.",
      "15": "same fix: platform/frameworks/base d8faf81970e4 in android-security-15.0.0_r12, identical hunk content at about line 933.",
      "16": "same fix: platform/frameworks/base 84dd2b90f4a2 in android-security-16.0.0_r3, identical hunk content at about line 933.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 1/4 lines of the patched region are present, the same value as at the fixed 14/15/16 tags."
     },
     "backport_notes": [
      "Single hunk; the diff adds no import. Only the line offset differs between 14 (about 852) and 15/16 (about 933). When backporting, check that callers of the selection method can handle a null ComponentName, which it can now return even when services are available."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the name of the enclosing method or how its callers use the return value, which can now be null.",
      "The material does not show how the 'available' list is ordered, so it is unclear which system recognizer is chosen when there are several.",
      "The material does not describe the concrete privilege a selected recognizer gains, beyond the bulletin's EoP/High rating."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48632": {
     "cve": "CVE-2025-48632",
     "summary": "Adds a shared validateDisplayName() length check (DISPLAY_NAME_LENGTH_LIMIT = 1024) to the private AssociationRequest constructor and the @hide setDisplayName() in core/java/android/companion/AssociationRequest.java. Before this, the display-name limit was enforced only in Builder.setDisplayName().",
     "component": "platform/frameworks/base, Companion Device Manager (android.companion.AssociationRequest)",
     "weakness": "Missing input validation (length bound enforced on only one of several paths that set the field)",
     "root_cause": "AssociationRequest.mDisplayName had a 1024-character limit (DISPLAY_NAME_LENGTH_LIMIT). Before the fix, that limit was checked only in Builder.setDisplayName(). The private constructor AssociationRequest(boolean, List, String, CharSequence, boolean, boolean) stored displayName without validation. The @hide public setDisplayName(CharSequence) overwrote mDisplayName with no validation at all. Any code that reached those paths could therefore create an AssociationRequest with an arbitrarily long display name. Per the field's javadoc, this name is shown in the CDM confirmation UI.",
     "fix_analysis": [
      "core/java/android/companion/AssociationRequest.java: new private static helper validateDisplayName(@Nullable CharSequence). It throws IllegalArgumentException when displayName is non-null and displayName.length() > DISPLAY_NAME_LENGTH_LIMIT (1024). Null is accepted.",
      "AssociationRequest.java: the private constructor AssociationRequest(singleDevice, deviceFilters, deviceProfile, displayName, selfManaged, forceConfirmation) now calls validateDisplayName(displayName) as its first statement, before any field is assigned.",
      "AssociationRequest.java: @hide setDisplayName(CharSequence) now calls validateDisplayName(displayName) before assigning mDisplayName.",
      "AssociationRequest.java: Builder.setDisplayName(@NonNull CharSequence) replaces its inline length check with a call to validateDisplayName(displayName). requireNonNull(displayName) and checkNotUsed() are kept, so behavior there is unchanged."
     ],
     "completeness": [
      "Adds validation to the explicit-argument private constructor and to the @hide setDisplayName() on AssociationRequest. The existing check in Builder.setDisplayName() (which sets the Builder's own field) is moved into the same helper.",
      "The Parcel-reading constructor AssociationRequest(Parcel in) is not modified by this commit. CREATOR.createFromParcel calls it ('return new AssociationRequest(in)' in the diff context). The material does not show whether an over-length displayName arriving via Parcel is validated.",
      "Validation is length-only (character count against 1024). No other constraints on displayName content are introduced."
     ],
     "behavior_changes": [
      "The private constructor now throws IllegalArgumentException(\"Length of the display name must be at most 1024 characters\") for a non-null displayName longer than 1024 characters. Before the fix it accepted any value.",
      "The @hide setDisplayName(CharSequence) now throws the same IllegalArgumentException for over-length values. Null is still accepted by that method and by the constructor.",
      "Builder.setDisplayName() behavior is unchanged. It still rejects null via requireNonNull and over-length values with the same exception message."
     ],
     "preconditions": [
      "An AssociationRequest must be created or changed through a path other than Builder.setDisplayName(). That means the private constructor or the @hide setDisplayName(CharSequence). The material does not show which callers use these paths.",
      "Per the field's javadoc, the display name is consumed by the CDM confirmation UI ('The Display name of the device to be shown in the CDM confirmation UI'). The material does not show the exact consumer or the effect of an over-length value there."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base a100e23a3fda in android-security-14.0.0_r24 (cherry-pick of 872038799ed1, Change-Id I41dc3804c35328a5968716f502c4320a58b75158); 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: e72cdc5c770d in android-security-15.0.0_r12. The logic is identical; the only difference is a blank line in Builder.setDisplayName(). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: de27b16b1af8 in android-security-16.0.0_r3. It makes the same three validateDisplayName() insertions, but the private constructor has extra parameters (skipRoleGrant, @Nullable Icon deviceIcon) and the hunks sit at different line offsets. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. 7/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, single-commit change with no dependencies. It only requires that DISPLAY_NAME_LENGTH_LIMIT and the inline check in Builder.setDisplayName() already exist; they do in all three fixed branches. If a fork's constructor signature differs (as on 16 with skipRoleGrant/deviceIcon), insert validateDisplayName(displayName) as the first statement of the explicit-argument constructor that stores mDisplayName."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers invoke the @hide setDisplayName(CharSequence) or otherwise reach the private constructor with an unvalidated displayName. The index search returned only unrelated setDisplayName methods (Contacts, SubscriptionManager).",
      "Whether the Parcel constructor AssociationRequest(Parcel) validates displayName. It is not in the diff or the provided context.",
      "What exactly the 7/8 partial match of the patched region at android-security-17.0.0_r1 reflects.",
      "The concrete consequence of an over-length display name in the CDM confirmation UI that led to the High/EoP rating. The material does not describe it."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48633": {
     "cve": "CVE-2025-48633",
     "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() is changed to query AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of iterating users and calling AccountManager.getAccounts(). Per the commit title, this makes it count all accounts regardless of visibility.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy)",
     "weakness": "Incomplete check: the account presence test relied on an account enumeration that, per the commit title, was subject to account visibility filtering",
     "root_cause": "hasAccountsOnAnyUser() cleared the calling identity, looped over mUserManagerInternal.getUsers(excludeDying=true), created a per-user context and called AccountManager.getAccounts(). The commit title (\"Get all accounts no matter the visibility\") indicates that this enumeration was affected by account visibility, so some existing accounts could be missed. The helper could then report no accounts while accounts existed. The material does not show which decision depends on this helper.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java:hasAccountsOnAnyUser(): the user loop, the per-user AccountManager contexts and Binder.clearCallingIdentity()/restoreCallingIdentity() are removed.",
      "The method now obtains the in-process AccountManagerService via AccountManagerService.getSingleton() and returns getAllAccountsForSystemProcess().length != 0. Per the method name and commit title, this is an account list for the system process that is not filtered by visibility.",
      "A new import com.android.server.accounts.AccountManagerService is added."
     ],
     "completeness": [
      "Only hasAccountsOnAnyUser() is changed. All callers of this private helper get the corrected result.",
      "Whether getAllAccountsForSystemProcess() covers all users and whether it includes dying users (previously excluded via excludeDying=true) is not shown in the material.",
      "No other AccountManager uses in DevicePolicyManagerService are touched by this commit. The material does not show whether other similar presence checks exist."
     ],
     "behavior_changes": [
      "hasAccountsOnAnyUser() can now return true in cases where the previous visibility-filtered enumeration returned no accounts.",
      "The helper no longer clears and restores the Binder calling identity and no longer creates per-user contexts. It uses the AccountManagerService singleton directly inside system_server.",
      "The helper now depends on AccountManagerService.getSingleton() being available when it runs."
     ],
     "preconditions": [
      "Some path in DevicePolicyManagerService must invoke hasAccountsOnAnyUser(). The material does not show the caller (no index results).",
      "An account must exist that the old AccountManager.getAccounts() path omitted because of visibility."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 527b25ca541f in android-security-14.0.0_r24 (cherry-pick of a8819d3bdc49); 8/8 lines present at android-security-14.0.0_r29",
      "15": "same fix: platform/frameworks/base e74b3376e899 in android-security-15.0.0_r12, identical diff; 8/8 lines present at android-security-15.0.0_r17",
      "16": "same fix: platform/frameworks/base d00bcda9f42d in android-security-16.0.0_r3, identical diff; 8/8 lines present at android-security-16.0.0_r8",
      "17": "not affected (not listed by the bulletin); patched code (8/8 lines) already present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Single-commit fix. The target branch must provide com.android.server.accounts.AccountManagerService.getSingleton() and getAllAccountsForSystemProcess(). Android 13 is listed as affected, but the material includes no Android 13 commit."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which DevicePolicyManagerService path calls hasAccountsOnAnyUser() and what decision it gates (neither the material nor the index shows the caller).",
      "Exact semantics of getAllAccountsForSystemProcess() (user coverage, handling of dying users).",
      "Exact visibility behavior of AccountManager.getAccounts() in the old code path, beyond what the commit title implies.",
      "The Android 13 fix commit is not included in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48639": {
     "cve": "CVE-2025-48639",
     "summary": "Halves the maximum animation duration enforced by WM Shell's DefaultTransitionHandler (MAX_ANIMATION_DURATION 3000 -> 1500 ms) to shrink the window in which a running transition animation can be used for tap-jacking.",
     "component": "platform/frameworks/base, libs/WindowManager/Shell (WM Shell transitions, DefaultTransitionHandler)",
     "weakness": "Overly permissive clamp on transition animation duration (UI redress / tap-jacking window during animation); no memory-safety or permission flaw is involved in the diff",
     "root_cause": "DefaultTransitionHandler, the fallback handler for window transitions in WM Shell, defines the constant MAX_ANIMATION_DURATION, which was set to 3000 ms. Per the commit message, 3 seconds is too long, especially because it allows tap-jacking while the animation is running. The material shows only the constant's definition, not where it is applied.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java: the class constant MAX_ANIMATION_DURATION is changed from 3000 to 1500. Any duration this handler clamps against the constant is now limited to 1.5 s instead of 3 s. The fix is a single-line change in one file, with no change to logic or API. It is a RESTRICT AUTOMERGE backport (Change-Id I5afd5c942b8f96347398a9b0aaafda28dc83d789, bug 266433089)."
     ],
     "completeness": [
      "The fix only lowers the clamp value. Animations during a transition still run, so a window of up to 1500 ms remains by design.",
      "Only DefaultTransitionHandler is touched. The commit does not change animation paths that are not clamped by MAX_ANIMATION_DURATION, or that are handled by another Transitions.TransitionHandler. The material does not show these paths.",
      "The code context does not show where MAX_ANIMATION_DURATION is used, so the material cannot confirm which animations are subject to the new cap."
     ],
     "behavior_changes": [
      "Transition animations clamped by DefaultTransitionHandler are limited to 1500 ms instead of 3000 ms.",
      "No new error paths, permissions or API changes."
     ],
     "preconditions": [
      "A window transition handled by DefaultTransitionHandler in WM Shell (the handler for anything not already handled) whose animation duration reaches the cap.",
      "The material does not show how an app influences the animation duration."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 848e575f07ff in android-security-14.0.0_r24 (RESTRICT AUTOMERGE backport); the constant is 1500 at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 06e697583c87 in android-security-15.0.0_r12, identical one-line change; 4/5 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 6d1697c96c5c in android-security-16.0.0_r3, identical one-line change (the hunk context also includes the adjacent SIZE_CHANGE_ANIMATION_DURATION line); 4/5 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 4/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single constant change in DefaultTransitionHandler.java. The changed line is the same on 14, 15 and 16; only the surrounding hunk context differs. WM Shell is a frameworks/base library built into SystemUI, so the fix ships with the platform image (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Where and how MAX_ANIMATION_DURATION is applied (which animation durations are clamped) is not shown in the code context.",
      "The bulletin lists Android 13 as affected, but no Android 13 fix commit is included in the material.",
      "How an app can extend a transition animation up to the cap is not shown.",
      "The value of MAX_ANIMATION_DURATION at android-security-17.0.0_r1 is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "15": {
   "counts": {
    "announced": 38,
    "certain": 37,
    "probable": 1,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2023-40130",
     "osv_id": "ASB-A-289809991",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Wraps the Context.unbindService(mConnection) call in CallRedirectionProcessor.notifyTimeout in a try/catch for IllegalArgumentException so that a timeout on an already-unregistered CallRedirectionServiceConnection no longer throws out of Telecom.",
      "confidence": "medium",
      "backport_notes": [
       "The 15/16 change is a self-contained hunk in notifyTimeout (6 insertions, 2 deletions) with no shown dependencies; it applies wherever the 'if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; }' block exists in notifyTimeout. finishCallRedirection has the same unguarded call; whether to guard it too is the maintainer's decision.",
       "For Android 14-based trees, Google's recorded fix is the separate onBindingDied commit (18299e2d9062). The material does not show the notifyTimeout try/catch on 14, so check for both changes."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "3623312367a57998140fc9545a3bad32564bff0d",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref a43a880beaa6",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-22420",
     "osv_id": "ASB-A-337775777",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PreferencesHelper.createNotificationChannel now verifies, via a new shared PermissionHelper.grantUriPermission helper, that the creating uid can read/grant a content:// channel sound Uri, and replaces the sound with the default notification Uri when the UriGrantsManager check throws SecurityException.",
      "confidence": "medium",
      "backport_notes": [
       "On Android 15, 6c7c10e6234b and ea0fc122e4df carry identical production code; they differ only in one PreferencesHelperTest construction site (TestPreferencesHelper vs PreferencesHelper), so check whether your test tree has TestPreferencesHelper before picking the test hunk.",
       "The PreferencesHelper constructor change requires updating every construction site (NotificationManagerService and all test instantiations). Decide between the 14-style throw and the 15/16-style default-sound fallback; Google chose the fallback on 15/16 to avoid app crashes per the code comment."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d028f03ea40a61f869ec83975f77f6459f5a417f",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "6c7c10e6234b8a3feb4e284d306d692cfdb390aa",
       "tag": "android-security-15.0.0_r10",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "5475e757915a59e340f644ba5e422df6fd7b8d3d",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "ea0fc122e4df7a826313579841c9015735851cb7",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 4,
     "first_commit_before_announcement_days": 238,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22432",
     "osv_id": "ASB-A-376461726",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Telecom's CallRedirectionProcessor now unbinds the CallRedirectionService connection and clears mConnection/mService when the redirection timeout fires in notifyTimeout(). Before the fix, the service stayed bound after the timeout.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained 8-line addition to notifyTimeout() in CallRedirectionProcessor.java. Apply the initial unbind commit first, then the follow-up try/catch (a43a880beaa6). Context.unbindService throws IllegalArgumentException for a connection that is not registered (general platform knowledge).",
       "The test changes only add verify(mContext).unbindService(...) assertions to the existing timeout tests."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "1b081682e2adf5021eeaa730e6edeec246962d2e",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 238,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-32328",
     "osv_id": "ASB-A-327137311",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The Autofill system service now inflates the fill service's RemoteViews presentations (fill UI, fill dialog, save UI) with a Context created for the current foreground user instead of the system_server (user 0) context, and clears the Binder calling identity around PackageManager lookups of the service's custom icon and label.",
      "confidence": "high",
      "backport_notes": [
       "This is a single commit. Helper.getUserContext must be present because FillUi, DialogFillUi and SaveUi call it. The Android 14 and 15 versions differ only in context lines and offsets, for example where mUserContext is placed in FillUi."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3ca035e4cdf3c8da9c1d38bdd3b567d47228dab5",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-32329",
     "osv_id": "ASB-A-326926596",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Autofill's FillUi, DialogFillUi and SaveUi now use a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user-0 Context when applying service-supplied RemoteViews presentations (and, in SaveUi, as the base Context of the save dialog). Session now clears the Binder calling identity around the custom service icon/label resource lookups.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching only services/autofill. The 14 and 15 cherry-picks are equivalent apart from line offsets and minor placement in the FillUi constructor, so it should port to trees where the FillUi/DialogFillUi/SaveUi constructors and Session.getServiceIcon/getServiceLabel exist. Android 13 is listed as affected, but no 13 commit is in the material.",
       "Helper.getUserContext relies on Context.createContextAsUser and on INTERACT_ACROSS_USERS being held by system_server. Keep the @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") annotations, or the errorprone permission check will flag the constructors (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3ca035e4cdf3c8da9c1d38bdd3b567d47228dab5",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref e030442861f4",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48525",
     "osv_id": "ASB-A-391895151",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CompanionDeviceManager now revokes Notification Listener Service (NLS) access for a package's listener components when that package's last companion association is removed. Before, notification access stayed in place after disassociation.",
      "confidence": "high",
      "backport_notes": [
       "For trees with DisassociationProcessor (15/16 style), apply both hunks: fetch NotificationManager in the CompanionDeviceManagerService constructor and pass it in. Any test that constructs DisassociationProcessor needs the extra argument. For 14-style trees, follow 9473c3649017 (CHANGE_TYPE_REMOVED branch in CompanionDeviceManagerService), noting the method name is getAssociationsForPackage there, not getAssociationsByPackage."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r10",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r10",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "52c030fa9ba2011b29c35cc9f0dda8e1fd827b43",
       "tag": "android-security-15.0.0_r10",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 90,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48536",
     "osv_id": "BUL-CVE-2025-48536",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SettingsSliceProvider.grantAllowlistedPackagePermissions now also reads a separate slice_allowlist_package_names_for_dev resource array. It grants slice URI permissions to the packages in that array only when Build.IS_DEBUGGABLE is true, so debug-only allowlist entries are not granted Settings slice access on non-debuggable builds.",
      "confidence": "high",
      "backport_notes": [
       "The SettingsSliceProvider.java and res/values/config.xml hunks are the same on 14, 15 and 16. Only the test differs: 14 uses ReflectionHelpers.setStaticField on Build.IS_DEBUGGABLE, while 15 and 16 use ShadowBuild.setDebuggable. Pick the test variant that compiles on your branch.",
       "With AOSP's empty arrays the code change has no effect. Debug-only allowlist entries in product overlays need to live in slice_allowlist_package_names_for_dev for the gating to apply."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "11b11215aa18f782267d74e7a90a397ad1fdce10",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48555",
     "osv_id": "BUL-CVE-2025-48555",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The Settings notification log (NotificationStation) now computes, on resume, the set of profiles whose lock-screen private-notification setting is off and that are currently secure and locked, and hides the title, text, notification extras and ranking extras of history rows belonging to those profiles.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching only NotificationStation.java; the same diff was applied on 14, 15 and 16. It relies on com.android.internal.widget.LockPatternUtils, KeyguardManager.isDeviceLocked(int) and Settings.Secure.getIntForUser, which are hidden/system APIs accessible to the Settings app (general platform knowledge); forks with a customized notification_log_row layout must keep the R.id.title, text, notification_extra and ranking_extra ids used by updatePreference."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "0db1143041a6f15c4f8ded027d408f5a385e290e",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48572",
     "osv_id": "ASB-A-385736540",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaButtonReceiverHolder.send stops attaching BroadcastOptions that unconditionally granted background-activity-start and FGS temp-allowlist capability to the media button receiver, and instead asks MediaSessionService.tempAllowlistTargetPkgIfPossible to allowlist the target package based on the caller's identity.",
      "confidence": "medium",
      "backport_notes": [
       "The fix calls MediaSessionService.tempAllowlistTargetPkgIfPossible(targetUid, targetPackage, callingPid, callingUid, callingPackage, reason), which this commit does not add; verify it exists in the target tree (or port it) before applying, and update any other callers of MediaButtonReceiverHolder.send to the new signature.",
       "Diff applies identically on 14/15/16 for MediaButtonReceiverHolder.java (same blob ids 72ce38b72340 -> e7e70fc1f7b2); only the MediaSessionService.java hunk offset changes."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "739ece18a63a03c1b21d26b95ac3f4f20193553c",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48573",
     "osv_id": "ASB-A-339637822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the mService.tempAllowlistTargetPkgIfPossible call from sendCommand in MediaSessionRecord.java, so MediaController.sendCommand no longer propagates while-in-use (WIU) or background foreground-service start (BFSL) privileges to the session owner's package.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained three-line deletion in sendCommand in MediaSessionRecord.java with no dependencies. To find it, look for the tempAllowlistTargetPkgIfPossible call with reason TAG + \":\" + command. The catch block uses Log.e on 14 and Slog.e on 15/16, which does not affect the hunk."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c88cb354b7facd813d2058efd846d7f93fffe327",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48575",
     "osv_id": "ASB-A-417463103",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller now requires that the package \"com.android.settings\" is actually installed under the system uid (UserHandle.isSameApp with Process.SYSTEM_UID) before taking the Settings-only CA certificate install path, instead of trusting the referrer package name alone.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit; the CertInstaller.java diff applies identically on 14, 15 and 16 (blob 570c400 -> 883c897 on all three). The new code uses CredentialHelper.getReferrer(). The commit removes calledBySettings(), so if a fork still has other callers of it, they must be migrated."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "b24bef75f32598eab5e79369891e7a079535128a",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2025-48576",
     "osv_id": "ASB-A-426205822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the unpublished, AIDL-only INotificationManager method updateNotificationChannelGroupFromPrivilegedListener and its implementation in NotificationManagerService. That method let a caller with a listener token that passes verifyPrivilegedListener create or update a notification channel group for a named package.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained deletion with no dependencies. Apply both hunks together (the AIDL declaration and the @Override implementation). In forks with a customized NotificationManagerService, check for vendor callers of updateNotificationChannelGroupFromPrivilegedListener first, because removing the AIDL method also renumbers later transaction codes (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "345b6f2a6900ea4990e46dc8af7f78c174913e05",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48580",
     "osv_id": "ASB-A-393582077",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaBrowser no longer unconditionally binds to a MediaBrowserService with Context.BIND_INCLUDE_CAPABILITIES; the flag is dropped when the connection's root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing key BrowserRoot.EXTRA_RECENT.",
      "confidence": "high",
      "backport_notes": [
       "Two-file change that relies on the existing BrowserRoot.EXTRA_RECENT constant and Context.BIND_INCLUDE_CAPABILITIES. On Android 14, Google's version places the MediaBrowserService.java hunk at a different offset (line 872) with identical content."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2588609ffab9bb0b018da070267fb97a60f7d5aa",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48583",
     "osv_id": "ASB-A-381885240",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "BaseBundle no longer returns the Parcel behind lazily-deserialized values to the Parcel pool with recycle(). It now destroys that Parcel through a newly exposed @hide Parcel.destroy(), so these Parcel instances are not put back in the pool for reuse.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with no dependency. For trees that still have the `Preconditions.checkState(mWeakParcelledData.get() != null, ...)` form, as Android 14 did, apply the Android 14 variant of the unwrapLazyValueFromMapLocked hunk. Also make the existing private Parcel.destroy() public (@hide), and remove the recycleParcel() helper once nothing uses it."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "26f25bbc2ae863df5edc8904cbc5a6db51a0d976",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48586",
     "osv_id": "ASB-A-337784859",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In Telephony's FDN contact editor, the onActivityResult handler now uses ComponentCaller.checkContentUriPermission to check that the caller holds a read grant for the returned contact URI. This replaces a user-ID comparison, and the handler throws SecurityException when the check is denied.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit with the same hunks on 15 and 16. It depends on the ComponentCaller API (Activity.getCurrentCaller, ComponentCaller.checkContentUriPermission), which was added in Android 15 (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "e4124fa76067ea2e4a35370ce0dfc85e9c79c127",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telephony"
     ]
    },
    {
     "cve": "CVE-2025-48588",
     "osv_id": "ASB-A-303408193",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Vpn.startAlwaysOnVpn() no longer resets the always-on VPN setting (setAlwaysOnPackage(null, false, null)) when the always-on package is found unsupported while the device is in safe mode. It now only skips starting the VPN and returns false.",
      "confidence": "high",
      "backport_notes": [
       "This is a single self-contained hunk: wrap the existing setAlwaysOnPackage(null, false, null) call in startAlwaysOnVpn() with if (!mContext.getPackageManager().isSafeMode()). It uses only the existing mContext field and PackageManager.isSafeMode(), which is a long-standing public API (general platform knowledge). On 14 the hunk sits at line 1252, with the same context lines as on 15.",
       "The added unit test relies on VpnTestBase mocks and helpers (mPackageManager, mVpnProfileStore, setAndVerifyAlwaysOnPackage). If a branch lacks them, port only the Vpn.java change or adapt the test."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "fccd4c6bc3478e81b6d25e040d85124b860012fd",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48589",
     "osv_id": "ASB-A-362492829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's HeaderPrivacyIconsController now filters the PermissionGroupUsage list it forwards to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS) so that only usages belonging to the current user and its profiles (plus phone-call usages) are sent, instead of usages from all users on the device.",
      "confidence": "high",
      "backport_notes": [
       "The fix is a single commit per branch; the only adaptation needed is the constructor parameter list of HeaderPrivacyIconsController (and its test instantiation), which differs on each release. UserTracker is already a Dagger-provided SystemUI dependency, so adding it to the @Inject constructor requires no module changes (general platform knowledge).",
       "When porting the test, note the PermissionGroupUsage constructor arity differs: 9 arguments on Android 14, 10 (trailing String) on Android 15/16."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e086bd9b0fa8487517f051f40ee0ca8a73ff9036",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48590",
     "osv_id": "ASB-A-417987184",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the compat-change based (SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, 'Q- apps') exemption in AppOpsService that marked invalid attribution tags as valid when the change was disabled for the package or calling uid, so isAttributionTagValid is no longer forced to true for any caller.",
      "confidence": "medium",
      "backport_notes": [
       "Single-file removal with no dependencies shown. It applies to branches whose AppOpsService.java still contains the mPlatformCompat SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE block, though the import hunk's context lines differ per branch. The bulletin lists Android 13 as affected, but the material includes no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "529f24023e1524fc379741a9709c650cdf754b6c",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48591",
     "osv_id": "ASB-A-305710469",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a callingUser parameter to the IMms AIDL methods that accept a client-supplied contentUri (sendMessage, downloadMessage, importMultimediaMessage, addMultimediaMessageDraft) and has MmsServiceBroker in system_server fill it from Binder.getCallingUserHandle() so MmsService can validate that the contentUri belongs to the originating user.",
      "confidence": "high",
      "backport_notes": [
       "This commit changes the IMms AIDL signature, so it must land together with the matching change in the MmsService implementation of IMms (which per the commit message performs the user-id validation); applying only the frameworks/base side provides no validation by itself. The MmsService-side change is not included in the material.",
       "Google's 14 and 15 commits have the same content with only hunk-offset differences in MmsServiceBroker.java; any fork with additional IMms implementations or extra IMms methods taking a contentUri needs the new parameter handled as well."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "eba8dce96baaccba2d76a622f955c4e36b98f36b",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 3df02a7df848",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/services/Mms"
     ]
    },
    {
     "cve": "CVE-2025-48592",
     "osv_id": "ASB-A-427113482",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The Codec2 software AV1 decoder plugin (C2SoftDav1dDec) now sets Dav1dSettings.all_layers = 0 before dav1d_open, so dav1d outputs only one layer for SVC streams instead of all layers, which the plugin was not designed to handle.",
      "confidence": "high",
      "backport_notes": [
       "This is a trivial one-line insert after dav1d_default_settings(&lib_settings) in C2SoftDav1dDec::initDecoder(). It should apply to any branch that has the dav1d plugin, provided the bundled dav1d exposes Dav1dSettings.all_layers. Branches without media/codec2/components/dav1d (e.g. the Android 14 security tags) have nothing to patch."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/av",
       "sha": "9d74d91a39f3e183d508a915734c70628176cac9",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/av"
     ]
    },
    {
     "cve": "CVE-2025-48594",
     "osv_id": "ASB-A-427206637",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The UID-triggered revoked-association cleanup in CompanionDeviceManager now looks up every package that shares the UID with PackageManager.getPackagesForUid and disassociates the revoked associations of each one. Before, it handled only the single package name returned by PackageManagerInternal.getNameForUid.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit fix. On branches without DisassociationProcessor (the Android 14 layout), equivalent logic is in CompanionDeviceManagerService. There the code uses getPackageNameByUid and getPendingRoleHolderRemovalAssociationsForUser for pending role holder removal, so apply the Android 14 variant. Check the disassociate() signature: one argument on 15, with REASON_REVOKED on 16."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9c8c99ba63bb8f8747794c3a47309c83759fee6f",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48596",
     "osv_id": "ASB-A-438098181",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In libbinder's Parcel::appendFrom, the fix replaces the unconditional \"mDataSize += len\" with \"if (mDataPos > mDataSize) mDataSize = mDataPos;\". The parcel's logical data size now grows only when the write position actually passes the current end of data.",
      "confidence": "high",
      "backport_notes": [
       "Trivial one-line replacement in Parcel::appendFrom, immediately after 'mDataPos += len;'. It should apply cleanly to any branch whose appendFrom still has 'mDataSize += len;'. Per index search, the NDK (AParcel_appendFrom) and JNI (android_os_Parcel_appendFrom) entry points both call this function."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "de36dac70667e4810e88d55c09443752ac3d4590",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48597",
     "osv_id": "ASB-A-436270922",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Moves the grant of the TRUSTED_OVERLAY surface flag for a PiP task out of system_server's RootWindowContainer#notifyActivityPipModeChanged (where it was applied as soon as the task was reported pinned) into WM Shell's PipTransition#onFinishResize, so it is only set after Shell has handled the enter-PiP transition, while WM keeps only the revocation when the task leaves pinned mode.",
      "confidence": "high",
      "backport_notes": [
       "Single commit touching two files that must go together: removing the WM grant without adding the Shell grant would leave PiP tasks without TRUSTED_OVERLAY, and adding the Shell grant without removing the WM grant leaves the early grant in place. WM Shell is built into the SystemUI apk while RootWindowContainer is in services.jar, so both must be updated together (general platform knowledge).",
       "On trees where onFinishResize differs (e.g. Android 16 adds leashOffset and mMoveToBackTransition), only move the leash/hasValidLeash locals above the enteringPip block and add the guarded tx.setTrustedOverlay(leash, true); keep the rest of the method unchanged."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "cfcef3f38c2fb053d66d1ba1aa081f62aad798a7",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48601",
     "osv_id": "ASB-A-426207912",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating PackagePreferences for packages that are not installed. The fix uses Process.INVALID_UID as the only missing-package sentinel, makes getOrCreatePackagePreferencesLocked throw on INVALID_UID, and changes read-only getters to lookups that do not create state and return defaults.",
      "confidence": "high",
      "backport_notes": [
       "Apply the commit as a whole: the constant change (UNKNOWN_UID to Process.INVALID_UID), the precondition and the getter conversions depend on each other. Differences between branches: the lock object (mPackagePreferences on 14, mLock on 15 and 16), the creationTime parameter (15 and 16), and the bundle-channel overloads (16).",
       "Tests that create preferences through a getter (e.g. mHelper.canShowBadge) break because getters no longer create state. Switch them to setShowBadge, as the upstream test diff does, and change getPackageUidAsUser stubs from thenReturn(UNKNOWN_UID) to throwing NameNotFoundException."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ed9af81926649d1e5c98fe0d375dde5d450c1af4",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48603",
     "osv_id": "ASB-A-416259832",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 200 KB size cap on an input method's SERVICE_META_DATA XML resource, enforced in InputMethodInfo before the XML is parsed, so an oversized metadata XML now fails with XmlPullParserException instead of being parsed in the system process.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit in InputMethodInfo.java with no new dependencies. The only merge point is the constructor hunk: move `Resources res = ...` above loadXmlMetaData and insert the validateXmlMetaData call. The new helper goes after the constructor, and its anchor differs (mIsVrOnly on 14, mIsVirtualDeviceOnly on 15/16)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e7824e85ad70db4cc019762ed5d51037c9b0c56e",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48604",
     "osv_id": "ASB-A-305710989",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MmsService now passes the calling user id into DownloadRequest and refuses to write a downloaded MMS PDU to a content URI whose embedded user id differs from the calling user.",
      "confidence": "high",
      "backport_notes": [
       "The fix relies on callingUser already being in scope in the MmsService download handler, which is the case in the 14, 15 and 16 diffs. Update every DownloadRequest constructor call site and any RequestManager implementation or mock, because the signatures change (the Android 14 variant has no TelephonyManager argument)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Mms",
       "sha": "ad9bf77e4577b2c4f0e81fc2f9b0824781d7fc31",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Mms"
     ]
    },
    {
     "cve": "CVE-2025-48607",
     "osv_id": "ASB-A-416490321",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes the IAppOpsService.getPackagesForOpsForDevice binder method to return a ParceledListSlice<AppOpsManager.PackageOps> instead of a plain List, so that large result sets are paginated across binder transactions instead of overflowing a single one.",
      "confidence": "high",
      "backport_notes": [
       "The three files must land together: the AIDL return type change has to be in the same build as the AppOpsService implementation and the AppOpsManager client, because all of them compile against the generated IAppOpsService stub (general platform knowledge). Keep the method's position in the AIDL so transaction codes and the native ordering described in the AIDL header stay valid. The added java.io.StringWriter import is not used by the diff.",
       "No 'Fix in fork' section was provided, so no per-file verdict for the fork can be given."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2114590c98f32dceb9f22604e81a34cb0c370b67",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48614",
     "osv_id": "ASB-A-430568718",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "RecoverySystem.rebootWipeUserData now queries DynamicSystemManager and throws a SecurityException (\"Wiping data is not allowed while in DSU mode.\") when a Dynamic System Update image is in use, so a factory reset can no longer be started through this method in DSU mode.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk. The only dependencies are android.os.image.DynamicSystemManager.isInUse() and Context.DYNAMIC_SYSTEM_SERVICE in the target tree. On branches with a different rebootWipeUserData signature (as on Android 14), apply the same insertions inside the overload that contains the DISALLOW_FACTORY_RESET check."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "aa4b01792c25a3afbba3b6727cf2bc35eb40e853",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48618",
     "osv_id": "ASB-A-404254549",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CommandParamsFactory.processLaunchBrowser now rejects a SIM Toolkit LAUNCH BROWSER proactive command with ResultCode.LAUNCH_BROWSER_ERROR when KeyguardManager.isDeviceLocked() is true. ResultException no longer asserts that LAUNCH_BROWSER_ERROR carries additional info, so the new rejection can be constructed.",
      "confidence": "high",
      "backport_notes": [
       "Take both files together. The ResultException.java hunk that removes LAUNCH_BROWSER_ERROR from the AssertionError switch is a hard dependency: ResultException(ResultCode, String) calls this(result), so without it the new throw in processLaunchBrowser would raise an AssertionError.",
       "The private constructor already receives a Context on 14/15/16 (used for context.getResources()), so the backport only adds the mContext field and its assignment. The Android 14 variant shows the hunk applies with different context lines only."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/opt/telephony",
       "sha": "8e7b0146d56be76e0a1c960a52ba76ba2603506e",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/opt/telephony"
     ]
    },
    {
     "cve": "CVE-2025-48620",
     "osv_id": "ASB-A-352024705",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes an early return in VoiceInteractionManagerService's package-change handler. When a voice interaction service is set and the recognizer's package is uninstalled, the code that resets the current voice recognition service now still runs.",
      "confidence": "high",
      "backport_notes": [
       "The diff is identical across 14, 15 and 16 (only the hunk offsets differ), so it should apply to any branch that has the curInteractor and curAssistant blocks in onSomePackagesChanged. After applying it, confirm that the target branch has the recognizer reset code after the assistant block; if it does not, removing the return has no effect."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0925826a6ad87104021a9324407c7494c075cec8",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48621",
     "osv_id": "ASB-A-266433089",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Two coordinated changes against tapjacking during window animations: InputDispatcher stops delivering motion events to windows with TouchOcclusionMode::USE_OPACITY whose alpha is below 0.5, and WM Shell's DefaultTransitionHandler lowers MAX_ANIMATION_DURATION from 3000 to 1500.",
      "confidence": "medium",
      "backport_notes": [
       "Both changes are small: a one-line constant change in DefaultTransitionHandler.java, and a 6-line early return at the top of InputDispatcher::canWindowReceiveMotionLocked that uses WindowInfo::touchOcclusionMode and WindowInfo::alpha. Apply the changes in both repos. The material shows no dependency between them.",
       "The 'code around the fix' excerpt for InputDispatcher.cpp at android-security-15.0.0_r17 shows an unrelated region (shouldPruneInboundQueueLocked), and the index snippet from android-15.0.0_r36 shows canWindowReceiveMotionLocked without the alpha check. Check that the native change is actually in the target branch instead of inferring it from the base-side constant."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "06e697583c87ad64c8ab23628268437e36abb2dc",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/native",
       "sha": "51f89fddd0a035c795bc2dec97c804efb29ce0f6",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48622",
     "osv_id": "ASB-A-412662901",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the Android copy of the Adobe DNG SDK (1.4 era) wholesale with DNG SDK 1.7.1 in platform/external/dng_sdk, with JXL and XMP support compiled out, and adapts the two in-tree consumers (skia SkRawCodec and the CTS dng-validate JNI) to the changed API.",
      "confidence": "medium",
      "backport_notes": [
       "Three-project change that must land together: the dng_sdk upgrade changes the dng_host/dng_area_task/dng_negative API, so platform/external/skia (SkRawCodec.cpp plus Android.bp linkage) and platform/cts (dng-validate-jni.cpp) need their companion commits to build against the new library.",
       "The dng_sdk Android.bp drops vendor_available and sets sdk_version: \"current\"; forks with additional libdng_sdk consumers must re-check linkage, and should keep qDNGUseXMP=0 and the source/jxl/color_encoding.h stub unless libjxl and the XMP SDK are available."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/cts",
       "sha": "586f75b10a395b311e92575e4d2ed274d7d06918",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/dng_sdk",
       "sha": "c210ca6441c5c38d9b8f2a3e4719781cc21c70d8",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/skia",
       "sha": "e729c3f74b8e39eab969169ded33c39f4ee04d5d",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "projects": [
      "platform/cts",
      "platform/external/dng_sdk",
      "platform/external/skia"
     ]
    },
    {
     "cve": "CVE-2025-48626",
     "osv_id": "ASB-A-381339822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Launcher3's OverviewCommandHelper no longer handles the TYPE_HOME command by calling startActivity(getHomeIntent()) directly. It now calls a new ISystemUiProxy.onKeyEvent(int keycode) binder method, and SystemUI sends a KEYCODE_HOME ACTION_DOWN/ACTION_UP key event.",
      "confidence": "medium",
      "backport_notes": [
       "Two-project fix: land the frameworks/base ISystemUiProxy/OverviewProxyService change first, keeping transaction id 60 as Android 16 does, then the Launcher3 change, which calls the new AIDL method.",
       "Per index search, android-15.0.0_r36 Launcher3 already has SystemUiProxy.onBackEvent(KeyEvent) and a Kotlin OverviewCommandHelper.kt, so trees based on it differ from the security-branch diff. The Android 16 shape of the change may be the closer reference there."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "99f89bb4fdd08cc242f64e730012be9c2610d9f6",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "db814647325936e51d9b73579fb0e01bd2c00a03",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2025-48628",
     "osv_id": "ASB-A-376462130",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id with ContentProvider.getUserIdFromAuthority(authority, resolvedUserId) and always applies the current-user-group check, instead of trusting Uri.getEncodedUserInfo and skipping the check when it returned null.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit in one file. The only new dependency is ContentProvider.getUserIdFromAuthority(String, int), a long-standing hidden framework API in frameworks/base (general platform knowledge). The same hunks were applied on 14, 15 and 16, so the patch is likely to apply on branches that already contain the one-argument validateIconUserBoundary(icon)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3723e843b6595a5e5440a19067ae27b0f6282c7d",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48629",
     "osv_id": "ASB-A-352518318",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "VoiceInteractionManagerService no longer falls back to the first available RecognitionService when the preferred/default recognizer package is not found; it now only auto-selects a service whose application has ApplicationInfo.FLAG_SYSTEM, and returns null otherwise.",
      "confidence": "high",
      "backport_notes": [
       "The fix is a single self-contained hunk in VoiceInteractionManagerService.java and is identical on 14/15/16 apart from the line offset. When porting, check that callers of the selection method tolerate a null ComponentName from this fallback path, which previously returned available.get(0)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d8faf81970e4ff2a02b3d363c9da86e8750fc403",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48632",
     "osv_id": "ASB-A-443742829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a shared validateDisplayName() length check (DISPLAY_NAME_LENGTH_LIMIT = 1024) to the private AssociationRequest constructor and the @hide setDisplayName() in core/java/android/companion/AssociationRequest.java, so the limit is enforced on every in-class write path instead of only in Builder.setDisplayName().",
      "confidence": "high",
      "backport_notes": [
       "This is a single self-contained commit in one file. Between 14/15 and 16, only the constructor signature differs (16 adds parameters including skipRoleGrant and deviceIcon). Place validateDisplayName(displayName) as the first statement of whichever private constructor exists, and check whether the Parcel constructor in the target tree goes through it."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e72cdc5c770dae7e36fcf93ad508d2216a4de6eb",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48633",
     "osv_id": "ASB-A-417988098",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() now calls AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of looping over users and calling AccountManager.getAccounts() per user. Per the commit title, the old query did not return all accounts regardless of visibility.",
      "confidence": "medium",
      "backport_notes": [
       "The patch needs AccountManagerService.getSingleton() and AccountManagerService.getAllAccountsForSystemProcess() to exist in the target branch. The material does not show whether these already existed or came from a companion change, so verify before applying to Android 13 or older forks.",
       "The hunk is identical across 14, 15 and 16 apart from line offsets. Only the anchor context of the import line differs: on Android 14 it follows com.android.server.PersistentDataBlockManagerInternal."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e74b3376e899f734b10c38b1f989992666f052dd",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48639",
     "osv_id": "ASB-A-301952571",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Reduces the MAX_ANIMATION_DURATION constant in WindowManager Shell's DefaultTransitionHandler from 3000 to 1500, shortening the time a default transition animation can run.",
      "confidence": "high",
      "backport_notes": [
       "Single-constant change with no dependencies shown. On 14, 15 and 16 it is the same one-line edit at different hunk offsets (16 has a neighbouring SIZE_CHANGE_ANIMATION_DURATION line). Fork code may differ around it."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "06e697583c87ad64c8ab23628268437e36abb2dc",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 6d1697c96c5c mentions bug 266433089, carried by 06e697583c87 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    }
   ],
   "tags": [
    "android-security-15.0.0_r12"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "441823943"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "82af7e1a18440c2098cda30b1b792e7d6ae44b19",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "d4e0771852668e41d74be8eb2f4279dd22b91ae0",
       "subject": "Restricting UserBuild from presistent carrierConfig Override",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "a746643a1d9b43bb9184511b515df349e0c565b3",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r12"
     ],
     "tag": "android-security-15.0.0_r12"
    },
    {
     "bug_ids": [
      "395640609",
      "419095873"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3540361264df0e5e3436653dd14dd3ef19e2976c",
       "subject": "Remove all pending keyguard exits on SHOW",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r12"
     ],
     "tag": "android-security-15.0.0_r12"
    },
    {
     "bug_ids": [
      "443970347"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "e4d2d212c5ba15e85b06f3647aee3bdbf8675edb",
       "subject": "Add each blur region as a separate request",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r12"
     ],
     "tag": "android-security-15.0.0_r12"
    },
    {
     "bug_ids": [
      "236134583",
      "236134775"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "c5beb708454eb7c8b8efdc1d7e0425b9a789783a",
       "subject": "Import translations. DO NOT MERGE ANYWHERE",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": [
        "restrict_automerge"
       ]
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "6a6a1f7f54a70410d06037d62b63faae7ec3622a",
       "subject": "Backport BT pairing dialog changes",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r12"
     ],
     "tag": "android-security-15.0.0_r12"
    },
    {
     "bug_ids": [
      "435188844"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/IntentResolver",
       "sha": "c6bc214f4502f037b15d31ef1a32e7a9a9494ef0",
       "subject": "Verify caller access rights for the additional content URI.",
       "tag": "android-security-15.0.0_r12",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r12"
     ],
     "tag": "android-security-15.0.0_r12"
    }
   ],
   "coverage": {
    "n_ingested_tags": 18,
    "first_ingested_tag": "android-security-15.0.0_r1",
    "latest_ingested_tag": "android-security-15.0.0_r18",
    "window_base_tag": "android-15.0.0_r1",
    "window_start": "2024-09-03",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2023-40130": {
     "cve": "CVE-2023-40130",
     "summary": "Wraps the Context.unbindService(mConnection) call in CallRedirectionProcessor.notifyTimeout in a try/catch for IllegalArgumentException so that a timeout on an already-unregistered CallRedirectionServiceConnection no longer throws out of Telecom.",
     "component": "platform/packages/services/Telecomm, call redirection (CallRedirectionProcessor / CallRedirectionServiceConnection)",
     "weakness": "Unhandled exception (uncaught IllegalArgumentException from Context.unbindService) in a system service path. The Android 14 commit for the same bug instead adds a missing onBindingDied handler so the service is unbound when the binding dies (its comment says this avoids 'background stating activity leaks').",
     "root_cause": "In CallRedirectionProcessor.notifyTimeout (src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java), when the call redirection timeout fires and mConnection is non-null, the code unconditionally calls mContext.unbindService(mConnection). If that ServiceConnection is not registered when the timeout occurs (per the commit message), Context.unbindService throws IllegalArgumentException. Nothing caught it, so the exception propagated out of notifyTimeout (the commit message says the fix prevents a crash). Because the exception was thrown before those assignments, mConnection = null and mService = null never ran on that path.",
     "fix_analysis": [
      "CallRedirectionProcessor.java:notifyTimeout (Android 15/16 commit 3623312367a5 / a43a880beaa6): the existing mContext.unbindService(mConnection) call is wrapped in try { ... } catch (IllegalArgumentException e) { Log.e(this, e, \"Error unbinding the connection\"); }. Control flow then continues as before: mConnection = null and mService = null run whether or not the unbind succeeded, so the processor state is reset and the exception no longer escapes.",
      "CallRedirectionProcessor.java:CallRedirectionServiceConnection (Android 14 commit 18299e2d9062, 'Fix vulnerability in CallRedirectionService'): adds an onBindingDied(ComponentName) override that takes mTelecomLock and calls finishCallRedirection(), so a died binding is unbound (the commit comment says this avoids 'background stating activity leaks'). This is a different change from the 15/16 commit. On Android 14 the resolver links this commit to the CVE by bug_id."
     ],
     "completeness": [
      "The try/catch covers only the unbindService call in notifyTimeout. The same unbind in finishCallRedirection (lines 117-121 in the Android 15 r17 context) is left unguarded. Whether that call can run with an unregistered connection is not shown.",
      "The fix tolerates the unregistered-connection state; it does not prevent that state from arising. The material does not show what sequence leaves mConnection non-null while unregistered.",
      "The commit adds no test change (Test: m Telecom only).",
      "The 14-branch commit (onBindingDied) and the 15/16-branch commit (try/catch) are different changes; the material does not show both on any single branch."
     ],
     "behavior_changes": [
      "On a redirection timeout with an unregistered connection, Telecom now logs 'Error unbinding the connection' at error level and continues instead of throwing; mConnection and mService are reset to null.",
      "No API, timeout value or validation changes; behavior when the connection is registered is unchanged."
     ],
     "preconditions": [
      "An outgoing call is being processed by Telecom's CallRedirectionProcessor with a carrier or user-defined call redirection service connection. Per index search, outgoing calls are placed through TelecomManager.placeCall (for example Dialer TelecomUtil.placeCall); how that path reaches CallRedirectionProcessor is not shown.",
      "The call redirection timeout fires (notifyTimeout) while mConnection is non-null but the ServiceConnection is not registered, per the commit message.",
      "The exact sequence producing the unregistered state is not shown in the material."
     ],
     "per_major": {
      "14": "different fix: the resolver matches by bug_id to commit 18299e2d9062 in android-security-14.0.0_r1, which adds an onBindingDied override that calls finishCallRedirection; 2/2 lines of that region present at android-security-14.0.0_r29. The material does not show the 15/16 try/catch change on 14.",
      "15": "same fix: commit 3623312367a5 (Change-Id I9494b1bf209579125b30fb859ad62cd58b5dffd7) in android-security-15.0.0_r12; 7/7 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit a43a880beaa6 (same Change-Id) in android-security-16.0.0_r3; 7/7 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 7/7 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The 15/16 change is a self-contained hunk in notifyTimeout (6 insertions, 2 deletions) with no shown dependencies; it applies wherever the 'if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; }' block exists in notifyTimeout. finishCallRedirection has the same unguarded call; whether to guard it too is the maintainer's decision.",
      "For Android 14-based trees, Google's recorded fix is the separate onBindingDied commit (18299e2d9062). The material does not show the notifyTimeout try/catch on 14, so check for both changes."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact sequence that leaves mConnection non-null while the connection is not registered.",
      "How the Telecom crash maps to the bulletin's EoP classification; the commit message only says the fix prevents a crash.",
      "Whether the onBindingDied override from the Android 14 commit is present on Android 15/16/17 (the provided context window ends before that part of the class).",
      "Whether the unguarded unbindService in finishCallRedirection can be reached in the same unregistered state.",
      "How TelecomManager.placeCall callers reach CallRedirectionProcessor."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22420": {
     "cve": "CVE-2025-22420",
     "summary": "PreferencesHelper.createNotificationChannel now verifies, via a new shared PermissionHelper.grantUriPermission helper, that the creating uid can read/grant a content:// channel sound Uri, and replaces the sound with the default notification Uri when the UriGrantsManager check throws SecurityException.",
     "component": "platform/frameworks/base, NotificationManagerService / notification channel preferences (services/core/java/com/android/server/notification)",
     "weakness": "Missing permission check on a caller-supplied content Uri (channel sound) at channel creation time",
     "root_cause": "PreferencesHelper.createNotificationChannel stored whatever sound Uri the calling app placed in a new NotificationChannel without checking that the app itself could read or grant that Uri. Existing checks were in NotificationRecord.visitGrantableUri (run when a notification is posted) and in NotificationManagerService for sound changes on channel updates (exercised by the updateNotificationChannelFromPrivilegedListener test). Per the commit message, \"Uri permissions would only be checked by NotificationRecord when a notification was posted\", so a channel could persist a content:// sound Uri the creating uid had no access to.",
     "fix_analysis": [
      "PermissionHelper.java: adds static grantUriPermission(UriGrantsManagerInternal ugmInternal, Uri uri, int sourceUid). It returns immediately if uri is null or its scheme is not ContentResolver.SCHEME_CONTENT; otherwise, under Binder.withCleanCallingIdentity, it calls ugmInternal.checkGrantUriPermission(sourceUid, null, ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))), which throws SecurityException if the uid cannot grant the Uri.",
      "PreferencesHelper.java: constructor gains a UriGrantsManagerInternal ugmInternal parameter stored in mUgmInternal. In createNotificationChannel, in the new-channel branch right after clearLockedFieldsLocked(channel), it calls PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid) inside try/catch; on SecurityException it calls channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT) so the channel is still created but with the default sound. The code comment notes it only checks new channels because regular apps can only set sound before creating.",
      "NotificationManagerService.java: passes mUgmInternal into the PreferencesHelper constructor; the existing sound-change check for channel updates (the block comparing originalSoundUri with soundUri) is replaced by a call to PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid), same semantics as before.",
      "NotificationRecord.java: visitGrantableUri now only early-returns on uri == null, then delegates to PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid) instead of inlining the checkGrantUriPermission call and manual clearCallingIdentity/restoreCallingIdentity; the content-scheme filter moved into the helper.",
      "Tests: PreferencesHelperTest adds testCreateChannel_hasSoundUriPermission_useCustomSound, testCreateChannel_noSoundUriPermission_replaceWithDefaultSound, and fileSchemaIgnored / resourceSchemaIgnored cases; NotificationManagerServiceTest adds testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm. All PreferencesHelper constructions gain the mUgmInternal argument.",
      "Android 15 material contains three commits: 6c7c10e6234b (android-security-15.0.0_r10), a revert 5475e757915a (r12, reason b/434558786) and ea0fc122e4df (r12) with the same subject. The production code of 6c7c10e6234b and ea0fc122e4df is identical; the only difference is in PreferencesHelperTest, where 6c7c10e6234b constructs mXmlHelper as TestPreferencesHelper at one site and ea0fc122e4df uses PreferencesHelper. The fix code is present at android-security-15.0.0_r17."
     ],
     "completeness": [
      "Covers only the new-channel branch of PreferencesHelper.createNotificationChannel; the update branch for existing channels is not checked there (the code comment states regular apps can only set sound before creating, and channel-update sound changes are checked in NotificationManagerService).",
      "Only content:// URIs are checked; file:// and android.resource:// sound URIs bypass the check by design (tests fileSchemaIgnored and resourceSchemaIgnored confirm). Only FLAG_GRANT_READ_URI_PERMISSION is verified.",
      "Channels loaded or restored from XML (readXml / backup restore paths of PreferencesHelper) are not touched by this diff; the material does not show whether a check is needed there.",
      "Side effect in NotificationRecord.visitGrantableUri: the scheme filter now lives inside the helper, which returns silently for non-content URIs, so the surrounding code proceeds to the mGrantableUris bookkeeping whereas before it returned before that point. The material does not show what consumes mGrantableUris.",
      "NotificationManagerService passes its existing mUgmInternal field (the same one used by the update-path check) to PreferencesHelper; tests stub a mock."
     ],
     "behavior_changes": [
      "Creating a channel with a content:// sound Uri the app cannot read/grant no longer stores that Uri: on Android 15/16 the channel is created with Settings.System.DEFAULT_NOTIFICATION_URI and Notification.AUDIO_ATTRIBUTES_DEFAULT instead; on Android 14 the SecurityException propagates to the caller and the channel is not created (testCreateChannel_noSoundUriPermission_contentSchemeVerified asserts the channel is null).",
      "UriGrantsManagerInternal.checkGrantUriPermission is now invoked at channel creation time (with cleared calling identity), not only at notification post time.",
      "PreferencesHelper constructor signature changes (new UriGrantsManagerInternal parameter), affecting all in-tree constructions including tests.",
      "NotificationRecord.visitGrantableUri no longer filters non-content schemes before the mGrantableUris bookkeeping (filter moved into the helper, see completeness)."
     ],
     "preconditions": [
      "An app creates a new notification channel (path reaching the new-channel branch of PreferencesHelper.createNotificationChannel).",
      "The channel's sound Uri uses the content:// scheme and refers to a Uri the creating uid cannot read or grant; non-content schemes are not checked.",
      "Bulletin: High severity EoP; affected Android 13, 14, 15, 16, 16-qpr2-next. No further trigger detail is given in the material."
     ],
     "per_major": {
      "14": "adapted fix: 04a5d5dd0910 in android-security-14.0.0_r19. Same PermissionHelper.grantUriPermission helper and PreferencesHelper hook, but no try/catch fallback: SecurityException propagates and the channel is not created. PreferencesHelper constructor differs (ugmInternal inserted before SysUiStatsEvent.BuilderFactory; no PermissionManager/UserProfiles/Clock params). NotificationRecord.visitGrantableUri keeps its SYSTEM_UID early return and NotificationRecordTest is updated to register the UriGrantsManagerInternal mock via LocalServices. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix as 16: 6c7c10e6234b (android-security-15.0.0_r10); android-security-15.0.0_r12 also contains a revert 5475e757915a (b/434558786) and ea0fc122e4df with identical production code (test-file difference only). Fix code present (8/8 lines) at android-security-15.0.0_r17.",
      "16": "same fix: fb8f76eca907 in android-security-16.0.0_r3, with try/catch fallback to default sound; tests use the TestPreferencesHelper subclass whose constructor also gains the ugmInternal parameter. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 8/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "On Android 15, 6c7c10e6234b and ea0fc122e4df carry identical production code; they differ only in one PreferencesHelperTest construction site (TestPreferencesHelper vs PreferencesHelper), so check whether your test tree has TestPreferencesHelper before picking the test hunk.",
      "The PreferencesHelper constructor change requires updating every construction site (NotificationManagerService and all test instantiations). Decide between the 14-style throw and the 15/16-style default-sound fallback; Google chose the fallback on 15/16 to avoid app crashes per the code comment."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete reason for the Android 15 revert (b/434558786) is not in the material.",
      "The exact ordering of the revert 5475e757915a and ea0fc122e4df within android-security-15.0.0_r12 is not established by the material (blob indexes and commit message point to different commits).",
      "What consumes NotificationRecord.mGrantableUris and whether recording non-content URIs there has any effect is not shown.",
      "Whether XML restore paths in PreferencesHelper can introduce unchecked content:// sound URIs is not shown.",
      "Initialization order of mUgmInternal relative to PreferencesHelper construction in NotificationManagerService is not shown.",
      "Android 13 is listed as affected by the bulletin but no Android 13 commit is included in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22432": {
     "cve": "CVE-2025-22432",
     "summary": "Telecom's CallRedirectionProcessor now unbinds the CallRedirectionService connection and clears mConnection/mService when the redirection timeout fires in notifyTimeout(). Before the fix, the service stayed bound after the timeout.",
     "component": "platform/packages/services/Telecomm, call redirection (com.android.server.telecom.callredirection.CallRedirectionProcessor)",
     "weakness": "Missing resource release: the service connection is not unbound on the timeout path (improper resource shutdown)",
     "root_cause": "CallRedirectionProcessor binds to a user-defined or carrier CallRedirectionService (bindServiceAsUser) while an outgoing call is being placed. If the service did not respond within the timeout, notifyTimeout() only forwarded mService.notifyTimeout() to the remote service and returned. finishCallRedirection() calls mContext.unbindService(mConnection) and nulls mConnection and mService, but the timeout path never unbound the connection. As a result, Telecom stayed bound to the redirection service after the redirection had been abandoned.",
     "fix_analysis": [
      "src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java:notifyTimeout (commit 1b081682e2ad on 15, 284cf771ae8f on 14): after the optional mService.notifyTimeout() call (guarded for RemoteException), the method logs that the redirection timed out. If mConnection != null, it calls mContext.unbindService(mConnection) and sets mConnection = null. It then sets mService = null. This mirrors the cleanup that finishCallRedirection() already does.",
      "src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java:notifyTimeout (follow-up a43a880beaa6 in android-security-16.0.0_r3, also present in the 15 r17 code context): wraps the new mContext.unbindService(mConnection) call in try/catch IllegalArgumentException and logs the error. mConnection is still set to null.",
      "tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java: the carrier timeout and user-defined timeout tests now check verify(mContext, times(1)).unbindService(any(ServiceConnection.class)) after the timeout completes."
     ],
     "completeness": [
      "Covers the timeout path (notifyTimeout) for both service types. The same code branches on mServiceType for SERVICE_TYPE_CARRIER and SERVICE_TYPE_USER_DEFINED.",
      "finishCallRedirection() already unbound the connection before this change and is unchanged. The fix brings notifyTimeout() in line with it.",
      "The initial fix (14 r19 and 15 r7) calls unbindService without exception handling. The follow-up commit (16 r3, also visible in the 15 r17 context) adds the IllegalArgumentException catch. Branches that carry only the initial commit lack that guard.",
      "The fix does not change the timeout values or the binding flags. It only releases the binding once the timeout has elapsed."
     ],
     "behavior_changes": [
      "On redirection timeout, Telecom now calls Context.unbindService on the CallRedirectionServiceConnection and releases its binding to the redirection service.",
      "New Log.i line: 'notifyTimeout: call redirection has timed out so unbinding the connection'.",
      "With the follow-up, an IllegalArgumentException from unbindService is logged (Log.e 'Error unbinding the connection') instead of propagated."
     ],
     "preconditions": [
      "An outgoing call is being placed and Telecom binds to a user-defined or carrier CallRedirectionService via bindServiceAsUser (CallRedirectionProcessor.performCallRedirection, per the test setup).",
      "The bound service does not finish the redirection before the carrier or user-defined timeout, so notifyTimeout() is invoked (the tests trigger this by waiting past the timeout with waitForHandlerActionDelayed)."
     ],
     "per_major": {
      "14": "same fix: 284cf771ae8f in android-security-14.0.0_r19. The source diff is identical to the 15 commit (adds the unbind in notifyTimeout, no try/catch). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: 1b081682e2ad in android-security-15.0.0_r7 (cherry-pick, Change-Id I76a65c3993f5107652f98bbe0283043ecff27a7c). The android-security-15.0.0_r17 code context also shows the IllegalArgumentException try/catch follow-up. 8/8 lines present.",
      "16": "adapted: the unbind-on-timeout change was already in the 16 base (the parent blob 15b8aa942 of the 16 commit is the post-fix file). android-security-16.0.0_r3 carries the follow-up a43a880beaa6, which wraps unbindService in try/catch IllegalArgumentException. 7/7 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). android-security-17.0.0_r1 already contains the patched region (8/8 lines)."
     },
     "backport_notes": [
      "Self-contained 8-line addition to notifyTimeout() in CallRedirectionProcessor.java. Apply the initial unbind commit first, then the follow-up try/catch (a43a880beaa6). Context.unbindService throws IllegalArgumentException for a connection that is not registered (general platform knowledge).",
      "The test changes only add verify(mContext).unbindService(...) assertions to the existing timeout tests."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe the concrete EoP consequence of leaving the redirection service bound (what the reported APK achieved), beyond the bulletin's High/EoP rating.",
      "Which apps can act as the user-defined or carrier redirection service in the reported scenario, and what conditions an attacker needs.",
      "Whether android-security-14.0.0_r29 also carries the IllegalArgumentException follow-up (only the 8-line initial region is confirmed present).",
      "Which commit originally brought the unbind change into Android 16 (it predates the listed 16 security commit)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32328": {
     "cve": "CVE-2025-32328",
     "summary": "The Autofill system service now inflates the fill service's RemoteViews presentations (fill UI, fill dialog, save UI) with a Context created for the current foreground user instead of the system_server (user 0) context, and clears the Binder calling identity around PackageManager lookups of the service's custom icon and label.",
     "component": "platform/frameworks/base, services/autofill (FillUi, DialogFillUi, SaveUi, Session, Helper)",
     "weakness": "Wrong user context when applying RemoteViews supplied by the autofill service (cross-user confusion: the system_server user 0 context was used)",
     "root_cause": "FillUi, DialogFillUi and SaveUi wrapped the system_server Context in a ContextThemeWrapper (mContext) and passed it to RemoteViews.applyWithTheme for the presentations from the FillResponse. Per the commit message, this made Autofill inflate views as the system_server user (0) instead of the current foreground user. Separately, Session.getServiceIcon and the custom-label lookup in Session called PackageManager.getDrawable/getText with the incoming Binder calling identity still in place.",
     "fix_analysis": [
      "Helper.java: adds Helper.getUserContext(Context), annotated @RequiresPermission(INTERACT_ACROSS_USERS). It reads ActivityManager.getCurrentUser() and returns context.createContextAsUser(UserHandle.of(userId), 0). When sDebug is on, it logs the ContentResolver user id of the new context.",
      "FillUi.java: the constructor sets a new final field mUserContext = Helper.getUserContext(mContext). Four applyWithTheme calls now receive mUserContext instead of mContext: response.getPresentation(), headerPresentation, footerPresentation, and each dataset presentation in the loop. mContext is still used by the LayoutInflater for Autofill's own decor layout. dump() now prints mUserContext. The constructor gets @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") with the comment 'System has all permissions, see b/228957088'.",
      "DialogFillUi.java: same pattern. It adds mUserContext = Helper.getUserContext(mContext), and three presentation.applyWithTheme calls, including the one in the per-dataset loop, now use mUserContext. The constructor gets the same SuppressWarnings annotation.",
      "SaveUi.java: the anonymous ContextThemeWrapper is now built on Helper.getUserContext(context) instead of context. That wrapper overrides startActivity and is passed to the LayoutInflater for R.layout.autofill_save. The constructor gets the same SuppressWarnings annotation.",
      "Session.java: getServiceIcon and the custom-label lookup wrap the PackageManager.getDrawable(...) and getText(...) calls for the response's iconResourceId and customServiceNameId in Binder.clearCallingIdentity()/restoreCallingIdentity(token)."
     ],
     "completeness": [
      "Every applyWithTheme call that the diff touches in FillUi and DialogFillUi now uses mUserContext, and the whole SaveUi wrapper context is now a user context. The material does not show whether other call sites exist.",
      "The user comes from ActivityManager.getCurrentUser() (the foreground user), not from the session's user id. The material does not show whether the two can differ in a way that matters.",
      "Autofill's own layouts (R.layout.autofill_fill_dialog, FillUi decor) are still inflated with mContext in FillUi and DialogFillUi.",
      "The Session.java change only clears the calling identity. The icon and label are still resolved through mService.getMaster().getContext()."
     ],
     "behavior_changes": [
      "RemoteViews from the autofill service in the fill UI and fill dialog, and the SaveUi context, are now applied with a Context created via createContextAsUser for the current user.",
      "SaveUi's overridden startActivity/resolveActivity now runs on a wrapper built on the foreground user's context.",
      "Custom service icon and label lookups in Session run under a cleared Binder identity.",
      "When sDebug is enabled, Helper.getUserContext writes a new debug log line. FillUi dump() now includes mUserContext."
     ],
     "preconditions": [
      "An autofill service is enabled and returns a FillResponse with RemoteViews presentations, or with a custom iconResourceId or serviceDisplayNameResourceId.",
      "The code runs in system_server, which the commit message calls user 0. The fix was tested with CtsAutoFillServiceTestCases --user-type secondary_user, which suggests the issue matters when a non-system user is in the foreground."
     ],
     "per_major": {
      "14": "same fix (minor context differences): platform/frameworks/base f08b9104a6e9 ([RESTRICT AUTOMERGE]) in android-security-14.0.0_r24. In FillUi, mUserContext is assigned right after mContext, and line offsets differ. The patched region (8/8 lines) is present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 3ca035e4cdf3 ([RESTRICT AUTOMERGE]) in android-security-15.0.0_r12. The patched region (8/8 lines) is present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin. The patched region (8/8 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a single commit. Helper.getUserContext must be present because FillUi, DialogFillUi and SaveUi call it. The Android 14 and 15 versions differ only in context lines and offsets, for example where mUserContext is placed in FillUi."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the concrete consequence of inflating as user 0 or the exact EoP trigger.",
      "The material does not show whether the bulletin's listed 16-qpr2-next version received the same commit.",
      "The material does not show why ActivityManager.getCurrentUser() was chosen over the session's user id.",
      "The material does not show which presentations the first two changed DialogFillUi calls handle, or whether SaveUi custom descriptions use the new context."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32329": {
     "cve": "CVE-2025-32329",
     "summary": "Autofill's FillUi, DialogFillUi and SaveUi now use a Context created for the current foreground user (new Helper.getUserContext) instead of the system_server user-0 Context when applying service-supplied RemoteViews presentations (and, in SaveUi, as the base Context of the save dialog). Session now clears the Binder calling identity around the custom service icon/label resource lookups.",
     "component": "platform/frameworks/base, services/autofill (AutofillManagerService UI: FillUi, DialogFillUi, SaveUi, Session, Helper)",
     "weakness": "Cross-user context confusion: RemoteViews supplied by the autofill service were inflated with the system_server (user 0) Context rather than a Context for the current foreground user",
     "root_cause": "In services/autofill/java/com/android/server/autofill/ui/FillUi.java and DialogFillUi.java, the UI classes wrapped the system_server Context in a ContextThemeWrapper (mContext) and passed it to RemoteViews.applyWithTheme for the response presentation, the header, the footer and each dataset presentation. In SaveUi.java the same system Context was the base of the dialog's ContextThemeWrapper. As a result, inflation of the service-controlled RemoteViews ran as user 0 instead of the current foreground user (commit: \"Fix Autofill Inflating as User 0\"). The commit's test runs as a secondary user, which indicates the problem appears when the foreground user is not user 0.",
     "fix_analysis": [
      "Helper.java: adds Helper.getUserContext(Context), annotated @RequiresPermission(INTERACT_ACROSS_USERS). It reads ActivityManager.getCurrentUser() and returns context.createContextAsUser(UserHandle.of(userId), 0), and logs the resulting content-resolver user id when sDebug is set.",
      "FillUi.java:FillUi constructor: adds the field mUserContext = Helper.getUserContext(mContext) and replaces mContext with mUserContext in four applyWithTheme calls: response.getPresentation(), headerPresentation, footerPresentation, and the per-dataset presentation loop. mContext is still used for the LayoutInflater. dump() now prints mUserContext. The constructor gets @SuppressWarnings(\"AndroidFrameworkRequiresPermission\").",
      "DialogFillUi.java:DialogFillUi constructor: adds mUserContext = Helper.getUserContext(mContext) and uses it in three applyWithTheme calls: two that apply a presentation into the decor and one in the dataset loop. It gets the same @SuppressWarnings annotation.",
      "SaveUi.java:SaveUi constructor: the anonymous ContextThemeWrapper is now built on Helper.getUserContext(context) instead of the raw system context, so the LayoutInflater created from it (which inflates autofill_save) uses the foreground-user Context. It gets the same @SuppressWarnings annotation.",
      "Session.java:getServiceIcon and getServiceLabel: the PackageManager.getDrawable / getText lookups for the FillResponse custom icon resource id and custom service display name resource id are now wrapped in Binder.clearCallingIdentity / restoreCallingIdentity."
     ],
     "completeness": [
      "The diff switches four applyWithTheme call sites in FillUi and three in DialogFillUi to the user Context. SaveUi switches the base Context of the whole dialog. The full files are not shown, so the material does not prove that no other call sites remain.",
      "Helper.getUserContext uses ActivityManager.getCurrentUser(), i.e. the foreground user, not the user id of the Session that owns the UI. The code does not tie the Context to the session's user.",
      "The user Context is computed once in each UI constructor (final fields) and is not refreshed if the current user changes while the UI object exists.",
      "FillUi and DialogFillUi still inflate their decor layouts (for example autofill_fill_dialog) through the system mContext. In SaveUi, autofill_save is inflated through the new user-based Context.",
      "The Session change only adds identity clearing around the icon/label resource lookups. It does not change which Context's PackageManager is used (mService.getMaster().getContext())."
     ],
     "behavior_changes": [
      "Service-supplied RemoteViews in the fill, fill-dialog and save UIs are now inflated with a Context created for the current foreground user (createContextAsUser).",
      "New debug log line in Helper.getUserContext when sDebug is enabled; FillUi.dump prints mUserContext.",
      "Custom service icon/label lookups in Session run with a cleared Binder calling identity.",
      "The autofill UI constructors now call a helper annotated @RequiresPermission(INTERACT_ACROSS_USERS). The commit comment says the system has all permissions, and the lint warning is suppressed in the constructors."
     ],
     "preconditions": [
      "An autofill service is enabled and returns a FillResponse (or SaveInfo) whose presentations the system shows through FillUi, DialogFillUi or SaveUi.",
      "The foreground user is not user 0 (the commit's test runs CtsAutoFillServiceTestCases with --user-type secondary_user).",
      "Reached through normal autofill UI flows from Session (for example the Session method that calls getUiForShowing().showFillDialog, shown in the context)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base f08b9104a6e9 in android-security-14.0.0_r24 (RESTRICT AUTOMERGE). The change is equivalent to the 15 commit; line offsets differ (for example Session.java ~4490/4520 vs 5117/5147), and the position of the mUserContext assignment in the FillUi constructor differs slightly. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 3ca035e4cdf3 in android-security-15.0.0_r12 (RESTRICT AUTOMERGE cherry-pick of c59352425ff3, Change-Id I8a2c96259976e61d297dec914d10d86e585a6d6f). 8/8 patched lines are present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin as reflected in the per-major facts. The patched region is already present 8/8 at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. The patched region is present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching only services/autofill. The 14 and 15 cherry-picks are equivalent apart from line offsets and minor placement in the FillUi constructor, so it should port to trees where the FillUi/DialogFillUi/SaveUi constructors and Session.getServiceIcon/getServiceLabel exist. Android 13 is listed as affected, but no 13 commit is in the material.",
      "Helper.getUserContext relies on Context.createContextAsUser and on INTERACT_ACROSS_USERS being held by system_server. Keep the @SuppressWarnings(\"AndroidFrameworkRequiresPermission\") annotations, or the errorprone permission check will flag the constructors (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific lookup during RemoteViews inflation (resources, URIs, drawables) led to the EoP; the material only shows that inflation ran as user 0.",
      "Whether a separate Android 13 commit exists (the bulletin lists 13 as affected, but no 13 fix is provided).",
      "Why the bulletin lists 16-qpr2-next while the per-major facts treat 16 as not listed. The fix lines are present in 16.0.0_r8, but the originating 16 commit is not shown.",
      "Whether using ActivityManager.getCurrentUser() instead of the Session's own user id matters for multi-user or multi-display configurations.",
      "Whether the custom description and other RemoteViews in SaveUi are inflated through the new base Context; the relevant code is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48525": {
     "cve": "CVE-2025-48525",
     "summary": "CompanionDeviceManager now revokes Notification Listener Service (NLS) access for a package's listener components when that package's last companion association is removed. Before, notification access stayed in place after disassociation.",
     "component": "platform/frameworks/base, services/companion (CompanionDeviceManagerService / DisassociationProcessor)",
     "weakness": "Missing privilege revocation: notification listener access was not revoked when the package's companion associations were removed. The code had a \"TODO: also revoke notification access\" comment.",
     "root_cause": "DisassociationProcessor.disassociate(int id) removed the association, cleared system data transfer requests and revoked the device-profile role when no other association used it. It did not touch notification listener access, and the code carried a TODO comment \"also revoke notification access\". As a result, a package's NotificationListenerService components kept notification access after all of its companion associations were gone.",
     "fix_analysis": [
      "services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java: the constructor CompanionDeviceManagerService(Context) now gets NotificationManager through context.getSystemService(NotificationManager.class). It passes it as a new last argument to the DisassociationProcessor constructor.",
      "services/companion/java/com/android/server/companion/association/DisassociationProcessor.java: the constructor gains a @NonNull NotificationManager parameter, stored in the new field mNotificationManager. Imports are added for NotificationManager, Intent, ResolveInfo, NotificationListenerService and java.util.List. The TODO comment is removed.",
      "DisassociationProcessor.disassociate(int id): right after mAssociationStore.removeAssociation(association.getId()), inside Binder.withCleanCallingIdentity, it checks mAssociationStore.getAssociationsByPackage(userId, packageName).isEmpty(). If the check passes, it builds an Intent for NotificationListenerService.SERVICE_INTERFACE and resolves services with mContext.getPackageManager().queryIntentServicesAsUser(nlsIntent, 0, userId). For every ResolveInfo whose component package equals packageName, it calls mNotificationManager.setNotificationListenerAccessGranted(componentName, false).",
      "The revocation runs before the existing role revocation block, which revokes the role when it is not in use by other associations. It runs with cleared calling identity."
     ],
     "completeness": [
      "Covers: disassociations that go through DisassociationProcessor.disassociate(int id). Revocation is per user (the association's userId) and applies to every NLS component the package declares that the query returns.",
      "The revocation does not depend on how the access was granted: it also revokes NLS access for that package that was granted outside CDM, once the package's last CDM association is removed.",
      "On Android 15/16, only the disassociate() path is changed. The Android 14 variant instead puts the logic in CompanionDeviceManagerService under the AssociationStore CHANGE_TYPE_REMOVED branch.",
      "Only notification listener access is revoked. No other grant is touched beyond the role revocation that already existed.",
      "At android-security-15.0.0_r17, the DisassociationProcessor imports also include PackageManager and RolesUtils.NLS_PROFILES, and only 7/8 patched lines remain. This suggests a later change to this region. Its content is not in the material."
     ],
     "behavior_changes": [
      "After a package's last companion association is removed for a user, that package's NotificationListenerService components returned by the query lose notification access through NotificationManager.setNotificationListenerAccessGranted(component, false).",
      "This includes NLS access granted outside CDM, which would then have to be granted again.",
      "The DisassociationProcessor constructor signature changes (new NotificationManager parameter). Any code or test that constructs it directly is affected."
     ],
     "preconditions": [
      "A package has at least one CDM association for a user and declares a NotificationListenerService component that has notification access.",
      "The association is removed through DisassociationProcessor.disassociate(int id), and it is the package's last association for that user."
     ],
     "per_major": {
      "14": "adapted fix: commit 9473c3649017 (android-security-14.0.0_r22) adds the same revocation logic inline in CompanionDeviceManagerService under `if (changeType == AssociationStore.CHANGE_TYPE_REMOVED)`. It uses getContext().getSystemService(NotificationManager.class) and mAssociationStore.getAssociationsForPackage. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "this fix: commit 52c030fa9ba2 (android-security-15.0.0_r10) in DisassociationProcessor.disassociate(int id), plus NotificationManager injection from CompanionDeviceManagerService. 7/8 lines present at android-security-15.0.0_r17 (later change, content unknown).",
      "16": "same fix: commit 31989869759e (android-security-16.0.0_r3), same logic. The differences are the disassociate(int id, String reason) and removeAssociation(id, reason) signatures and a local packageManagerInternal variable. 7/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: not listed by the bulletin. 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "For trees with DisassociationProcessor (15/16 style), apply both hunks: fetch NotificationManager in the CompanionDeviceManagerService constructor and pass it in. Any test that constructs DisassociationProcessor needs the extra argument. For 14-style trees, follow 9473c3649017 (CHANGE_TYPE_REMOVED branch in CompanionDeviceManagerService), noting the method name is getAssociationsForPackage there, not getAssociationsByPackage."
     ],
     "confidence": "high",
     "unknowns": [
      "What the later change behind 7/8 lines at android-security-15.0.0_r17 contains. The imports of PackageManager and NLS_PROFILES hint at it, but the diff is not provided.",
      "What the remaining lines at android-security-17.0.0_r1 look like (6/8 present).",
      "Whether association removal paths other than disassociate() exist on Android 15/16 that would bypass the revocation.",
      "Whether queryIntentServicesAsUser with flags 0 misses some listener components (for example, disabled ones) that keep access.",
      "No test comes with the change (Test: manual test)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48536": {
     "cve": "CVE-2025-48536",
     "summary": "SettingsSliceProvider.grantAllowlistedPackagePermissions now also reads a separate slice_allowlist_package_names_for_dev resource array. It grants slice URI permissions to the packages in that array only when Build.IS_DEBUGGABLE is true, so debug-only allowlist entries are not granted Settings slice access on non-debuggable builds.",
     "component": "platform/packages/apps/Settings, slices (SettingsSliceProvider)",
     "weakness": "Overly permissive grant of slice URI permissions to statically allowlisted package names, with no distinction by build type",
     "root_cause": "grantAllowlistedPackagePermissions in src/com/android/settings/slices/SettingsSliceProvider.java took every entry of R.array.slice_allowlist_package_names and, for each descendant URI it was given, called SliceManager.grantSlicePermission(toPackage, descendant) without any further condition. There was no way to mark an entry as debugging-only, so every allowlisted package name was granted access on all build types. The commit title calls this the provider \"accessing unused packages\".",
     "fix_analysis": [
      "res/values/config.xml: adds a new empty string-array resource, slice_allowlist_package_names_for_dev, documented as the allowlist for slice URI access for debugging purposes. It sits next to the existing slice_allowlist_package_names.",
      "src/com/android/settings/slices/SettingsSliceProvider.java:grantAllowlistedPackagePermissions: replaces the single String[] allowlistPackages with a List<String> allowlist. It first adds all entries from slice_allowlist_package_names (null-checked). Then, only if Build.IS_DEBUGGABLE is true, it reads slice_allowlist_package_names_for_dev and appends those entries. The early-return empty check now tests allowlist.size() == 0, the log message uses allowlist.size(), and the grant loop iterates over the combined list. It also imports android.os.Build.",
      "tests/robotests/res/values-mcc998/config.xml and values-mcc999/config.xml: add the new array. It is empty for mcc998 and contains com.android.settings.slice_allowlist_package_dev for mcc999.",
      "tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java: grantAllowlistedPackagePermissions_hasPackageAllowlist_shouldGrant now sets debuggable to false and verifies the dev package is never granted. The new test grantAllowlistedPackagePermissions_hasPackageAllowlistAndDebuggable_shouldGrant sets debuggable to true and verifies the dev package is granted."
     ],
     "completeness": [
      "In AOSP both slice_allowlist_package_names and slice_allowlist_package_names_for_dev default to empty. Any entries that a device overlay keeps in slice_allowlist_package_names are still granted on all build types, so the fix only helps where debug-only entries are placed in the _for_dev array. No overlay changes are in the material.",
      "The fix adds no check that the target package is installed, is a system package, or has a particular signature before grantSlicePermission is called.",
      "The grant still covers every descendant URI passed in. The fix does not narrow which URIs are granted, only which packages receive them.",
      "The dev entries are gated on Build.IS_DEBUGGABLE, so on debuggable builds the dev packages are still granted."
     ],
     "behavior_changes": [
      "On non-debuggable builds, packages listed only in slice_allowlist_package_names_for_dev receive no slice permission grants.",
      "On debuggable builds, packages from both arrays are granted, and the 'Allowlisting %d uris to %d pkgs' log reports the combined count.",
      "The 'No packages to allowlist, skipping' early return now triggers only when the combined list is empty."
     ],
     "preconditions": [
      "SettingsSliceProvider.grantAllowlistedPackagePermissions must run. Its caller is not shown in the material.",
      "A device resource overlay must put package names in the allowlist arrays. AOSP's default arrays are empty.",
      "Per index search, the grant goes through SliceManagerService.grantSlicePermission in frameworks/base. It calls verifyCaller and enforceOwner for the calling package and then calls mPermissions.grantSliceAccess for the target package."
     ],
     "per_major": {
      "14": "Same production fix with an adapted test: commit b1fdab0d0065 in android-security-14.0.0_r24. The config.xml and SettingsSliceProvider.java changes match 15 except for line offsets and import context (no UserManager import nearby). The test toggles Build.IS_DEBUGGABLE via ReflectionHelpers.setStaticField instead of ShadowBuild.setDebuggable. All 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Same fix: commit 11b11215aa18 in android-security-15.0.0_r12, a cherry-pick of 4c0413484999 (Change-Id Ia655fbb9cb46f192559b82f957e3b2f0dd86946c). All 8/8 patched lines are present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 586f8dedd8e0 in android-security-16.0.0_r3. The production and test changes match 15 (ShadowBuild.setDebuggable); only line offsets differ. All 8/8 patched lines are present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The SettingsSliceProvider.java and res/values/config.xml hunks are the same on 14, 15 and 16. Only the test differs: 14 uses ReflectionHelpers.setStaticField on Build.IS_DEBUGGABLE, while 15 and 16 use ShadowBuild.setDebuggable. Pick the test variant that compiles on your branch.",
      "With AOSP's empty arrays the code change has no effect. Debug-only allowlist entries in product overlays need to live in slice_allowlist_package_names_for_dev for the gating to apply."
     ],
     "confidence": "high",
     "unknowns": [
      "Which package names were in the allowlist on affected devices, and whether overlay changes came with this commit.",
      "Whether the old allowlist held package names that are absent on user builds (the commit title only says 'unused packages').",
      "Where grantAllowlistedPackagePermissions is called within the provider; the caller and timing are not in the material.",
      "What privilege a package gains from receiving the slice grants (the bulletin only rates it High EoP)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48555": {
     "cve": "CVE-2025-48555",
     "summary": "The Settings notification log (NotificationStation) now computes, on resume, the set of profiles whose lock-screen private-notification setting is off and that are currently secure and locked, and hides the title, text, notification extras and ranking extras of history rows belonging to those profiles.",
     "component": "platform/packages/apps/Settings, notification history (NotificationStation, notification log)",
     "weakness": "Missing visibility/authorization check before displaying sensitive data (information disclosure of locked-profile notification content)",
     "root_cause": "NotificationStation.java builds HistoricalNotificationInfo entries from StatusBarNotification objects, including those of the current user's profiles (createFromSbn maps info.user from sbn.getUserId()), and HistoricalNotificationPreference.updatePreference unconditionally rendered info.title, info.text, mInfo.notificationExtra and mInfo.rankingExtra. It never consulted the per-profile LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS setting or the profile's lock state, so notification content of a locked profile configured to hide private notification content was shown in the Settings notification log.",
     "fix_analysis": [
      "NotificationStation.java:onResume - adds a new field mContentRestrictedUsers (ArrayList<Integer>), clears it, iterates UserManager.getProfiles(mContext.getUserId()), and for each profile where Settings.Secure.getIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, default 0, user.id) == 0 AND LockPatternUtils.isSecure(user.id) AND KeyguardManager.isDeviceLocked(user.id), adds user.id to the list.",
      "NotificationStation.java (list rebuild loop, notification removal path, add/update path, ranking update path) - every construction of HistoricalNotificationPreference and every call to updatePreference now passes mContentRestrictedUsers.contains(...user) (info.user, newInfo.user or mNotificationInfos.get(i).user) as a new boolean restrictContent.",
      "NotificationStation.java:HistoricalNotificationPreference - constructor gains a restrictContent parameter stored in mRestrictContent; the bind method (taking the PreferenceViewHolder row) calls updatePreference(mInfo, mRestrictContent).",
      "NotificationStation.java:HistoricalNotificationPreference.updatePreference(info, restrictContent) - when restrictContent is true, R.id.title and R.id.text are set GONE instead of being populated, and R.id.notification_extra and R.id.ranking_extra are set GONE instead of having their text set. pkgname, timestamp and icon are still shown."
     ],
     "completeness": [
      "Covers the four content views in the row (title, text, notification_extra, ranking_extra) for notifications whose info.user is in mContentRestrictedUsers; package name, icon and timestamp of restricted notifications remain visible.",
      "The restricted-user set is computed only in onResume; changes in a profile's lock state or setting while the fragment stays resumed are not re-evaluated until the next resume.",
      "The restriction only applies when the profile has a secure lock (LockPatternUtils.isSecure), is currently locked (KeyguardManager.isDeviceLocked) and LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS is 0 (the default used when unset). Unlocked profiles or profiles with the setting enabled are shown in full, as before.",
      "The sensitive strings are still loaded into HistoricalNotificationInfo (createFromSbn still calls getTitleString/getTextString); only the rendering is suppressed.",
      "In the unrestricted branch, notification_extra and ranking_extra visibility is not explicitly reset to VISIBLE; whether a preference instance can transition from restricted to unrestricted is not shown by the material."
     ],
     "behavior_changes": [
      "Rows for notifications from a locked, secure profile that hides private notification content now show only package name, icon and timestamp; title, text and the expandable notification/ranking extras are hidden.",
      "onResume now performs additional work: UserManager.getProfiles, a Settings.Secure read per profile, LockPatternUtils.isSecure and KeyguardManager.isDeviceLocked per profile.",
      "HistoricalNotificationPreference constructor and updatePreference signatures changed (new boolean parameter)."
     ],
     "preconditions": [
      "A user with UI access to the Settings notification log screen backed by NotificationStation (reached from Developer options in Settings; general platform knowledge).",
      "A profile of the current user with a secure lock that is currently locked and whose LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS setting is 0 or unset.",
      "Notifications from that profile present in the notification log or posted while the fragment's NotificationListenerService is registered."
     ],
     "per_major": {
      "14": "same fix: commit 06a1643e44a6 in android-security-14.0.0_r24, identical hunk content applied to NotificationStation.java (different base index); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 0db1143041a6 in android-security-15.0.0_r12 (cherry-pick of b1bfda5a03c6, Change-Id Ie7705a1ceea54203585c3b3737d0bada68aa1469); 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 596c7b9911f2 in android-security-16.0.0_r3, identical diff to Android 15; 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed); the patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching only NotificationStation.java; the same diff was applied on 14, 15 and 16. It relies on com.android.internal.widget.LockPatternUtils, KeyguardManager.isDeviceLocked(int) and Settings.Secure.getIntForUser, which are hidden/system APIs accessible to the Settings app (general platform knowledge); forks with a customized notification_log_row layout must keep the R.id.title, text, notification_extra and ranking_extra ids used by updatePreference."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether onResume triggers a list rebuild so that existing preferences are recreated with the newly computed restricted-user set (the diff does not show the rest of onResume).",
      "Whether an existing preference can switch from restricted to unrestricted, leaving the extras views GONE.",
      "The exact disclosure scenario the bug report (396666065) described; the material only shows the commit title 'Hide sensistive content from locked profiles'.",
      "Whether any other Settings screen renders notification history content for other profiles; the index search returned nothing relevant outside this file."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48572": {
     "cve": "CVE-2025-48572",
     "summary": "MediaButtonReceiverHolder.send stops attaching BroadcastOptions that unconditionally granted background-activity-start and FGS temp-allowlist capability to the media button receiver, and instead asks MediaSessionService.tempAllowlistTargetPkgIfPossible to allowlist the target package based on the caller's identity.",
     "component": "platform/frameworks/base, services/core media (MediaSessionService / MediaButtonReceiverHolder)",
     "weakness": "Improper privilege propagation: system_server granted background activity start and FGS allowlist capabilities to an app-controlled receiver without checking the caller's privileges (CWE-269 style)",
     "root_cause": "In services/core/java/com/android/server/media/MediaButtonReceiverHolder.java, send() built a BroadcastOptions with setTemporaryAppAllowlist(fgsAllowlistDurationMs, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_MEDIA_BUTTON) and setBackgroundActivityStartsAllowed(true), and passed options.toBundle() to both mPendingIntent.send(...) and the legacy context.sendBroadcastAsUser(...). These options were set unconditionally, so the registered media button receiver (an app-supplied PendingIntent or component) received background activity launch (BAL) permission and FGS-from-background allowlisting whenever a media key event was routed to it, regardless of who dispatched the key event. The commit message states the change avoids 'accidentally propagating BAL abilities to the receiver'.",
     "fix_analysis": [
      "MediaButtonReceiverHolder.java:send - removes the BroadcastOptions construction (setTemporaryAppAllowlist and setBackgroundActivityStartsAllowed(true)) entirely; mPendingIntent.send is now called with the 5-argument form (context, resultCode, mediaButtonIntent, onFinishedListener, handler) with no options bundle, and the legacy default-case context.sendBroadcastAsUser(mediaButtonIntent, userHandle) is called without receiverPermission/options.",
      "MediaButtonReceiverHolder.java:send - new signature takes MediaSessionService mediaSessionService, callingPackageName, callingPid, callingUid and a separate reportedPackageName. It resolves targetUid via context.getPackageManager().getPackageUidAsUser(mPackageName, mUserId) and calls mediaSessionService.tempAllowlistTargetPkgIfPossible(targetUid, mPackageName, callingPid, callingUid, callingPackageName, TAG), so WIU/BFSL allowlisting of the receiver package is delegated to MediaSessionService using the caller's pid/uid/package instead of being granted unconditionally. NameNotFoundException is caught and only logged under DEBUG_KEY_EVENT; the send still proceeds without allowlisting.",
      "MediaButtonReceiverHolder.java:send - Intent.EXTRA_PACKAGE_NAME is now populated from reportedPackageName (the value previously named callingPackageName), so the receiver-visible package name is unchanged, while the caller identity is passed separately for the allowlist decision.",
      "MediaSessionService.java (media key dispatch, around line 2640) - the caller computes reportedPackageName = asSystemService ? mContext.getPackageName() : packageName and passes MediaSessionService.this, packageName, pid, uid, reportedPackageName to send(); the MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs() argument is dropped."
     ],
     "completeness": [
      "Both send paths that previously carried the options bundle are covered: the PendingIntent path (mPendingIntent.send) and the legacy broadcast fallback (context.sendBroadcastAsUser in the switch default case).",
      "The other switch branches for restored component types (cases before 'default') are not shown in the diff and are not modified; the material does not show whether they used the options bundle.",
      "Allowlisting now depends on resolving mPackageName via getPackageUidAsUser. Holders created with an empty package name (MediaButtonReceiverHolder.create(mCurrentFullUserRecord.mFullUserId, pi, \"\") in MediaSessionService around line 2603 for mCustomMediaKeyDispatcher) likely fail this lookup, in which case no allowlisting is applied; the material does not confirm the lookup outcome for an empty name.",
      "The actual policy is delegated to MediaSessionService.tempAllowlistTargetPkgIfPossible, whose implementation is not in the diff; the strength of the fix depends on that method's checks."
     ],
     "behavior_changes": [
      "Media button receivers no longer receive setBackgroundActivityStartsAllowed(true) via the media button intent; background activity starts from the receiver are subject to normal BAL rules.",
      "FGS temp-allowlisting of the receiver is no longer unconditional with duration MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs(); it is granted (or not) by tempAllowlistTargetPkgIfPossible based on callingPid/callingUid/callingPackageName.",
      "A PackageManager uid lookup is performed on every send; if it throws NameNotFoundException the event is still delivered but without allowlisting (debug log only).",
      "Signature of MediaButtonReceiverHolder.send changed (new parameters: MediaSessionService, callingPid, callingUid, reportedPackageName; fgsAllowlistDurationMs removed)."
     ],
     "preconditions": [
      "A media key event is dispatched through MediaSessionService's key dispatch path and no MediaSessionRecord handles it, so the event falls through to a MediaButtonReceiverHolder (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder, or a PendingIntent from mCustomMediaKeyDispatcher.getMediaButtonReceiver).",
      "An app has registered a media button receiver (PendingIntent or component) that becomes the target holder; before the fix that receiver was granted BAL and FGS allowlist capabilities when the intent was delivered.",
      "Who may trigger the dispatch (which callers can invoke the key event dispatch API) is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 7dcb4043f5d7 in android-security-14.0.0_r24, identical diff to Android 15 (only MediaSessionService.java hunk offset differs, line 2311); at android-security-14.0.0_r29 5/8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 739ece18a63a in android-security-15.0.0_r12 (manual cherry-pick of ag/33211293); code at android-security-15.0.0_r17 shows the new send() signature and caller (5/8 resolver lines of the patched region present).",
      "16": "same fix: platform/frameworks/base e707f6600330 in android-security-16.0.0_r3, identical diff (MediaSessionService.java hunk at line 2677); at android-security-16.0.0_r8 5/8 lines of the patched region are present.",
      "17": "not listed by the bulletin; at android-security-17.0.0_r1 5/8 lines of the patched region are present. The material does not show whether this is the fixed code."
     },
     "backport_notes": [
      "The fix calls MediaSessionService.tempAllowlistTargetPkgIfPossible(targetUid, targetPackage, callingPid, callingUid, callingPackage, reason), which this commit does not add; verify it exists in the target tree (or port it) before applying, and update any other callers of MediaButtonReceiverHolder.send to the new signature.",
      "Diff applies identically on 14/15/16 for MediaButtonReceiverHolder.java (same blob ids 72ce38b72340 -> e7e70fc1f7b2); only the MediaSessionService.java hunk offset changes."
     ],
     "confidence": "medium",
     "unknowns": [
      "Implementation and checks performed by MediaSessionService.tempAllowlistTargetPkgIfPossible (not in the diff).",
      "Which callers/permissions can reach the MediaSessionService media key dispatch path that invokes MediaButtonReceiverHolder.send.",
      "Whether the non-default restored-component branches in send() (not shown) previously carried any options bundle.",
      "Whether getPackageUidAsUser fails for holders created with an empty package name.",
      "Whether the 5/8 resolver match at the latest 14/16/17 tags reflects fully fixed code.",
      "Whether tests were added; none appear in the diff stat."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48573": {
     "cve": "CVE-2025-48573",
     "summary": "Removes the mService.tempAllowlistTargetPkgIfPossible call from sendCommand in MediaSessionRecord.java, so MediaController.sendCommand no longer propagates while-in-use (WIU) or background foreground-service start (BFSL) privileges to the session owner's package.",
     "component": "platform/frameworks/base, system_server media session service (services/core/java/com/android/server/media/MediaSessionRecord.java)",
     "weakness": "Improper privilege propagation: a path meant only for exchanging information granted a temporary allowlist (WIU/BFSL) to another app's package",
     "root_cause": "Before forwarding the command to the session callback (mCb.onCommand), sendCommand in MediaSessionRecord called mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), pid, uid, packageName, reason) with reason TAG + \":\" + command. As a result, any controller sendCommand could temporarily allowlist the session owner's uid/package and pass on WIU/BFSL. The commit message says sendCommand is only for exchanging technical information and should not grant another app permission to start a foreground service or gain while-in-use permissions.",
     "fix_analysis": [
      "services/core/java/com/android/server/media/MediaSessionRecord.java:sendCommand (inner session callback wrapper): deletes the three lines that built the reason string and called mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), pid, uid, packageName, reason). The method now only calls mCb.onCommand(packageName, pid, uid, command, args, cb) inside the existing try/catch.",
      "No other logic changes. The command and its args are still delivered to the session unchanged."
     ],
     "completeness": [
      "Covers only the sendCommand path in MediaSessionRecord.",
      "Sibling methods shown in the context still call mService.tempAllowlistTargetPkgIfPossible: sendMediaButton (gated on KeyEvent.isMediaSessionKey), sendCustomAction (reason TAG + \":custom-\" + action) and prepare (reason TAG + \":prepare\"). The commit does not touch them, and its message justifies the removal only for sendCommand.",
      "The material does not show whether tempAllowlistTargetPkgIfPossible itself applies any caller-side restriction."
     ],
     "behavior_changes": [
      "MediaController.sendCommand no longer triggers tempAllowlistTargetPkgIfPossible for the session owner's package. A session app that relied on this after receiving a controller command could lose the WIU/BFSL grant it previously got.",
      "No new error codes or exceptions. The command still reaches the session callback."
     ],
     "preconditions": [
      "A caller with a MediaController for a target MediaSession calls sendCommand. This reaches sendCommand in MediaSessionRecord in system_server with the caller's pid/uid/packageName.",
      "Before the fix, the allowlist call targeted the session owner (getUid(), getPackageName()) and used the caller's pid/uid/packageName. The material does not show any further conditions enforced inside tempAllowlistTargetPkgIfPossible."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 21481e44fa92 in android-security-14.0.0_r24 removes the identical three lines from sendCommand. The context differs only in Log.e vs Slog.e and the class header. At the latest tag, android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base c88cb354b7fa in android-security-15.0.0_r12 (manual cherry-pick of ag/33211646, Change-Id I7cedaa5a17501b8e5d4517cf2080d3cb3f4ee8ea). At android-security-15.0.0_r17, sendCommand contains only the mCb.onCommand call.",
      "16": "same fix: platform/frameworks/base 039030a6b0e7 in android-security-16.0.0_r3, the identical three-line removal. 7/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "A single self-contained three-line deletion in sendCommand in MediaSessionRecord.java with no dependencies. To find it, look for the tempAllowlistTargetPkgIfPossible call with reason TAG + \":\" + command. The catch block uses Log.e on 14 and Slog.e on 15/16, which does not affect the hunk."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not include the implementation of mService.tempAllowlistTargetPkgIfPossible (what it checks on the calling uid, and what allowlist duration/type it grants).",
      "The material does not show whether the remaining tempAllowlistTargetPkgIfPossible calls in sendCustomAction, prepare and sendMediaButton were reviewed under the same bug.",
      "The bulletin lists Android 13 as affected, but no Android 13 fix commit is provided.",
      "The name of the inner class that contains sendCommand is not shown in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48575": {
     "cve": "CVE-2025-48575",
     "summary": "CertInstaller now requires that the package \"com.android.settings\" is actually installed under the system uid (UserHandle.isSameApp with Process.SYSTEM_UID) before taking the Settings-only CA certificate install path, instead of trusting the referrer package name alone.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA certificate installation flow)",
     "weakness": "Improper authorization: the trust decision was based only on a package name (no check of the uid behind that name)",
     "root_cause": "CredentialHelper.calledBySettings() returned true whenever mReferrer equalled the string \"com.android.settings\". CertInstaller.java used this check in two places: in an else-branch dispatch that runs InstallOthersAction, and on the success path that removes the PKCS12 password dialog before calling installCertificateOrShowNameDialog(). The fix comment says some systems have no preinstalled com.android.settings. On those systems the name-only comparison could let a Settings impostor reach the Settings-only CA certificate install path.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java: adds private isCalledBySettings(). It first requires mCredentials.getReferrer() to equal SETTINGS_PACKAGE (\"com.android.settings\"). It then resolves pm.getApplicationInfo(SETTINGS_PACKAGE, 0).uid and returns UserHandle.isSameApp(settingsUid, Process.SYSTEM_UID). If the package is not found (NameNotFoundException), it logs \"Unable to find info for settings package\" and returns false.",
      "src/com/android/certinstaller/CertInstaller.java: both call sites of mCredentials.calledBySettings() now use isCalledBySettings(). These are the else-branch dispatch that runs InstallOthersAction and the success path before installCertificateOrShowNameDialog().",
      "src/com/android/certinstaller/CredentialHelper.java: removes the SETTINGS_PACKAGE constant and the calledBySettings() method, so the name-only check no longer exists."
     ],
     "completeness": [
      "Both uses of the former calledBySettings() check in CertInstaller.java are replaced. No other caller of calledBySettings() appears in the material, and because the method is deleted, any remaining caller would fail to compile.",
      "The new check verifies the uid of whatever package is installed as com.android.settings, not the uid of the actual calling process. It still uses mCredentials.getReferrer() as the first gate. The material does not show how mReferrer is populated, so it cannot tell whether the referrer value itself can be controlled on a device that has a system-uid Settings.",
      "The fix is Java only and stays within the CertInstaller app. This commit does not change the KeyChain or DevicePolicyManager CA install paths."
     ],
     "behavior_changes": [
      "On devices where no package is named com.android.settings, or where that package does not have the system uid, CertInstaller no longer takes the Settings path (InstallOthersAction / installCertificateOrShowNameDialog), even when the referrer string matches. In the not-found case it logs the error \"Unable to find info for settings package\".",
      "A PackageManager.getApplicationInfo lookup is performed every time isCalledBySettings() is evaluated."
     ],
     "preconditions": [
      "The CertInstaller activity is reached with a referrer that CredentialHelper reports as \"com.android.settings\" (mCredentials.getReferrer()).",
      "The device's com.android.settings package name is not held by a preinstalled system-uid app (per the fix comment, some systems have no preinstalled com.android.settings).",
      "The flow reaches either the else-branch dispatch or the success path before installCertificateOrShowNameDialog() in CertInstaller.java."
     ],
     "per_major": {
      "14": "same fix: commit 17c124efb1f1 in android-security-14.0.0_r24, identical hunks (CredentialHelper.java has different blob ids but the same hunk content); 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit b24bef75f325 in android-security-15.0.0_r12 (cherry-picked from internal commits 46580f2f6f02 / cb9f6a5f1e9a, Change-Id I3779b0d38dafee2333716113f6d5f4de78529380); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit d688ebdbfd40 in android-security-16.0.0_r3, identical diff; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit; the CertInstaller.java diff applies identically on 14, 15 and 16 (blob 570c400 -> 883c897 on all three). The new code uses CredentialHelper.getReferrer(). The commit removes calledBySettings(), so if a fork still has other callers of it, they must be migrated."
     ],
     "confidence": "high",
     "unknowns": [
      "How CredentialHelper.mReferrer is populated (activity referrer or intent extra) is not shown, so the material does not let us judge how far the referrer string can be trusted on devices that do have a system Settings.",
      "The bulletin does not say which device configurations (for example OEM builds without com.android.settings) are affected."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48576": {
     "cve": "CVE-2025-48576",
     "summary": "Removes the unpublished, AIDL-only INotificationManager method updateNotificationChannelGroupFromPrivilegedListener and its implementation in NotificationManagerService. That method let a caller with a listener token that passes verifyPrivilegedListener create or update a notification channel group for a named package.",
     "component": "platform/frameworks/base, NotificationManagerService (notification listener / channel group binder API)",
     "weakness": "Unused but binder-exposed entry point. The bulletin classifies the impact as DoS. The exact fault mechanism is not shown in the material.",
     "root_cause": "INotificationManager.aidl declared updateNotificationChannelGroupFromPrivilegedListener(token, pkg, user, group). NotificationManagerService implemented it with Objects.requireNonNull(user), then verifyPrivilegedListener(token, user, false), then createNotificationChannelGroup(pkg, getUidForPackageAndUser(pkg, user), group, false, true), then handleSavePolicyFile(). The commit message says it was never used from NLS or anywhere else, yet it stayed callable over binder. Compared with the adjacent updateNotificationChannelFromPrivilegedListener (which null-checks channel, pkg and user and calls verifyPrivilegedListenerUriPermission), this method only null-checked user. The material does not show that this difference is what causes the DoS.",
     "fix_analysis": [
      "core/java/android/app/INotificationManager.aidl: interface INotificationManager. Removes the declaration `void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group)`, so the transaction no longer exists in the interface.",
      "services/core/java/com/android/server/notification/NotificationManagerService.java: binder service implementation inside NotificationManagerService. Deletes the @Override implementation of updateNotificationChannelGroupFromPrivilegedListener. This removes the call chain verifyPrivilegedListener -> createNotificationChannelGroup(pkg, getUidForPackageAndUser(pkg, user), group, false, true) -> handleSavePolicyFile() for listener callers.",
      "Pure code deletion (Flag: EXEMPT Code deletion, Test: build). No validation was added elsewhere."
     ],
     "completeness": [
      "Fully removes the one entry point named in the commit. The AIDL and service code shown at android-security-15.0.0_r17 no longer contain updateNotificationChannelGroupFromPrivilegedListener.",
      "The underlying createNotificationChannelGroup and handleSavePolicyFile paths are untouched. This fix does not change other callers of those paths, and the material does not show them.",
      "Sibling listener methods updateNotificationChannelFromPrivilegedListener, getNotificationChannelsFromPrivilegedListener and getNotificationChannelGroupsFromPrivilegedListener remain unmodified. The first two null-check pkg and user. The shown code for the third is cut off after its pkg null-check."
     ],
     "behavior_changes": [
      "The binder method updateNotificationChannelGroupFromPrivilegedListener no longer exists, so a caller can no longer reach this listener path to create or update channel groups.",
      "Removing a method from the middle of an AIDL interface shifts the transaction codes generated for the methods declared after it, so prebuilt clients compiled against the old interface would mismatch (general platform knowledge). The commit says there were no callers.",
      "No new error codes, timeouts or stricter validation were added. The change is purely a removal."
     ],
     "preconditions": [
      "The caller must invoke INotificationManager over binder with an INotificationListener token that passes verifyPrivilegedListener(token, user, false) for the targeted UserHandle. The material does not show what verifyPrivilegedListener enforces.",
      "Per the commit message the method was unpublished (AIDL only) and never used from NLS, so reaching it meant calling the raw AIDL interface."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base 7b28c9436fff in android-security-14.0.0_r24 removes the same AIDL line and the same 11-line service method. Context differs slightly: setInterruptionFilter has no fromUser parameter, and the hunk is at line 5754.",
      "15": "Same fix: platform/frameworks/base 345b6f2a6900 in android-security-15.0.0_r12 (cherry-pick of 6077349a00fd). The method is absent from both the AIDL and the service code at android-security-15.0.0_r17.",
      "16": "Same fix: platform/frameworks/base b812baa1463c in android-security-16.0.0_r3. The deletion is identical. The AIDL line sits after createConversationNotificationChannelForPackageFromPrivilegedListener and the service hunk is at line 7091.",
      "17": "Not listed by the bulletin (not affected per the bulletin). At android-security-17.0.0_r1, 6/8 lines of the patched region are present. The material does not show whether the method is present on that branch."
     },
     "backport_notes": [
      "Single self-contained deletion with no dependencies. Apply both hunks together (the AIDL declaration and the @Override implementation). In forks with a customized NotificationManagerService, check for vendor callers of updateNotificationChannelGroupFromPrivilegedListener first, because removing the AIDL method also renumbers later transaction codes (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The precise DoS mechanism, and whether the missing pkg/group validation plays any role in it, are not shown in the material.",
      "What verifyPrivilegedListener(token, user, false) requires of the caller, and so the exact privilege level needed to reach the removed method.",
      "Whether Android 13 (listed as affected by the bulletin) received the same deletion. No Android 13 commit is included in the material.",
      "Whether the method is present on Android 17 (android-security-17.0.0_r1)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48580": {
     "cve": "CVE-2025-48580",
     "summary": "MediaBrowser no longer unconditionally binds to a MediaBrowserService with Context.BIND_INCLUDE_CAPABILITIES; the flag is dropped when the connection's root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing key BrowserRoot.EXTRA_RECENT.",
     "component": "platform/frameworks/base, media framework (android.media.browse.MediaBrowser / android.service.media.MediaBrowserService)",
     "weakness": "Improper privilege management: unconditional propagation of the binding client's capabilities to the bound MediaBrowserService (BIND_INCLUDE_CAPABILITIES always set)",
     "root_cause": "In media/java/android/media/browse/MediaBrowser.java the connect path called mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES) for every connection, whatever the purpose of the connection or the root hints supplied. BIND_INCLUDE_CAPABILITIES causes the bound service to inherit the caller's process capabilities for the duration of the binding (general platform knowledge). As a result, even one-off short-lived requests such as querying recent playback (EXTRA_RECENT) passed the client's capabilities to the target service, and a client had no way to opt out of that propagation.",
     "fix_analysis": [
      "media/java/android/service/media/MediaBrowserService.java, nested class BrowserRoot: adds the @hide constant EXTRA_EXCLUDE_CAPABILITIES = \"android.service.media.extra.EXCLUDE_CAPABILITIES\". It is documented as a root hint key meaning the bind connection should not include capabilities, and as implied by EXTRA_RECENT.",
      "media/java/android/media/browse/MediaBrowser.java (connect path, inside the try around bindService): replaces the fixed flag set with a computed int bindServiceFlags = Context.BIND_AUTO_CREATE. BIND_INCLUDE_CAPABILITIES is OR-ed in only if mRootHints == null, or if mRootHints contains neither BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES nor BrowserRoot.EXTRA_RECENT. bindService is then called with bindServiceFlags. An import of android.service.media.MediaBrowserService.BrowserRoot is added.",
      "The check uses Bundle.containsKey, so the mere presence of either key (whatever its value) disables capability propagation."
     ],
     "completeness": [
      "Only the single bindService call in MediaBrowser.java is changed. The default (no root hints, or root hints without the two keys) still includes BIND_INCLUDE_CAPABILITIES, so the fix is opt-in on the browser side and automatic only for EXTRA_RECENT connections.",
      "EXTRA_EXCLUDE_CAPABILITIES is @hide, so public-API users cannot reference the constant symbolically. Because the check is a containsKey on the string key, the key can still be placed in the root hints Bundle.",
      "On the service side (MediaBrowserService) the only change is the new constant.",
      "The material does not show whether any platform clients of MediaBrowser were updated to pass EXTRA_EXCLUDE_CAPABILITIES. The commit only adds the mechanism and the EXTRA_RECENT inference."
     ],
     "behavior_changes": [
      "MediaBrowser connections whose root hints contain EXTRA_RECENT (any value) or EXTRA_EXCLUDE_CAPABILITIES no longer bind with BIND_INCLUDE_CAPABILITIES, so the client's capabilities are not propagated to the target MediaBrowserService for those bindings.",
      "Connections with null root hints or without those keys behave exactly as before (BIND_AUTO_CREATE | BIND_INCLUDE_CAPABILITIES).",
      "New hidden API constant MediaBrowserService.BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES."
     ],
     "preconditions": [
      "A process creates a MediaBrowser targeting a MediaBrowserService component and calls connect(), which reaches the bindService call (before the fix always with BIND_INCLUDE_CAPABILITIES).",
      "After the fix, the propagation still happens unless the client supplies root hints containing EXTRA_RECENT or EXTRA_EXCLUDE_CAPABILITIES.",
      "The specific client and target service involved in the EoP are unknown from the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 874eaecb47b8 in android-security-14.0.0_r24, with hunks identical in content (the MediaBrowserService.java hunk is at line 872 instead of 608); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 2588609ffab9 in android-security-15.0.0_r12, a manual cherry-pick of ag/33253388; 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base eb19b27ed8ab in android-security-16.0.0_r3, diff identical to 15; 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed); the patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Two-file change that relies on the existing BrowserRoot.EXTRA_RECENT constant and Context.BIND_INCLUDE_CAPABILITIES. On Android 14, Google's version places the MediaBrowserService.java hunk at a different offset (line 872) with identical content."
     ],
     "confidence": "high",
     "unknowns": [
      "Which privileged MediaBrowser client and which target service (bug 393582077) made up the EoP scenario; the material does not identify them.",
      "Whether companion changes updated platform callers to set EXTRA_EXCLUDE_CAPABILITIES for non-EXTRA_RECENT connections, or whether the internal change ag/33253388 contained anything beyond these two files.",
      "Whether equivalent compat/library browser implementations outside frameworks/base received a similar change.",
      "The bulletin lists Android 13 as affected, but the material gives no Android 13 fix commit."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48583": {
     "cve": "CVE-2025-48583",
     "summary": "BaseBundle no longer returns the Parcel behind lazily-deserialized values to the Parcel pool with recycle(). It now destroys that Parcel through a newly exposed @hide Parcel.destroy(), so these Parcel instances are not put back in the pool for reuse.",
     "component": "platform/frameworks/base, core/java/android/os (Bundle / Parcel)",
     "weakness": "Use after free (reuse of a pooled Parcel object that was recycled while lazy Bundle values were tied to it)",
     "root_cause": "When a Bundle is unparceled, Parcelable items can be left in mMap as lazy BiFunction values that are resolved on demand. BaseBundle kept the backing Parcel in mWeakParcelledData. When it owned the lazy values, it called recycleParcel() (Parcel.recycle()) in two places: in unwrapLazyValueFromMapLocked once mLazyValues reached zero, and in clear(). recycle() puts the Parcel object back into the static sOwnedPool/sHolderPool, the pool from which Parcel.obtain() hands out Parcels. Per the commit message, recycling the Parcel in these lazy-value paths 'may lead to several UAF issues' through reuse of these Parcel instances. The early release exists so that resources tied to the Parcel, especially file descriptors, are freed promptly instead of waiting for GC.",
     "fix_analysis": [
      "core/java/android/os/Parcel.java:destroy() goes from private to public (@hide), so BaseBundle can release the Parcel's resources without returning the Java object to the pool.",
      "core/java/android/os/Parcel.java: the `mClassCookies = null` reset is removed from recycle() and added to the end of the buffer-freeing method that calls nativeFreeBuffer() and resets mReadWriteHelper (most likely freeBuffer(), which recycle() still calls).",
      "core/java/android/os/BaseBundle.java:unwrapLazyValueFromMapLocked: when mOwnsLazyValues is true and mLazyValues reaches 0 with a live mWeakParcelledData referent, it calls parcel.destroy() instead of recycleParcel(parcel), then sets mWeakParcelledData to null.",
      "core/java/android/os/BaseBundle.java:clear(): when mOwnsLazyValues is true and mWeakParcelledData is set, it null-checks the referent and calls parcel.destroy() instead of recycleParcel().",
      "core/java/android/os/BaseBundle.java:initializeFromParcelLocked: the no-lazy-value path (ownsParcel, numLazyValues[0] == 0) still pools the Parcel but now calls parcelledData.recycle() directly. The static helper recycleParcel(), which also skipped EMPTY_PARCEL, is deleted. initializeFromParcelLocked already returns early for isEmptyParcel() before it reaches this code.",
      "core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java: the tests for the lazy-value and clear() scenarios are renamed and now verify destroy() counts instead of recycle(). The no-lazy case still verifies recycle()."
     ],
     "completeness": [
      "The fix covers both places where an owned Parcel backing lazy values was released early: the refcount reaching zero in unwrapLazyValueFromMapLocked, and an explicit clear(). In both, the Parcel is now destroyed instead of pooled.",
      "The no-lazy-value path in initializeFromParcelLocked still recycles (pools) the Parcel. This is intentional: no lazy values are left that refer to it.",
      "Bundles that do not own the lazy values (mOwnsLazyValues false, such as the shared or copied Parcels in the tests) are untouched. The tests assert that destroy() is never called for them.",
      "The fix removes the pool-reuse hazard but adds no defensive check against a stale lazy value using a destroyed Parcel. The material does not show whether such a use is handled safely."
     ],
     "behavior_changes": [
      "Parcel.destroy() becomes a public @hide API.",
      "When the last lazy value is unwrapped or the Bundle is cleared, Parcels backing Bundles with lazy values are no longer returned to the Parcel pool. Per the commit message, resources are still released right away, and the Parcel is not reused.",
      "mClassCookies is now cleared in the buffer-freeing method (most likely freeBuffer()), not directly in recycle().",
      "Test expectations in BundleRecyclingTest change from recycle() to destroy() for the lazy-value scenarios."
     ],
     "preconditions": [
      "A Bundle is deserialized from a Parcel it owns (ownsParcel) and contains at least one lazily-deserialized value (numLazyValues[0] > 0), so mWeakParcelledData is set and mOwnsLazyValues is true.",
      "The early release is reached in one of two ways: by unwrapping the last lazy value (getValueAt -> unwrapLazyValueFromMapLocked with mLazyValues reaching 0), or by calling clear() on the Bundle."
     ],
     "per_major": {
      "14": "Adapted fix: platform/frameworks/base 401cc81d7d94 in android-security-14.0.0_r24. In the Android 14 base, unwrapLazyValueFromMapLocked had Preconditions.checkState(mWeakParcelledData.get() != null, \"Parcel recycled earlier than expected\") before recycleParcel(). The patch replaces that with the `Parcel parcel = mWeakParcelledData.get(); if (mLazyValues == 0 && parcel != null)` form plus parcel.destroy(). The Parcel.java hunks sit at different line offsets. At android-security-14.0.0_r29, 4/8 lines of the patched region are present.",
      "15": "Same fix: platform/frameworks/base 26f25bbc2ae8 in android-security-15.0.0_r12 (the analysed commit). The code at android-security-15.0.0_r17 shows parcel.destroy() in unwrapLazyValueFromMapLocked and no mClassCookies reset in recycle(). 6/8 lines of the patched region are present.",
      "16": "Same fix: platform/frameworks/base 02751bc65824 in android-security-16.0.0_r3. The hunks are identical; only the context differs (BaseBundle implements Parcel.ClassLoaderProvider, and the Parcel.java offsets differ). 6/8 lines are present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit with no dependency. For trees that still have the `Preconditions.checkState(mWeakParcelledData.get() != null, ...)` form, as Android 14 did, apply the Android 14 variant of the unwrapLazyValueFromMapLocked hunk. Also make the existing private Parcel.destroy() public (@hide), and remove the recycleParcel() helper once nothing uses it."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact scenario in which a recycled Parcel was reused while a lazy value still referred to it. The commit message only says 'several UAF issues'; bugs 377704076 and 381885240 are not provided.",
      "Who can supply the Bundle contents that reach these paths (the attack surface) is not shown in the material.",
      "The full body of Parcel.destroy(), and how a destroyed Parcel behaves if a stale lazy value later reads from it.",
      "The name of the Parcel method that now clears mClassCookies is not visible in the hunk context.",
      "Why the per-major line match is 4/8 on Android 14 versus 6/8 elsewhere. The adapted hunk is a likely reason, but this is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48586": {
     "cve": "CVE-2025-48586",
     "summary": "In Telephony's FDN contact editor, the onActivityResult handler now uses ComponentCaller.checkContentUriPermission to check that the caller holds a read grant for the returned contact URI. This replaces a user-ID comparison, and the handler throws SecurityException when the check is denied.",
     "component": "platform/packages/services/Telephony, Phone app settings (Fixed Dialing Numbers), com.android.phone.settings.fdn.EditFdnContactScreen",
     "weakness": "Missing/insufficient permission check on a content URI returned by another component (the phone process queried a URI supplied in an activity result without checking the caller's access to it).",
     "root_cause": "EditFdnContactScreen.onActivityResult took the URI from the result Intent of a contact-pick activity and queried it with getContentResolver().query. The only gate was a comparison between UserHandle.getUserId(Process.myUid()) and ContentProvider.getUserIdFromUri(intent.getData(), currentUser). That checks the user ID encoded in the URI, with the current user as the default when the URI has none. It does not check whether the component that returned the result was allowed to read that URI. The fix commit's test steps involve a work-profile contact and a PoC app (bug 337784859).",
     "fix_analysis": [
      "src/com/android/phone/settings/fdn/EditFdnContactScreen.java:onActivityResult (RESULT_OK branch): gets ComponentCaller currentCaller = getCurrentCaller() and Uri contactUri = intent != null ? intent.getData() : null. If contactUri is null, it logs and returns early.",
      "Same function: calls currentCaller.checkContentUriPermission(contactUri, Intent.FLAG_GRANT_READ_URI_PERMISSION). If the result is PackageManager.PERMISSION_DENIED, it writes an EventLog entry (tag 0x534e4554, bug 337784859, caller uid) and throws SecurityException naming the caller uid/package and the URI. The authorization question changes from 'does the URI claim to belong to my user' to 'does the caller have read access to this URI'.",
      "Same function: removes the old ContentProvider.getUserIdFromUri / Process.myUid() comparison and its imports (android.content.ContentProvider, android.os.Process). Adds imports for android.app.ComponentCaller, android.content.pm.PackageManager and android.util.EventLog. The getContentResolver().query call now uses the local contactUri."
     ],
     "completeness": [
      "The fix covers only the RESULT_OK path of EditFdnContactScreen.onActivityResult. This is the only place in the diff where a returned URI is queried.",
      "The check is tied to the read grant for the exact URI returned. The code no longer parses a user ID out of the URI, so a URI without a user prefix no longer passes by defaulting to the current user.",
      "The material does not show whether other activities in com.android.phone.settings.fdn (e.g. BaseFdnContactScreen or sibling screens) receive and query result URIs in the same way. Nothing in the provided diff or context touches them."
     ],
     "behavior_changes": [
      "If the caller lacks read permission on the returned URI, onActivityResult now throws a SecurityException. Previously it logged a warning (Log.w) and returned for cross-user URIs, and went ahead with the query otherwise.",
      "A new EventLog SafetyNet entry (0x534e4554 / 337784859) is written on denial.",
      "A null Intent or null Intent.getData() is now handled explicitly by logging and returning.",
      "The check changed from user-ID matching to grant checking. A same-user URI returned without a read grant is now rejected, and the cross-user rejection based on the user ID in the URI is gone."
     ],
     "preconditions": [
      "EditFdnContactScreen (FDN settings in the Phone app) must be opened and its contact-pick flow started, so that onActivityResult runs with RESULT_OK.",
      "The activity that returns the result supplies the content URI in the result Intent. The commit's test steps involve a work profile with a contact and a PoC app."
     ],
     "per_major": {
      "14": "not affected per the bulletin (not listed); no fix commit shown. At android-security-14.0.0_r29, 7/8 lines of the patched region are present. The material does not explain this.",
      "15": "same fix: commit e4124fa76067 in android-security-15.0.0_r12 (cherry-pick of 522822360789, Change-Id I858041662a19a179e1fd49b3747d0fc013625015); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 851fc787e961 in android-security-16.0.0_r3, same hunks as the 15 commit; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed); 8/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit with the same hunks on 15 and 16. It depends on the ComponentCaller API (Activity.getCurrentCaller, ComponentCaller.checkContentUriPermission), which was added in Android 15 (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether other FDN screens or other Telephony activities use the same user-ID-based URI validation; the material only shows this one file.",
      "The state of the Android 14 code (7/8 lines of the patched region reported present).",
      "Whether the phone process's own contacts access is what let the old query succeed on URIs the caller could not read.",
      "Whether anything catches a SecurityException thrown from onActivityResult or whether it ends the Phone settings activity.",
      "Whether the earlier onActivityResult code (not shown) checked intent for null."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48588": {
     "cve": "CVE-2025-48588",
     "summary": "Vpn.startAlwaysOnVpn() no longer resets the always-on VPN setting (setAlwaysOnPackage(null, false, null)) when the always-on package is found unsupported while the device is in safe mode. It now only skips starting the VPN and returns false.",
     "component": "platform/frameworks/base, system_server connectivity (services/core/java/com/android/server/connectivity/Vpn.java, always-on VPN)",
     "weakness": "Improper handling of a transient state (safe mode) that leads to persistent removal of a security setting (always-on/lockdown VPN). A state check was missing before a destructive reset.",
     "root_cause": "In Vpn.startAlwaysOnVpn(), when isAlwaysOnPackageSupported(alwaysOnPackage) returned false, the code always called setAlwaysOnPackage(null, false, null), which resets the always-on VPN setting (null package, lockdown false, null allowlist). Per the commit message, third-party VPNs may have limited functionality in safe mode and can then be deemed unsupported for always-on use. So booting into safe mode could reset a legitimately configured always-on VPN, and per the added code comment it would then not start again after rebooting to normal mode.",
     "fix_analysis": [
      "services/core/java/com/android/server/connectivity/Vpn.java:startAlwaysOnVpn(): inside the !isAlwaysOnPackageSupported(alwaysOnPackage) branch, the setAlwaysOnPackage(null, false, null) call is now wrapped in if (!mContext.getPackageManager().isSafeMode()). In safe mode the setting is left intact. The method still returns false, so the VPN is not started in that boot.",
      "services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java: the new test testStartAlwaysOnVpnOnSafeMode() sets an always-on package and verifies that it starts. It then mocks mVpnProfileStore.get() returning null, queryIntentServicesAsUser() returning null and isSafeMode() returning true, and asserts that startAlwaysOnVpn() returns false while getAlwaysOnPackage() still returns TEST_VPN_PKG."
     ],
     "completeness": [
      "The fix covers only the unsupported-package branch of startAlwaysOnVpn(). Outside safe mode, behavior is unchanged: the setting is still reset when the package is unsupported.",
      "isAlwaysOnPackageSupported() itself is not changed. In safe mode the package is still reported as unsupported, and the always-on VPN does not run until a normal-mode reboot. The fix preserves the setting; it does not provide VPN coverage during safe mode.",
      "A comment in the r17 context (lines 1133-1138) mentions another path: an exception from startVpnProfilePrivileged() is caught below and returns false, 'clearing the always-on VPN'. That code is not shown in the material and is not touched by this diff, so whether it has an equivalent safe-mode guard is not visible."
     ],
     "behavior_changes": [
      "In safe mode, an always-on VPN package that fails isAlwaysOnPackageSupported() no longer has its always-on setting reset (getAlwaysOnPackage() still returns it). startAlwaysOnVpn() still returns false.",
      "Per the added code comment, the always-on VPN can start again after the device reboots from safe mode to normal mode, instead of having been removed.",
      "There are no new error codes or API changes. The failure path makes one extra PackageManager.isSafeMode() call."
     ],
     "preconditions": [
      "An always-on VPN package is configured for the user.",
      "The device is booted into safe mode, so that isAlwaysOnPackageSupported(alwaysOnPackage) returns false for the configured package.",
      "startAlwaysOnVpn() is invoked in that state (its callers are not shown in the material)."
     ],
     "per_major": {
      "14": "Same fix: commit da768ae60459 in android-security-14.0.0_r24 adds the identical guard to Vpn.java (hunk at line 1252 instead of 1109). The 4/4 lines of the patched region are present at android-security-14.0.0_r29. The material only shows the Vpn.java hunk for 14, so it does not show whether the VpnTest addition was included.",
      "15": "Same fix: commit fccd4c6bc347 in android-security-15.0.0_r12 (Vpn.java guard plus testStartAlwaysOnVpnOnSafeMode in VpnTest.java). The 7/7 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected (not listed by the bulletin, although the bulletin's affected versions also include 16-qpr2-next). The 7/7 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). The 7/7 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a single self-contained hunk: wrap the existing setAlwaysOnPackage(null, false, null) call in startAlwaysOnVpn() with if (!mContext.getPackageManager().isSafeMode()). It uses only the existing mContext field and PackageManager.isSafeMode(), which is a long-standing public API (general platform knowledge). On 14 the hunk sits at line 1252, with the same context lines as on 15.",
      "The added unit test relies on VpnTestBase mocks and helpers (mPackageManager, mVpnProfileStore, setAndVerifyAlwaysOnPackage). If a branch lacks them, port only the Vpn.java change or adapt the test."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show who calls startAlwaysOnVpn() or how safe mode is entered.",
      "The body of setAlwaysOnPackage() and how it persists the reset are not shown.",
      "The exception-handling tail of startAlwaysOnVpn() is not shown (a comment says it clears the always-on VPN on failure), so its behavior in safe mode is unknown.",
      "The material does not show whether the Android 14 cherry-pick included the VpnTest change (only the Vpn.java hunk is provided).",
      "The bulletin lists Android 13 as affected, but the material has no Android 13 commit or tag status."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48589": {
     "cve": "CVE-2025-48589",
     "summary": "SystemUI's HeaderPrivacyIconsController now filters the PermissionGroupUsage list it forwards to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS) so that only usages belonging to the current user and its profiles (plus phone-call usages) are sent, instead of usages from all users on the device.",
     "component": "platform/frameworks/base, SystemUI quick settings privacy chip (packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt)",
     "weakness": "Missing user-boundary filtering of per-user data (cross-user information exposure); bulletin classifies it as EoP",
     "root_cause": "HeaderPrivacyIconsController.showSafetyCenter() called permGroupUsage(), which returned permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) unmodified, and placed the whole list in the Intent extra PermissionManager.EXTRA_PERMISSION_USAGES for ACTION_VIEW_SAFETY_CENTER_QS. The returned usages were not restricted to the current user, so mic/camera usage records belonging to other users (not the current user or its profiles) were handed to Safety Center when the privacy indicator was clicked.",
     "fix_analysis": [
      "HeaderPrivacyIconsController.kt: constructor gains a new injected dependency `userTracker: UserTracker` (com.android.systemui.settings.UserTracker) and imports android.os.UserHandle.",
      "HeaderPrivacyIconsController.kt:permGroupUsage(): changed from private returning List<PermissionGroupUsage> to public returning ArrayList<PermissionGroupUsage>. It copies permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) into an ArrayList, reads userTracker.userProfiles, and for each usage computes UserHandle.getUserId(usage.uid); a usage is kept if usage.isPhoneCall is true or if any profile in userProfiles has id == userId, otherwise it is collected into invalidUserUsages and removed via usages.removeAll(invalidUserUsages).",
      "HeaderPrivacyIconsController.kt:showSafetyCenter(): `val usage = ArrayList(permGroupUsage())` becomes `val usage = permGroupUsage()`; the filtered list is then logged via privacyLogger.logUnfilteredPermGroupUsage and put into the intent extra as before.",
      "HeaderPrivacyIconsControllerTest.kt: adds a mocked UserTracker to the constructor call and three tests (testPermGroupUsage_filtersOutInactiveUsers, testPermGroupUsage_alwaysReturnPhoneCallUsage, testPermGroupUsage_returnsProfileUsages) exercising the new filter."
     ],
     "completeness": [
      "Covers only the Safety Center path: the filter lives in permGroupUsage(), whose sole caller in the diff is showSafetyCenter(). The underlying PermissionManager.getIndicatorAppOpUsageData() is not changed; filtering is done client-side in SystemUI.",
      "Phone-call usages (usage.isPhoneCall) are deliberately exempt from the user filter and are forwarded regardless of which user they belong to (confirmed by testPermGroupUsage_alwaysReturnPhoneCallUsage).",
      "The allow-list is userTracker.userProfiles, i.e. the current user and its profiles; the filter does not otherwise inspect profile type or running state.",
      "The non-Safety-Center branch (privacyDialogController / PrivacyDialogControllerV2.showDialog, referenced in the test file) is not touched by this commit; the material does not show whether that path has any user filtering."
     ],
     "behavior_changes": [
      "The ACTION_VIEW_SAFETY_CENTER_QS intent's EXTRA_PERMISSION_USAGES now contains only usages for the current user and its profiles plus any phone-call usages; usages of other users are dropped.",
      "privacyLogger.logUnfilteredPermGroupUsage now receives the already filtered list despite its name.",
      "permGroupUsage() changed from private to public and its return type changed from List to ArrayList.",
      "HeaderPrivacyIconsController's @Inject constructor gains a UserTracker parameter, so any manual instantiation (tests, downstream forks) must be updated."
     ],
     "preconditions": [
      "The mic/camera privacy indicator is clicked and the click reaches showSafetyCenter() (per the commit message); the condition selecting this path over the privacy dialog is not shown in the material.",
      "A multi-user device where a user other than the current user (and not one of its profiles) has mic/camera app-op usage reported by PermissionManager.getIndicatorAppOpUsageData()."
     ],
     "per_major": {
      "14": "adapted fix: platform/frameworks/base ec11eca03974 in android-security-14.0.0_r24 (RESTRICT AUTOMERGE). Same filter logic; constructor differs (no featureFlags parameter, UserTracker appended after deviceProvisionedController), import of SHADE_HEADER comes from ShadeModule, and the test's PermissionGroupUsage constructor uses 9 arguments. Patched region fully present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base e086bd9b0fa8 in android-security-15.0.0_r12 (RESTRICT AUTOMERGE cherry-pick of 2c602e402e21). UserTracker appended after featureFlags; tests in packages/SystemUI/tests. Patched region fully present at android-security-15.0.0_r17.",
      "16": "adapted fix: platform/frameworks/base 2aeba76a58c1 in android-security-16.0.0_r3. Same filter logic; UserTracker appended after shadeDialogContextInteractor and the test lives in packages/SystemUI/multivalentTests. Patched region fully present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is fully present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "The fix is a single commit per branch; the only adaptation needed is the constructor parameter list of HeaderPrivacyIconsController (and its test instantiation), which differs on each release. UserTracker is already a Dagger-provided SystemUI dependency, so adding it to the @Inject constructor requires no module changes (general platform knowledge).",
      "When porting the test, note the PermissionGroupUsage constructor arity differs: 9 arguments on Android 14, 10 (trailing String) on Android 15/16."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the Safety Center receiver side, so what Safety Center displayed or did with other users' usages is unknown.",
      "Whether the privacy dialog path (PrivacyDialogController / PrivacyDialogControllerV2) applies equivalent user filtering is not shown in the material.",
      "The condition that routes a privacy chip click to showSafetyCenter() versus the privacy dialog is not shown.",
      "Bug 362492829 details and the original upstream commit 2c602e402e21 contents are not included."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48590": {
     "cve": "CVE-2025-48590",
     "summary": "Removes the compat-change based (SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, 'Q- apps') exemption in AppOpsService that marked invalid attribution tags as valid when the change was disabled for the package or calling uid, so isAttributionTagValid is no longer forced to true for any caller.",
     "component": "platform/frameworks/base, AppOps (services/core/java/com/android/server/appop/AppOpsService.java)",
     "weakness": "Improper input validation (a compat exemption bypassed attribution tag validation; the bulletin classifies the impact as DoS)",
     "root_cause": "In AppOpsService.java, when an attribution tag could not be validated (the hunk shows the 'package ... not found, can't check for attributionTag' message branch), the code queried mPlatformCompat.isChangeEnabledByPackageName and isChangeEnabledByUid for SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE. If the change was disabled for either the package or the calling uid (legacy Q-and-lower apps, per the commit title), the code set isAttributionTagValid = true, so an invalid tag was treated as valid. The test name (AppOpMemoryUsageTest) and the bulletin type (DoS) suggest a memory or resource impact from accepting such tags, but the material does not show the downstream mechanism.",
     "fix_analysis": [
      "services/core/java/com/android/server/appop/AppOpsService.java, attribution tag validation block (the enclosing method name is not shown in the hunk header): the try/catch that queried mPlatformCompat for SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE and set isAttributionTagValid = true is deleted. Only Slog.e(TAG, msg) remains, so isAttributionTagValid is no longer overridden to true at this site.",
      "The now-unused static import of android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE is removed from AppOpsService.java. The constant itself is not touched by this commit."
     ],
     "completeness": [
      "The fix removes the compat exemption at the one site shown in AppOpsService.java. After the change, this block has no compat-dependent path that marks an invalid attribution tag as valid.",
      "The commit does not change what happens downstream when isAttributionTagValid is false (that code is outside the diff), and it adds no cap on stored attribution tags.",
      "The SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE constant and any other users of it are not modified. The material does not show whether other users exist."
     ],
     "behavior_changes": [
      "Apps for which SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE was disabled (legacy apps) no longer have invalid attribution tags treated as valid at this site. They now go through the same invalid-tag handling as other apps.",
      "Slog.e is still emitted. The mPlatformCompat binder queries at this site are no longer performed.",
      "The exact result for legacy apps passing an invalid tag is decided by code outside the diff and is not shown."
     ],
     "preconditions": [
      "A caller reaching the AppOpsService attribution tag validation path with an attribution tag that fails validation (the diff shows the case where the package is not found).",
      "Before the fix, the exemption applied only when SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE was disabled for the package or the calling uid."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 0c46a5ec453b in android-security-14.0.0_r24, same change at a different offset (~line 3937). 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 529f24023e15 in android-security-15.0.0_r12 (cherry-pick of c4287772ec8b, Change-Id I6f3d47bc2416d3a00d0b13f65370f4de6efe89db). 7/8 lines of the patched region present at android-security-15.0.0_r17, and the static import is absent from the shown context.",
      "16": "same fix: platform/frameworks/base 848f94492175 in android-security-16.0.0_r3, same change at ~line 5017. 7/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file removal with no dependencies shown. It applies to branches whose AppOpsService.java still contains the mPlatformCompat SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE block, though the import hunk's context lines differ per branch. The bulletin lists Android 13 as affected, but the material includes no 13 commit."
     ],
     "confidence": "medium",
     "unknowns": [
      "The name of the enclosing method and the downstream handling when isAttributionTagValid is false.",
      "Which AppOps data structure is affected by accepted invalid tags (the memory impact implied by AppOpMemoryUsageTest).",
      "No Android 13 commit is provided, although the bulletin lists 13 as affected.",
      "Which line of the patched region is missing in each latest tag (7/8 present)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48591": {
     "cve": "CVE-2025-48591",
     "summary": "Adds a callingUser parameter to the IMms AIDL methods that accept a client-supplied contentUri (sendMessage, downloadMessage, importMultimediaMessage, addMultimediaMessageDraft) and has MmsServiceBroker in system_server fill it from Binder.getCallingUserHandle() so MmsService can validate that the contentUri belongs to the originating user.",
     "component": "platform/frameworks/base, MMS (MmsManager / IMms AIDL / services/core MmsServiceBroker)",
     "weakness": "Missing user-identity propagation across nested binder calls, preventing cross-user validation of a caller-supplied content URI (information disclosure per bulletin)",
     "root_cause": "MmsServiceBroker (system_server) forwarded sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft to the MmsService (IMms) with only subId, callingPkg and the caller-provided contentUri. Because the call is nested (app -> system_server -> MmsService), the receiving service had no information about which user the request originated from, so it could not check that the contentUri referred to content of the same user. The commit message states the purpose is to \"validate the contentURI is coming from the same userId\".",
     "fix_analysis": [
      "mms/java/com/android/internal/telephony/IMms.aidl: adds an `in int callingUser` parameter to sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft, the four methods that take a client-supplied `contentUri`. Other IMms methods visible in the material (importTextMessage, deleteStoredMessage, the stored-message send method) are unchanged.",
      "services/core/java/com/android/server/MmsServiceBroker.java: new private helper getCallingUserId() returning Binder.getCallingUserHandle().getIdentifier(). In the BinderService implementations of sendMessage, downloadMessage, importMultimediaMessage and addMultimediaMessageDraft, the value passed to getServiceGuarded().<method>() is getCallingUserId(), not the callingUser argument received from the client. The client-supplied callingUser is therefore ignored by the broker. Existing permission/AppOps checks (SEND_SMS, RECEIVE_MMS, OP_WRITE_SMS) and URI permission grants are untouched.",
      "services/core/java/com/android/server/MmsServiceBroker.java: the mServiceStubForFailure fallback IMms implementation gets the new signatures (behavior unchanged: returns error PendingIntent or null).",
      "mms/java/android/telephony/MmsManager.java: sendMessage and downloadMessage pass UserHandle.USER_NULL as a placeholder callingUser to the broker (the broker overwrites it), with a new import of android.os.UserHandle.",
      "Per the commit message, the validation of contentUri against the propagated user id happens in MmsService; that check is not part of this diff, which only supplies the trusted user id."
     ],
     "completeness": [
      "Covers all four IMms methods that accept a caller-provided contentUri: sendMessage, downloadMessage, importMultimediaMessage, addMultimediaMessageDraft.",
      "The user id forwarded to MmsService is taken from Binder.getCallingUserHandle() inside the broker, so a client cannot influence it by passing a different callingUser value; MmsManager's USER_NULL placeholder is overwritten.",
      "The frameworks/base change by itself enforces nothing: it is the plumbing half of the fix. Whether and how MmsService validates contentUri against callingUser is outside this material.",
      "Other IMms methods (e.g. importTextMessage, deleteStoredMessage, the stored-message send method) are not given a callingUser; the material does not show whether they need one."
     ],
     "behavior_changes": [
      "IMms AIDL interface signature change for four methods (parameter inserted after subId or as first parameter). IMms implementations and clients built against the old interface must be updated; the commit notes the change cannot be flag-guarded because AIDL does not support overloading.",
      "MmsServiceBroker now forwards the originating user id to MmsService on every sendMessage/downloadMessage/importMultimediaMessage/addMultimediaMessageDraft call; no new error codes or permission failures are introduced in this commit.",
      "MmsManager passes UserHandle.USER_NULL as a placeholder callingUser in its two calls."
     ],
     "preconditions": [
      "Multi-user device (the fix concerns the case where the originating user differs from the user whose content the contentUri references).",
      "Caller reaches MmsServiceBroker through MmsManager/IMms: sendMessage requires SEND_SMS, downloadMessage requires RECEIVE_MMS, importMultimediaMessage and addMultimediaMessageDraft require AppOps OP_WRITE_SMS allowed (default SMS app), per the unchanged checks in MmsServiceBroker.",
      "MmsService must be connected (otherwise mServiceStubForFailure returns an error/null)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 1dd5a8353b99 in android-security-14.0.0_r24, same content as the 15 commit (only line offsets in MmsServiceBroker.java differ); at android-security-14.0.0_r29, 7/8 lines of the patched region present per resolver.",
      "15": "same fix: platform/frameworks/base eba8dce96baa in android-security-15.0.0_r12 (Change-Id I9beb19f1a94aae76e6cd48a72feeb6c1dc47cf5a); at android-security-15.0.0_r17, 7/8 lines of the patched region present per resolver.",
      "16": "not affected per the bulletin; at android-security-16.0.0_r8, 7/8 lines of the patched region present per resolver.",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1, 7/8 lines of the patched region present per resolver."
     },
     "backport_notes": [
      "This commit changes the IMms AIDL signature, so it must land together with the matching change in the MmsService implementation of IMms (which per the commit message performs the user-id validation); applying only the frameworks/base side provides no validation by itself. The MmsService-side change is not included in the material.",
      "Google's 14 and 15 commits have the same content with only hunk-offset differences in MmsServiceBroker.java; any fork with additional IMms implementations or extra IMms methods taking a contentUri needs the new parameter handled as well."
     ],
     "confidence": "high",
     "unknowns": [
      "The MmsService side of the fix (its project/location and the check validating contentUri against callingUser) is not in the material.",
      "The exact scenario in bug 350760118 (which content was disclosed and via which of the four methods) is not shown.",
      "Why the resolver reports 7/8 rather than 8/8 lines of the patched region present in the latest tags."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48592": {
     "cve": "CVE-2025-48592",
     "summary": "The Codec2 software AV1 decoder plugin (C2SoftDav1dDec) now sets Dav1dSettings.all_layers = 0 before dav1d_open, so dav1d outputs only one layer for SVC streams instead of all layers, which the plugin was not designed to handle.",
     "component": "platform/frameworks/av, media/codec2 software dav1d (AV1) decoder plugin",
     "weakness": "Improper decoder configuration: dav1d's default all_layers setting is left enabled, so the plugin receives multi-layer output that it does not handle. The bulletin classes the result as information disclosure. The exact memory-safety class is not evident from the diff.",
     "root_cause": "C2SoftDav1dDec::initDecoder() in media/codec2/components/dav1d/C2SoftDav1dDec.cpp used dav1d_default_settings() unchanged except for n_threads and max_frame_delay. Per the commit message, dav1d by default outputs all layers for SVC clips, and the plugin is not designed to handle that. The material does not show the exact downstream fault. The bulletin rates it High / ID.",
     "fix_analysis": [
      "media/codec2/components/dav1d/C2SoftDav1dDec.cpp:C2SoftDav1dDec::initDecoder(): adds `lib_settings.all_layers = 0;` right after `dav1d_default_settings(&lib_settings);` and before dav1d_open(), so the dav1d context is created with single-layer output for SVC streams.",
      "This is a single one-line commit. The plugin's output and picture handling path is not changed. The fix removes the trigger at the library-configuration level instead of making the plugin robust to multi-layer output."
     ],
     "completeness": [
      "In the code shown, initDecoder() is where Dav1dSettings is built and dav1d_open() is called, so contexts created there get the new setting.",
      "The fix adds no handling in the plugin for multi-layer output. It relies on dav1d honoring all_layers = 0. The material does not show whether any other code path could re-enable all_layers or create a context elsewhere.",
      "Only the dav1d plugin is touched. The material does not show whether other AV1 decoder components have an analogous issue."
     ],
     "behavior_changes": [
      "For AV1 SVC streams decoded through C2SoftDav1dDec, dav1d now outputs only one layer instead of all layers (per the commit message).",
      "No new error codes, API changes or validation failures are introduced by the diff."
     ],
     "preconditions": [
      "The software Codec2 dav1d AV1 decoder (C2SoftDav1dDec) must be the decoder used for the stream.",
      "The AV1 bitstream must be an SVC clip with multiple layers, so that dav1d's default all-layers output applies."
     ],
     "per_major": {
      "14": "not affected: the bulletin does not list Android 14, and media/codec2/components/dav1d/C2SoftDav1dDec.cpp does not exist at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/av 9d74d91a39f3 in android-security-15.0.0_r12 (one-line all_layers = 0). 4/4 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/av 8febdebcb5e8 in android-security-16.0.0_r3, an identical diff at a shifted line offset. 4/4 lines are present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. The patched region (including all_layers = 0) is already present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a trivial one-line insert after dav1d_default_settings(&lib_settings) in C2SoftDav1dDec::initDecoder(). It should apply to any branch that has the dav1d plugin, provided the bundled dav1d exposes Dav1dSettings.all_layers. Branches without media/codec2/components/dav1d (e.g. the Android 14 security tags) have nothing to patch."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact fault in the plugin's output path when multiple layer pictures are delivered is not shown. The only evidence is the commit message's statement that the plugin is not designed for it.",
      "Whether the second bug (427113482) refers to a distinct symptom of the same configuration issue.",
      "Whether other AV1 decoder components have a comparable SVC handling issue.",
      "Which callers invoke C2SoftDav1dDec::initDecoder() and when (the index results are unrelated components)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48594": {
     "cve": "CVE-2025-48594",
     "summary": "The UID-triggered revoked-association cleanup in CompanionDeviceManager now looks up every package that shares the UID with PackageManager.getPackagesForUid and disassociates the revoked associations of each one. Before, it handled only the single package name returned by PackageManagerInternal.getNameForUid.",
     "component": "platform/frameworks/base, CompanionDeviceManager service (services/companion, DisassociationProcessor)",
     "weakness": "Incomplete cleanup from a wrong UID-to-package mapping (shared UID not handled), which leaves revoked associations of other packages in the shared UID in place",
     "root_cause": "In DisassociationProcessor.java, the UID-based listener (the block guarded by the \"Not interested in this uid\" check) mapped the UID to one package with mPackageManagerInternal.getNameForUid(uid). It then queried mAssociationStore.getRevokedAssociations(userId, packageName) for that one name only. When several packages share a UID (android:sharedUserId), only one package name came back, so revoked associations of the other packages in the shared UID were never passed to disassociate() and stayed revoked but not removed. The commit message states the intent: \"Make sure to disassociate all the packages with shared UID\".",
     "fix_analysis": [
      "services/companion/java/com/android/server/companion/association/DisassociationProcessor.java: imports android.content.pm.PackageManager and adds a PackageManager field (mPackageManager), set from context.getPackageManager() in the constructor.",
      "DisassociationProcessor.java, UID listener body: replaces mPackageManagerInternal.getNameForUid(uid) with mPackageManager.getPackagesForUid(uid) and returns early if the result is null or empty.",
      "DisassociationProcessor.java, same block: wraps the existing loop in an outer loop over all returned package names. For each package it calls mAssociationStore.getRevokedAssociations(userId, packageName) and disassociate(association.getId()). The final check on mAssociationStore.getRevokedAssociations().isEmpty() is unchanged."
     ],
     "completeness": [
      "Covers only the UID-triggered revoked-association cleanup path in DisassociationProcessor. All packages of a shared UID are now processed.",
      "The early return on a null or empty package list is kept, so UIDs with no packages are still skipped.",
      "This diff does not touch any other getNameForUid call sites in the companion service. The material does not show whether other UID-to-package lookups exist there.",
      "On Android 14 the matching logic is in CompanionDeviceManagerService (pending role holder removal). It was fixed with the same getPackagesForUid loop pattern."
     ],
     "behavior_changes": [
      "When the listener fires, revoked associations of every package sharing the UID are now disassociated, not just those of one package.",
      "The lookup moves from PackageManagerInternal.getNameForUid to PackageManager.getPackagesForUid. The callback may now run several store queries and disassociate calls per UID event.",
      "No new error codes or API changes."
     ],
     "preconditions": [
      "Two or more packages are installed under the same shared UID (android:sharedUserId), and at least one of them has a revoked CDM association (present in mAssociationStore.getRevokedAssociations).",
      "The UID-based listener in DisassociationProcessor must fire for that UID. The patched block follows an early-return check that the diff does not show. Imports of ActivityManager and IMPORTANCE_VISIBLE suggest a UID importance callback, but the material does not show the exact trigger."
     ],
     "per_major": {
      "14": "adapted fix: platform/frameworks/base 6b8c659a8cb3 in android-security-14.0.0_r24 patches CompanionDeviceManagerService.java instead. It adds mPackageManager, replaces getPackageNameByUid with getPackagesForUid, and wraps the getPendingRoleHolderRemovalAssociationsForUser / maybeRemoveRoleHolderForAssociation loop in a loop over all packages. Patched region present (8/8 lines) at android-security-14.0.0_r29.",
      "15": "same fix: 9c8c99ba63bb in android-security-15.0.0_r12, DisassociationProcessor.java, disassociate(association.getId()). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: ea2bcc665342 in android-security-16.0.0_r3, DisassociationProcessor.java. Identical except the call is disassociate(association.getId(), REASON_REVOKED). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-commit fix. On branches without DisassociationProcessor (the Android 14 layout), equivalent logic is in CompanionDeviceManagerService. There the code uses getPackageNameByUid and getPendingRoleHolderRemovalAssociationsForUser for pending role holder removal, so apply the Android 14 variant. Check the disassociate() signature: one argument on 15, with REASON_REVOKED on 16."
     ],
     "confidence": "high",
     "unknowns": [
      "The diff does not show the exact listener type or the early-return condition before the patched block.",
      "The material does not include the CTS test named in the commit message.",
      "The material does not show what privilege a lingering revoked association keeps on 15 and 16 (role holder, notification listener, etc.). Only the Android 14 variant shows role-holder removal."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48596": {
     "cve": "CVE-2025-48596",
     "summary": "In libbinder's Parcel::appendFrom, the fix replaces the unconditional \"mDataSize += len\" with \"if (mDataPos > mDataSize) mDataSize = mDataPos;\". The parcel's logical data size now grows only when the write position actually passes the current end of data.",
     "component": "platform/frameworks/native, libs/binder (Parcel)",
     "weakness": "Incorrect buffer size bookkeeping: mDataSize was over-counted on append, so the logical data size could be larger than the data written and than the capacity checked. This is a possible out-of-bounds condition; the material shows no concrete access.",
     "root_cause": "Parcel::appendFrom in libs/binder/Parcel.cpp copies len bytes from the source parcel to mData + mDataPos, advances mDataPos by len, and then always ran mDataSize += len. That is only correct when mDataPos == mDataSize before the append. If the destination parcel's position had been moved back (mDataPos < mDataSize), the appended bytes overwrite existing data, yet mDataSize still grew by len. The capacity check and growData(len) earlier in the function are computed from mDataPos + len, so the inflated mDataSize could exceed both the bytes written and mDataCapacity. The result is a mismatch between the parcel's claimed size and its backing buffer.",
     "fix_analysis": [
      "libs/binder/Parcel.cpp:Parcel::appendFrom: after memcpy and mDataPos += len, the line 'mDataSize += len;' is replaced by 'if (mDataPos > mDataSize) mDataSize = mDataPos;'. mDataSize now becomes max(mDataSize, mDataPos). It grows only by the part of the append that lies past the previous end of data, and never past the position that was capacity-checked and written.",
      "No other code in the function changes. These stay as before: the source range checks against parcel->mDataSize, validateReadData(mDataPos + len), the growData(len) path and the kernel object (flat_binder_object) copying.",
      "Single one-line commit; the same change landed on the Android 14, 15 and 16 branches."
     ],
     "completeness": [
      "Covers the size accounting for data bytes in appendFrom: after an append at a rewound position, mDataSize can no longer exceed mDataPos.",
      "Only appendFrom is modified. The object-table merging later in the same function (counting objects in [offset, offset+len) and growing kernelFields->mObjects) is not touched by this commit.",
      "Other Parcel write paths (writeInplace, setDataSize, etc.) are not part of the diff. The material does not show whether their own size handling needed changes."
     ],
     "behavior_changes": [
      "Appending into the middle of a parcel (mDataPos < mDataSize) now leaves dataSize() unchanged unless the write passes the old end. Before the fix, dataSize() grew by len every time.",
      "No new error codes; appendFrom's return values are unchanged."
     ],
     "preconditions": [
      "A caller invokes Parcel::appendFrom on a destination parcel whose mDataPos is less than mDataSize (position moved back before the append).",
      "Per index search, appendFrom is reachable from the NDK via AParcel_appendFrom in frameworks/native/libs/binder/ndk/parcel.cpp, and from Java via the JNI android_os_Parcel_appendFrom in frameworks/base/core/jni/android_os_Parcel.cpp."
     ],
     "per_major": {
      "14": "Same fix: commit aa67ec623b7d in android-security-14.0.0_r24, the same one-line change (hunk at line 509).",
      "15": "Same fix: commit de36dac70667 in android-security-15.0.0_r12, the same one-line change (hunk at line 553; the changed line is at 556 in android-security-15.0.0_r17).",
      "16": "Same fix: commit 6ffdde944d4e in android-security-16.0.0_r3, the same one-line change (hunk at line 558).",
      "17": "Not affected per the bulletin. At android-security-17.0.0_r1 the patched region matches the fixed branches (2/3 lines present), which is consistent with the fix already being present."
     },
     "backport_notes": [
      "Trivial one-line replacement in Parcel::appendFrom, immediately after 'mDataPos += len;'. It should apply cleanly to any branch whose appendFrom still has 'mDataSize += len;'. Per index search, the NDK (AParcel_appendFrom) and JNI (android_os_Parcel_appendFrom) entry points both call this function."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which concrete consumer (system service or app-facing API) was the EoP vector; only the generic entry points are visible.",
      "Whether companion test changes landed with the fix: the commit touches only Parcel.cpp, and its Test line references binder_parcel_fuzzer.",
      "Whether the Rust binder stack reaches appendFrom; the material does not show it."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48597": {
     "cve": "CVE-2025-48597",
     "summary": "Moves the grant of the TRUSTED_OVERLAY surface flag for a PiP task out of system_server's RootWindowContainer#notifyActivityPipModeChanged (where it was applied as soon as the task was reported pinned) into WM Shell's PipTransition#onFinishResize, so it is only set after Shell has handled the enter-PiP transition, while WM keeps only the revocation when the task leaves pinned mode.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/RootWindowContainer.java) and WindowManager Shell PiP (libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java)",
     "weakness": "Privilege granted too early / on an insufficient condition (trusted overlay flag applied to an app task surface based solely on it being reported pinned, before the PiP transition was handled by Shell)",
     "root_cause": "In RootWindowContainer#notifyActivityPipModeChanged, system_server applied setTrustedOverlay(task.getSurfaceControl(), inPip) with its own transaction (guarded only by a null check on the surface on Android 15). When an activity was reported as entering PiP (r != null), the task's surface immediately received TRUSTED_OVERLAY, regardless of whether WM Shell went on to handle the enter-PiP transition. The commit message states the intent: the flag must only be granted after WMShell successfully handled the entering PiP transition. The surrounding context shows that an enter-PiP abort timeout exists (mMaybeAbortPipEnterRunnable / abortPipEnter). The material does not show whether this path was how a task could hold the flag without being shown as PiP.",
     "fix_analysis": [
      "libs/WindowManager/Shell/.../pip/PipTransition.java:onFinishResize: the computation of leash = mPipOrganizer.getSurfaceControl() and hasValidLeash = leash != null && leash.isValid() is moved above the enteringPip block. When enteringPip is true, after setting mPipTransitionState to ENTERED_PIP, and only if hasValidLeash, the fix logs a ProtoLog WM_SHELL_PICTURE_IN_PICTURE message and calls tx.setTrustedOverlay(leash, true) on the passed transaction. The grant therefore happens in Shell, at the end of the enter-PiP resize/transition, on the PiP leash.",
      "libs/WindowManager/Shell/.../pip/PipTransition.java:onFinishResize: the previous local declarations of leash/hasValidLeash inside the (mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null block are removed because they are now declared earlier. The rest of that block is unchanged.",
      "services/core/java/com/android/server/wm/RootWindowContainer.java:notifyActivityPipModeChanged: the null-guarded setTrustedOverlay(task.getSurfaceControl(), inPip).apply() after setPipVisibilityLw is removed. In its place, WM applies setTrustedOverlay(task.getSurfaceControl(), false) through mWmService.mTransactionFactory.get() as a blanket revocation. This runs only in the else branch (r == null, after notifyActivityUnpinned) and only if task.getSurfaceControl() != null. WM no longer grants the flag at all."
     ],
     "completeness": [
      "Covers the pin-time grant in RootWindowContainer#notifyActivityPipModeChanged: WM now never sets TRUSTED_OVERLAY to true; it only clears it on unpin.",
      "The new grant lives only in PipTransition#onFinishResize (class PipTransition extends PipTransitionController). The material does not show whether any other PiP controller implementation also needs to grant the flag; nothing in the provided diffs touches such paths.",
      "The grant is skipped if the PiP leash is null or invalid at onFinishResize time. In that case the task never gets TRUSTED_OVERLAY (fail-closed).",
      "Revocation in WM applies to task.getSurfaceControl(), while the grant in Shell applies to mPipOrganizer.getSurfaceControl() (the PiP leash). The material does not show whether these are the same SurfaceControl.",
      "Revocation uses a separate server-side transaction (mTransactionFactory.get()...apply()) rather than a Shell transaction, so the diff does not synchronize its ordering relative to Shell transactions."
     ],
     "behavior_changes": [
      "TRUSTED_OVERLAY is set on the PiP surface later than before: at the end of the enter-PiP transition in Shell (onFinishResize with an in-PiP direction) rather than when the activity is first reported pinned in WM.",
      "If Shell never reaches onFinishResize for entering PiP (or the leash is invalid), the task never receives TRUSTED_OVERLAY.",
      "New ProtoLog debug line in WM_SHELL_PICTURE_IN_PICTURE: \"Set TRUSTED_OVERLAY for Task#%d\".",
      "On unpin, WM still clears the flag (null-guarded; on Android 14 the null check is new). On pin, WM no longer touches the flag."
     ],
     "preconditions": [
      "An activity enters picture-in-picture so that RootWindowContainer#notifyActivityPipModeChanged is invoked with a non-null ActivityRecord.",
      "Before the fix, the grant happened at that point regardless of whether WM Shell later completed the enter-PiP transition.",
      "Exact trigger conditions for an app to retain or abuse the early grant are not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix (3972f55646f4 in android-security-14.0.0_r24): same PipTransition#onFinishResize change. In RootWindowContainer the removed code had no null check on task.getSurfaceControl(); the new revoke-only branch adds one. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix (cfcef3f38c2f in android-security-15.0.0_r12, cherry-picked from 8a84266ada6944eff7e5c73bdee3caefda364bf3, Change-Id Id849d93bc92209fc296ee91ef70b0ffe2a09550e). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix (68170bad5225 in android-security-16.0.0_r3): identical logic. The onFinishResize signature includes a leashOffset parameter and the finish-callback condition also checks mMoveToBackTransition, so the hunk is split differently. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); patched region present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit touching two files that must go together: removing the WM grant without adding the Shell grant would leave PiP tasks without TRUSTED_OVERLAY, and adding the Shell grant without removing the WM grant leaves the early grant in place. WM Shell is built into the SystemUI apk while RootWindowContainer is in services.jar, so both must be updated together (general platform knowledge).",
      "On trees where onFinishResize differs (e.g. Android 16 adds leashOffset and mMoveToBackTransition), only move the leash/hasValidLeash locals above the enteringPip block and add the guarded tx.setTrustedOverlay(leash, true); keep the rest of the method unchanged."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete scenario by which an app benefited from the early TRUSTED_OVERLAY grant (e.g. aborted enter-PiP, or timing between pin and Shell transition) is not shown in the material.",
      "Whether other PiP implementations exist on these branches and need an equivalent grant is not shown.",
      "Whether task.getSurfaceControl() (revoke target in WM) and mPipOrganizer.getSurfaceControl() (grant target in Shell) refer to the same surface is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48601": {
     "cve": "CVE-2025-48601",
     "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating PackagePreferences for packages that are not installed. The fix uses Process.INVALID_UID as the only missing-package sentinel, makes getOrCreatePackagePreferencesLocked throw on INVALID_UID, and changes read-only getters to lookups that do not create state and return defaults.",
     "component": "platform/frameworks/base, system_server notification service (NotificationManagerService, PreferencesHelper)",
     "weakness": "Missing validation of a uid sentinel. The two classes used different 'missing package' constants (NMS INVALID_UID = -1, PreferencesHelper UNKNOWN_UID = UserHandle.USER_NULL), so per-package notification preference state could be created for packages that do not exist.",
     "root_cause": "For a missing package, NotificationManagerService.getUidForPackageAndUser passes along the result of mPackageManager.getPackageUid, which is INVALID_UID (-1), into PreferencesHelper. PreferencesHelper treated only UNKNOWN_UID (UserHandle.USER_NULL) as 'no uid', so -1 was handled like a real uid, and getOrCreatePackagePreferencesLocked(pkg, uid) created and stored a PackagePreferences keyed by pkg/-1. Many getters (canShowBadge, getBubblePreference, isImportanceLocked, getPackagePriority, ...) also went through getOrCreate... and so created state as a side effect. Per the commit message, most binder entry points were shielded by canNotifyAsPackage, which fails for missing packages. The 'fromPrivilegedListener' methods had no equivalent check; the diff's tests cover getNotificationChannelsFromPrivilegedListener, getNotificationChannelGroupsFromPrivilegedListener and updateNotificationChannelFromPrivilegedListener. The role-handling paths (init, onRoleHoldersChanged) also passed -1 uids from getUidForPackage to mPreferencesHelper.updateDefaultApps.",
     "fix_analysis": [
      "PreferencesHelper.java: removes UNKNOWN_UID (USER_NULL) and uses android.os.Process.INVALID_UID everywhere: readXml uid/delegate parsing, createDefaultChannelIfNeededLocked, XML writing, dump, the PackagePreferences.uid default, Delegate.isAllowed, and the permission-migration loop. The helper now recognises the same -1 sentinel that NMS uses.",
      "PreferencesHelper.getOrCreatePackagePreferencesLocked(pkg, uid): now calls Objects.requireNonNull(pkg) and Preconditions.checkArgument(uid != INVALID_UID, \"Valid uid required to get settings of %s\", pkg) and is annotated @NonNull. The `if (r == null) throw IllegalArgumentException(\"Invalid package\")` blocks after its calls in the channel/group create and update methods are removed.",
      "PreferencesHelper: the multi-argument creating overload is renamed getOrCreatePackagePreferencesSupportingInvalidUidLocked. It is documented as the only path that accepts INVALID_UID (storing a time-limited entry in mRestoredWithoutUids) and is meant for readXml only. In the diff it is called from readXml and from the validated 2-argument wrapper.",
      "PreferencesHelper getters switch from getOrCreatePackagePreferencesLocked to the non-creating getPackagePreferencesLocked (now @Nullable) and return defaults when no entry exists: getBubblePreference, getAppLockedFields, canShowBadge, isInInvalidMsgState, hasUserDemotedInvalidMsgApp, hasSentInvalidMsg, hasSentValidMsg, didUserEverDemoteInvalidMsgApp, hasSentValidBubble, isImportanceLocked, the group-blocked check (returns false), getPackagePriority, getPackageVisibility.",
      "PreferencesHelper.getNotificationChannel and getConversationNotificationChannel return null early when uid == INVALID_UID. getNotificationChannels returns ParceledListSlice.emptyList() for INVALID_UID. getNotificationChannelsByConversationId is removed, and setAppImportanceLocked is annotated @VisibleForTesting.",
      "NotificationManagerService.java: drops the local `static final int INVALID_UID = -1` in favour of Process.INVALID_UID. getUidForPackageAndUser now returns mPackageManager.getPackageUid(...) directly. Two canNotifyAsPackage-gated methods now initialise targetUid to INVALID_UID instead of -1.",
      "NotificationManagerService role handling (init, onRoleHoldersChanged): a (pkg, uid) pair is added to the set passed to mPreferencesHelper.updateDefaultApps only when uid != INVALID_UID; otherwise Slog.e is logged. The browser/trampoline update and getUidForPackage use INVALID_UID instead of the literal -1.",
      "Tests: NotificationManagerServiceTest adds MISSING_PACKAGE cases for the three *FromPrivilegedListener methods (channels empty, groups empty, update throws IllegalArgumentException with the precondition message). PreferencesHelperTest replaces thenReturn(UNKNOWN_UID) stubs with NameNotFoundException and creates preferences with setShowBadge instead of canShowBadge. RoleObserverTest adds testInit_withInvalidPackages_invalidIgnored."
     ],
     "completeness": [
      "Every PreferencesHelper read-only getter shown in the diff that used getOrCreatePackagePreferencesLocked is converted. The remaining creating callers go through the validated wrapper, which throws on INVALID_UID.",
      "The readXml restore path still accepts INVALID_UID on purpose, through getOrCreatePackagePreferencesSupportingInvalidUidLocked, storing into mRestoredWithoutUids (time-limited when Flags.persistIncompleteRestoreData is enabled).",
      "The Android 15 diff does not change the *FromPrivilegedListener binder methods themselves. Their protection relies on PreferencesHelper rejecting INVALID_UID or returning early for it.",
      "Setter paths given INVALID_UID now surface as IllegalArgumentException to the binder caller. The diff adds no caller-side handling beyond the test that expects the exception."
     ],
     "behavior_changes": [
      "PreferencesHelper setters that go through getOrCreatePackagePreferencesLocked (create/update channel and group, among others) throw IllegalArgumentException(\"Valid uid required to get settings of <pkg>\") for Process.INVALID_UID. updateNotificationChannelFromPrivilegedListener for a missing package now throws.",
      "getNotificationChannel/getConversationNotificationChannel return null and getNotificationChannels returns an empty list for INVALID_UID. Per the tests, getNotificationChannelsFromPrivilegedListener and getNotificationChannelGroupsFromPrivilegedListener return empty lists for a missing package.",
      "Read-only getters no longer create PackagePreferences as a side effect and return defaults when no entry exists.",
      "When readXml parses a persisted uid of -1 (or finds no uid attribute), it now treats it as INVALID_UID and routes the entry to mRestoredWithoutUids.",
      "Role handling no longer passes role holders with an invalid uid to updateDefaultApps and logs an error instead. Dump output prints \"INVALID_UID\" instead of \"UNKNOWN_UID\".",
      "PreferencesHelper.getNotificationChannelsByConversationId is removed."
     ],
     "preconditions": [
      "The caller reaches the NMS *FromPrivilegedListener binder methods. In the added tests this means a registered notification listener (mListeners.checkServiceTokenLocked) with a CompanionDeviceManager association (mCompanionMgr.getAssociations).",
      "The target package is not installed for the given user, so getPackageUid yields INVALID_UID and NMS forwards it to PreferencesHelper.",
      "The role-holder paths (init, onRoleHoldersChanged) are driven by role manager state, not directly by the caller."
     ],
     "per_major": {
      "14": "adapted fix: commit 30b11a3523d6 in android-security-14.0.0_r24. Same logic, but it locks on mPackagePreferences instead of mLock, has no creationTime parameter, and makes setAppImportanceLocked package-private. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit ed9af8192664 in android-security-15.0.0_r12. 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: commit 93629e575f69 in android-security-16.0.0_r3. It also adds an INVALID_UID early return to the bundle-type channel lookup, moves bundle channel creation under synchronized(mLock) using the validated getOrCreatePackagePreferencesLocked, guards getNotificationChannels(pkg, uid, includeDeleted, includeBundles), and removes a now-redundant INVALID_UID check in an NMS parent-channel lookup. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected: not listed by the bulletin. Patched region present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply the commit as a whole: the constant change (UNKNOWN_UID to Process.INVALID_UID), the precondition and the getter conversions depend on each other. Differences between branches: the lock object (mPackagePreferences on 14, mLock on 15 and 16), the creationTime parameter (15 and 16), and the bundle-channel overloads (16).",
      "Tests that create preferences through a getter (e.g. mHelper.canShowBadge) break because getters no longer create state. Switch them to setShowBadge, as the upstream test diff does, and change getPackageUidAsUser stubs from thenReturn(UNKNOWN_UID) to throwing NameNotFoundException."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show what a listener could gain from a pkg/-1 PackagePreferences entry. The bulletin only states EoP, High.",
      "The *FromPrivilegedListener method bodies are not in the diff. Their permission checks are known only from the commit message and the test setup.",
      "Whether other NMS callers can reach PreferencesHelper setters with INVALID_UID, and now surface IllegalArgumentException to clients, is not shown.",
      "Whether any path forwards a stale but numerically valid uid for a missing package is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48603": {
     "cve": "CVE-2025-48603",
     "summary": "Adds a 200 KB size cap on an input method's SERVICE_META_DATA XML resource, enforced in InputMethodInfo before the XML is parsed, so an oversized metadata XML now fails with XmlPullParserException instead of being parsed in the system process.",
     "component": "platform/frameworks/base, input method framework (android.view.inputmethod.InputMethodInfo, constructed by InputMethodManagerService per the commit message)",
     "weakness": "Missing size check on untrusted input leading to uncontrolled memory consumption (resource exhaustion / DoS)",
     "root_cause": "The InputMethodInfo constructor loaded and parsed the IME's metadata XML (si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA)) with no limit on the size of that app-supplied XML resource. Per the commit message, a malicious IME could ship an extremely large metadata XML, and IMMS could run out of memory while constructing InputMethodInfo from it.",
     "fix_analysis": [
      "core/java/android/view/inputmethod/InputMethodInfo.java: adds the constant MAX_METADATA_SIZE_BYTES = 200 * 1024 and imports android.os.Bundle and java.io.InputStream.",
      "InputMethodInfo constructor (the one that calls si.loadXmlMetaData): moves `Resources res = pm.getResourcesForApplication(si.applicationInfo)` above the metadata load and calls the new validateXmlMetaData(si, res) before si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA). The parsing logic that follows is unchanged.",
      "New private static validateXmlMetaData(ServiceInfo si, Resources res): returns early if si.metaData is null or the InputMethod.SERVICE_META_DATA resource id is 0. Otherwise it opens the resource with res.openRawResource(resourceId) in try-with-resources and calls InputStream.skip() in a loop until MAX_METADATA_SIZE_BYTES bytes are skipped or skip returns <= 0. If exactly 200 KB were skipped, it calls is.read(). A result other than -1 means the resource is larger than the cap, and an XmlPullParserException naming the package is thrown. The check uses skip() and a single read() instead of reading the content into a buffer.",
      "The exception is thrown before loadXmlMetaData, so the oversized XML never reaches the parser. The exception text says the InputMethod will not be loaded."
     ],
     "completeness": [
      "The check bounds only the raw byte size of the resource referenced by InputMethod.SERVICE_META_DATA. It does not bound the number of <subtype> elements or other parsed structures in a file under 200 KB (the parse loop after the check is unchanged in the diff).",
      "Only the InputMethodInfo constructor path that calls si.loadXmlMetaData is guarded. validateXmlMetaData is private and the diff calls it from nowhere else.",
      "The check is skipped when si.metaData is null or the resource id is 0. The material does not show how loadXmlMetaData behaves in those cases.",
      "Resources referenced from the XML (strings, icons, subtype resources) are not size-limited by this change."
     ],
     "behavior_changes": [
      "IMEs whose metadata XML resource is larger than 200 KB (200 * 1024 bytes) now make InputMethodInfo construction throw XmlPullParserException (\"Input method metadata exceeds maximum allowed limit of 200KB for <pkg>. InputMethod will not be loaded.\").",
      "pm.getResourcesForApplication(si.applicationInfo) is now called before loadXmlMetaData rather than after. Its NameNotFoundException can therefore surface before the \"No ... meta-data\" XmlPullParserException.",
      "Every InputMethodInfo construction with metadata now does an extra open/skip pass over the metadata resource."
     ],
     "preconditions": [
      "An installed app declares an input method service whose InputMethod.SERVICE_META_DATA metadata points at an XML resource, so that the system (IMMS, per the commit message) constructs an InputMethodInfo for it.",
      "The size check only runs when si.metaData is non-null and contains a non-zero resource id for InputMethod.SERVICE_META_DATA."
     ],
     "per_major": {
      "14": "Same fix, applied as commit ea309e799b21 in android-security-14.0.0_r24. The logic is identical and only the context differs (mIsVrOnly instead of mIsVirtualDeviceOnly, no Collections import). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Same fix, commit e7824e85ad70 in android-security-15.0.0_r12 (cherry-pick of 5e31d9c067f0). 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "Same fix, commit b4c6786312a2 in android-security-16.0.0_r3, with the same diff as Android 15. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin (not listed). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit in InputMethodInfo.java with no new dependencies. The only merge point is the constructor hunk: move `Resources res = ...` above loadXmlMetaData and insert the validateXmlMetaData call. The new helper goes after the constructor, and its anchor differs (mIsVrOnly on 14, mIsVirtualDeviceOnly on 15/16)."
     ],
     "confidence": "high",
     "unknowns": [
      "How the constructor's catch block and the IMMS caller handle the thrown XmlPullParserException (not shown in the material).",
      "How loadXmlMetaData behaves when metaData is null or the resource id is 0.",
      "The memory footprint or metadata size that triggered the original OOM (the commit message says only 'extremely large').",
      "Whether any other system component parses InputMethod.SERVICE_META_DATA outside this constructor."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48604": {
     "cve": "CVE-2025-48604",
     "summary": "MmsService now passes the calling user id into DownloadRequest and refuses to write a downloaded MMS PDU to a content URI whose embedded user id differs from the calling user.",
     "component": "platform/packages/services/Mms, MmsService download path (DownloadRequest / writePduToContentUri)",
     "weakness": "Missing cross-user authorization check on a caller-supplied content URI (confused deputy)",
     "root_cause": "The MMS download entry point in MmsService builds a DownloadRequest from a caller-supplied contentUri, which is the destination for the downloaded MMS PDU. MmsService.writePduToContentUri(contentUri, pdu) wrote to that URI without checking which Android user the URI belongs to, and DownloadRequest did not keep the calling user. A download request could therefore have the PDU written to a content URI of another user. In the surrounding code the read path (RequestManager.readPduFromContentUri) already took a callingUser parameter, but the write path did not.",
     "fix_analysis": [
      "src/com/android/mms/service/DownloadRequest.java:DownloadRequest(...) constructor: new int callingUser parameter, stored in a new final field mCallingUser.",
      "src/com/android/mms/service/DownloadRequest.java:transferResponse: passes mCallingUser to mRequestManager.writePduToContentUri(mContentUri, response, mCallingUser).",
      "src/com/android/mms/service/MmsRequest.java:RequestManager.writePduToContentUri: the interface signature gains an int callingUser parameter.",
      "src/com/android/mms/service/MmsService.java (download handler): DownloadRequest is now constructed with the callingUser value that is already in scope.",
      "src/com/android/mms/service/MmsService.java:writePduToContentUri: after the null checks, computes contentUriUserID = ContentProvider.getUserIdFromUri(contentUri, UserHandle.myUserId()). If callingUser != contentUriUserID it logs an error and returns false, before the copy Callable is built.",
      "tests/robotests/src/com/android/mms/service/MmsRequestRoboTest.java:downloadRequest_noSatellite_downloadSuccessful: updated to the new constructor signature using CALLING_USER (15 and 16 only)."
     ],
     "completeness": [
      "Covers the in-platform download path: a downloaded PDU written through RequestManager.writePduToContentUri now requires the user ids to match. SendRequest.transferResponse does not write to a URI (per index search).",
      "The check runs in transferResponse, after the HTTP download has completed, not when the request is submitted. A mismatch is only detected at the write step.",
      "A URI without an explicit user prefix resolves to UserHandle.myUserId() of the MmsService process, so the check then requires the caller to be that same user.",
      "The carrier-app branch (request.tryDownloadingByCarrierApp, taken when getCarrierMessagingServicePackageIfExists(subId) is non-null) returns before addSimRequest. The material does not show whether this branch reaches writePduToContentUri.",
      "No test asserting rejection of a mismatched-user URI is added."
     ],
     "behavior_changes": [
      "Downloads whose destination content URI belongs to a different user than the caller now fail at the write step: writePduToContentUri returns false and logs 'Uri belongs to a different user ...'.",
      "The DownloadRequest constructor signature changes (new int callingUser argument before creator), and RequestManager.writePduToContentUri gains a callingUser parameter. This affects any implementer or test mock."
     ],
     "preconditions": [
      "The caller reaches the MmsService download entry point (behind SmsManager.downloadMultimediaMessage (general platform knowledge)) with a contentUri that encodes a user id different from the calling user.",
      "MMS is enabled in the loaded config for the subscription, no carrier messaging service package is present, and MMS data is enabled for the subscription. Otherwise the code returns before addSimRequest.",
      "The HTTP download must succeed for transferResponse to be invoked."
     ],
     "per_major": {
      "14": "adapted fix (ed4355456181 in android-security-14.0.0_r24): same logic, but the DownloadRequest constructor has no TelephonyManager parameter and the robotest file is not touched. 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix (ad9bf77e4577 in android-security-15.0.0_r12, cherry-pick of 0b8c94aa0a37). 7/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix (c60a828b9fa1 in android-security-16.0.0_r3), diff identical to 15. 7/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix relies on callingUser already being in scope in the MmsService download handler, which is the case in the 14, 15 and 16 diffs. Update every DownloadRequest constructor call site and any RequestManager implementation or mock, because the signatures change (the Android 14 variant has no TelephonyManager argument)."
     ],
     "confidence": "high",
     "unknowns": [
      "How callingUser is derived in the MmsService download handler (not in the diff hunk).",
      "How a false return from transferResponse/writePduToContentUri is reported to the caller (for example through downloadedIntent).",
      "Whether the carrier-app download path (tryDownloadingByCarrierApp) forwards contentUri to CarrierMessagingService.onDownloadMms, and whether that path has any equivalent user check.",
      "Which line of the 8-line patched region differs in the latest security tags (7/8 reported for every major)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48607": {
     "cve": "CVE-2025-48607",
     "summary": "Changes the IAppOpsService.getPackagesForOpsForDevice binder method to return a ParceledListSlice<AppOpsManager.PackageOps> instead of a plain List, so that large result sets are paginated across binder transactions instead of overflowing a single one.",
     "component": "platform/frameworks/base, AppOps (AppOpsService / AppOpsManager / IAppOpsService.aidl)",
     "weakness": "Unbounded binder reply size (resource exhaustion leading to TransactionTooLargeException in a privileged caller; DoS)",
     "root_cause": "AppOpsService.getPackagesForOpsForDevice(int[] ops, String persistentDeviceId) built a List<AppOpsManager.PackageOps> and returned it in a single binder reply. Per the commit message, an earlier fix (ag/31434165) capped the number of attributed op entries per package, but the number of packages in the reply was still unbounded. A PoC app created attributed op entries for hundreds of packages, which caused TransactionTooLargeException in the getPackagesForOpsForDevice binder API. The commit message says SystemUI crashed, and implies the device failed to boot, since it says the device could boot after the fix.",
     "fix_analysis": [
      "core/java/com/android/internal/app/IAppOpsService.aidl: the declaration of getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId) now returns ParceledListSlice<AppOpsManager.PackageOps> instead of List<AppOpsManager.PackageOps>. The method keeps its position (last method in the interface on Android 15, before noteOperationsInBatch on Android 16), so transaction codes do not change.",
      "services/core/java/com/android/server/appop/AppOpsService.java:getPackagesForOpsForDevice: the return type becomes ParceledListSlice<AppOpsManager.PackageOps>, and the final `return res;` becomes `return res == null ? null : new ParceledListSlice<>(res);`. The permission check (mContext.checkPermission for hasAllPackageAccess) and the loop that builds the result are untouched.",
      "services/core/java/com/android/server/appop/AppOpsService.java:getPackagesForOps(int[] ops): still returns List<AppOpsManager.PackageOps>. It now calls getPackagesForOpsForDevice(ops, PERSISTENT_DEVICE_ID_DEFAULT) and unwraps the result with packageOps.getList(), keeping null as null.",
      "core/java/android/app/AppOpsManager.java:getPackagesForOps(String[] ops, String persistentDeviceId): receives the ParceledListSlice from mService.getPackagesForOpsForDevice and returns packageOps.getList(), or Collections.emptyList() when the result is null. The public return type is unchanged.",
      "core/java/android/app/AppOpsManager.java:getPackagesForOps(int[] ops) (@UnsupportedAppUsage): unwraps the same way with getList() and returns null when the service returned null, so the earlier null behavior is kept.",
      "Imports: AppOpsService.java gains android.content.pm.ParceledListSlice (needed) and java.io.StringWriter (no hunk in the diff uses it; it appears incidental). IAppOpsService.aidl already imported android.content.pm.ParceledListSlice."
     ],
     "completeness": [
      "Covers the single binder method getPackagesForOpsForDevice and both AppOpsManager client wrappers that call it (getPackagesForOps(String[], String) and getPackagesForOps(int[])). Null handling is kept on both sides.",
      "The fix changes only the transport. The total number of PackageOps returned is still unbounded: ParceledListSlice splits the list across transactions, but each PackageOps element must still fit on its own. That relies on the earlier per-package attributed-op-entry threshold mentioned in the commit message, which is not part of this diff.",
      "AppOpsService.getPackagesForOps(int[] ops) keeps its @Override and its List return type. If that method is itself declared in IAppOpsService.aidl (its declaration is not in the AIDL excerpt shown), a direct binder caller would still receive an unpaginated List. The in-tree AppOpsManager wrappers no longer use it.",
      "This commit does not touch other AppOps binder methods that return op state lists. The material does not show whether their replies can grow in the same way."
     ],
     "behavior_changes": [
      "The reply of the getPackagesForOpsForDevice binder transaction changes on the wire from a List<PackageOps> to a ParceledListSlice. Any out-of-tree code that calls the hidden IAppOpsService interface directly must be rebuilt against the new AIDL.",
      "Large results are delivered over several binder round trips instead of one, because ParceledListSlice fetches the remaining elements from the server on demand (general platform knowledge). Callers see the same List contents.",
      "Permission checks, filtering, and the null/empty-list return behavior of the public AppOpsManager APIs do not change."
     ],
     "preconditions": [
      "A caller invokes AppOpsManager.getPackagesForOps(...) or IAppOpsService.getPackagesForOpsForDevice. The public API requires GET_APP_OPS_STATS. The service uses mContext.checkPermission to decide on full package access, and callers without it get a filtered result.",
      "Op state exists for a large number of packages (per the commit message, a PoC app created attributed op entries for hundreds of packages), so the serialized reply exceeds the binder transaction size.",
      "Per the commit message, the process that crashed is SystemUI, a privileged system client that calls this API, not the app that created the entries."
     ],
     "per_major": {
      "14": "Not affected (not listed by the bulletin). The material shows no Android 14 code for this method. The 5/8 lines region metric is the same on every major, so it tells nothing either way.",
      "15": "Same fix: platform/frameworks/base 2114590c98f3 in android-security-15.0.0_r12 (cherry-pick, Change-Id If4590160d1d743f58c1aed3f923d3668a8936c57), touching AppOpsManager.java, IAppOpsService.aidl and AppOpsService.java.",
      "16": "Same fix: platform/frameworks/base 03d704069914 in android-security-16.0.0_r3. The logic is identical. The AIDL hunk also re-indents the adjacent noteOperationsInBatch line (whitespace only), and line offsets differ.",
      "17": "Not affected (not listed by the bulletin). The material does not show the code for 17."
     },
     "backport_notes": [
      "The three files must land together: the AIDL return type change has to be in the same build as the AppOpsService implementation and the AppOpsManager client, because all of them compile against the generated IAppOpsService stub (general platform knowledge). Keep the method's position in the AIDL so transaction codes and the native ordering described in the AIDL header stay valid. The added java.io.StringWriter import is not used by the diff.",
      "No 'Fix in fork' section was provided, so no per-file verdict for the fork can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether getPackagesForOps(int[]) is itself declared in IAppOpsService.aidl (the AIDL excerpt does not include it), and so whether a direct binder path still returns an unpaginated List.",
      "Whether Android 14 and 17 contain the affected method. The bulletin does not list them, and the material shows no code from those branches.",
      "Details of the earlier fix ag/31434165 (the per-package attributed-op-entry threshold) that this change builds on."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48614": {
     "cve": "CVE-2025-48614",
     "summary": "RecoverySystem.rebootWipeUserData now queries DynamicSystemManager and throws a SecurityException (\"Wiping data is not allowed while in DSU mode.\") when a Dynamic System Update image is in use, so a factory reset can no longer be started through this method in DSU mode.",
     "component": "platform/frameworks/base, core/java/android/os/RecoverySystem.java (RecoverySystem / factory reset, interaction with DynamicSystemManager)",
     "weakness": "Missing state check (factory reset allowed regardless of DSU-in-use state); bulletin classifies the impact as DoS",
     "root_cause": "RecoverySystem.rebootWipeUserData(Context, boolean shutdown, String reason, boolean force, boolean wipeEuicc, boolean keepMemtagMode) only checked the UserManager.DISALLOW_FACTORY_RESET user restriction (and only when force was false) before proceeding to the ConditionVariable / MASTER_CLEAR_NOTIFICATION intent and the wipe. It never checked whether a Dynamic System Update (DSU) image was in use, so a user-data wipe could be initiated while in DSU mode. The commit message states the intent (\"Disallow factory reset while in DSU mode\"); the material does not describe why wiping in that state is harmful beyond the bulletin's DoS classification.",
     "fix_analysis": [
      "core/java/android/os/RecoverySystem.java: adds import android.os.image.DynamicSystemManager.",
      "core/java/android/os/RecoverySystem.java:rebootWipeUserData(Context, boolean, String, boolean, boolean, boolean): obtains DynamicSystemManager via context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE) next to the existing UserManager lookup.",
      "Same function: after the existing DISALLOW_FACTORY_RESET check, adds `if (dsuManager.isInUse()) throw new SecurityException(\"Wiping data is not allowed while in DSU mode.\")`, placed before the ConditionVariable / MASTER_CLEAR_NOTIFICATION intent, so no wipe-related work starts when a DSU image is in use.",
      "Unlike the user-restriction check, the new DSU check is not gated on `force`; it applies to every caller of this overload."
     ],
     "completeness": [
      "Covers only the single rebootWipeUserData overload shown in the diff. The material does not show whether other RecoverySystem overloads or entry points route through it, so coverage of other wipe paths cannot be confirmed from the code provided.",
      "The returned DynamicSystemManager is dereferenced without a null check. The material does not show whether the service can be absent on any device.",
      "Other RecoverySystem operations, e.g. wipePartitionToExt4 used by packages/apps/Settings Enable16kPagesPreferenceController.wipeData (per index search), are not touched by this fix. Their relevance to DSU mode is not shown.",
      "The added check is a runtime state test (isInUse), not a caller authorization check."
     ],
     "behavior_changes": [
      "rebootWipeUserData throws SecurityException with message \"Wiping data is not allowed while in DSU mode.\" whenever DynamicSystemManager.isInUse() is true, even when force == true.",
      "Factory reset requests that reach this overload now fail while a DSU image is in use instead of wiping."
     ],
     "preconditions": [
      "A DSU image is in use, so that DynamicSystemManager.isInUse() returns true.",
      "A caller invokes RecoverySystem.rebootWipeUserData (the fixed overload). The material does not show which production callers reach it or which ones an attacker could drive."
     ],
     "per_major": {
      "14": "same fix (different context): commit ed10409eb6de in android-security-14.0.0_r24. The inserted lines are identical, applied in the 5-argument overload rebootWipeUserData(Context, boolean, String, boolean, boolean) (no keepMemtagMode parameter). 8/8 lines present at android-security-14.0.0_r29.",
      "15": "this fix: commit aa4b01792c25 in android-security-15.0.0_r12 (cherry-pick, Change-Id I36afa074f16bb7ebf8b617de73dab3d4df5fb5dc); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit ec0c32ea736b in android-security-16.0.0_r3, identical hunk on the 6-argument overload; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: not listed by the bulletin; the patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk. The only dependencies are android.os.image.DynamicSystemManager.isInUse() and Context.DYNAMIC_SYSTEM_SERVICE in the target tree. On branches with a different rebootWipeUserData signature (as on Android 14), apply the same insertions inside the overload that contains the DISALLOW_FACTORY_RESET check."
     ],
     "confidence": "high",
     "unknowns": [
      "Why a factory reset in DSU mode leads to the DoS described by the bulletin is not shown in the material.",
      "Whether other RecoverySystem overloads or wipe entry points delegate to the fixed overload is not shown.",
      "Which production callers (e.g. DevicePolicyManagerService) reach the fixed overload, and at what privilege level, is not shown. The index only shows test mocks.",
      "Whether getSystemService(Context.DYNAMIC_SYSTEM_SERVICE) can return null on some devices (the manager is not null-checked) is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48618": {
     "cve": "CVE-2025-48618",
     "summary": "CommandParamsFactory.processLaunchBrowser now rejects a SIM Toolkit LAUNCH BROWSER proactive command with ResultCode.LAUNCH_BROWSER_ERROR when KeyguardManager.isDeviceLocked() is true. ResultException no longer asserts that LAUNCH_BROWSER_ERROR carries additional info, so the new rejection can be constructed.",
     "component": "platform/frameworks/opt/telephony, CAT/SIM Toolkit (STK) proactive command parsing (com.android.internal.telephony.cat)",
     "weakness": "Missing device-lock (keyguard) state check before a user-visible action (improper authorization of the LAUNCH BROWSER proactive command)",
     "root_cause": "CommandParamsFactory.processLaunchBrowser parsed and accepted a LAUNCH BROWSER proactive command from the modem regardless of the device lock state. Per index search, the Stk app's StkAppService.launchBrowser builds an Intent.ACTION_VIEW from the URL in the command or from a default URL, so a browser could be launched while the device was locked. The code had no reference to KeyguardManager, and CommandParamsFactory did not keep the Context passed to its constructor. Also, ResultException(ResultCode) threw an AssertionError for LAUNCH_BROWSER_ERROR (0x26) unless additional info was supplied, which prevented using that result code as a simple rejection.",
     "fix_analysis": [
      "src/java/com/android/internal/telephony/cat/CommandParamsFactory.java: adds the import android.app.KeyguardManager and a new private Context mContext field. The constructor CommandParamsFactory(RilMessageDecoder, IccFileHandler, Context) now stores its context parameter in mContext.",
      "CommandParamsFactory.processLaunchBrowser: at method entry, gets KeyguardManager via mContext.getSystemService(KeyguardManager.class). If that is non-null and isDeviceLocked() returns true, it logs 'The device is locked, cannot launch the Browser' and throws new ResultException(ResultCode.LAUNCH_BROWSER_ERROR, \"The device is locked, unable to process the command.\"). This happens before the 'process LaunchBrowser' log and the rest of the command processing.",
      "src/java/com/android/internal/telephony/cat/ResultException.java: in ResultException(ResultCode), removes 'case LAUNCH_BROWSER_ERROR: // 0x26' from the switch that throws AssertionError when additional information is missing, and drops '26' from the ETSI TS 102 223 comment. This is needed because ResultException(ResultCode, String) delegates to this(result). Without this change, the new throw in processLaunchBrowser would raise an AssertionError instead."
     ],
     "completeness": [
      "Only the LAUNCH BROWSER proactive command path (processLaunchBrowser) is gated. This commit changes no other proactive command handler in CommandParamsFactory.",
      "The check is done once, at parse time, in the telephony framework. Per index search, StkAppService.launchBrowser in packages/apps/Stk is not modified by this commit.",
      "The gate relies on KeyguardManager.isDeviceLocked(). If getSystemService returns null, the command proceeds as before (fail-open on null).",
      "ResultException(ResultCode, String) sets mExplanation but leaves mAdditionalInfo at -1. Removing the assertion also means any other caller that constructs ResultException(LAUNCH_BROWSER_ERROR) without additional info no longer triggers an AssertionError."
     ],
     "behavior_changes": [
      "When the device is locked, a LAUNCH BROWSER proactive command is rejected with ResultCode.LAUNCH_BROWSER_ERROR (0x26) and a CatLog debug line, instead of being processed.",
      "ResultException(ResultCode.LAUNCH_BROWSER_ERROR) without additional info no longer throws AssertionError. A 0x26 result may now be produced without the additional information that the old ETSI TS 102 223 comment listed as mandatory.",
      "CommandParamsFactory now holds a reference to the Context passed at construction (new mContext field)."
     ],
     "preconditions": [
      "A SIM application sends a LAUNCH BROWSER proactive command through the modem to the CAT stack, where it is decoded by CommandParamsFactory (its constructor takes RilMessageDecoder caller, IccFileHandler fh, Context context).",
      "Per index search, the Stk app's StkAppService.launchBrowser fires Intent.ACTION_VIEW on the URL from the command or on a default URL.",
      "The new check only blocks when KeyguardManager.isDeviceLocked() returns true."
     ],
     "per_major": {
      "14": "same logic, different context lines: commit cc7d88a59cbd in android-security-14.0.0_r24. CommandParamsFactory and ResultException get the same changes; only the hunk context differs (mContext is inserted after the MAX_GSM7_DEFAULT_CHARS/MAX_UCS2_CHARS constants). 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: commit 8e7b0146d56b in android-security-15.0.0_r12 (cherry-pick of internal dcf5c112a93d, Change-Id I9f651c7f1c5674df5774f5a7609f6d3749b5c50c). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit fee68bcdcf02 in android-security-16.0.0_r3. The diff is identical to the Android 15 change (same blob indices cb96c67e07..8264f6e25e). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not listed by the bulletin (not affected per the bulletin). The patched region is already present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Take both files together. The ResultException.java hunk that removes LAUNCH_BROWSER_ERROR from the AssertionError switch is a hard dependency: ResultException(ResultCode, String) calls this(result), so without it the new throw in processLaunchBrowser would raise an AssertionError.",
      "The private constructor already receives a Context on 14/15/16 (used for context.getResources()), so the backport only adds the mContext field and its assignment. The Android 14 variant shows the hunk applies with different context lines only."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how the code that calls processLaunchBrowser (in CommandParamsFactory and CatService) turns the thrown ResultException into a terminal response, or how the response is encoded.",
      "The material does not show whether the Stk app (packages/apps/Stk) checks the lock state itself, or whether it got a companion change for this bug. Only the frameworks/opt/telephony commit is linked by bug_id.",
      "Whether a lock-state change between parsing and the browser launch matters is not shown.",
      "Beyond the High EoP rating, the bulletin does not describe the concrete trigger or impact."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48620": {
     "cve": "CVE-2025-48620",
     "summary": "Removes an early return in VoiceInteractionManagerService's package-change handler. When a voice interaction service is set and the recognizer's package is uninstalled, the code that resets the current voice recognition service now still runs.",
     "component": "platform/frameworks/base, services/voiceinteraction (VoiceInteractionManagerService, system_server)",
     "weakness": "Incorrect control flow (early return) that leaves a stale reference to an uninstalled package in the voice_recognition_service secure setting",
     "root_cause": "In VoiceInteractionManagerService.java, the onSomePackagesChanged handler (named in the commit message) had a block for the case where a voice interaction service is configured (curInteractor != null). That block ended with an unconditional `return;`. Because of that return, the later logic in the same handler was never reached whenever an interactor was set. Per the commit message, this included the logic that resets the current voice recognition service. As a result, uninstalling the package of the current voice recognition service did not reset the recognizer; the commit's Test line checks this through the voice_recognition_service secure setting.",
     "fix_analysis": [
      "VoiceInteractionManagerService.java, package-change handler (onSomePackagesChanged per the commit message): the `return;` at the end of the curInteractor != null block is deleted. Execution now continues to the rest of the handler, including the voice recognition service reset logic further down.",
      "Same function: the next assistant check changes from `if (curAssistant != null)` to `if (curInteractor == null && curAssistant != null)`. The assistant-removal handling (isPackageDisappearing(curAssistant.getPackageName()) == PACKAGE_PERMANENT_CHANGE) therefore still runs only when no interactor is set, as the new code comment states, while later code can now run.",
      "Single commit, one file, 3 insertions and 2 deletions. The diff is identical on 14, 15 and 16."
     ],
     "completeness": [
      "The fix only changes control flow in the package-change handler. The recognizer reset logic itself is not shown in the material and is not changed; it is now reachable whether curInteractor is null or not.",
      "The assistant handling is explicitly limited to curInteractor == null, so the assistant path keeps its previous behavior.",
      "Other paths that react to the interactor package are not touched. For example, handleUserStop (shown in the context) only calls switchImplementationIfNeededLocked for the interactor. The material does not show whether these paths need recognizer handling."
     ],
     "behavior_changes": [
      "When a voice interaction service is set and the current voice recognition service package is uninstalled, the recognizer is now reset. The commit's Test line checks this with `adb shell settings get secure voice_recognition_service`.",
      "Code after the assistant block in the package-change handler now runs even when an interactor is configured. The diff adds no new error codes or API changes."
     ],
     "preconditions": [
      "A voice interaction service (curInteractor) is configured for the user; this is the case that hit the early return.",
      "The package of the current voice recognition service is uninstalled, which makes the package monitor call the handler in system_server.",
      "The code runs in system_server's VoiceInteractionManagerService. The diff involves no caller-side permission check."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 66a76238a130 in android-security-14.0.0_r24, identical diff at a different line offset (@@ -2457). 4/5 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 0925826a6ad8 in android-security-15.0.0_r12 (cherry-pick, Change-Id I563ad50dac05a019c2ce8b727b5e5744ba848cc0). 4/5 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base db86972777c8 in android-security-16.0.0_r3, identical diff (@@ -2676). 4/5 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). 4/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The diff is identical across 14, 15 and 16 (only the hunk offsets differ), so it should apply to any branch that has the curInteractor and curAssistant blocks in onSomePackagesChanged. After applying it, confirm that the target branch has the recognizer reset code after the assistant block; if it does not, removing the return has no effect."
     ],
     "confidence": "high",
     "unknowns": [
      "The voice recognition service reset code after the patched hunk is not in the material, so its exact behavior is not shown.",
      "The material does not describe the concrete EoP impact beyond the bulletin's High/EoP rating.",
      "The material does not show whether Android 13 (listed as affected) received the same commit.",
      "The per-major facts report 4/5 patched-region lines present in the latest tags but do not say which line is missing, so they do not by themselves confirm the fixed state."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48621": {
     "cve": "CVE-2025-48621",
     "summary": "Two coordinated changes against tapjacking during window animations: InputDispatcher stops delivering motion events to windows with TouchOcclusionMode::USE_OPACITY whose alpha is below 0.5, and WM Shell's DefaultTransitionHandler lowers MAX_ANIMATION_DURATION from 3000 to 1500.",
     "component": "platform/frameworks/native (inputflinger InputDispatcher) and platform/frameworks/base (libs/WindowManager/Shell transitions)",
     "weakness": "Improper restriction of rendered UI layers (tapjacking): touch delivered to a nearly transparent window during transition animations",
     "root_cause": "InputDispatcher::canWindowReceiveMotionLocked decided whether a window could receive touch without looking at the window's current alpha. A window declared opaque (touchOcclusionMode USE_OPACITY) kept receiving motion events even when its alpha was low. Per the commit message, that low alpha is likely a fade-out animation or other visual change. Separately, DefaultTransitionHandler had MAX_ANIMATION_DURATION = 3000 for default transitions. The commit message calls 3 seconds way too long, especially given tapjacking during animation.",
     "fix_analysis": [
      "frameworks/native services/inputflinger/dispatcher/InputDispatcher.cpp:InputDispatcher::canWindowReceiveMotionLocked: a new check is added at the top of the function, before verifyTargetedInjection and the PAUSE_DISPATCHING, NO_INPUT_CHANNEL and connection checks. If info.touchOcclusionMode == TouchOcclusionMode::USE_OPACITY and info.alpha < 0.5f, it logs at INFO level ('Not sending motion to <name>, window opacity=<alpha> is below the threshold') and returns false, so the window does not receive the motion.",
      "frameworks/base libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java: the constant MAX_ANIMATION_DURATION changes from 3000 to 1500, which lowers the maximum duration of the default transition.",
      "The two commits are in different repos. Both are RESTRICT AUTOMERGE backports linked by Bug 266433089. The material shows no ordering dependency between them."
     ],
     "completeness": [
      "The alpha check applies only to windows whose touchOcclusionMode is USE_OPACITY. canWindowReceiveMotionLocked still does not check alpha for windows in other touch occlusion modes.",
      "The threshold is a hard-coded 0.5f on WindowInfo::alpha. The check does not look at how the alpha came about (animation or app-set).",
      "The duration change touches only DefaultTransitionHandler's MAX_ANIMATION_DURATION. Other transition handlers or animation paths are not changed by this commit.",
      "The new check is the first one in canWindowReceiveMotionLocked. The material does not show the callers, so which windows are evaluated through it is not established."
     ],
     "behavior_changes": [
      "Motion events are no longer sent to a window with touchOcclusionMode USE_OPACITY while its alpha is below 0.5. An INFO log line is written each time such a window is skipped.",
      "The maximum duration for the default transition in DefaultTransitionHandler drops from 3000 to 1500."
     ],
     "preconditions": [
      "For the InputDispatcher change: a motion event reaches canWindowReceiveMotionLocked for a window whose WindowInfo has touchOcclusionMode USE_OPACITY and alpha < 0.5.",
      "For the Shell change: a window transition handled by DefaultTransitionHandler."
     ],
     "per_major": {
      "14": "Same fix: frameworks/base 848e575f07ff in android-security-14.0.0_r24 changes MAX_ANIMATION_DURATION from 3000 to 1500 in DefaultTransitionHandler.java (identical one-line change at a slightly different line offset). The material does not show a frameworks/native counterpart for 14. At android-security-14.0.0_r29 the resolver reports 4/5 lines of the patched region present.",
      "15": "Same fix: frameworks/base 06e697583c87 and frameworks/native 51f89fddd0a0 in android-security-15.0.0_r12, both RESTRICT AUTOMERGE cherry-picks. Code at android-security-15.0.0_r17 shows MAX_ANIMATION_DURATION = 1500. The resolver reports 4/5 lines of the patched region present.",
      "16": "Same fix: frameworks/base 6d1697c96c5c in android-security-16.0.0_r3, the same constant change (its context also has SIZE_CHANGE_ANIMATION_DURATION = 400). The material does not show a frameworks/native counterpart for 16. At android-security-16.0.0_r8 the resolver reports 4/5 lines present.",
      "17": "Not affected per the bulletin (not listed). At android-security-17.0.0_r1 the resolver reports 4/5 lines of the patched region present."
     },
     "backport_notes": [
      "Both changes are small: a one-line constant change in DefaultTransitionHandler.java, and a 6-line early return at the top of InputDispatcher::canWindowReceiveMotionLocked that uses WindowInfo::touchOcclusionMode and WindowInfo::alpha. Apply the changes in both repos. The material shows no dependency between them.",
      "The 'code around the fix' excerpt for InputDispatcher.cpp at android-security-15.0.0_r17 shows an unrelated region (shouldPruneInboundQueueLocked), and the index snippet from android-15.0.0_r36 shows canWindowReceiveMotionLocked without the alpha check. Check that the native change is actually in the target branch instead of inferring it from the base-side constant."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which window states or transitions trigger the issue. The commits only describe tapjacking during animation.",
      "How MAX_ANIMATION_DURATION is applied inside DefaultTransitionHandler (clamp, timeout or other); its uses are not shown.",
      "Whether the frameworks/native alpha check was also backported to the Android 14 and 16 security branches (only the frameworks/base commits are shown for those majors).",
      "Which of the two patched regions the '4/5 lines present' figure refers to, and whether the InputDispatcher.cpp change is present at android-security-15.0.0_r17.",
      "Which callers invoke canWindowReceiveMotionLocked, and for which window types.",
      "Whether the inputflinger_tests named in the native commit message were added or updated in the same change (no test diff is included)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48622": {
     "cve": "CVE-2025-48622",
     "summary": "Replaces the Android copy of the Adobe DNG SDK (1.4 era) wholesale with DNG SDK 1.7.1 in platform/external/dng_sdk, with JXL and XMP support compiled out, and adapts the two in-tree consumers (skia SkRawCodec and the CTS dng-validate JNI) to the changed API.",
     "component": "platform/external/dng_sdk (libdng_sdk, Adobe DNG SDK); consumers platform/external/skia src/codec/SkRawCodec.cpp and platform/cts tests/camera/libctscamera2jni/dng-validate-jni.cpp",
     "weakness": "unknown (bulletin rates it High / ID; the fix is a full library version bump and no single flawed check is isolated in the material)",
     "root_cause": "The material does not isolate a specific defect. Android shipped a DNG SDK derived from the 1.4 line (old file headers carry \"//mondo/dng_sdk_1_4/\" $Id tags) and the fix for this bug is \"Crude DNG SDK 1.7.1 upgrade\": the sources under source/ are replaced by the 1.7.1 version (159 files, about 61k insertions and 16k deletions), including parsers and readers such as dng_ifd.cpp, dng_info.cpp, dng_read_image.cpp, dng_shared.cpp, dng_negative.cpp, dng_stream.cpp and dng_lossless_jpeg. The bulletin classifies the issue only as information disclosure (ID); which of the many upstream changes addresses it is not shown.",
     "fix_analysis": [
      "platform/external/dng_sdk (c210ca6441c5 on 15, 56e64aaa5b59 on 14, de700ad461e3 on 16): source/*.cpp and *.h replaced with DNG SDK 1.7.1. New files added to the build in Android.bp (source/dng_big_table.cpp, source/dng_bmff.cpp, source/dng_local_string.cpp, source/dng_lossless_jpeg_shared.cpp). JXL support is stubbed via source/jxl/color_encoding.h ('stub definitions necessary to trick dng_sdk into compiling without bringing in libjxl'), dng_jxl.cpp and dng_update_meta.cpp are not listed in srcs, and -DqDNGUseXMP=0 is kept so the dng_xmp.cpp body (guarded by #if qDNGUseXMP) is compiled out.",
      "platform/external/dng_sdk Android.bp: adds -DqDNGUsingSanitizer=1 and -Wno-implicit-fallthrough, moves -DqAndroid=1 to the android target and adds -DqLinux=1 for host_linux; libdng_sdk drops vendor_available: true, gains sdk_version: \"current\" and stl: \"c++_shared\"; windows build of libdng_sdk disabled; cc_binary_host dng_validate_host removed and the remaining cc_binary now links libdng_sdk statically.",
      "platform/external/skia e729c3f74b8e src/codec/SkRawCodec.cpp: SkDngHost::PerformAreaTask takes the new dng_area_task_progress* parameter, task.Start is called with the extra dstArea argument and task.ProcessOnThread with a nullptr progress; SkDngStream now constructs dng_stream with offsetInOriginalFile = 0 instead of the default invalid (-1), with a comment that the invalid offset makes dng_negative hit an unsigned-overflow bug path and crash. Android.bp moves libdng_sdk to android-only shared_libs and host_linux/darwin static_libs.",
      "platform/cts 586f75b10a39 tests/camera/libctscamera2jni/dng-validate-jni.cpp dng_validate(): RawJPEGImage() -> RawLossyCompressedImage(), imagePreview->fImage.Reset() -> SetImage(host, ...), dngVersion_Current -> dngVersion_SaveDefault, also strips XMP_NS_CRD and XMP_NS_CRLCP, and WriteTIFF now receives &negative->Metadata() and render.FinalSpace(profilePtr) with the profile looked up via negative->GetProfileByID(render.CameraProfileID(), profile) for non-monochrome negatives. Java_android_hardware_camera2_cts_DngCreatorTest_validateDngNative now closes pipeFds[0] after reading and fails if any output line was captured (lineCount > 0, 'Unexpected stderr output!') instead of failing when fewer than 3 lines were produced."
     ],
     "completeness": [
      "The fix is a full library replacement, so every DNG parsing path compiled into libdng_sdk moves to the 1.7.1 code base; nothing in the old parser is selectively patched.",
      "JXL (dng_jxl.cpp not in srcs) and XMP (qDNGUseXMP=0) code from 1.7.1 is not compiled, so those new upstream paths are not exposed on Android.",
      "The skia change works around a crash in the upgraded dng_negative when dng_stream reports an invalid original-file offset by passing offset 0; the underlying unsigned overflow path in dng_negative is not itself changed and is only described in the comment.",
      "No regression test specific to the vulnerability is added; the commits cite 'make' and android.graphics.cts.BitmapFactoryTest only."
     ],
     "behavior_changes": [
      "libdng_sdk API/ABI break: dng_host::PerformAreaTask(task, area, dng_area_task_progress*), dng_area_task::Start(threadCount, dstArea, tileSize, allocator, sniffer), ProcessOnThread(..., sniffer, progress), dng_negative::RawJPEGImage replaced by RawLossyCompressedImage, dng_image_preview::SetImage replaces direct fImage access, dngVersion_SaveDefault replaces dngVersion_Current, WriteTIFF takes a dng_metadata* and FinalSpace(profile).",
      "libdng_sdk loses vendor_available and gains sdk_version: \"current\" with stl c++_shared; Windows build and dng_validate_host are removed.",
      "dng_1d_table becomes variable-size with DNG_REQUIRE on count >= 512 and power of two; dng_priority gains background and very_high levels; the non-debug DNG_ASSERT definition becomes empty.",
      "SkDngStream reports offset 0 in the original file rather than an invalid offset.",
      "CTS validateDngNative treats any captured output as a validation failure (previously required at least 3 lines)."
     ],
     "preconditions": [
      "A DNG/RAW image is decoded through libdng_sdk; on Android the consumer shown is skia SkRawCodec (SkDngHost / SkDngStream), and the dng_sdk commit was tested with android.graphics.cts.BitmapFactoryTest.",
      "The specific malformed input or tag that triggers the disclosure is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 56e64aaa5b59 'Crude DNG SDK 1.7.1 upgrade' in android-security-14.0.0_r24; patched region fully present (8/8) at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk c210ca6441c5 in android-security-15.0.0_r12, with companion commits platform/external/skia e729c3f74b8e and platform/cts 586f75b10a39; patched region fully present (8/8) at android-security-15.0.0_r17.",
      "16": "same fix: platform/external/dng_sdk de700ad461e3 in android-security-16.0.0_r3; patched region fully present (8/8) at android-security-16.0.0_r8.",
      "17": "not listed by the bulletin (not affected per the bulletin); at android-security-17.0.0_r1 6/8 lines of the patched region are present; which DNG SDK version and commit is there is not shown."
     },
     "backport_notes": [
      "Three-project change that must land together: the dng_sdk upgrade changes the dng_host/dng_area_task/dng_negative API, so platform/external/skia (SkRawCodec.cpp plus Android.bp linkage) and platform/cts (dng-validate-jni.cpp) need their companion commits to build against the new library.",
      "The dng_sdk Android.bp drops vendor_available and sets sdk_version: \"current\"; forks with additional libdng_sdk consumers must re-check linkage, and should keep qDNGUseXMP=0 and the source/jxl/color_encoding.h stub unless libjxl and the XMP SDK are available."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which specific function or tag parser in the old SDK leaked information; the fix is a wholesale upgrade and the material identifies no isolated flaw.",
      "Whether any compiled 1.7.1 source still references code from the uncompiled dng_jxl.cpp / dng_update_meta.cpp beyond what the stub header covers.",
      "Fix status for Android 13, which the bulletin lists as affected but for which no per-major facts are provided.",
      "What DNG SDK version and commit Android 17 carries, and whether the skia/cts companion commits exist on 14 and 16 (only the dng_sdk commits are shown there)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48626": {
     "cve": "CVE-2025-48626",
     "summary": "Launcher3's OverviewCommandHelper no longer handles the TYPE_HOME command by calling startActivity(getHomeIntent()) directly. It now calls a new ISystemUiProxy.onKeyEvent(int keycode) binder method, and SystemUI sends a KEYCODE_HOME ACTION_DOWN/ACTION_UP key event.",
     "component": "platform/frameworks/base (SystemUI OverviewProxyService / ISystemUiProxy) and platform/packages/apps/Launcher3 (quickstep OverviewCommandHelper, SystemUiProxy, TouchInteractionService)",
     "weakness": "unknown (the diff moves the home action from a direct activity start to a key event; the material does not state the bypassed check)",
     "root_cause": "In quickstep/src/com/android/quickstep/OverviewCommandHelper.java, executeCommand handled TYPE_HOME with mService.startActivity(mOverviewComponentObserver.getHomeIntent()). In other words, TouchInteractionService started the home activity itself instead of sending a KEYCODE_HOME event. Before the fix, the Android 15 ISystemUiProxy shown in the material had no generic key-event method: onBackPressed() was the only method that sent a key (KEYCODE_BACK) through OverviewProxyService.sendEvent. The material does not show which system-side handling the direct activity start skipped.",
     "fix_analysis": [
      "frameworks/base packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl: adds 'oneway void onKeyEvent(int keycode) = 60;' (the 'Next id' comment goes from 57 to 61) and rewords the onBackPressed() doc to say it sends ACTION_DOWN followed by ACTION_UP for KEYCODE_BACK.",
      "frameworks/base packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java: the new onKeyEvent(int keycode) wraps the work in verifyCallerAndClearCallingIdentityPostMain(\"onKeyEvent \" + KeyEvent.keyCodeToString(keycode), ...) and calls the existing sendEvent(KeyEvent.ACTION_DOWN, keycode) and then sendEvent(KeyEvent.ACTION_UP, keycode). onBackPressed() now just calls onKeyEvent(KEYCODE_BACK), so both share the same caller verification wrapper. The Android 15 sendEvent body is not shown; in the Android 16 code it builds a KeyEvent and injects it through InputManagerGlobal.injectInputEvent.",
      "Launcher3 quickstep/src/com/android/quickstep/SystemUiProxy.java: adds an onKeyEvent(int keycode) override that forwards to mSystemUiProxy.onKeyEvent(keycode) when the proxy is non-null and logs 'Failed call onKeyEvent' on RemoteException.",
      "Launcher3 quickstep/src/com/android/quickstep/OverviewCommandHelper.java: the constructor gains a SystemUiProxy parameter, stored in mSystemUiProxy. In executeCommand, the TYPE_HOME case replaces mService.startActivity(mOverviewComponentObserver.getHomeIntent()) with mSystemUiProxy.onKeyEvent(KeyEvent.KEYCODE_HOME).",
      "Launcher3 quickstep/src/com/android/quickstep/TouchInteractionService.java: onUserUnlocked passes SystemUiProxy.INSTANCE.get(this) to the new OverviewCommandHelper constructor.",
      "Order: the frameworks/base change (AIDL method and SystemUI implementation) must land before or together with the Launcher3 change, because Launcher3's SystemUiProxy implements ISystemUiProxy and calls the new method."
     ],
     "completeness": [
      "Only the TYPE_HOME branch of OverviewCommandHelper.executeCommand changes. The material does not show whether any other Launcher3 code path starts the home intent directly.",
      "The commit messages name the flag com.android.launcher3.home_button_uses_keycode_home, but the Android 15 diffs contain no flag check, so the new behaviour is unconditional on this branch.",
      "The Android 15 onKeyEvent takes only a keycode, and the display used for injection is whatever sendEvent already uses (not shown for 15; the Android 16 pre-image used mContext.getDisplay().getDisplayId()). The displayId addition to onBackEvent mentioned in the commit message is not in the Android 15 diff; it appears only in the Android 16 version.",
      "OverviewProxyService.onKeyEvent does not restrict the keycode value. Its only wrapper is verifyCallerAndClearCallingIdentityPostMain, and the material does not show what that method checks."
     ],
     "behavior_changes": [
      "A TYPE_HOME command in OverviewCommandHelper now asks SystemUI to send a KEYCODE_HOME ACTION_DOWN/ACTION_UP pair instead of starting the home intent directly.",
      "New ISystemUiProxy method onKeyEvent with transaction id 60.",
      "onBackPressed now goes through onKeyEvent, so its verification/log label becomes 'onKeyEvent ' + KeyEvent.keyCodeToString(KEYCODE_BACK) instead of 'onBackPressed'."
     ],
     "preconditions": [
      "The device uses Launcher3/Quickstep (TouchInteractionService) with SystemUI's OverviewProxyService providing the ISystemUiProxy stub.",
      "A TYPE_HOME command must reach OverviewCommandHelper.executeCommand. Per index search, TISBinder methods such as onOverviewShown, onOverviewHidden and onOverviewToggle add other command types; the material does not show how TYPE_HOME is added.",
      "The user must be unlocked (OverviewCommandHelper is created in TouchInteractionService.onUserUnlocked)."
     ],
     "per_major": {
      "14": "adapted fix: Launcher3 9f5688403e25 in android-security-14.0.0_r24. It changes the same three Launcher3 files; OverviewCommandHelper uses an if (cmd.type == TYPE_HOME) branch, and SystemUiProxy implements only ISystemUiProxy. The matching frameworks/base commit for 14 is not in the material. 8/8 patched-region lines present at android-security-14.0.0_r29.",
      "15": "this fix: frameworks/base 99f89bb4fdd0 (ISystemUiProxy.aidl onKeyEvent(int keycode) = 60, OverviewProxyService) and Launcher3 db8146473259, both in android-security-15.0.0_r12. 8/8 patched-region lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: frameworks/base 9fb37191609f in android-security-16.0.0_r3. The stub is in LauncherProxyService.java and the method is onKeyEvent(int keycode, int displayId) = 60. onBackEvent(in KeyEvent keyEvent, int displayId) calls onKeyEvent(KEYCODE_BACK, displayId) when predictive back is not used, and sendEvent takes a displayId. The Launcher3 counterpart for 16 is not in the material. 5/8 patched-region lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 6/8 patched-region lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Two-project fix: land the frameworks/base ISystemUiProxy/OverviewProxyService change first, keeping transaction id 60 as Android 16 does, then the Launcher3 change, which calls the new AIDL method.",
      "Per index search, android-15.0.0_r36 Launcher3 already has SystemUiProxy.onBackEvent(KeyEvent) and a Kotlin OverviewCommandHelper.kt, so trees based on it differ from the security-branch diff. The Android 16 shape of the change may be the closer reference there."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which system-side check or state the direct startActivity path bypassed, and the concrete scenario behind the High EoP rating.",
      "How TYPE_HOME commands are added to OverviewCommandHelper.",
      "The Android 15 body of OverviewProxyService.sendEvent and what verifyCallerAndClearCallingIdentityPostMain checks.",
      "What happens if Launcher3 calls onKeyEvent on a SystemUI that lacks it.",
      "The frameworks/base counterpart for Android 14 and the Launcher3 counterpart for Android 16.",
      "Whether the home_button_uses_keycode_home flag gates the behaviour on main/QPR branches."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48628": {
     "cve": "CVE-2025-48628",
     "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id with ContentProvider.getUserIdFromAuthority(authority, resolvedUserId) and always applies the current-user-group check, instead of trusting Uri.getEncodedUserInfo and skipping the check when it returned null.",
     "component": "platform/frameworks/base, print service (services/print, PrintManagerService)",
     "weakness": "Improper validation of a user-scoped URI (incorrect user id parsing leading to a bypassed cross-user boundary check)",
     "root_cause": "In services/print/java/com/android/server/print/PrintManagerService.java, validateIconUserBoundary(Icon) worked out which user an Icon of type TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP referred to by calling icon.getUri().getEncodedUserInfo(). The commit message says a specially crafted URI can make Uri.getEncodedUserInfo return an incorrect value. When it returned null, the code treated the URI as belonging to the calling user and skipped the \"only current group members\" check entirely. When it returned a value, the code ran Integer.parseInt on it and resolved it with resolveCallingUserEnforcingPermissions. The user id in the URI authority was never used. As a result, a crafted icon URI that refers to another user could pass validation and be returned to the caller. Per the commit title (\"Prevent cross-user icon access\") and the bulletin type, the impact is information disclosure.",
     "fix_analysis": [
      "PrintManagerService.java, binder method that returns the custom printer icon (it fetches the icon via userState.getCustomPrinterIcon(printerId)): passes the already-resolved calling user (resolvedUserId) to validateIconUserBoundary(icon, resolvedUserId).",
      "PrintManagerService.java:validateIconUserBoundary(Icon, int resolvedCallingId): replaces the getEncodedUserInfo()/Integer.parseInt()/resolveCallingUserEnforcingPermissions() sequence with ContentProvider.getUserIdFromAuthority(icon.getUri().getAuthority(), resolvedCallingId). The commit message describes this as the more robust option. resolvedCallingId is passed as the default user.",
      "PrintManagerService.java:validateIconUserBoundary: the old 'if (encodedUser != null)' guard is removed. The synchronized(mLock) check (resolveCallingProfileParentLocked(iconUserId) != getCurrentUserId(), then return null) now runs for every URI-type icon.",
      "Adds import android.content.ContentProvider."
     ],
     "completeness": [
      "Covers both URI icon types (Icon.TYPE_URI and Icon.TYPE_URI_ADAPTIVE_BITMAP) in the call site shown. Non-URI icon types do not enter the check.",
      "The check still only enforces that the icon's user resolves (via resolveCallingProfileParentLocked) to the current user group. It does not verify that the caller has access to the specific authority or provider.",
      "The call to resolveCallingUserEnforcingPermissions on the icon's user was dropped. The user id from the authority is now compared directly, with the caller's resolvedUserId as the default.",
      "validateIconUserBoundary is private and the diff shows one call site. The material does not show whether other icon-returning paths exist in this file."
     ],
     "behavior_changes": [
      "URI icons whose authority has no user prefix are now subject to the group-membership check (using the caller's resolved user as the default) instead of being returned without any check. For a caller in the current user group, the outcome is unchanged.",
      "The user id is now taken from the URI authority rather than from the encoded userinfo component, so a URI whose userinfo and authority disagree is judged by the authority.",
      "Integer.parseInt on the raw encoded user info is removed from this method, so a non-numeric userinfo no longer causes a NumberFormatException on this code path (ContentProvider.getUserIdFromAuthority handles parsing of the authority prefix (general platform knowledge)).",
      "resolveCallingUserEnforcingPermissions is no longer invoked for the icon's user id. At that point the code now relies only on the direct current-user-group comparison."
     ],
     "preconditions": [
      "A caller reaches the PrintManagerService binder method that returns a custom printer icon (obtained via userState.getCustomPrinterIcon(printerId)). The exact entry point is not shown in the diff.",
      "The icon is of type Icon.TYPE_URI or Icon.TYPE_URI_ADAPTIVE_BITMAP and carries a URI crafted so that Uri.getEncodedUserInfo returns a value that does not reflect the user the URI refers to.",
      "The check only matters on a device with a user outside the current user's profile group."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base 0d6bfc8a1f50 in android-security-14.0.0_r24. The hunks match the Android 15 commit; only the pre-image index differs. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/frameworks/base 3723e843b659 in android-security-15.0.0_r12 (cherry-pick of 8d40513846ee, Change-Id I020c64c8dd1b755877826c7fc7dadecae98dd5f4). 8/8 patched lines are present at android-security-15.0.0_r17.",
      "16": "Same fix: platform/frameworks/base 9489a5dcd3cd in android-security-16.0.0_r3, identical diff. 8/8 patched lines are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin. The patched code (8/8 lines) is already present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit in one file. The only new dependency is ContentProvider.getUserIdFromAuthority(String, int), a long-standing hidden framework API in frameworks/base (general platform knowledge). The same hunks were applied on 14, 15 and 16, so the patch is likely to apply on branches that already contain the one-argument validateIconUserBoundary(icon)."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact URI shape that makes Uri.getEncodedUserInfo return an incorrect value is not shown in the material; the commit message only describes it.",
      "Whether an automated test accompanied the fix (the commit says manual test only).",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 fix commit or per-major status.",
      "The name of the binder entry point that calls validateIconUserBoundary, and whether it has other callers, are not shown in the diff."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48629": {
     "cve": "CVE-2025-48629",
     "summary": "VoiceInteractionManagerService no longer falls back to the first available RecognitionService when the preferred/default recognizer package is not found; it now only auto-selects a service whose application has ApplicationInfo.FLAG_SYSTEM, and returns null otherwise.",
     "component": "platform/frameworks/base, system_server voiceinteraction (VoiceInteractionManagerService, default speech recognizer selection)",
     "weakness": "Missing trust/origin check on automatic selection of a privileged default component (untrusted third-party app auto-selected as default recognizer without user choice)",
     "root_cause": "In services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java, the recognizer selection logic goes through a list `available` of RecognitionService candidates and tries to match `prefPackage` (the current recognizer or the default one, per the fix's comment). If nothing matched, it logged a warning when `numAvailable > 1` and then returned `available.get(0)` as the recognizer ComponentName. The fallback did not filter by trust, so when no default or system recognizer existed, any installed third-party app declaring a recognition service could become the default recognizer without user selection.",
     "fix_analysis": [
      "VoiceInteractionManagerService.java (recognizer selection method; the diff hunk does not show its name): the fix removes the `if (numAvailable > 1) { Slog.w(...picking first) }` block and the unconditional `return new ComponentName(...)` built from `available.get(0)`.",
      "A new loop iterates `available` from 0 to numAvailable and skips any entry whose `serviceInfo.applicationInfo.flags` lacks `ApplicationInfo.FLAG_SYSTEM`. The first system-app candidate is returned as `new ComponentName(serviceInfo.packageName, serviceInfo.name)`.",
      "If no system candidate exists, the method logs `no auto selectable voice recognition services found for user <userHandle>` and returns null instead of a third-party component."
     ],
     "completeness": [
      "The fix covers only the fallback path taken when prefPackage is not matched among the available services. The earlier prefPackage-matching path (not shown in the diff) is unchanged.",
      "The filter is FLAG_SYSTEM only, so it does not tell privileged and non-privileged system apps apart. Updated system apps still carry FLAG_SYSTEM (general platform knowledge).",
      "When there are several system candidates, the first one in list order is still chosen.",
      "Callers now have to handle a null return from this path. The diff does not show whether all callers already did so."
     ],
     "behavior_changes": [
      "Devices with no config_systemSpeechRecognizer default and no system RecognitionService now get no automatic default recognizer (the method returns null) instead of a third-party one.",
      "There is a new warning log, 'no auto selectable voice recognition services found for user ...'. The old 'more than one voice recognition service found, picking first' warning is removed.",
      "Third-party recognizers can no longer become the default automatically without user selection."
     ],
     "preconditions": [
      "No default recognizer is configured via config_systemSpeechRecognizer, or the preferred/current recognizer package is not found among the available services.",
      "A third-party app that declares a RecognitionService is installed, so it appears in the `available` list.",
      "The recognizer selection method in VoiceInteractionManagerService runs. The material does not show the exact trigger paths."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 6c030f1f30b4 in android-security-14.0.0_r24, identical hunk at a different line offset (@@ -852). The per-major check reports only 1/4 patched lines present at android-security-14.0.0_r29 (see unknowns).",
      "15": "same fix: platform/frameworks/base d8faf81970e4 in android-security-15.0.0_r12 (@@ -933). The per-major check reports only 1/4 patched lines present at android-security-15.0.0_r17 (see unknowns).",
      "16": "same fix: platform/frameworks/base 84dd2b90f4a2 in android-security-16.0.0_r3, identical hunk (@@ -933). The per-major check reports only 1/4 patched lines present at android-security-16.0.0_r8 (see unknowns).",
      "17": "not affected (the bulletin does not list Android 17)."
     },
     "backport_notes": [
      "The fix is a single self-contained hunk in VoiceInteractionManagerService.java and is identical on 14/15/16 apart from the line offset. When porting, check that callers of the selection method tolerate a null ComponentName from this fallback path, which previously returned available.get(0)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the name of the enclosing method or its callers (the code context provided is only the file header).",
      "The per-major check reports only 1/4 patched lines present in the latest security tags of 14/15/16 even though the fix is resolved there. The material cannot show whether the region was later refactored or the line-match heuristic is weak.",
      "The material does not show how callers handle the new null return, or what users see when no system recognizer exists.",
      "The material does not show why Android 17 shows only 1/4 patched lines."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48632": {
     "cve": "CVE-2025-48632",
     "summary": "Adds a shared validateDisplayName() length check (DISPLAY_NAME_LENGTH_LIMIT = 1024) to the private AssociationRequest constructor and the @hide setDisplayName() in core/java/android/companion/AssociationRequest.java, so the limit is enforced on every in-class write path instead of only in Builder.setDisplayName().",
     "component": "platform/frameworks/base, CompanionDeviceManager (android.companion.AssociationRequest)",
     "weakness": "Missing input validation (length bound enforced on only one of several setters)",
     "root_cause": "AssociationRequest defines DISPLAY_NAME_LENGTH_LIMIT = 1024. Before the fix, only Builder.setDisplayName() checked displayName.length() against it. The private AssociationRequest(...) constructor stored mDisplayName = displayName without any check, and the @hide setDisplayName(CharSequence) overwrote mDisplayName without a check. Any path that built an AssociationRequest without going through Builder.setDisplayName(), or that later changed the field through the hidden setter, could hold a display name longer than the intended limit. Per the javadoc, this display name is shown in the CDM confirmation UI.",
     "fix_analysis": [
      "core/java/android/companion/AssociationRequest.java:validateDisplayName (new private static helper): if displayName != null and displayName.length() > DISPLAY_NAME_LENGTH_LIMIT, it throws IllegalArgumentException(\"Length of the display name must be at most \" + DISPLAY_NAME_LENGTH_LIMIT + \" characters\"). Null is accepted (displayName is @Nullable).",
      "core/java/android/companion/AssociationRequest.java:AssociationRequest(boolean, List, String, CharSequence, boolean, boolean) private constructor: calls validateDisplayName(displayName) as its first statement, before any field assignment.",
      "core/java/android/companion/AssociationRequest.java:setDisplayName (@hide instance setter): calls validateDisplayName(displayName) before assigning mDisplayName.",
      "core/java/android/companion/AssociationRequest.java:Builder.setDisplayName: the inline length check is replaced by a call to validateDisplayName(displayName). checkNotUsed() and requireNonNull are kept, so the Builder path behaves as before."
     ],
     "completeness": [
      "Covers the three in-class write paths for mDisplayName visible in the diff: the private all-args constructor, the @hide setDisplayName(), and Builder.setDisplayName().",
      "The Parcel-based constructor AssociationRequest(Parcel in), used by CREATOR.createFromParcel, is not modified in the diff and is not shown in the context. Whether a displayName read from a Parcel is validated depends on whether that constructor delegates to the patched one, and the material does not show this.",
      "The check covers length only (CharSequence.length()). No other content validation is added."
     ],
     "behavior_changes": [
      "Building an AssociationRequest through the private constructor, or calling the @hide setDisplayName(), with a display name longer than 1024 characters now throws IllegalArgumentException. Before the fix, the name was accepted without a check.",
      "Builder.setDisplayName() behaves as before (same exception, same message), and it still rejects null through requireNonNull.",
      "The new helper accepts a null displayName, so nullable paths are unaffected."
     ],
     "preconditions": [
      "An AssociationRequest whose displayName is set through the private constructor or the @hide setDisplayName() rather than through Builder.setDisplayName(). The material does not show which callers use these paths.",
      "A displayName longer than DISPLAY_NAME_LENGTH_LIMIT (1024 characters)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base a100e23a3fda in android-security-14.0.0_r24, with the same hunks (constructor, @hide setDisplayName, Builder.setDisplayName, new validateDisplayName). The only difference from 15 is that a blank line is kept in the Builder hunk. 8/8 lines are present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base e72cdc5c770d in android-security-15.0.0_r12. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix, context-adapted: platform/frameworks/base de27b16b1af8 in android-security-16.0.0_r3. The logic is the same, but the private constructor has extra parameters (including skipRoleGrant and Icon deviceIcon), so the hunk offsets differ. 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "This is a single self-contained commit in one file. Between 14/15 and 16, only the constructor signature differs (16 adds parameters including skipRoleGrant and deviceIcon). Place validateDisplayName(displayName) as the first statement of whichever private constructor exists, and check whether the Parcel constructor in the target tree goes through it."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers use the @hide setDisplayName() and the private constructor outside the Builder. This is not shown, and the index search returned only unrelated setDisplayName methods.",
      "Whether AssociationRequest(Parcel in) validates displayName or delegates to the patched constructor.",
      "The concrete downstream effect of an over-long display name that led to the High/EoP rating (not shown in the material).",
      "Why only 7/8 lines of the patched region are present at android-security-17.0.0_r1."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48633": {
     "cve": "CVE-2025-48633",
     "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() now calls AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of looping over users and calling AccountManager.getAccounts() per user. Per the commit title, the old query did not return all accounts regardless of visibility.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy)",
     "weakness": "unknown (bulletin type ID). Per the commit title, the account-existence check depended on account visibility.",
     "root_cause": "hasAccountsOnAnyUser() looped over all non-dying users (mUserManagerInternal.getUsers(excludeDying=true)). For each user it created a per-user Context and called AccountManager.getAccounts() under a cleared Binder identity. The commit title, 'Get all accounts no matter the visibility', shows that this query was subject to account visibility, so the method could miss existing accounts. The material does not show which callers depend on the result or what they decide.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java:hasAccountsOnAnyUser(): the fix removes the per-user loop over mUserManagerInternal.getUsers(true), the createContextAsUser(...).getSystemService(AccountManager.class).getAccounts() calls, and the Binder.clearCallingIdentity()/restoreCallingIdentity() wrapper.",
      "These are replaced by an in-process call, AccountManagerService.getSingleton().getAllAccountsForSystemProcess(). The method returns true if the resulting array is non-empty. Per the commit title, this gets all accounts regardless of visibility.",
      "A new import, com.android.server.accounts.AccountManagerService, is added."
     ],
     "completeness": [
      "The fix covers the single helper hasAccountsOnAnyUser(), so every caller of that helper inherits the changed result.",
      "Correctness now depends on AccountManagerService.getAllAccountsForSystemProcess() covering all users. Its implementation is not in the material, so the diff cannot show whether it spans all users the way the removed loop did.",
      "The material shows no other uses of AccountManager.getAccounts() in DevicePolicyManagerService, so no sibling path is identified either way."
     ],
     "behavior_changes": [
      "hasAccountsOnAnyUser() is intended to count accounts regardless of visibility (per the commit title). Previously, some accounts could be missed.",
      "The check no longer clears and restores the Binder calling identity, and no longer enumerates users through UserManagerInternal. It is now a direct in-process call to the AccountManagerService singleton.",
      "The method no longer explicitly excludes dying users. Whatever filtering getAllAccountsForSystemProcess() applies now governs."
     ],
     "preconditions": [
      "Some code path in DevicePolicyManagerService reaches hasAccountsOnAnyUser(). Its callers are not shown in the material, and the index search returned nothing.",
      "At least one account exists that the old visibility-dependent query did not return."
     ],
     "per_major": {
      "14": "same fix: commit 527b25ca541f in android-security-14.0.0_r24, identical hunk (line offset differs); patched region fully present at android-security-14.0.0_r29.",
      "15": "same fix: commit e74b3376e899 in android-security-15.0.0_r12 (cherry-pick of a8819d3bdc49); patched region fully present at android-security-15.0.0_r17.",
      "16": "same fix: commit d00bcda9f42d in android-security-16.0.0_r3, identical hunk; patched region fully present at android-security-16.0.0_r8.",
      "17": "not affected (the bulletin does not list it); patched region present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "The patch needs AccountManagerService.getSingleton() and AccountManagerService.getAllAccountsForSystemProcess() to exist in the target branch. The material does not show whether these already existed or came from a companion change, so verify before applying to Android 13 or older forks.",
      "The hunk is identical across 14, 15 and 16 apart from line offsets. Only the anchor context of the import line differs: on Android 14 it follows com.android.server.PersistentDataBlockManagerInternal."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which callers use hasAccountsOnAnyUser() and what decision they control (not shown; the index search found none).",
      "The exact visibility semantics of the old AccountManager.getAccounts() query in this context.",
      "The implementation of AccountManagerService.getAllAccountsForSystemProcess(), including whether it spans all users and whether it existed before this fix in each branch.",
      "The concrete information-disclosure outcome that the bulletin's ID classification refers to.",
      "The Android 13 fix commit (the bulletin lists 13 as affected, but no 13 commit is in the material)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48639": {
     "cve": "CVE-2025-48639",
     "summary": "Reduces the MAX_ANIMATION_DURATION constant in WindowManager Shell's DefaultTransitionHandler from 3000 to 1500, shortening the time a default transition animation can run.",
     "component": "platform/frameworks/base, WindowManager Shell transitions (libs/WindowManager/Shell, DefaultTransitionHandler)",
     "weakness": "Overly permissive upper bound on transition animation duration (the commit message cites tapjacking during animation). The exact flaw class beyond the constant is not evident from the diff.",
     "root_cause": "DefaultTransitionHandler, the shell handler for any transition not handled elsewhere, defined MAX_ANIMATION_DURATION = 3000. According to the commit message, a 3 second maximum is too long, especially for tapjacking during an animation. The code that applies this constant is not in the material.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java: the class constant MAX_ANIMATION_DURATION changes from 3000 to 1500. No logic changes; this single constant edit is the whole fix.",
      "The commit is a RESTRICT AUTOMERGE backport (Cherrypick-From 1d37fc960d93, Change-Id I597e43434a6372299ee33618704042e2c583a2c3), applied as separate commits on the 14, 15 and 16 security branches."
     ],
     "completeness": [
      "Covers only the value of MAX_ANIMATION_DURATION in DefaultTransitionHandler. It shortens the duration window rather than removing the underlying condition, so animations up to the new maximum still occur.",
      "Other transition handlers or animation paths that do not use this constant are untouched. The material does not show where the constant is used, so which animations are actually limited cannot be confirmed from the diff."
     ],
     "behavior_changes": [
      "Based on the constant's name, default-handler transition animations are expected to be limited to 1500 instead of 3000. The code that applies the limit is not shown in the material."
     ],
     "preconditions": [
      "A window transition is animated by DefaultTransitionHandler (the shell fallback handler). How the duration cap is reached, and how an app influences it, is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 848e575f07ff in android-security-14.0.0_r24, identical one-line constant change; at android-security-14.0.0_r29, 4/5 lines of the patched region are present.",
      "15": "same fix: commit 06e697583c87 in android-security-15.0.0_r12, identical one-line constant change; at android-security-15.0.0_r17, line 122 shows MAX_ANIMATION_DURATION = 1500.",
      "16": "same fix: commit 6d1697c96c5c in android-security-16.0.0_r3, same constant change with an adjacent SIZE_CHANGE_ANIMATION_DURATION line in the context; at android-security-16.0.0_r8, 4/5 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin); at android-security-17.0.0_r1, 4/5 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-constant change with no dependencies shown. On 14, 15 and 16 it is the same one-line edit at different hunk offsets (16 has a neighbouring SIZE_CHANGE_ANIMATION_DURATION line). Fork code may differ around it."
     ],
     "confidence": "high",
     "unknowns": [
      "Where MAX_ANIMATION_DURATION is used inside DefaultTransitionHandler and which animation sources it limits (the code that uses it is not in the material).",
      "How an app can cause a long default transition animation; only the commit message mentions tapjacking, and the bulletin gives only EoP/High.",
      "Whether the original change 1d37fc960d93 included additional changes beyond this constant.",
      "Which line of the patched region is missing at the latest 14, 16 and 17 tags (4/5 present)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "16": {
   "counts": {
    "announced": 37,
    "certain": 37,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2023-40130",
     "osv_id": "ASB-A-289809991",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Wraps the mContext.unbindService(mConnection) call in CallRedirectionProcessor's notifyTimeout() in a try/catch for IllegalArgumentException, so that a call-redirection timeout with an unregistered service connection no longer crashes Telecom.",
      "confidence": "medium",
      "backport_notes": [
       "Two distinct changes carry this CVE id: the Android 14 onBindingDied addition (18299e2d9062) and the 15/16 notifyTimeout() try/catch (Change-Id I9494b1bf209579125b30fb859ad62cd58b5dffd7). A branch should carry both. The try/catch is a self-contained 6-line change in notifyTimeout(). The onBindingDied override relies only on finishCallRedirection() and mTelecomLock in the same class.",
       "No 'Fix in fork' comparison was provided, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "a43a880beaa6a64348a1d0c821e8c7e98d741a79",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref a43a880beaa6",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-22420",
     "osv_id": "ASB-A-337775777",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a Uri grant permission check on a channel's content:// sound Uri when a notification channel is created in PreferencesHelper.createNotificationChannel. The check runs through a new shared helper, PermissionHelper.grantUriPermission, which calls UriGrantsManagerInternal.checkGrantUriPermission under a clean calling identity and also replaces the inline checks in NotificationManagerService and NotificationRecord.",
      "confidence": "high",
      "backport_notes": [
       "The PreferencesHelper constructor gains a UriGrantsManagerInternal parameter, so every construction site must be updated: NotificationManagerService and all test helpers and subclasses such as TestPreferencesHelper. The Android 14 and the 15/16 versions fail differently: 14 throws SecurityException, while 15/16 fall back to the default sound. The 15/16 try/catch form avoids surfacing an exception to apps."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "fb8f76eca9079c34af3e14ee0a58bc10a580ec42",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22432",
     "osv_id": "ASB-A-376461726",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Telecom's CallRedirectionProcessor now unbinds the call redirection service connection when the redirection request times out, and the Android 16 commit additionally wraps that unbindService call in a try-catch for IllegalArgumentException so an unregistered connection does not crash Telecom.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the two commits in order. First 'Ensure connection is unbound on call redirection timeout' (adds the unbind block in notifyTimeout() and the unbindService checks in CallRedirectionProcessorTest.java). Then a43a880beaa6, which wraps that exact unbindService line. The second commit does not apply to a tree at blob 05e73d544.",
       "If a branch carries only the first step, consider also carrying the try-catch, since the added unbindService call is what can raise IllegalArgumentException on the timeout path."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "a43a880beaa6a64348a1d0c821e8c7e98d741a79",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref a43a880beaa6",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2025-32319",
     "osv_id": "ASB-A-291281543",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the flag-gated Context.BIND_INCLUDE_CAPABILITIES term (previously gated by the aconfig flag com.android.server.print.do_not_include_capabilities) from the bindServiceAsUser call in RemotePrintService.ensureBound(), so system_server no longer binds PrintService implementations with BIND_INCLUDE_CAPABILITIES.",
      "confidence": "high",
      "backport_notes": [
       "The fix is a one-line deletion in RemotePrintService.ensureBound(). If a fork carries the term without the Flags.doNotIncludeCapabilities() gate, remove the term directly and do not add the aconfig flag dependency, matching the security-branch approach."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "70ab82c4546aa893682a4507664dc2c471d6cd95",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48525",
     "osv_id": "ASB-A-391895151",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CompanionDeviceManager now revokes notification listener (NLS) access for a companion app's NotificationListenerService components when the package's last association for a user is removed, instead of leaving the grant in place after disassociation.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit fix. For trees with DisassociationProcessor (15+), add the NotificationManager constructor argument and the revocation block in disassociate(); for 14-style trees, place the block in CompanionDeviceManagerService under CHANGE_TYPE_REMOVED as Google did, matching the local store API name (getAssociationsForPackage vs getAssociationsByPackage).",
       "Any test or other caller constructing DisassociationProcessor must be updated for the new constructor parameter."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "31989869759e9b6119dc1cf324c395d789024908",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48536",
     "osv_id": "BUL-CVE-2025-48536",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Splits the Settings slice-permission allowlist into the existing production array and a new array (slice_allowlist_package_names_for_dev). SettingsSliceProvider.grantAllowlistedPackagePermissions reads the new array only when Build.IS_DEBUGGABLE, so packages listed there get no slice URI grants on non-debuggable builds.",
      "confidence": "medium",
      "backport_notes": [
       "The Java change is small and self-contained, but it only has an effect if the device's overlay of slice_allowlist_package_names is reviewed and debug-only packages are moved to slice_allowlist_package_names_for_dev.",
       "The Android 14 test sets Build.IS_DEBUGGABLE with ReflectionHelpers.setStaticField, while 15 and 16 use ShadowBuild.setDebuggable. Pick whichever works with the test setup on the target branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "586f8dedd8e0e8a7ca5577cd1f06891f7e84e1e1",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48555",
     "osv_id": "BUL-CVE-2025-48555",
     "source": "bulletin",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' notification log (NotificationStation) now hides title, text and expanded extras of historical notifications that belong to a profile which has a secure lock, is currently locked, and does not allow private notifications on the lock screen.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained change to one file. The same patch was applied with identical hunk offsets on 14, 15 and 16, so forks with an unmodified NotificationStation.java should be able to take it cleanly. It depends on com.android.internal.widget.LockPatternUtils, KeyguardManager.isDeviceLocked(int), UserManager.getProfiles and Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; Settings, as a platform-signed system app, builds against framework internals (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "596c7b9911f2004df83b8d2708ad4b50e8d53805",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48572",
     "osv_id": "ASB-A-385736540",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaButtonReceiverHolder.send stops attaching BroadcastOptions that unconditionally gave the media button receiver a temporary FGS allowlist and background-activity-start (BAL) permission. Instead it calls MediaSessionService.tempAllowlistTargetPkgIfPossible for the target package and passes it the caller's pid/uid/package.",
      "confidence": "medium",
      "backport_notes": [
       "The diff calls MediaSessionService.tempAllowlistTargetPkgIfPossible but does not add it, so the method must already exist in the target tree. A tree without it needs that helper first. Android 13 is listed as affected by the bulletin, but the material contains no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e707f6600330691f9c67dc023c09f4cd2fc59192",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48573",
     "osv_id": "ASB-A-339637822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the mService.tempAllowlistTargetPkgIfPossible call from the sendCommand method in MediaSessionRecord, so a MediaController.sendCommand call no longer propagates a temporary allowlist (while-in-use / background foreground-service start, WIU/BFSL) to the session-owning app.",
      "confidence": "high",
      "backport_notes": [
       "The change is a three-line deletion in sendCommand with no dependencies. On forks, find the tempAllowlistTargetPkgIfPossible call right before mCb.onCommand and remove it together with its reason string."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "039030a6b0e7d255af70609a3607e805ad2a99ff",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48575",
     "osv_id": "ASB-A-417463103",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller now treats a request as coming from Settings only if the referrer package is \"com.android.settings\" AND the installed package with that name runs under the system UID. This replaces a check on the referrer name alone that gated CA certificate installation.",
      "confidence": "high",
      "backport_notes": [
       "A self-contained change to two files; the same diff was applied on 14, 15 and 16 (on 14 the CredentialHelper.java base differs). Because calledBySettings() is removed from CredentialHelper, any vendor-added callers of it must be switched to the new isCalledBySettings() in CertInstaller."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "d688ebdbfd404df1e25654bfdf9e790ad9f0db3c",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2025-48576",
     "osv_id": "ASB-A-426205822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the unused AIDL-only binder method updateNotificationChannelGroupFromPrivilegedListener from INotificationManager and its implementation in NotificationManagerService, closing a privileged-listener entry point that created or updated notification channel groups for a caller-specified package.",
      "confidence": "high",
      "backport_notes": [
       "Pure deletion of one AIDL line and one @Override method, to be removed together; applies to any branch that has the method with only offset adjustments. Any test or OEM code still calling the method will no longer compile and must be removed too.",
       "Fix in fork: no fork comparison was provided in the material, so no per-file verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b812baa1463c9f9e81efa617c9d08ed7a63488b4",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48580",
     "osv_id": "ASB-A-393582077",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MediaBrowser no longer always binds to the target MediaBrowserService with BIND_INCLUDE_CAPABILITIES. The flag is now left out when the root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing BrowserRoot.EXTRA_RECENT.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit touching two files in frameworks/base. It relies only on the existing BrowserRoot.EXTRA_RECENT constant and Context.BIND_INCLUDE_CAPABILITIES. Google applied the same diff on 14, 15 and 16 with only line-offset differences.",
       "The library change only helps clients that pass EXTRA_RECENT or the hidden EXTRA_EXCLUDE_CAPABILITIES in their root hints. When backporting, check whether any privileged in-tree MediaBrowser clients need a companion change to set the key, since this commit does not include one."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "eb19b27ed8abe9070df9fb85bc9693c8d4ba321b",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48583",
     "osv_id": "ASB-A-381885240",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "BaseBundle no longer returns a lazy-value-backed Parcel to the Parcel pool with recycle(). It now calls the newly exposed Parcel.destroy() when the last lazy value is unwrapped or the bundle is cleared, so these Parcel instances are not reused.",
      "confidence": "high",
      "backport_notes": [
       "Single commit, no dependencies shown. The backport needs Parcel.destroy() made public (@hide). For trees shaped like Android 14 (with the Preconditions.checkState in unwrapLazyValueFromMapLocked()), use the 14 diff.",
       "The material includes no fork comparison, so no per-file verdict for a fork can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "02751bc65824a3877bdc21d865cd801b5e9f5e6c",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48584",
     "osv_id": "ASB-A-425662627",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a per-listener cap (5,000) on the conversation channels a privileged NotificationListenerService can create for other packages via createConversationNotificationChannelForPackageFromPrivilegedListener, and adds null and edge-case checks in that method so it no longer hits NPEs in system_server.",
      "confidence": "medium",
      "backport_notes": [
       "The fix changes the NotificationManagerService.init(...) signature (new trailing NotificationListenerStats parameter), so every caller of init() must be updated, including NotificationManagerServiceTest, RoleObserverTest and any OEM tests. The service change also relies on verifyPrivilegedListener returning ManagedServiceInfo and on the existing ManagedServiceInfo.isSystemUi().",
       "The fix only applies to branches that contain createConversationNotificationChannelForPackageFromPrivilegedListener."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "08a0766708db2071d9b8b65abf40d7e8057daaa1",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48586",
     "osv_id": "ASB-A-337784859",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In EditFdnContactScreen.onActivityResult, the returned contact URI is now validated by asking whether the current caller (ComponentCaller from getCurrentCaller()) has read permission for it via checkContentUriPermission, instead of comparing the user ID encoded in the URI with the Phone process's own user ID. A denial now throws SecurityException.",
      "confidence": "high",
      "backport_notes": [
       "The fix depends on android.app.ComponentCaller and Activity.getCurrentCaller(), which were added in Android 15 (API 35) (general platform knowledge). A branch without that API cannot take the diff as-is.",
       "Single-file, single-commit change. The 15 and 16 diffs are identical apart from the file index hashes."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "851fc787e96189a37f88cb9eaa688087883357c3",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telephony"
     ]
    },
    {
     "cve": "CVE-2025-48589",
     "osv_id": "ASB-A-362492829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's HeaderPrivacyIconsController now filters the permission usage list it sends to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS, EXTRA_PERMISSION_USAGES). Only usages that belong to the current user and its profiles, plus phone-call usages, are included. Before the fix, the unfiltered list was sent.",
      "confidence": "high",
      "backport_notes": [
       "The only branch-specific differences are the HeaderPrivacyIconsController constructor parameter list (featureFlags and shadeDialogContextInteractor exist only on newer branches) and the test location and PermissionGroupUsage constructor arity. The permGroupUsage() body is identical on 14, 15 and 16. None of the fix commits change a Dagger module.",
       "If a fork has customized HeaderPrivacyIconsController, apply the filter inside permGroupUsage() and make sure every path that builds EXTRA_PERMISSION_USAGES uses the filtered list."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2aeba76a58c18f66502ecbba4c2e73a8d6e2928c",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48590",
     "osv_id": "ASB-A-417987184",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the platform-compat exemption in AppOpsService that treated an invalid (undeclared) attribution tag as valid when SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE was not enabled for the package or the calling uid (\"Q- apps\"), so isAttributionTagValid is no longer forced to true whatever the caller's compat state.",
      "confidence": "high",
      "backport_notes": [
       "Google applied the same deletion on 14, 15 and 16; only the line offsets differ. On a fork, find the try/catch around mPlatformCompat.isChangeEnabledByPackageName/isChangeEnabledByUid with SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE in AppOpsService.java, delete it so only Slog.e(TAG, msg) remains, and drop the unused import. The bulletin lists Android 13 as affected, but the material has no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "848f944921756467dba98069ea33531a2f180373",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48592",
     "osv_id": "ASB-A-427113482",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The dav1d-based Codec2 software AV1 decoder (C2SoftDav1dDec) now sets Dav1dSettings.all_layers = 0 before dav1d_open. For scalable (SVC) AV1 clips, the decoder therefore outputs only one layer instead of every layer, which the plugin was not designed to handle.",
      "confidence": "high",
      "backport_notes": [
       "Single-line, self-contained change with no dependencies: insert lib_settings.all_layers = 0; right after dav1d_default_settings(&lib_settings) in C2SoftDav1dDec::initDecoder() wherever C2SoftDav1dDec.cpp exists."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/av",
       "sha": "8febdebcb5e8736ec013a7d64e70f50e87649b52",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/av"
     ]
    },
    {
     "cve": "CVE-2025-48594",
     "osv_id": "ASB-A-427206637",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The CompanionDeviceManager disassociation path now uses PackageManager.getPackagesForUid() to resolve a UID to every package that shares it, and processes revoked associations for each of those packages. Before, it handled only the single name returned by PackageManagerInternal.getNameForUid().",
      "confidence": "high",
      "backport_notes": [
       "Each branch gets a single commit. On branches where the logic is still in CompanionDeviceManagerService.java (the Android 14 layout, with a pending role-holder removal list), use the 14 variant. On branches that have DisassociationProcessor.java, use the 15/16 variant and match the local disassociate() signature (with or without REASON_REVOKED).",
       "In forked companion services, check every UID-to-package mapping used for revocation cleanup. Each one should iterate getPackagesForUid(uid) rather than rely on a single getNameForUid result."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ea2bcc66534263fac4c337f1a5149704c2262169",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48596",
     "osv_id": "ASB-A-438098181",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Parcel::appendFrom in libbinder now sets mDataSize to mDataPos only when the new position exceeds the current size, instead of always adding len to mDataSize. The old code could inflate the recorded data size beyond the bytes actually written, and possibly beyond what the capacity check ensured.",
      "confidence": "high",
      "backport_notes": [
       "Single-line, self-contained change in Parcel::appendFrom with no visible dependencies. Android 13 is listed as affected, but its code and patch are not in the material."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "6ffdde944d4e0b440b1dfc1f232687299700e039",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48597",
     "osv_id": "ASB-A-436270922",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Moves the TRUSTED_OVERLAY grant for a PiP task's surface out of WindowManager's RootWindowContainer.notifyActivityPipModeChanged, which set it on every PiP mode change based on inPip, into WMShell's PipTransition.onFinishResize. The flag is now granted only once Shell handles the enter-PiP transition, and WM keeps only the revoke on exit from pinned mode.",
      "confidence": "high",
      "backport_notes": [
       "Single commit, two files that must go together. Removing the WM grant without adding the Shell grant would leave PiP tasks without TRUSTED_OVERLAY, and adding the Shell grant without removing the WM grant leaves the original issue. Where RootWindowContainer lacks the task.getSurfaceControl() null guard (as on 14), the fix adds it in the revoke branch.",
       "The PipTransition hunk moves the existing leash/hasValidLeash locals up. On branches with a different onFinishResize finish condition (for example without mMoveToBackTransition), the change is the same but a context line differs."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "68170bad52250399d2e4a1a8023a3e7aeda1887d",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48598",
     "osv_id": "ASB-A-418774489",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings biometric enrollment now only honors Intent.EXTRA_USER_ID when the activity was launched by the Settings package itself, and the FaceEnroll trampoline strips EXTRA_USER_ID before forwarding extras from external callers, so a third-party app can no longer steer face enrollment to another user id.",
      "confidence": "medium",
      "backport_notes": [
       "The fix depends on Activity.getLaunchedFromPackage(), a public Activity API since API 34 (general platform knowledge). Trees without FaceEnroll.kt only need the BiometricEnrollBase.onCreate and FaceEnrollIntroduction.launchFaceSettingsActivity hunks, plus the getLaunchedFromPackage() overrides in the robolectric test subclasses.",
       "Apply as a single commit. The FaceEnrollIntroduction hunk relies on mUserId being set by the new BiometricEnrollBase.onCreate logic, so do not take it without the base-class hunk."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "83447688f8e3e8f009f1e7d275a14ea00ee7953a",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48601",
     "osv_id": "ASB-A-426207912",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating PackagePreferences for a package that is not installed when an API (notably the fromPrivilegedListener methods) supplies a package that resolves to an invalid uid. It does this by unifying the invalid-uid sentinel on Process.INVALID_UID, rejecting it in the creating path and making most getters non-creating.",
      "confidence": "medium",
      "backport_notes": [
       "Core pieces: in both files, import Process.INVALID_UID and delete UNKNOWN_UID and the NMS-local INVALID_UID. Add the Preconditions.checkArgument in the 2-arg getOrCreatePackagePreferencesLocked and rename the multi-arg overload. Switch the getters to getPackagePreferencesLocked with defaults. Add INVALID_UID early exits in getNotificationChannel, getConversationNotificationChannel and getNotificationChannels. Filter INVALID_UID in RoleObserver. Match branch-specific pieces (lock object, creationTime, bundle channels, getNotificationChannels signature) to the local PreferencesHelper; the 14 and 15 commits show the adaptations.",
       "Tests that create prefs via canShowBadge(...) need setShowBadge(pkg, uid, true) instead. mPm.getPackageUidAsUser mocks that returned UNKNOWN_UID should throw NameNotFoundException, as in PreferencesHelperTest."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "93629e575f69accd51fa400f291e54217893e514",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48603",
     "osv_id": "ASB-A-416259832",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 200 KB size limit on an input method's metadata XML resource (InputMethod.SERVICE_META_DATA). The limit is checked in InputMethodInfo before the XML is loaded and parsed, so an oversized IME metadata file causes an XmlPullParserException instead of being parsed in the system process.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The only parts likely to conflict on merge are the import block and the line 'Resources res = pm.getResourcesForApplication(...)', which must move above loadXmlMetaData so res is available to validateXmlMetaData. The bulletin lists 13 as affected, but the material has no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b4c6786312a217ad9dfd97041b2f1e2f77e39b94",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48604",
     "osv_id": "ASB-A-305710989",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "MmsService.writePduToContentUri now receives the calling user id from DownloadRequest and refuses to write the downloaded MMS PDU when the user id encoded in the caller-supplied contentUri does not match the calling user.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit; the callingUser local in MmsService.downloadMessage already exists and the diff adds no imports (so ContentProvider/UserHandle are presumably already imported), and readPduFromContentUri already carried the user id; only the DownloadRequest constructor arity (with or without TelephonyManager, see the 14 vs 15/16 diffs) and any robotest callers need adjusting.",
       "No 'Fix in fork' comparison was provided in the material, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Mms",
       "sha": "c60a828b9fa18f67260775a46c752f353fcc0d43",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Mms"
     ]
    },
    {
     "cve": "CVE-2025-48607",
     "osv_id": "ASB-A-416490321",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes the IAppOpsService.getPackagesForOpsForDevice binder method to return a ParceledListSlice<AppOpsManager.PackageOps> instead of a plain List. A very large result (op entries for hundreds of packages) is then paginated across binder transactions instead of raising TransactionTooLargeException. Per the commit message, that exception crashed SystemUI and stopped the device from booting.",
      "confidence": "high",
      "backport_notes": [
       "The changes to the AIDL return type, the AppOpsService override and both AppOpsManager wrappers depend on each other and must be applied together. The change alters a return type, not method ordering, and getPackagesForOpsForDevice is outside the native-ordered block. Vendor code that calls the hidden method directly must be updated. The java.io.StringWriter import is unused."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "03d7040699148c961df09dec301d8a1e982ee231",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48614",
     "osv_id": "ASB-A-430568718",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "RecoverySystem.rebootWipeUserData now queries DynamicSystemManager and throws a SecurityException instead of proceeding with the user-data wipe when the device is running in DSU (Dynamic System Update) mode.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained 7-line change. It needs android.os.image.DynamicSystemManager and Context.DYNAMIC_SYSTEM_SERVICE in the target branch: the applied diffs show them on 14 to 16, and the patched region is present on 17. On trees with a different rebootWipeUserData signature, insert the two new blocks around the existing DISALLOW_FACTORY_RESET check, as done on Android 14.",
       "The bulletin lists Android 13 as affected, but the material contains no 13 commit. The same insertion would be needed there."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ec0c32ea736ba3c594352c345358a778334bc773",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48618",
     "osv_id": "ASB-A-404254549",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CommandParamsFactory.processLaunchBrowser now rejects the SIM Toolkit LAUNCH BROWSER proactive command with ResultCode.LAUNCH_BROWSER_ERROR when KeyguardManager.isDeviceLocked() is true. ResultException's single-argument constructor no longer asserts on LAUNCH_BROWSER_ERROR, so that error can be raised.",
      "confidence": "high",
      "backport_notes": [
       "Apply both files together. Without the ResultException.java change, the new throw of ResultException(LAUNCH_BROWSER_ERROR, String) triggers the AssertionError in the single-argument constructor it delegates to.",
       "In all shown branches the private CommandParamsFactory constructor already receives a Context (used for mNoAlphaUsrCnf), so only the new mContext field and its assignment are needed. A fork with a different constructor signature must supply a Context some other way."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/opt/telephony",
       "sha": "fee68bcdcf029e8f40980616d09367610544bc62",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/opt/telephony"
     ]
    },
    {
     "cve": "CVE-2025-48620",
     "osv_id": "ASB-A-352024705",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes an early `return` in VoiceInteractionManagerService's package-change handler so that, when a voice interaction service is set, the handler still falls through to the logic that resets the current voice recognition service when its package is uninstalled.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with a three-line change in one function. The diff is identical on 14, 15 and 16 apart from line offsets. Forks with the same `return;` / `if (curAssistant != null)` structure can take it directly. Check that the fork's handler still has the recognizer reset logic after the curAssistant block, because otherwise removing the return has no effect."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "db86972777c84a386d8a6d2d34879923bdbccdf6",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48621",
     "osv_id": "ASB-A-266433089",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Two-part tapjacking mitigation: InputDispatcher stops delivering motion events to a window whose touchOcclusionMode is USE_OPACITY and whose alpha is below 0.5, and the WM Shell DefaultTransitionHandler halves the maximum default transition animation duration from 3000 ms to 1500 ms.",
      "confidence": "medium",
      "backport_notes": [
       "The fix is two independent commits in two projects (frameworks/native InputDispatcher and frameworks/base WM Shell); take both. The test fixture change (mBottomWindow / BOTTOM_APP_UID) must go in with the new tests.",
       "On branches where the window eligibility logic is not a DispatcherTouchState method, the check must be adapted to the equivalent function. The material only shows the Android 16 layout."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6d1697c96c5cae5062f6aea58cf2665b7d646cb8",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/native",
       "sha": "cc34c7b416b964c05a42ae3e9c2929b59b92c64f",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-48622",
     "osv_id": "ASB-A-412662901",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the Android copy of the Adobe DNG SDK in external/dng_sdk (sources still carrying dng_sdk_1_4 $Id tags) with a \"crude\" DNG SDK 1.7.1 import that has JXL and XMP support compiled out. It also updates the two in-tree consumers, Skia's SkRawCodec and the CTS dng-validate JNI, to the new 1.7.1 API.",
      "confidence": "medium",
      "backport_notes": [
       "Land the three commits together, dng_sdk first. The skia changes (PerformAreaTask, Start and ProcessOnThread signatures, SkDngStream offset) and the cts API renames depend on the 1.7.1 API. Also carry the skia Android.bp change that moves libdng_sdk to target.android shared_libs and to host static_libs.",
       "Any other module that links libdng_sdk must be rebuilt against the 1.7.1 headers. Dropping vendor_available makes the library unavailable to vendor-partition modules (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/cts",
       "sha": "1bcf948f5e555ad7b9b54549698c3e569d7a0af5",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/dng_sdk",
       "sha": "de700ad461e35af50b28b861943a0b0753b10929",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/skia",
       "sha": "40c3f0a50fb9b47f543be0949f9004e77510f494",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "projects": [
      "platform/cts",
      "platform/external/dng_sdk",
      "platform/external/skia"
     ]
    },
    {
     "cve": "CVE-2025-48626",
     "osv_id": "ASB-A-381339822",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Launcher3's OverviewCommandHelper HOME command no longer calls startActivity(homeIntent) directly; it asks SystemUI (new ISystemUiProxy.onKeyEvent) to inject a KEYCODE_HOME down/up key event via InputManagerGlobal, and onBackEvent/sendEvent now carry an explicit displayId.",
      "confidence": "medium",
      "backport_notes": [
       "The frameworks/base commit (ISystemUiProxy.aidl + LauncherProxyService) and the Launcher3 commit must ship together. The Launcher3 change calls the new onKeyEvent transaction and the changed onBackEvent(KeyEvent, int) signature. The 14/15 adapted Launcher3 fixes instead expect a one-argument onKeyEvent(int keycode) on the SystemUI side. On those branches SystemUiProxy implements ISystemUiProxy directly with @Override, so the AIDL must declare that method.",
       "In trees where TaskbarNavButtonController construction lives in TaskbarManagerImpl.createNavButtonController (seen in the android-16.0.0_r4 index), apply the displayId constructor change there."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9fb37191609f7cb7b2374531cafb2d00ec8b4bec",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "7628af9bf77f1d145359bf4075a6674574cae496",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2025-48628",
     "osv_id": "ASB-A-376462130",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id with ContentProvider.getUserIdFromAuthority(authority, resolvedCallingId) and always enforces the current-user-group check, instead of trusting Uri.getEncodedUserInfo and skipping the check when it returned null.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching one file; the same diff content landed on 14, 15 and 16. It needs ContentProvider.getUserIdFromAuthority and the existing resolveCallingProfileParentLocked and getCurrentUserId helpers in PrintManagerService. On a customized tree, make sure the caller still computes resolvedUserId before the validateIconUserBoundary call so it can be passed in."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9489a5dcd3cdd426d5b39d9caf6bb78142af2399",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48629",
     "osv_id": "ASB-A-352518318",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "VoiceInteractionManagerService no longer auto-selects the first available RecognitionService when the preferred/default recognizer is not found; it now only auto-selects a service whose application has ApplicationInfo.FLAG_SYSTEM and returns null otherwise.",
      "confidence": "high",
      "backport_notes": [
       "The change is self-contained: replace the `available.get(0)` fallback with a loop that requires `serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM`, and return null otherwise. Confirm ApplicationInfo is imported and that the caller of this selection method tolerates a null ComponentName on the target branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "84dd2b90f4a2ea1ebc5b78f08f14c5a3b92c9c2d",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48632",
     "osv_id": "ASB-A-443742829",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a shared validateDisplayName() length check to AssociationRequest so that the private constructor and the @hide setDisplayName() enforce DISPLAY_NAME_LENGTH_LIMIT, not only Builder.setDisplayName().",
      "confidence": "high",
      "backport_notes": [
       "Self-contained, single-file change. If the private constructor signature differs in your tree, add validateDisplayName(displayName) as the first statement of the constructor that assigns mDisplayName, and replace the inline check in Builder.setDisplayName with the helper call.",
       "No 'Fix in fork' section was provided, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "de27b16b1af86d4ce18c9134d85b53331a8d2147",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48633",
     "osv_id": "ASB-A-417988098",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() is changed to query AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of per-user AccountManager.getAccounts(), so that, per the commit title, accounts are counted regardless of their visibility when deciding whether any account exists.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained hunk plus one import; it applies identically on 14, 15 and 16. The target branch must have com.android.server.accounts.AccountManagerService with getSingleton() and getAllAccountsForSystemProcess(); Android 13 is listed as affected but no 13 commit is in the material, so verify those methods exist there before backporting."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d00bcda9f42dcf272d329e9bf9298f32af732f93",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48639",
     "osv_id": "ASB-A-301952571",
     "source": "osv",
     "announced": "2025-12-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Lowers the MAX_ANIMATION_DURATION constant in WindowManager Shell's DefaultTransitionHandler from 3000 to 1500. Per the commit message, this shortens the time during which a transition animation can be abused for tapjacking.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained constant change with no dependencies. On a branch whose DefaultTransitionHandler.java has MAX_ANIMATION_DURATION = 3000, apply the same one-line edit. The bulletin lists Android 13 as affected, but the material includes no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6d1697c96c5cae5062f6aea58cf2665b7d646cb8",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 6d1697c96c5c",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base",
      "platform/frameworks/native"
     ]
    }
   ],
   "tags": [
    "android-security-16.0.0_r3"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "441823943"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2ae33e496027d4ec197953f6bf8511ecd5873e57",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "2afec5750d44f9cc584815253dc2617c724093cc",
       "subject": "Restricting UserBuild from presistent carrierConfig Override",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      },
      {
       "project": "platform/packages/services/Telephony",
       "sha": "2f0eeeee70e8e7081f61d31e8d625e91808ebed7",
       "subject": "Protect shell overriding the carrier config",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r3"
     ],
     "tag": "android-security-16.0.0_r3"
    },
    {
     "bug_ids": [
      "395640609",
      "419095873"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "de2cbccd1070075c546616ab51815ae2ba178b3d",
       "subject": "Remove all pending keyguard exits on SHOW",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r3"
     ],
     "tag": "android-security-16.0.0_r3"
    },
    {
     "bug_ids": [
      "443970347"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "de59cf7083710e97c421081cd68c172a2b546876",
       "subject": "Add each blur region as a separate request",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r3"
     ],
     "tag": "android-security-16.0.0_r3"
    },
    {
     "bug_ids": [
      "236134583",
      "236134775"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "e62ac1ecd3ae0e3c91bd024fb70dc21b47f0ef8c",
       "subject": "Import translations. DO NOT MERGE ANYWHERE",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": [
        "restrict_automerge"
       ]
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "8ec6581ae706511c1c9fbe882faf385d29e998d1",
       "subject": "Backport BT pairing dialog changes",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r3"
     ],
     "tag": "android-security-16.0.0_r3"
    },
    {
     "bug_ids": [
      "435188844"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/IntentResolver",
       "sha": "bbe2dc3fb85fac9053b427b6d3c4af3506e0d9b4",
       "subject": "Verify caller access rights for the additional content URI.",
       "tag": "android-security-16.0.0_r3",
       "merged": "2025-10-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r3"
     ],
     "tag": "android-security-16.0.0_r3"
    }
   ],
   "coverage": {
    "n_ingested_tags": 9,
    "first_ingested_tag": "android-security-16.0.0_r1",
    "latest_ingested_tag": "android-security-16.0.0_r9",
    "window_base_tag": "android-16.0.0_r2",
    "window_start": "2025-06-10",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2023-40130": {
     "cve": "CVE-2023-40130",
     "summary": "Wraps the mContext.unbindService(mConnection) call in CallRedirectionProcessor's notifyTimeout() in a try/catch for IllegalArgumentException, so that a call-redirection timeout with an unregistered service connection no longer crashes Telecom.",
     "component": "platform/packages/services/Telecomm, call redirection (CallRedirectionProcessor / CallRedirectionServiceConnection)",
     "weakness": "Unhandled exception: Context.unbindService() throws IllegalArgumentException for a connection that is not registered, and notifyTimeout() did not catch it. The earlier Android 14 fix for this CVE addressed a different gap in the same class: there was no onBindingDied handling, so the service binding was not cleaned up when the binding died (commit text: \"to avoid background stating activity leaks\").",
     "root_cause": "In src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java, notifyTimeout() (in the inner redirection-attempt class) runs when call redirection times out. If mConnection is non-null, it calls mContext.unbindService(mConnection) unconditionally. Per the commit message, the service connection may not be registered when the timeout occurs. In that case unbindService throws IllegalArgumentException, nothing caught it, and the result was a crash (commit message: \"preventing a crash\"). The earlier Android 14 fix tagged to this CVE (18299e2d9062) added an onBindingDied override to CallRedirectionServiceConnection. That override calls finishCallRedirection() under mTelecomLock, so the service is unbound when the binding dies.",
     "fix_analysis": [
      "CallRedirectionProcessor.java : notifyTimeout(): the existing `mContext.unbindService(mConnection)` call (guarded by `if (mConnection != null)`) is wrapped in `try { ... } catch (IllegalArgumentException e) { Log.e(this, e, \"Error unbinding the connection\"); }`. The following `mConnection = null;` and `mService = null;` stay outside the try, so this state is reset whether or not unbind threw.",
      "Android 14 (18299e2d9062, 'Fix vulnerability in CallRedirectionService'): CallRedirectionProcessor.java : CallRedirectionServiceConnection gains `onBindingDied(ComponentName)`. It starts a 'CRSC.oBD' log session, takes `mTelecomLock`, and calls `finishCallRedirection()`. When the pending flag for the current service type is set, finishCallRedirection() unbinds mConnection, nulls mService and calls onCallRedirectionComplete(mCall). This is a different, earlier change tied to the same CVE id; the Android 15/16 commits only add the try/catch."
     ],
     "completeness": [
      "The try/catch covers only the unbindService call in notifyTimeout(). The sibling path finishCallRedirection() (lines 117-121 at android-security-16.0.0_r8) still calls mContext.unbindService(mConnection) with no try/catch. The material does not show whether that path can see an unregistered connection.",
      "The fix does not change why the connection can be unregistered at timeout. It only tolerates the condition by logging and then clearing mConnection/mService.",
      "The Android 14 onBindingDied addition is not visible in the Android 16 code excerpt (the excerpt ends before that part of CallRedirectionServiceConnection), so the material does not confirm that both changes are present together on 15/16."
     ],
     "behavior_changes": [
      "When unbindService throws IllegalArgumentException during a call redirection timeout, Telecom now logs 'Error unbinding the connection' with the exception and continues (mConnection and mService are set to null) instead of crashing.",
      "No API, permission or return value changes; no new timeouts."
     ],
     "preconditions": [
      "Call redirection is in progress in CallRedirectionProcessor with a carrier or user-defined call redirection service (SERVICE_TYPE_CARRIER or SERVICE_TYPE_USER_DEFINED).",
      "The redirection timeout fires (notifyTimeout()) while mConnection is non-null but the connection is not registered (commit message: 'if the service connection isn't registered when the timeout occurs').",
      "The material does not show the exact sequence that leaves mConnection set but unregistered."
     ],
     "per_major": {
      "14": "Different fix: resolved by bug_id at android-security-14.0.0_r1 with commit 18299e2d9062, which adds CallRedirectionServiceConnection.onBindingDied() calling finishCallRedirection(). The material does not show the notifyTimeout() try/catch commit for Android 14. At android-security-14.0.0_r29 the resolver reports 2/2 lines of the patched region present.",
      "15": "Same fix as 16: identical diff (3623312367a5, resolved by change_id) in android-security-15.0.0_r12; 7/7 lines present at android-security-15.0.0_r17.",
      "16": "This fix: a43a880beaa6 in android-security-16.0.0_r3 (cherry-pick of 615a452b1eb9); 7/7 lines present at android-security-16.0.0_r8.",
      "17": "Not affected (the bulletin does not list it); the patched try/catch region is present (7/7 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Two distinct changes carry this CVE id: the Android 14 onBindingDied addition (18299e2d9062) and the 15/16 notifyTimeout() try/catch (Change-Id I9494b1bf209579125b30fb859ad62cd58b5dffd7). A branch should carry both. The try/catch is a self-contained 6-line change in notifyTimeout(). The onBindingDied override relies only on finishCallRedirection() and mTelecomLock in the same class.",
      "No 'Fix in fork' comparison was provided, so no per-file fork verdict can be given."
     ],
     "confidence": "medium",
     "unknowns": [
      "The precise sequence that leaves mConnection non-null while the connection is unregistered at timeout (the material only says it 'can occur').",
      "Why the bulletin rates this change EoP/High. The diff itself shows a crash-avoidance fix, and the material does not explain how the 2023 onBindingDied change relates to the 2025 try/catch change.",
      "Whether the Android 14 onBindingDied change is present on the 15/16/17 branches (the code excerpt does not reach that part of the class).",
      "Whether Android 14 received the notifyTimeout() try/catch change (not shown).",
      "Which entry points (for example TelecomManager.placeCall) lead to CallRedirectionProcessor; the index results do not establish this."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22420": {
     "cve": "CVE-2025-22420",
     "summary": "Adds a Uri grant permission check on a channel's content:// sound Uri when a notification channel is created in PreferencesHelper.createNotificationChannel. The check runs through a new shared helper, PermissionHelper.grantUriPermission, which calls UriGrantsManagerInternal.checkGrantUriPermission under a clean calling identity and also replaces the inline checks in NotificationManagerService and NotificationRecord.",
     "component": "platform/frameworks/base, system_server notification service (NotificationManagerService / PreferencesHelper / NotificationRecord / PermissionHelper)",
     "weakness": "Missing permission check on an app-supplied content Uri (the system stores a sound Uri the app may not be allowed to read or grant)",
     "root_cause": "PreferencesHelper.createNotificationChannel stored whatever sound Uri the creating app put in the NotificationChannel. It did not verify that the app's uid could read or grant that content:// Uri. Uri permission checks happened only later: in NotificationRecord.visitGrantableUri when a notification was posted, and in NotificationManagerService when a privileged listener changed a channel's sound. A channel with an inaccessible content Uri could therefore exist in system state without the app ever proving access to the Uri. The commit message says: \"Previously, Uri permissions would only be checked by NotificationRecord when a notification was posted.\"",
     "fix_analysis": [
      "PermissionHelper.java: adds a new package-private static method grantUriPermission(UriGrantsManagerInternal ugmInternal, Uri uri, int sourceUid). It returns immediately if uri is null or its scheme is not ContentResolver.SCHEME_CONTENT. Otherwise, inside Binder.withCleanCallingIdentity, it calls ugmInternal.checkGrantUriPermission(sourceUid, null, ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))), which throws SecurityException if sourceUid cannot grant the Uri.",
      "PreferencesHelper.java: the constructor takes a new UriGrantsManagerInternal ugmInternal parameter, stored in the new field mUgmInternal. In createNotificationChannel's new-channel branch, right after clearLockedFieldsLocked(channel), it calls PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid). On SecurityException it calls channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT), so the channel is still created, with the default sound. The code comment says the check is limited to new channels on purpose, because regular apps can only set the sound before creation (NotificationChannel#setSound).",
      "NotificationManagerService.java: passes mUgmInternal into the PreferencesHelper constructor. In the channel update path, the inline check for a changed sound Uri (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) is replaced by PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid). The added test exercises this path through updateNotificationChannelFromPrivilegedListener. A failure still throws SecurityException, but non-content:// sound Uris are now skipped by the helper's scheme filter.",
      "NotificationRecord.java: visitGrantableUri keeps the null early return and the mGrantableUris cache check. The manual clearCallingIdentity / checkGrantUriPermission / restoreCallingIdentity sequence is replaced by PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid). The content-scheme filter moves from this function into the helper. The unused imports (ContentProvider, ContentResolver, Intent, Binder) are removed.",
      "Tests: PreferencesHelperTest adds testCreateChannel_hasSoundUriPermission_useCustomSound, testCreateChannel_noSoundUriPermission_replaceWithDefaultSound, and two tests showing that file:// and android.resource:// sound Uris are ignored. NotificationManagerServiceTest adds testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm. Every PreferencesHelper construction is updated for the new parameter."
     ],
     "completeness": [
      "Covers the sound Uri of a newly created channel (the new-channel branch of PreferencesHelper.createNotificationChannel), the sound-change check in NotificationManagerService's channel update path, and the post-time checks in NotificationRecord.visitGrantableUri. All three now go through one helper.",
      "Only content:// Uris are checked. Null, file:// and android.resource:// sound Uris skip the check by design, as the added fileSchemaIgnored and resourceSchemaIgnored tests confirm.",
      "Only the new-channel branch of createNotificationChannel is checked. The existing-channel branch of the same function is untouched; per the in-code comment, it relies on regular apps being unable to set the sound after creation. This diff does not touch any other code paths that write a channel's sound.",
      "The creation-time check uses the `uid` argument passed to createNotificationChannel."
     ],
     "behavior_changes": [
      "Android 15 and 16: if an app creates a channel with a content:// sound Uri it cannot grant, that Uri is no longer stored. The channel is created with Settings.System.DEFAULT_NOTIFICATION_URI and Notification.AUDIO_ATTRIBUTES_DEFAULT instead, and no exception reaches the app.",
      "Android 14: the same condition makes PreferencesHelper.createNotificationChannel throw SecurityException, and the channel is not created. The test testCreateChannel_noSoundUriPermission_contentSchemeVerified asserts both the exception and a null channel.",
      "Channel creation with a content:// sound now makes an extra UriGrantsManagerInternal.checkGrantUriPermission call, run with a cleared calling identity.",
      "In the NotificationManagerService channel update path, a changed sound Uri that is not content:// is no longer checked, because of the helper's scheme filter.",
      "NotificationRecord.visitGrantableUri: the SCHEME_CONTENT filter moved into the helper. Non-content Uris therefore no longer return early; the helper call does nothing for them and they reach the code after it in the try block (the mGrantableUris bookkeeping). On Android 14 the null check was also removed, so null Uris do the same. The rest of that block is not shown in the diff.",
      "The PreferencesHelper constructor signature changed (new UriGrantsManagerInternal parameter)."
     ],
     "preconditions": [
      "A caller creates a new notification channel (PreferencesHelper.createNotificationChannel is reached with no existing channel of that id) whose sound is a content:// Uri that the owning uid cannot read or grant.",
      "The scheme must be content://. file:// and android.resource:// Uris are not checked, before or after the fix.",
      "Before the fix, the stored Uri was only validated later, at notification post time (NotificationRecord.visitGrantableUri) or on a privileged-listener update. The material does not show how the stored Uri was otherwise used."
     ],
     "per_major": {
      "14": "Adapted fix (04a5d5dd0910, android-security-14.0.0_r19; 8/8 patched-region lines present at r29). It has the same PermissionHelper.grantUriPermission helper and NotificationManagerService refactor. The PreferencesHelper constructor differs: it has no PermissionManager, UserProfiles or Clock parameters and takes a SysUiStatsEvent.BuilderFactory. The creation-time check has no try/catch, so a SecurityException propagates and the channel is not created. NotificationRecord.visitGrantableUri keeps its SYSTEM_UID early return but drops the null/scheme check entirely. NotificationRecordTest is also modified: UriGrantsManagerInternal is injected through LocalServices, and the expected failure now happens in the NotificationRecord constructor.",
      "15": "Same fix (6c7c10e6234b, android-security-15.0.0_r10; 8/8 patched-region lines present at r17). The production diff is identical to 16, including the default-sound fallback. The test diff differs slightly, for example it constructs PreferencesHelper directly in most places.",
      "16": "Same fix (fb8f76eca907, android-security-16.0.0_r3; 8/8 patched-region lines present at r8). This is the analysed commit.",
      "17": "Not affected (the bulletin does not list Android 17). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The PreferencesHelper constructor gains a UriGrantsManagerInternal parameter, so every construction site must be updated: NotificationManagerService and all test helpers and subclasses such as TestPreferencesHelper. The Android 14 and the 15/16 versions fail differently: 14 throws SecurityException, while 15/16 fall back to the default sound. The 15/16 try/catch form avoids surfacing an exception to apps."
     ],
     "confidence": "high",
     "unknowns": [
      "How the system used a stored but inaccessible channel sound Uri before the fix. The material only says that checks happened at post time.",
      "The rest of the try block in NotificationRecord.visitGrantableUri after the helper call, which is only partly shown. The exact effect of non-content Uris now reaching that code is therefore not fully visible.",
      "Whether Google considers other channel-sound write paths in scope. The diff does not touch them.",
      "The full signature of createNotificationChannel and how its `uid` argument relates to the calling uid."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22432": {
     "cve": "CVE-2025-22432",
     "summary": "Telecom's CallRedirectionProcessor now unbinds the call redirection service connection when the redirection request times out, and the Android 16 commit additionally wraps that unbindService call in a try-catch for IllegalArgumentException so an unregistered connection does not crash Telecom.",
     "component": "platform/packages/services/Telecomm, call redirection (CallRedirectionProcessor)",
     "weakness": "Missing release of a bound service connection on the timeout path (improper resource shutdown), plus an unhandled IllegalArgumentException in the added unbind path",
     "root_cause": "In src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java, notifyTimeout() only forwarded the timeout to the remote service via mService.notifyTimeout(). It never called mContext.unbindService(mConnection) and never cleared mConnection or mService. Only finishCallRedirection() unbound the connection, so when a user-defined or carrier redirection service did not answer within the timeout, Telecom kept that service bound. The first fix (Android 14/15 commits 284cf771ae8f / 1b081682e2ad, whose post-image blob is the pre-image of the Android 16 commit) added the unbind in notifyTimeout(). Context.unbindService throws IllegalArgumentException when the ServiceConnection is not registered (general platform knowledge), so the new call could throw if the connection was not registered at timeout. The Android 16 commit a43a880beaa6 guards against that.",
     "fix_analysis": [
      "Step 1 (commit 'Ensure connection is unbound on call redirection timeout', 284cf771ae8f on 14 / 1b081682e2ad on 15, blob 05e73d544 -> 15b8aa942): CallRedirectionProcessor.java:notifyTimeout() gains a log line and, if mConnection != null, calls mContext.unbindService(mConnection) and sets mConnection = null, then sets mService = null, mirroring finishCallRedirection(). tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java adds four verify(mContext, times(1)).unbindService(any(ServiceConnection.class)) checks across three timeout test cases.",
      "Step 2 (commit a43a880beaa6, 'Catch IllegalArgumentException when unbinding CallRedirectionServiceConnection', blob 15b8aa942 -> 96c5a0638): in the same notifyTimeout() block, mContext.unbindService(mConnection) is wrapped in try { } catch (IllegalArgumentException e) { Log.e(this, e, \"Error unbinding the connection\"); }. mConnection = null and mService = null still run after the try-catch."
     ],
     "completeness": [
      "Covers the timeout path in notifyTimeout(): the connection is unbound, mConnection and mService are both cleared, and an IllegalArgumentException from unbindService is caught and logged.",
      "The sibling unbind in finishCallRedirection() (line 119 at android-security-16.0.0_r8) still calls mContext.unbindService(mConnection) without a try-catch. The try-catch commit touches only notifyTimeout().",
      "The material shows no change to how mConnection is set on bind or to the bind failure path. The condition that leaves an unregistered connection at timeout is therefore only handled by catching the exception, not prevented."
     ],
     "behavior_changes": [
      "On redirection timeout, Telecom now unbinds the redirection service connection and nulls mConnection and mService, so the app no longer stays bound to Telecom after the timeout.",
      "Every timeout now emits a new Log.i line: 'notifyTimeout: call redirection has timed out so unbinding the connection'.",
      "An IllegalArgumentException from unbindService on the timeout path is now logged via Log.e ('Error unbinding the connection') instead of propagating."
     ],
     "preconditions": [
      "An outgoing call is placed (for example via TelecomManager.placeCall, which Dialer's TelecomUtil.placeCall calls, per index search) and Telecom binds a user-defined or carrier call redirection service for it (SERVICE_TYPE_USER_DEFINED or SERVICE_TYPE_CARRIER).",
      "The bound service does not complete the redirection before the timeout, so notifyTimeout() runs.",
      "For the IllegalArgumentException branch: mConnection is non-null but not registered with the Context at timeout (the material does not show the exact scenario)."
     ],
     "per_major": {
      "14": "Different commit, first step only: 284cf771ae8f ('Ensure connection is unbound on call redirection timeout', with CallRedirectionProcessorTest changes) at android-security-14.0.0_r19. 8/8 lines of that region are present at android-security-14.0.0_r29. The material does not show whether the try-catch follow-up is on 14.",
      "15": "Different commit, first step only: same change as 14 (1b081682e2ad) at android-security-15.0.0_r7. 8/8 lines are present at android-security-15.0.0_r17. The material does not show whether the try-catch follow-up is on 15.",
      "16": "Try-catch commit a43a880beaa6 (cherry-pick of 615a452b1eb9) at android-security-16.0.0_r3, applied on top of the unbind step (its pre-image blob 15b8aa942 is the post-image of the 14/15 commit). Both are present at android-security-16.0.0_r8 (7/7 lines).",
      "17": "Not affected per the bulletin (not listed). The patched region (unbind plus try-catch) is already present at android-security-17.0.0_r1 (7/7 lines)."
     },
     "backport_notes": [
      "Apply the two commits in order. First 'Ensure connection is unbound on call redirection timeout' (adds the unbind block in notifyTimeout() and the unbindService checks in CallRedirectionProcessorTest.java). Then a43a880beaa6, which wraps that exact unbindService line. The second commit does not apply to a tree at blob 05e73d544.",
      "If a branch carries only the first step, consider also carrying the try-catch, since the added unbindService call is what can raise IllegalArgumentException on the timeout path."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether the try-catch follow-up (a43a880beaa6) was also applied to the Android 14 and 15 security branches. The per-major facts only confirm the first step's region.",
      "The concrete scenario in which mConnection is non-null but unregistered at timeout. The commit message only says 'if the service connection isn't registered when the timeout occurs'.",
      "The exact EoP consequence of the leaked binding as Google assessed it. The bulletin only gives High/EoP.",
      "The name of the inner class that holds notifyTimeout() and finishCallRedirection(); the material does not show it."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32319": {
     "cve": "CVE-2025-32319",
     "summary": "Removes the flag-gated Context.BIND_INCLUDE_CAPABILITIES term (previously gated by the aconfig flag com.android.server.print.do_not_include_capabilities) from the bindServiceAsUser call in RemotePrintService.ensureBound(), so system_server no longer binds PrintService implementations with BIND_INCLUDE_CAPABILITIES.",
     "component": "platform/frameworks/base, services/print (RemotePrintService)",
     "weakness": "Improper privilege management: system_server bound an app-provided service with BIND_INCLUDE_CAPABILITIES",
     "root_cause": "In services/print/java/com/android/server/print/RemotePrintService.java, ensureBound() bound the print service (mIntent, for mUserId) with mContext.bindServiceAsUser using BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_INSTANT, plus BIND_INCLUDE_CAPABILITIES whenever Flags.doNotIncludeCapabilities() returned false. BIND_INCLUDE_CAPABILITIES makes the bound service inherit the while-in-use capabilities of the binding process (general platform knowledge). Because the binding process here is system_server, the app's PrintService could receive capabilities it does not hold itself. The bulletin rates the issue as a High EoP.",
     "fix_analysis": [
      "services/print/java/com/android/server/print/RemotePrintService.java:ensureBound(): deletes the line `| (Flags.doNotIncludeCapabilities() ? 0 : Context.BIND_INCLUDE_CAPABILITIES)` from the bindServiceAsUser flags. The bind flags are now always BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_INSTANT.",
      "Commit 70ab82c4546a (android-security-16.0.0_r3) is a backport of ag/31156800 (Change-Id I2b9d6b68e7aaa71374a6c5e45796c73a1f3abfa0). The original change was developed behind the aconfig flag and landed in 25Q3. The backport removes the flag dependency instead of flipping the flag, so the fix applies unconditionally."
     ],
     "completeness": [
      "The fix covers the single bindServiceAsUser call in RemotePrintService.ensureBound(), the only bind site in the diff and its surrounding context. The code at android-security-16.0.0_r8 (lines 573-576) confirms that BIND_INCLUDE_CAPABILITIES is gone.",
      "The material shows no other bind sites in the print stack, so it cannot tell whether other system_server-to-app bindings there use BIND_INCLUDE_CAPABILITIES.",
      "On the security branch the removal is unconditional, so the fix works regardless of the aconfig flag state. Per the commit message, the original 25Q3 change was developed behind com.android.server.print.do_not_include_capabilities."
     ],
     "behavior_changes": [
      "Print services bound by system_server are no longer bound with BIND_INCLUDE_CAPABILITIES. A PrintService that relied on inherited while-in-use capabilities (general platform knowledge about this flag) would now be limited to its own capabilities.",
      "This code path no longer checks Flags.doNotIncludeCapabilities() on the security branch."
     ],
     "preconditions": [
      "RemotePrintService.ensureBound() must bind a PrintService that an app provides (BIND_ALLOW_INSTANT is set, so instant apps can be bound too). ensureBound() only binds when the service is not already bound or binding (the isBound()/mBinding checks).",
      "Before the fix on the 16 branch, Flags.doNotIncludeCapabilities() had to return false for BIND_INCLUDE_CAPABILITIES to be included."
     ],
     "per_major": {
      "14": "Not affected per the bulletin. At android-security-14.0.0_r29, 4/5 lines of the patched region are present, but the material does not show the actual bind flags on that branch.",
      "15": "Not affected per the bulletin. At android-security-15.0.0_r17, 4/5 lines of the patched region are present, but the material does not show the actual bind flags on that branch.",
      "16": "Resolved by commit 70ab82c4546a in android-security-16.0.0_r3 (backport of ag/31156800, Change-Id I2b9d6b68e7aaa71374a6c5e45796c73a1f3abfa0), which removes the flag-gated BIND_INCLUDE_CAPABILITIES line unconditionally. The code at android-security-16.0.0_r8 confirms the line is absent.",
      "17": "Not affected per the bulletin (not listed). At android-security-17.0.0_r1, 4/5 lines of the patched region are present, but the material does not show the actual bind flags or whether a flag gate remains."
     },
     "backport_notes": [
      "The fix is a one-line deletion in RemotePrintService.ensureBound(). If a fork carries the term without the Flags.doNotIncludeCapabilities() gate, remove the term directly and do not add the aconfig flag dependency, matching the security-branch approach."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the pre-flag code or the exact bind flags on the 14, 15 and 17 security branches, only that the bulletin does not list them.",
      "The default state of the aconfig flag com.android.server.print.do_not_include_capabilities is not shown.",
      "The material does not show which concrete capabilities a bound PrintService could obtain, or the exact scenario behind the High EoP rating.",
      "How print services are enabled or selected before RemotePrintService binds them is not shown.",
      "Whether other print-related bind sites use BIND_INCLUDE_CAPABILITIES is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48525": {
     "cve": "CVE-2025-48525",
     "summary": "CompanionDeviceManager now revokes notification listener (NLS) access for a companion app's NotificationListenerService components when the package's last association for a user is removed, instead of leaving the grant in place after disassociation.",
     "component": "platform/frameworks/base, CompanionDeviceManagerService (services/companion, association/DisassociationProcessor)",
     "weakness": "Incomplete cleanup / privilege retained after revocation (notification listener access not revoked on disassociation)",
     "root_cause": "DisassociationProcessor.disassociate() removed the association, its system data transfer requests, and (if no longer in use) the device-profile role, but never revoked notification listener access held by the companion app. The code carried an explicit \"TODO: also revoke notification access\" comment. As a result an app whose association was removed kept NotificationListenerService access.",
     "fix_analysis": [
      "services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java:CompanionDeviceManagerService(Context): obtains NotificationManager via context.getSystemService(NotificationManager.class) and passes it as a new last constructor argument to DisassociationProcessor.",
      "services/companion/java/com/android/server/companion/association/DisassociationProcessor.java:constructor: new @NonNull NotificationManager parameter stored in mNotificationManager; new imports for NotificationManager, Intent, ResolveInfo, NotificationListenerService, List.",
      "DisassociationProcessor.java:disassociate(int id, String reason): after mAssociationStore.removeAssociation(...) and before role revocation, inside Binder.withCleanCallingIdentity, checks mAssociationStore.getAssociationsByPackage(userId, packageName).isEmpty(); if so, queries mContext.getPackageManager().queryIntentServicesAsUser(new Intent(NotificationListenerService.SERVICE_INTERFACE), 0, userId) and for every ResolveInfo whose component package equals packageName calls mNotificationManager.setNotificationListenerAccessGranted(componentName, false). The TODO comment is removed."
     ],
     "completeness": [
      "Revocation happens only when the package has zero remaining associations for that userId; if other associations remain the listener access is kept.",
      "On Android 15/16 the revocation is placed only in DisassociationProcessor.disassociate(); any association-removal path that does not go through disassociate() is not covered by this diff (none is shown in the material). On Android 14 the block is in CompanionDeviceManagerService under changeType == AssociationStore.CHANGE_TYPE_REMOVED.",
      "Only components returned by queryIntentServicesAsUser with flags 0 are revoked.",
      "The revocation does not check how NLS access was granted: it does not distinguish access granted via CDM from access granted by the user."
     ],
     "behavior_changes": [
      "After the last CDM association of a package (per user) is removed, that package's NotificationListenerService components returned by the PackageManager query lose notification listener access.",
      "A package's NLS access granted manually by the user is also revoked if that package's last association is removed.",
      "Extra PackageManager query and NotificationManager calls run during disassociation under a cleared calling identity.",
      "DisassociationProcessor constructor signature changed (new NotificationManager argument)."
     ],
     "preconditions": [
      "A companion app holds a CDM association and has notification listener access (how CDM grants this is not shown; DisassociationProcessor imports RolesUtils.NLS_PROFILES).",
      "The association is removed via DisassociationProcessor.disassociate() and it is the package's last association for that user."
     ],
     "per_major": {
      "14": "adapted fix (platform/frameworks/base 9473c3649017, android-security-14.0.0_r22): the revocation block is added in CompanionDeviceManagerService under changeType == AssociationStore.CHANGE_TYPE_REMOVED, using getAssociationsForPackage() and a locally fetched NotificationManager. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix (platform/frameworks/base 52c030fa9ba2, android-security-15.0.0_r10): same DisassociationProcessor revocation block; disassociate(int id) has no reason parameter, removeAssociation takes only the id, and the service passes mPackageManagerInternal. 7/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix (platform/frameworks/base 31989869759e, android-security-16.0.0_r3, cherry-pick of a987e9d91a93). 7/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-commit fix. For trees with DisassociationProcessor (15+), add the NotificationManager constructor argument and the revocation block in disassociate(); for 14-style trees, place the block in CompanionDeviceManagerService under CHANGE_TYPE_REMOVED as Google did, matching the local store API name (getAssociationsForPackage vs getAssociationsByPackage).",
      "Any test or other caller constructing DisassociationProcessor must be updated for the new constructor parameter."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact mechanism by which CDM grants NLS and whether other association-removal paths exist on 15/16 outside disassociate() are not shown in the material.",
      "Which removal paths trigger the Android 14 CHANGE_TYPE_REMOVED callback is not shown.",
      "Which listener components (if any) queryIntentServicesAsUser with flags 0 omits is not shown.",
      "Why the per-major check reports 7/8 rather than 8/8 lines on 15/16/17 is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48536": {
     "cve": "CVE-2025-48536",
     "summary": "Splits the Settings slice-permission allowlist into the existing production array and a new array (slice_allowlist_package_names_for_dev). SettingsSliceProvider.grantAllowlistedPackagePermissions reads the new array only when Build.IS_DEBUGGABLE, so packages listed there get no slice URI grants on non-debuggable builds.",
     "component": "platform/packages/apps/Settings, slices (SettingsSliceProvider)",
     "weakness": "Likely improper access control (inferred from the fix's shape): slice permissions were granted by package name to a configured allowlist with no build-type restriction. Commit title: \"Prevent SettingsSliceProvider from accessing unused packages\".",
     "root_cause": "SettingsSliceProvider.grantAllowlistedPackagePermissions(Context, List<Uri>) read a single resource array, R.array.slice_allowlist_package_names. It called SliceManager.grantSlicePermission(toPackage, descendant) for every descendant URI passed in and every listed package, with no build-type gating and no check beyond the package name. The fix adds a separate debug-only list, which suggests that packages needed only for debugging were being granted on production builds. The commit describes the issue as \"accessing unused packages\", but the material does not show which entries were involved.",
     "fix_analysis": [
      "res/values/config.xml: adds a new empty string-array resource, slice_allowlist_package_names_for_dev, documented as the allowlist 'for debugging purpose'. The existing slice_allowlist_package_names is unchanged and still empty in AOSP.",
      "src/com/android/settings/slices/SettingsSliceProvider.java:grantAllowlistedPackagePermissions: builds a List<String> allowlist from slice_allowlist_package_names (null-tolerant), then appends the contents of slice_allowlist_package_names_for_dev only if Build.IS_DEBUGGABLE is true. The early return now fires when allowlist.size() == 0, the log line reports allowlist.size(), and the grant loop iterates over the combined list. The commit also imports android.os.Build.",
      "tests/robotests/res/values-mcc998/config.xml and values-mcc999/config.xml: add the new array. It is empty for mcc998 and holds one fake package, com.android.settings.slice_allowlist_package_dev, for mcc999.",
      "tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java: the existing test now forces a non-debuggable build and checks that the dev package is never granted. A new test, grantAllowlistedPackagePermissions_hasPackageAllowlistAndDebuggable_shouldGrant, forces a debuggable build and checks that the dev package is granted."
     ],
     "completeness": [
      "Only grantAllowlistedPackagePermissions changes. It is the only place in the diff that calls SliceManager.grantSlicePermission.",
      "The fix provides a mechanism, not a policy. In AOSP both arrays are empty, so the patched code grants nothing by itself. It only helps if device or OEM overlays put debug-only entries in slice_allowlist_package_names_for_dev instead of slice_allowlist_package_names. The material does not show any overlay contents.",
      "Entries left in slice_allowlist_package_names are still granted unconditionally by package name. The fix adds no check that the package is installed, is a system package, or has a particular signature.",
      "The gate is Build.IS_DEBUGGABLE, a static field initialised from the ro.debuggable system property (general platform knowledge). On non-debuggable builds the dev array is never read."
     ],
     "behavior_changes": [
      "Adds a new resource string-array, slice_allowlist_package_names_for_dev, that overlays can populate.",
      "On debuggable builds, packages in the dev array also receive grantSlicePermission for every descendant URI. On non-debuggable builds they receive nothing.",
      "The 'Allowlisting %d uris to %d pkgs' log now reports the size of the combined list, and the 'No packages to allowlist' early return applies to the combined list.",
      "Devices whose overlays leave both arrays empty see no change."
     ],
     "preconditions": [
      "grantAllowlistedPackagePermissions must run with a non-empty descendant URI list. Its callers are not shown in the material.",
      "A device overlay must populate slice_allowlist_package_names (the AOSP default is empty).",
      "Which installed package carries an allowlisted name decides who receives the grant. The material does not show the specific overlay entries involved."
     ],
     "per_major": {
      "14": "Same fix (b1fdab0d0065, android-security-14.0.0_r24). The added production lines are identical. The test is adapted to use ReflectionHelpers.setStaticField(Build.class, \"IS_DEBUGGABLE\", ...) instead of ShadowBuild.setDebuggable. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Same fix (11b11215aa18, android-security-15.0.0_r12), with identical code and tests. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Same fix (586f8dedd8e0, android-security-16.0.0_r3). 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin (not listed). The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Java change is small and self-contained, but it only has an effect if the device's overlay of slice_allowlist_package_names is reviewed and debug-only packages are moved to slice_allowlist_package_names_for_dev.",
      "The Android 14 test sets Build.IS_DEBUGGABLE with ReflectionHelpers.setStaticField, while 15 and 16 use ShadowBuild.setDebuggable. Pick whichever works with the test setup on the target branch."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which package names were in the affected device overlays, and which of them were 'unused' on production builds.",
      "The callers of grantAllowlistedPackagePermissions and when it runs.",
      "Which slice URIs or descendants were exposed, and what those slices allow the receiving package to do.",
      "Why the Android 14 test uses ReflectionHelpers instead of ShadowBuild."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48555": {
     "cve": "CVE-2025-48555",
     "summary": "Settings' notification log (NotificationStation) now hides title, text and expanded extras of historical notifications that belong to a profile which has a secure lock, is currently locked, and does not allow private notifications on the lock screen.",
     "component": "platform/packages/apps/Settings, notification history (src/com/android/settings/notification/history/NotificationStation.java)",
     "weakness": "Missing visibility/authorization check before displaying sensitive data (information disclosure of locked-profile notification content)",
     "root_cause": "NotificationStation registers a NotificationListenerService as a system service for the current user and renders the notifications it receives. HistoricalNotificationPreference.updatePreference(info) always set the title, text, notificationExtra and rankingExtra TextViews from the notification. It ignored which user/profile the notification belonged to (info.user, derived from sbn.getUserId()), that profile's lock state, and its LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS setting. As a result, the parent user's Settings notification log showed the content of notifications from a locked profile (for example a work profile with its own credential).",
     "fix_analysis": [
      "NotificationStation.java:onResume: adds a field mContentRestrictedUsers (ArrayList<Integer>), cleared on each resume. It iterates over UserManager.getProfiles(mContext.getUserId()). For each profile where Settings.Secure.getIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, default 0, user.id) == 0, it checks new LockPatternUtils(mContext).isSecure(user.id) && KeyguardManager.isDeviceLocked(user.id). If both hold, it adds user.id to the restricted list.",
      "NotificationStation.java: each creation and update site in the diff now passes a per-notification boolean, mContentRestrictedUsers.contains(info.user). The sites are: the initial population loop (new HistoricalNotificationPreference(..., i, restricted)), the removal update (updatePreference(info, restricted)), the posted update and addFirst path (new preference with mContentRestrictedUsers.contains(newInfo.user)), and the ranking update path (updatePreference(info, restricted)).",
      "NotificationStation.java:HistoricalNotificationPreference: the constructor gains a restrictContent parameter, stored in mRestrictContent. The bind method calls updatePreference(mInfo, mRestrictContent).",
      "NotificationStation.java:HistoricalNotificationPreference.updatePreference(info, restrictContent): when restrictContent is true, R.id.title and R.id.text are set to View.GONE (the condition is now 'restrictContent || TextUtils.isEmpty(...)'). R.id.notification_extra and R.id.ranking_extra are also set to View.GONE instead of having their text filled in. Package name (R.id.pkgname), icon and timestamp are still displayed.",
      "createFromSbn is unchanged apart from whitespace. Title, text and extras are still extracted and kept in HistoricalNotificationInfo; only the rendering is suppressed."
     ],
     "completeness": [
      "Covers the call sites of HistoricalNotificationPreference and updatePreference shown in the diff (initial list, posted/updated, removed and ranking-update paths) and the expanded extras (notification_extra, ranking_extra). The full file is not in the material.",
      "The restricted-user set is computed only in onResume. If a profile's lock state changes while the fragment stays resumed, it is not re-evaluated until the next onResume, so notifications rendered in that window use the stale decision. In the other direction, a profile unlocked while the fragment is resumed stays hidden until the next resume (conservative).",
      "Only profiles returned by UserManager.getProfiles(mContext.getUserId()) are considered; notifications attributed to any other user id are never restricted. Notifications with sbn.getUserId() == USER_ALL are mapped to USER_SYSTEM, so they are judged by the system user's state.",
      "Restriction requires both LockPatternUtils.isSecure(user) and KeyguardManager.isDeviceLocked(user), so a locked profile without a secure credential is not restricted. Because the setting is read with a default of 0, a profile with LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS unset is treated as restricted.",
      "Hiding is per-user and applies to all of that user's notifications; it does not consult each notification's own visibility or its channel's lockscreen visibility. Package name, icon and timestamp of locked-profile notifications remain visible.",
      "The sensitive strings are still parsed and held in memory (createFromSbn unchanged); the fix only changes what is displayed.",
      "When restrictContent is false, the visibility of notification_extra and ranking_extra is not explicitly reset to VISIBLE. A row previously bound as restricted may therefore keep those two views GONE (a UI side effect, not a disclosure)."
     ],
     "behavior_changes": [
      "Notification log rows for notifications from a secure, locked profile with private notifications disallowed now show only package name, icon and timestamp. Title, text and the long-press expanded details (notification_extra, ranking_extra) are hidden.",
      "Lock and visibility state is snapshotted in onResume through UserManager, Settings.Secure, LockPatternUtils and KeyguardManager calls.",
      "The HistoricalNotificationPreference constructor and updatePreference signatures changed (inner class of NotificationStation)."
     ],
     "preconditions": [
      "The current user has a profile (e.g. a managed profile) that has its own secure lock (LockPatternUtils.isSecure), is currently locked (KeyguardManager.isDeviceLocked), and has LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS set to 0 or unset.",
      "Notifications from that profile are among those delivered to NotificationStation's listener.",
      "Someone with access to the unlocked parent user opens the Settings NotificationStation fragment (notification log). The entry point to this fragment is not shown in the material."
     ],
     "per_major": {
      "13": "Listed as affected by the bulletin; the material has no fix commit or tag information for Android 13 (unknown).",
      "14": "Same fix: platform/packages/apps/Settings 06a1643e44a6 in android-security-14.0.0_r24. The diff matches the 16 change hunk for hunk, with different pre/post blob ids. At android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "Same fix: 0db1143041a6 in android-security-15.0.0_r12, with a diff and blob ids identical to the 16 change. At android-security-15.0.0_r17, 8/8 lines are present.",
      "16": "Fix 596c7b9911f2 in android-security-16.0.0_r3 (cherry-pick of b1bfda5a03c6, Change-Id Ie7705a1ceea54203585c3b3737d0bada68aa1469). At android-security-16.0.0_r8, 8/8 lines are present.",
      "17": "Not affected: the bulletin does not list Android 17. The patched code is already present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "A single self-contained change to one file. The same patch was applied with identical hunk offsets on 14, 15 and 16, so forks with an unmodified NotificationStation.java should be able to take it cleanly. It depends on com.android.internal.widget.LockPatternUtils, KeyguardManager.isDeviceLocked(int), UserManager.getProfiles and Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; Settings, as a platform-signed system app, builds against framework internals (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "How NotificationStation is launched (its entry point) and who can reach it is not shown in the material.",
      "The Android 13 fix commit/tag, although the bulletin lists 13 as affected.",
      "Whether the original report concerned a work profile specifically or another profile type; the commit message only says 'locked profiles'.",
      "Whether NotificationStation.java has other rendering paths for notification content outside the hunks in the diff."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48572": {
     "cve": "CVE-2025-48572",
     "summary": "MediaButtonReceiverHolder.send stops attaching BroadcastOptions that unconditionally gave the media button receiver a temporary FGS allowlist and background-activity-start (BAL) permission. Instead it calls MediaSessionService.tempAllowlistTargetPkgIfPossible for the target package and passes it the caller's pid/uid/package.",
     "component": "platform/frameworks/base, system_server MediaSessionService / MediaButtonReceiverHolder (services/core/java/com/android/server/media)",
     "weakness": "Improper privilege propagation: BAL and FGS-from-background allowlisting were granted to the media button receiver unconditionally via BroadcastOptions on every dispatch.",
     "root_cause": "In MediaButtonReceiverHolder.send, every media key dispatch built BroadcastOptions.makeBasic() and called setTemporaryAppAllowlist(fgsAllowlistDurationMs, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_MEDIA_BUTTON, \"\") and setBackgroundActivityStartsAllowed(true). It then passed options.toBundle() to both mPendingIntent.send(...) and context.sendBroadcastAsUser(...). As a result, the receiver package got these abilities on every dispatch, with no check tied to the caller. The commit message states this accidentally propagated BAL abilities to the receiver.",
     "fix_analysis": [
      "MediaButtonReceiverHolder.java:send: the signature now takes MediaSessionService mediaSessionService, String callingPackageName, int callingPid, int callingUid and String reportedPackageName, and drops long fgsAllowlistDurationMs. EXTRA_PACKAGE_NAME is now filled from reportedPackageName, which is the same value the caller previously passed as callingPackageName.",
      "MediaButtonReceiverHolder.send: the BroadcastOptions construction (setTemporaryAppAllowlist plus setBackgroundActivityStartsAllowed(true)) is removed. Instead, send resolves targetUid = context.getPackageManager().getPackageUidAsUser(mPackageName, mUserId) and calls mediaSessionService.tempAllowlistTargetPkgIfPossible(targetUid, mPackageName, callingPid, callingUid, callingPackageName, TAG). On NameNotFoundException it logs (if DEBUG_KEY_EVENT) and continues without allowlisting.",
      "MediaButtonReceiverHolder.send: mPendingIntent.send now uses the 5-argument overload (context, resultCode, mediaButtonIntent, onFinishedListener, handler) with no options bundle. The legacy default-case path now calls context.sendBroadcastAsUser(mediaButtonIntent, userHandle) with no options.",
      "MediaSessionService.java, media key dispatch branch where session == null and mediaButtonReceiverHolder != null: the local is renamed to reportedPackageName (the system package if asSystemService, else packageName). The call now passes MediaSessionService.this, packageName, pid, uid and reportedPackageName, and no longer passes MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs()."
     ],
     "completeness": [
      "Both delivery paths in MediaButtonReceiverHolder.send (PendingIntent and legacy sendBroadcastAsUser) are changed. Neither carries an options bundle anymore, so this code no longer grants BAL.",
      "FGS allowlisting is now delegated to MediaSessionService.tempAllowlistTargetPkgIfPossible, whose body is not in the material. How well the fix works on the FGS side depends on what that method checks.",
      "The only caller of send shown in the material (the MediaSessionService media key dispatch branch) is updated."
     ],
     "behavior_changes": [
      "Media button receivers no longer get background-activity-start permission through BroadcastOptions on the ACTION_MEDIA_BUTTON delivery.",
      "Temporary FGS allowlisting is no longer attached to the intent via BroadcastOptions with a device-config duration. Instead, tempAllowlistTargetPkgIfPossible is called for the resolved target UID with the caller's pid/uid/package.",
      "If the receiver package cannot be resolved for mUserId, no allowlisting is requested but the key event is still delivered.",
      "MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs() is no longer used on this path.",
      "The content of Intent.EXTRA_PACKAGE_NAME is unchanged: the system package name when asSystemService, otherwise the caller package."
     ],
     "preconditions": [
      "A media key event is dispatched through MediaSessionService and no media session handles it, so the service falls back to a MediaButtonReceiverHolder (mLastMediaButtonReceiverHolder, or one built from mCustomMediaKeyDispatcher.getMediaButtonReceiver). This is shown in the context around the patched call site.",
      "A media button receiver (PendingIntent or component) registered by an app is the target."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 7dcb4043f5d7 in android-security-14.0.0_r24. The diff matches Android 16 except that the MediaSessionService hunk is at about line 2311. At android-security-14.0.0_r29, 5/8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 739ece18a63a in android-security-15.0.0_r12. The diff is the same, with the MediaSessionService hunk at about line 2640. At android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/base e707f6600330 in android-security-16.0.0_r3 (manual cherry-pick of ag/33211293). At android-security-16.0.0_r8, 5/8 lines of the patched region are present.",
      "17": "not affected: the bulletin does not list it. At android-security-17.0.0_r1, 5/8 lines of the patched region are present."
     },
     "backport_notes": [
      "The diff calls MediaSessionService.tempAllowlistTargetPkgIfPossible but does not add it, so the method must already exist in the target tree. A tree without it needs that helper first. Android 13 is listed as affected by the bulletin, but the material contains no 13 commit."
     ],
     "confidence": "medium",
     "unknowns": [
      "The implementation and exact checks of MediaSessionService.tempAllowlistTargetPkgIfPossible are not in the material.",
      "Whether holders created via MediaButtonReceiverHolder.create(userId, pi, \"\") resolve to a package UID (and so get allowlisted) is not shown.",
      "The exact name of the method that encloses the patched MediaSessionService call site is not shown.",
      "No Android 13 fix commit is included, although the bulletin lists 13 as affected.",
      "Why only 5/8 lines of the patched region match at the latest tags is not explained."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48573": {
     "cve": "CVE-2025-48573",
     "summary": "Removes the mService.tempAllowlistTargetPkgIfPossible call from the sendCommand method in MediaSessionRecord, so a MediaController.sendCommand call no longer propagates a temporary allowlist (while-in-use / background foreground-service start, WIU/BFSL) to the session-owning app.",
     "component": "platform/frameworks/base, media session service (services/core/java/com/android/server/media/MediaSessionRecord.java)",
     "weakness": "Improper privilege propagation: a temporary WIU/BFSL allowlist grant was tied to an API path that only exchanges technical information",
     "root_cause": "In MediaSessionRecord's sendCommand(packageName, pid, uid, command, args, cb), the code called mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), pid, uid, packageName, reason) with reason TAG + \":\" + command before forwarding the command via mCb.onCommand. Based on the argument order, this asked for the session owner (getUid()/getPackageName()) to be temporarily allowlisted on behalf of the calling controller (pid/uid/packageName). The commit message says sendCommand is only for exchanging technical information and should not be used to grant another app permission to start a foreground service or gain while-in-use permissions. The grant on this path was therefore an unintended privilege propagation.",
     "fix_analysis": [
      "services/core/java/com/android/server/media/MediaSessionRecord.java : sendCommand: deletes the three lines that build the reason string and call mService.tempAllowlistTargetPkgIfPossible(...). The method now only forwards to mCb.onCommand(packageName, pid, uid, command, args, cb) inside the existing try/catch for RemoteException.",
      "Single-commit fix: no other files or methods are modified."
     ],
     "completeness": [
      "Covers only the sendCommand path. The temporary allowlist call is removed outright there, not gated behind a condition.",
      "Sibling methods visible in the code context still call mService.tempAllowlistTargetPkgIfPossible: sendMediaButton (gated on KeyEvent.isMediaSessionKey), sendCustomAction (reason TAG + \":custom-\" + action) and prepare (reason TAG + \":prepare\"). The commit does not touch them, and the material does not say whether those grants are intended."
     ],
     "behavior_changes": [
      "When a controller calls MediaController.sendCommand on a session, the app that owns the session no longer gets the temporary WIU/BFSL allowlist through this path. onCommand is still delivered unchanged.",
      "No new error paths or return values: the only observable difference is that the allowlist grant is gone."
     ],
     "preconditions": [
      "A MediaController client calls sendCommand on a MediaSession, which reaches the sendCommand method in MediaSessionRecord in system_server with the controller's packageName/pid/uid.",
      "Before the fix, whether the grant actually happened depended on mService.tempAllowlistTargetPkgIfPossible, whose internal conditions the material does not show."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 21481e44fa92 in android-security-14.0.0_r24 removes the same three lines from sendCommand. The surrounding context differs only by Log.e instead of Slog.e and the class declaration. 7/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base c88cb354b7fa in android-security-15.0.0_r12, the same diff at a different line offset. 7/8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 039030a6b0e7 in android-security-16.0.0_r3 (manual cherry-pick of ag/33211646). 7/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. 7/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The change is a three-line deletion in sendCommand with no dependencies. On forks, find the tempAllowlistTargetPkgIfPossible call right before mCb.onCommand and remove it together with its reason string."
     ],
     "confidence": "high",
     "unknowns": [
      "The Android 13 fix commit and code are not in the material, although the bulletin lists 13 as affected.",
      "The internal conditions of tempAllowlistTargetPkgIfPossible (what it checks before granting) are not shown.",
      "Whether the remaining tempAllowlistTargetPkgIfPossible calls in sendCustomAction, prepare and sendMediaButton are intended is not stated.",
      "The name of the inner class that contains sendCommand is not shown in the context."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48575": {
     "cve": "CVE-2025-48575",
     "summary": "CertInstaller now treats a request as coming from Settings only if the referrer package is \"com.android.settings\" AND the installed package with that name runs under the system UID. This replaces a check on the referrer name alone that gated CA certificate installation.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA/credential installation flow)",
     "weakness": "Improper authorization: the trust decision relied on a package name string and did not verify the package's identity (missing UID check)",
     "root_cause": "CredentialHelper.calledBySettings() returned true whenever mReferrer equalled the string \"com.android.settings\". CertInstaller used it to pick the privileged InstallOthersAction path, and the install path after PKCS12 extraction, both of which install CA certificates. The check only compared a package name. It did not verify that the package with that name was the genuine system Settings app. The fix commit's comment says some systems have no preinstalled \"com.android.settings\", so a settings impostor using that name could pass the check.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java: a new private method, isCalledBySettings(), first requires SETTINGS_PACKAGE (\"com.android.settings\") to equal mCredentials.getReferrer(). It then looks up the installed package of that name with getPackageManager().getApplicationInfo(SETTINGS_PACKAGE, 0) and returns UserHandle.isSameApp(settingsUid, Process.SYSTEM_UID). If the package is not found (NameNotFoundException), it logs an error and returns false.",
      "src/com/android/certinstaller/CertInstaller.java: both call sites that used mCredentials.calledBySettings() now call isCalledBySettings(). These are the branch in the install dispatch that runs InstallOthersAction, and the branch after successful PKCS12 extraction that calls installCertificateOrShowNameDialog().",
      "src/com/android/certinstaller/CredentialHelper.java: the SETTINGS_PACKAGE constant and the calledBySettings() method are removed, so no remaining code can use the name-only check.",
      "CertInstaller.java gains an import of android.os.UserHandle and its own SETTINGS_PACKAGE constant."
     ],
     "completeness": [
      "Covers both uses of the Settings trust decision in CertInstaller.java. The old name-only helper is deleted from CredentialHelper, so the diff leaves no caller of the weak check.",
      "The new check verifies that the package named com.android.settings is a system-UID app. It does not separately check the UID of the actual caller, so it still depends on mReferrer correctly identifying the invoking package. The material does not show how mReferrer is populated.",
      "If com.android.settings is absent, the privileged path is now denied entirely (fail closed)."
     ],
     "behavior_changes": [
      "On devices where com.android.settings is not installed or does not run as the system UID, requests with that referrer no longer take the Settings-only install path. They fall through to the non-Settings branch. When the package is missing, the error log \"Unable to find info for settings package\" is written.",
      "The two affected paths now make an extra PackageManager.getApplicationInfo lookup."
     ],
     "preconditions": [
      "The CertInstaller activity is invoked with a referrer equal to \"com.android.settings\". The material does not show how mReferrer is set.",
      "Per the fix comment, the scenario involves systems with no preinstalled genuine com.android.settings package, so another package can carry that name.",
      "The flow reaches either the install dispatch (non-PKCS12 path) or the success handler after PKCS12 extraction in CertInstaller."
     ],
     "per_major": {
      "14": "same fix: commit 17c124efb1f1 in android-security-14.0.0_r24. The CertInstaller.java diff is identical; the CredentialHelper.java hunk has the same content on a different base (index eab58f5). 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit b24bef75f325 in android-security-15.0.0_r12, same diff as the 16 fix. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit d688ebdbfd40 in android-security-16.0.0_r3 (cherry-picked from internal changes 46580f2f6f02 / 2f858ccb45cd). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A self-contained change to two files; the same diff was applied on 14, 15 and 16 (on 14 the CredentialHelper.java base differs). Because calledBySettings() is removed from CredentialHelper, any vendor-added callers of it must be switched to the new isCalledBySettings() in CertInstaller."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how CredentialHelper.mReferrer is populated (for example, from the launching intent or the activity referrer). So it cannot tell whether an attacker can control the referrer value on a fully patched build.",
      "The material does not show what InstallOthersAction installs beyond CA certificates or the exact privilege gained; the bulletin only rates the issue High EoP.",
      "Android 13 is listed as affected by the bulletin, but the material includes no Android 13 fix commit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48576": {
     "cve": "CVE-2025-48576",
     "summary": "Removes the unused AIDL-only binder method updateNotificationChannelGroupFromPrivilegedListener from INotificationManager and its implementation in NotificationManagerService, closing a privileged-listener entry point that created or updated notification channel groups for a caller-specified package.",
     "component": "platform/frameworks/base, notification service (INotificationManager / NotificationManagerService)",
     "weakness": "unknown (the diff only shows removal of an unused, unpublished binder entry point; the bulletin classifies the impact as DoS)",
     "root_cause": "INotificationManager.aidl exposed updateNotificationChannelGroupFromPrivilegedListener(token, pkg, user, group), which NotificationManagerService implemented by calling verifyPrivilegedListener(token, user, false), then createNotificationChannelGroup(pkg, getUidForPackageAndUser(pkg, user), group, false, true) with the caller-supplied NotificationChannelGroup, followed by handleSavePolicyFile(). Per the commit message the method was never used from NotificationListenerService or anywhere else, so it was unused attack surface: a caller passing the privileged-listener check could write channel groups for a caller-specified package and trigger a policy file save. The material does not show the specific failure mode that the bulletin rates as DoS.",
     "fix_analysis": [
      "core/java/android/app/INotificationManager.aidl :: interface INotificationManager: deletes the declaration `void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);` so no binder transaction for it exists anymore.",
      "services/core/java/com/android/server/notification/NotificationManagerService.java :: updateNotificationChannelGroupFromPrivilegedListener: deletes the whole @Override implementation (Objects.requireNonNull(user); verifyPrivilegedListener(token, user, false); createNotificationChannelGroup(pkg, getUidForPackageAndUser(pkg, user), group, false, true); handleSavePolicyFile();). Code deletion only, no new validation logic; Flag: EXEMPT."
     ],
     "completeness": [
      "Fully removes the one entry point: the AIDL declaration and the service implementation are both gone, so there is no transaction left to reach the deleted code.",
      "The adjacent privileged-listener methods remain unchanged in the same AIDL block: createConversationNotificationChannelForPackageFromPrivilegedListener, updateNotificationChannelFromPrivilegedListener, getNotificationChannelsFromPrivilegedListener, getNotificationChannelGroupsFromPrivilegedListener.",
      "The underlying createNotificationChannelGroup(...) and handleSavePolicyFile() in NotificationManagerService are not modified; any hardening of the group creation logic itself is not part of this fix, and other callers of createNotificationChannelGroup are not shown in the material."
     ],
     "behavior_changes": [
      "The updateNotificationChannelGroupFromPrivilegedListener binder method no longer exists on INotificationManager.",
      "No functional change for NotificationListenerService or any in-tree caller, since the commit message states the method was never used.",
      "Removing a method from the AIDL interface renumbers the transaction codes of the methods declared after it; this only matters for code not rebuilt together with the framework (general platform knowledge)."
     ],
     "preconditions": [
      "Caller must hold an INotificationListener token that passes verifyPrivilegedListener(token, user, false) in NotificationManagerService (the exact criteria are not shown in the material).",
      "Caller must invoke the raw INotificationManager binder interface, since the method was AIDL-only and was never used from NotificationListenerService."
     ],
     "per_major": {
      "14": "same fix: commit 7b28c9436fff in android-security-14.0.0_r24 deletes the identical AIDL line and the identical 11-line service method; context differs (line offsets, setInterruptionFilter without fromUser, no createConversationNotificationChannelForPackageFromPrivilegedListener before the removed line).",
      "15": "same fix: commit 345b6f2a6900 in android-security-15.0.0_r12, identical deletions, different line offsets.",
      "16": "same fix: commit b812baa1463c in android-security-16.0.0_r3 (cherry-pick of 6077349a00fdac), identical deletions; the AIDL block there also contains createConversationNotificationChannelForPackageFromPrivilegedListener immediately before the removed line.",
      "17": "not listed by the bulletin (not affected per the bulletin); at android-security-17.0.0_r1 5/8 lines of the patched region are present, the same ratio as the patched 14/15/16 tags. The material does not show the 17 history."
     },
     "backport_notes": [
      "Pure deletion of one AIDL line and one @Override method, to be removed together; applies to any branch that has the method with only offset adjustments. Any test or OEM code still calling the method will no longer compile and must be removed too.",
      "Fix in fork: no fork comparison was provided in the material, so no per-file verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete DoS mechanism (what about the written NotificationChannelGroup or the policy save caused the failure) is not shown by the diff or bulletin.",
      "The exact checks performed by verifyPrivilegedListener(token, user, false) and getUidForPackageAndUser are not included in the material.",
      "Why Android 17 is not listed as affected, and whether the method is absent there, is not evidenced in the material.",
      "How un-rebuilt clients of the old interface behave after the removal is not shown."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48580": {
     "cve": "CVE-2025-48580",
     "summary": "MediaBrowser no longer always binds to the target MediaBrowserService with BIND_INCLUDE_CAPABILITIES. The flag is now left out when the root hints contain the new hidden key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES or the existing BrowserRoot.EXTRA_RECENT.",
     "component": "platform/frameworks/base, media framework (android.media.browse.MediaBrowser / android.service.media.MediaBrowserService)",
     "weakness": "Unconditional capability propagation on service bind (privilege propagation via BIND_INCLUDE_CAPABILITIES; improper privilege management)",
     "root_cause": "In media/java/android/media/browse/MediaBrowser.java, the connect path called mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES) for every connection, whatever the root hints or the purpose of the connection. BIND_INCLUDE_CAPABILITIES extends the binding client's capabilities (such as while-in-use permission capabilities) to the bound service process (general platform knowledge). As a result, every MediaBrowserService a browser client connected to received that client's capabilities. This included short-lived one-off queries such as recent-playback lookups using EXTRA_RECENT. A client had no way to opt out of the propagation.",
     "fix_analysis": [
      "media/java/android/service/media/MediaBrowserService.java, nested class BrowserRoot: adds a new @hide constant EXTRA_EXCLUDE_CAPABILITIES = \"android.service.media.extra.EXCLUDE_CAPABILITIES\". Its documentation describes it as a root hint key meaning the bind connection should not include capabilities, and says EXTRA_RECENT implies it.",
      "media/java/android/media/browse/MediaBrowser.java, connect path, at the bindService call: the fixed flag combination is replaced by a computed int bindServiceFlags = Context.BIND_AUTO_CREATE. BIND_INCLUDE_CAPABILITIES is OR'ed in only if mRootHints == null, or if mRootHints contains neither BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES nor BrowserRoot.EXTRA_RECENT. bindService is then called with bindServiceFlags.",
      "Adds import android.service.media.MediaBrowserService.BrowserRoot in MediaBrowser.java. The check uses Bundle.containsKey, so what matters is whether the key is present, not its boolean value."
     ],
     "completeness": [
      "Covers only the bindService call in MediaBrowser.java. It is the only bind site shown in the diff, and the material shows no other bindService call in MediaBrowser.",
      "Default behavior is unchanged: when root hints are null or contain neither key, BIND_INCLUDE_CAPABILITIES is still included. Protection depends on the client either passing EXTRA_RECENT or opting in with the hidden EXTRA_EXCLUDE_CAPABILITIES key. Callers that query arbitrary MediaBrowserServices without either key still propagate capabilities.",
      "The fix commit has no caller-side changes, so the material does not show whether platform clients were updated to set the new key.",
      "EXTRA_EXCLUDE_CAPABILITIES is @hide, so it is not part of the public SDK. Any client can still use the key string itself by putting it in the root hints Bundle."
     ],
     "behavior_changes": [
      "MediaBrowser connections whose root hints contain EXTRA_RECENT now bind without BIND_INCLUDE_CAPABILITIES, so the target MediaBrowserService no longer inherits the client's capabilities for recent-playback queries.",
      "The new hidden root hint key BrowserRoot.EXTRA_EXCLUDE_CAPABILITIES lets a client explicitly bind without capability propagation.",
      "Because the check uses containsKey, a root hint of EXTRA_RECENT=false also disables capability propagation.",
      "No new error paths: the bind still uses BIND_AUTO_CREATE and the same exception handling."
     ],
     "preconditions": [
      "A client process creates an android.media.browse.MediaBrowser targeting a MediaBrowserService and calls connect(), which reaches the bindService call in MediaBrowser.java.",
      "Before the fix: the client holds capabilities that BIND_INCLUDE_CAPABILITIES would extend to the bound service.",
      "After the fix: capabilities are still propagated unless the root hints Bundle contains EXTRA_EXCLUDE_CAPABILITIES or EXTRA_RECENT."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 874eaecb47b8 in android-security-14.0.0_r24. The code changes to MediaBrowser.java and MediaBrowserService.java are identical; only hunk offsets differ (the BrowserRoot constants hunk is at line ~872). At android-security-14.0.0_r29, 8/8 patched lines are present.",
      "15": "same fix: platform/frameworks/base 2588609ffab9 in android-security-15.0.0_r12. The diff is identical to the 16 fix (same blob indexes). At android-security-15.0.0_r17, 8/8 patched lines are present.",
      "16": "same fix: platform/frameworks/base eb19b27ed8ab in android-security-16.0.0_r3 (manual cherry-pick of ag/33253388, Change-Id Idc9b428f2ff53cb490ee596da35d24e1e7fca681). At android-security-16.0.0_r8, 8/8 patched lines are present.",
      "17": "not affected per the bulletin (not listed). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching two files in frameworks/base. It relies only on the existing BrowserRoot.EXTRA_RECENT constant and Context.BIND_INCLUDE_CAPABILITIES. Google applied the same diff on 14, 15 and 16 with only line-offset differences.",
      "The library change only helps clients that pass EXTRA_RECENT or the hidden EXTRA_EXCLUDE_CAPABILITIES in their root hints. When backporting, check whether any privileged in-tree MediaBrowser clients need a companion change to set the key, since this commit does not include one."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which privileged client triggered the issue, and whether a companion caller-side change (setting EXTRA_EXCLUDE_CAPABILITIES) was landed. The material shows only the framework-library commit.",
      "Whether other code that binds to MediaBrowserService outside android.media.browse (for example support-library MediaBrowser wrappers) uses BIND_INCLUDE_CAPABILITIES. The material does not show this.",
      "Why the bulletin lists Android 13 (and 16-qpr2-next) as affected when the material includes no Android 13 fix commit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48583": {
     "cve": "CVE-2025-48583",
     "summary": "BaseBundle no longer returns a lazy-value-backed Parcel to the Parcel pool with recycle(). It now calls the newly exposed Parcel.destroy() when the last lazy value is unwrapped or the bundle is cleared, so these Parcel instances are not reused.",
     "component": "platform/frameworks/base, core/java/android/os (Bundle / Parcel lazy value handling)",
     "weakness": "Use-after-free style object reuse: a pooled Parcel was recycled (returned to the Parcel pool) while Bundle lazy-value state could still reference it",
     "root_cause": "In core/java/android/os/BaseBundle.java, a Bundle that owned the parcel backing its lazy values (mOwnsLazyValues / mWeakParcelledData) called the private helper recycleParcel(), i.e. Parcel.recycle(), in two places: in unwrapLazyValueFromMapLocked() when mLazyValues reached 0, and in clear(). Parcel.recycle() frees the buffer and puts the Java Parcel object on the static sOwnedPool / sHolderPool for reuse. The commit message says recycling the parcel in this lazy-value situation 'may lead to several UAF issues'. The likely reason is that code still holding a reference to the parcel could act on an instance that has been handed out again, but the material does not show the exact stale reference. The early release was there to free parcel-bound resources (especially file descriptors) without waiting for GC, so the call could not simply be dropped.",
     "fix_analysis": [
      "core/java/android/os/Parcel.java: destroy() changes from private to public (@hide). The visible part of destroy() calls resetSqaushingState() and then handles the native parcel when mNativePtr != 0 (branching on mOwnsNativeParcelObject). The commit message says destroy() is used so these Parcel instances are never recycled and reused.",
      "core/java/android/os/Parcel.java: the mClassCookies = null reset moves out of recycle() into the method that calls nativeFreeBuffer(mNativePtr) and resets mReadWriteHelper (most likely freeBuffer()). recycle() still calls freeBuffer().",
      "core/java/android/os/BaseBundle.java:unwrapLazyValueFromMapLocked(): when mOwnsLazyValues is set, mLazyValues drops to 0 and the weak parcel is still alive, the code now calls parcel.destroy() instead of recycleParcel(parcel), then sets mWeakParcelledData to null.",
      "core/java/android/os/BaseBundle.java:clear(): when mOwnsLazyValues is set and mWeakParcelledData is non-null, the code gets the parcel and, if it is non-null, calls parcel.destroy() instead of recycleParcel(). mWeakParcelledData is set to null afterwards, so a second clear() does not destroy again (covered by tests).",
      "core/java/android/os/BaseBundle.java:initializeFromParcelLocked(): the only remaining recycle path is when ownsParcel is true and numLazyValues[0] == 0. It now calls parcelledData.recycle() directly. The isEmptyParcel() guard that was in recycleParcel() is not needed here because the function returns early for an empty parcel at its top.",
      "core/java/android/os/BaseBundle.java: the private static recycleParcel(Parcel) helper is deleted.",
      "core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java: the lazy-value tests are renamed and now verify destroy() instead of recycle() (exactly once, and never for shared or copied parcels). bundleClear_whenUnparcelledWithoutLazy_recyclesParcelOnce still verifies recycle() for the case with no lazy values."
     ],
     "completeness": [
      "The fix covers both places where a bundle that owns its lazy-value parcel released it early: unwrapLazyValueFromMapLocked() (last lazy value consumed) and clear(). Both now use destroy(). The commit message says this removes any possibility of UAF with regards to Bundle and lazy values.",
      "The path in initializeFromParcelLocked() with no lazy values intentionally keeps recycle(). In that case no lazy value in mMap was created from the parcel (numLazyValues[0] == 0).",
      "Bundles that do not own the parcel (mOwnsLazyValues false, e.g. copies sharing a parcel) never destroy or recycle it. This is unchanged and the tests check it.",
      "The parcel is still released early (the commit message's stated intent is to free resources such as file descriptors as soon as possible). What changes is that the Java object is destroyed rather than returned to the pool."
     ],
     "behavior_changes": [
      "Parcel instances that backed lazy Bundle values are destroyed instead of being returned to the Parcel pool. This can mean somewhat less pool reuse for workloads that unparcel many bundles with lazy values.",
      "Parcel.destroy() becomes a public @hide method.",
      "mClassCookies is now cleared in the buffer-freeing method rather than in recycle() itself.",
      "On Android 14, the Preconditions.checkState(\"Parcel recycled earlier than expected\") in unwrapLazyValueFromMapLocked() is removed. A null weak parcel is now tolerated instead of throwing."
     ],
     "preconditions": [
      "A Bundle is deserialized from a Parcel it owns (mOwnsLazyValues) and contains at least one lazily deserialized value.",
      "The last lazy value is unwrapped (e.g. through a typed getParcelable call) or the bundle is cleared. In the vulnerable code, both trigger the early Parcel.recycle().",
      "Another reference to the same Parcel instance must still be in use after the recycle. The material does not show which reference that is.",
      "The bulletin rates it High severity EoP. The privilege boundary is not described in the material."
     ],
     "per_major": {
      "14": "adapted fix: platform/frameworks/base 401cc81d7d94 in android-security-14.0.0_r24. Same logic, but the Android 14 unwrapLazyValueFromMapLocked() hunk also replaces the older Preconditions.checkState(mWeakParcelledData.get() != null, \"Parcel recycled earlier than expected\") + recycleParcel() form with the null-tolerant parcel.destroy() form. In this tree, BaseBundle does not implement Parcel.ClassLoaderProvider. The Parcel.java hunks sit at different line offsets (~573, ~5271). Test changes are identical. 4/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 26f25bbc2ae8 in android-security-15.0.0_r12. The diff matches the 16 commit apart from line offsets (Parcel.java ~5455) and the BaseBundle class declaration. 6/8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 02751bc65824 in android-security-16.0.0_r3 (cherry-picked from d20f3599f89388d181735db351879d2487cc331b, Change-Id Ibb28bf81f9028c18baad4e898e387a3e6192db5d). 6/8 lines are present at android-security-16.0.0_r8. The code context there shows parcel.destroy() in unwrapLazyValueFromMapLocked().",
      "17": "not affected: the bulletin does not list Android 17. 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, no dependencies shown. The backport needs Parcel.destroy() made public (@hide). For trees shaped like Android 14 (with the Preconditions.checkState in unwrapLazyValueFromMapLocked()), use the 14 diff.",
      "The material includes no fork comparison, so no per-file verdict for a fork can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete use-after-recycle scenario (which stale reference to the recycled Parcel was used, and across which privilege boundary) is not shown. The commit message only says 'several UAF issues'.",
      "The full body of Parcel.destroy() is not visible: whether it calls freeBuffer() (and so clears mClassCookies), and exactly how it releases native resources.",
      "The material does not say whether the EoP can be reached directly from an untrusted app or requires a privileged process to unparcel an attacker-supplied Bundle.",
      "Which value types are deserialized lazily is not shown in the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48584": {
     "cve": "CVE-2025-48584",
     "summary": "Adds a per-listener cap (5,000) on the conversation channels a privileged NotificationListenerService can create for other packages via createConversationNotificationChannelForPackageFromPrivilegedListener, and adds null and edge-case checks in that method so it no longer hits NPEs in system_server.",
     "component": "platform/frameworks/base, NotificationManagerService (services/core/java/com/android/server/notification)",
     "weakness": "Missing resource limit (unbounded channel creation by one listener across packages), combined with missing null checks (NPE when .copy() is called on a null channel lookup result)",
     "root_cause": "In NotificationManagerService.createConversationNotificationChannelForPackageFromPrivilegedListener, a privileged listener (one with a CDM association for the target package, or a notification assistant) could create conversation channels for other packages with no per-listener aggregate limit. The method also called .copy() directly on the results of mPreferencesHelper.getNotificationChannel(...) and getConversationNotificationChannel(...) without null checks, and did not check the resolved uid. Per the commit message, these edge conditions could result in NPEs. It also always re-created the conversation channel from the parent, which per the new code comment would override an existing conversation's customizations with the parent's values.",
     "fix_analysis": [
      "New class services/core/java/com/android/server/notification/NotificationListenerStats.java: keeps a SparseIntArray mapping nlsUid to the number of channels created, guarded by mLock. MAX_CHANNELS = 5_000, and a constructor allows tests to override it. isAllowedToCreateChannel(ManagedServiceInfo) returns count < max. logCreatedChannels(nls, increase) adds to the count. onPackageRemoved(uid, pkg) drops the entry for that uid. writeXml/readXml persist the counts under the tag 'nlsStats', with one 'nls' child per uid (attributes uid, channelCount). dump() prints the per-uid counts.",
      "NotificationManagerService.java: new field mNotificationListenerStats, injected through init(...) as a new last parameter and constructed with new NotificationListenerStats() next to publishBinderService. The policy XML parsing loop reads the 'nlsStats' tag via NotificationListenerStats.isXmlTag/readXml. The policy XML writer writes it only when !forBackup. The listener dump section prints the stats. The package-removed handler calls mNotificationListenerStats.onPackageRemoved(uid, pkg).",
      "NotificationManagerService.java:verifyPrivilegedListener now returns the @NonNull ManagedServiceInfo it validated instead of void.",
      "NotificationManagerService.java:createConversationNotificationChannelForPackageFromPrivilegedListener: after verifyPrivilegedListener, if the listener is not SystemUI (nlsInfo.isSystemUi()), is not a notification assistant (isNotificationAssistant(nlsInfo.service)), and isAllowedToCreateChannel returns false, the method logs with Slog.e and returns null.",
      "Same method: returns null if getUidForPackageAndUser yields INVALID_UID. Returns null if the parent channel lookup is null. If getConversationNotificationChannel already returns a channel, returns that existing channel. Otherwise it copies the parent, sets the id and conversation id, calls createNotificationChannelsImpl, then re-reads the conversation channel. Only if that result is non-null does it call logCreatedChannels(nlsInfo, 1) and handleSavePolicyFile(). The result is returned without .copy().",
      "Renames isServiceTokenValid to isNotificationAssistant (used in isInteractionVisibleToListener and in the channel-changed notification path). The body is unchanged, so this is a pure rename.",
      "Tests: NotificationManagerServiceTest uses the real PreferencesHelper with a test cap of 10 (MAX_CHANNELS_CREATED_BY_NLS_FOR_TESTING) and adds the tests tooManyChannels_ignored, noParentChannel_ignored and existingChannel_returnsSame. RoleObserverTest is updated for the new init() parameter."
     ],
     "completeness": [
      "Within the diff, the cap is checked and counted only in createConversationNotificationChannelForPackageFromPrivilegedListener. No other channel-creation path is changed to use NotificationListenerStats.",
      "The counter only goes up: it is never decremented when conversation channels are later deleted. An entry is cleared only when a package whose uid matches the NLS uid is removed (onPackageRemoved deletes by uid). Removing a target package does not reduce the listener's count.",
      "SystemUI listeners and notification assistants are exempt from the cap, so the cap applies to other privileged (for example CDM-associated) listeners.",
      "Counts are persisted in the notification policy XML but not in backups, so they survive a reboot. readXml defaults missing attributes to 0.",
      "The edge cases handled are an unknown package uid, a missing parent channel, and a null result after creation. An existing conversation channel is now returned instead of being re-created.",
      "The cap is keyed on nls.uid, so multiple listener components in the same uid share one budget."
     ],
     "behavior_changes": [
      "Once a non-SystemUI, non-assistant listener has created 5,000 channels in total, createConversationNotificationChannelForPackageFromPrivilegedListener returns null and logs with Slog.e instead of creating the channel.",
      "The method returns null when the package uid is INVALID_UID, when the parent channel does not exist, or when creation did not produce a channel.",
      "Requesting an existing conversation channel again returns the stored channel unchanged instead of re-creating it from the parent.",
      "The returned channel is no longer a copy (the test asserts isSameInstanceAs on a repeated call).",
      "handleSavePolicyFile is called only when a channel was actually created.",
      "New 'nlsStats' section in the persisted notification policy XML and in dumpsys output.",
      "NotificationManagerService.init() gains a NotificationListenerStats parameter."
     ],
     "preconditions": [
      "The caller must be a bound notification listener that passes verifyPrivilegedListener(token, user, true): per the tests, it either has a CDM association with the target package or is a notification assistant, and enabledAndUserMatches for the user must hold.",
      "The tests for this path are annotated @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT). Whether the service code itself checks the flag is not shown.",
      "For the cap to apply, the listener must be neither SystemUI nor a notification assistant."
     ],
     "per_major": {
      "14": "not affected per bulletin. The resolver reports 7/8 patched-region lines present at android-security-14.0.0_r29. The material does not show whether the vulnerable method exists on 14.",
      "15": "not affected per bulletin. The resolver reports 8/8 patched-region lines present at android-security-15.0.0_r17. The material does not show whether the vulnerable method exists on 15.",
      "16": "same fix: commit 08a0766708db in android-security-16.0.0_r3 (cherry-pick of 16b6836078042fc1b2288912e9656d01d9d20af5, Change-Id I21033ce09185992adc0a377c701a442e8e81a9be). The patched region is fully present at android-security-16.0.0_r8.",
      "17": "not affected per bulletin (not listed). The resolver reports 8/8 patched-region lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix changes the NotificationManagerService.init(...) signature (new trailing NotificationListenerStats parameter), so every caller of init() must be updated, including NotificationManagerServiceTest, RoleObserverTest and any OEM tests. The service change also relies on verifyPrivilegedListener returning ManagedServiceInfo and on the existing ManagedServiceInfo.isSystemUi().",
      "The fix only applies to branches that contain createConversationNotificationChannelForPackageFromPrivilegedListener."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether the reported DoS was the unbounded channel creation, the NPE paths, or both. The commit message covers both.",
      "How getUidForPackageAndUser behaves (returns INVALID_UID or throws) is not shown in the material.",
      "Whether other privileged-listener channel APIs have similar null-handling issues or lack a cap is not shown.",
      "The scope of PreferencesHelper#NOTIFICATION_CHANNEL_COUNT_LIMIT is not shown.",
      "Whether the vulnerable method exists on Android 14, 15 and 17 is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48586": {
     "cve": "CVE-2025-48586",
     "summary": "In EditFdnContactScreen.onActivityResult, the returned contact URI is now validated by asking whether the current caller (ComponentCaller from getCurrentCaller()) has read permission for it via checkContentUriPermission, instead of comparing the user ID encoded in the URI with the Phone process's own user ID. A denial now throws SecurityException.",
     "component": "platform/packages/services/Telephony, Phone app FDN settings (com.android.phone.settings.fdn.EditFdnContactScreen)",
     "weakness": "Improper access control: insufficient validation of an activity-result content URI (a user-ID comparison used in place of a permission check, confused-deputy style)",
     "root_cause": "EditFdnContactScreen.onActivityResult takes a contact URI from intent.getData() and queries it with the Phone app's own ContentResolver. The only guard was `UserHandle.getUserId(Process.myUid()) != ContentProvider.getUserIdFromUri(intent.getData(), currentUser)`, which only checks that the user ID encoded in the URI matches the Phone app's user. It did not check whether the caller that delivered the result may read that URI, so the Phone app could read contact data (the test steps mention a work-profile contact) on behalf of a caller that had no access to it. The shown old code also had no null check on intent or intent.getData().",
     "fix_analysis": [
      "src/com/android/phone/settings/fdn/EditFdnContactScreen.java:onActivityResult, after the cancelled check: obtains `ComponentCaller currentCaller = getCurrentCaller()` and `Uri contactUri = (intent != null) ? intent.getData() : null`. If contactUri is null, it logs a warning and returns.",
      "Same function: calls `currentCaller.checkContentUriPermission(contactUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)`. If the result is PackageManager.PERMISSION_DENIED, it writes an EventLog entry (tag 0x534e4554, \"337784859\", caller uid) and throws a SecurityException that names the caller uid, package and URI.",
      "Same function: removes the old user-ID comparison block (ContentProvider.getUserIdFromUri vs UserHandle.getUserId(Process.myUid())) and queries `contactUri` instead of `intent.getData()`. The ContentProvider and Process imports are dropped; ComponentCaller, PackageManager and EventLog imports are added."
     ],
     "completeness": [
      "Covers the intent.getData() result path shown in EditFdnContactScreen.onActivityResult: the URI is queried only if the caller has read permission for it, whatever user the URI points to.",
      "The material does not show whether other FDN screens (e.g. BaseFdnContactScreen or sibling activities in the same package) handle onActivityResult URIs the same way."
     ],
     "behavior_changes": [
      "A result URI the caller cannot read now leads to a SecurityException thrown before the try block, instead of a silent `return` with a log line. Before, only cross-user URIs were rejected. The material does not show whether enclosing code catches the exception.",
      "Stricter: a same-user URI that the caller has no read access to is now rejected; before, it was queried.",
      "New EventLog SafetyNet-style entry (0x534e4554 / 337784859) on denial.",
      "A null Intent or null intent.getData() now produces a warning log and a return, instead of reaching the user-ID check and the query."
     ],
     "preconditions": [
      "EditFdnContactScreen (Phone app FDN editing UI) receives an onActivityResult that passes the cancelled check (RESULT_OK) and carries a content URI in the Intent data. The request code and the launching intent are not shown.",
      "The URI must point to data that the Phone app can read but the caller cannot. The fix's test steps use a work-profile contact with a managed profile set up."
     ],
     "per_major": {
      "14": "Not affected (not listed by the bulletin). The per-major facts report 7/8 lines of the patched region present at android-security-14.0.0_r29; the material does not explain the difference.",
      "15": "Same fix: commit e4124fa76067 in android-security-15.0.0_r12, with a diff identical to the Android 16 commit; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 851fc787e961 in android-security-16.0.0_r3 (cherry-pick of 522822360789, Change-Id I858041662a19a179e1fd49b3747d0fc013625015); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin); 8/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix depends on android.app.ComponentCaller and Activity.getCurrentCaller(), which were added in Android 15 (API 35) (general platform knowledge). A branch without that API cannot take the diff as-is.",
      "Single-file, single-commit change. The 15 and 16 diffs are identical apart from the file index hashes."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how a URI could pass the original user-ID check while being one the caller had no access to; only the test steps hint at a work-profile contact.",
      "The excerpt does not show which request code or contact-pick intent leads to this branch, or whether any enclosing code catches the SecurityException.",
      "The diff shows only part of onActivityResult, so it is unclear whether other URI sources (e.g. ClipData or extras) are used.",
      "Why 7/8 lines of the region are reported present on Android 14, although the bulletin does not list 14 and the ComponentCaller API is newer than 14."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48589": {
     "cve": "CVE-2025-48589",
     "summary": "SystemUI's HeaderPrivacyIconsController now filters the permission usage list it sends to Safety Center (ACTION_VIEW_SAFETY_CENTER_QS, EXTRA_PERMISSION_USAGES). Only usages that belong to the current user and its profiles, plus phone-call usages, are included. Before the fix, the unfiltered list was sent.",
     "component": "platform/frameworks/base, SystemUI quick settings privacy indicators (packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt)",
     "weakness": "Missing user-boundary check: usage data was passed to another component without filtering by the current user (cross-user information exposure)",
     "root_cause": "In HeaderPrivacyIconsController.kt, permGroupUsage() returned permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) unmodified. showSafetyCenter() wrapped that whole list in an ArrayList and attached it as PermissionManager.EXTRA_PERMISSION_USAGES on the Intent.ACTION_VIEW_SAFETY_CENTER_QS intent. Nothing limited the PermissionGroupUsage entries to the current user or its profiles, so usage entries attributed to other users could be sent to Safety Center when the mic/camera indicator was clicked. The commit message says only the current users (and profiles) should be sent.",
     "fix_analysis": [
      "HeaderPrivacyIconsController.kt, constructor: gains an injected com.android.systemui.settings.UserTracker parameter (userTracker). The imports android.os.UserHandle and UserTracker are added.",
      "HeaderPrivacyIconsController.kt:permGroupUsage(): copies permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted) into an ArrayList and reads userTracker.userProfiles. For each usage it computes UserHandle.getUserId(usage.uid). Usages that are neither phone calls (usage.isPhoneCall) nor tied to a user id in userProfiles are collected into invalidUserUsages and removed with usages.removeAll(invalidUserUsages). The method changes from private returning List<PermissionGroupUsage> to public returning ArrayList<PermissionGroupUsage>. The new tests call it directly.",
      "HeaderPrivacyIconsController.kt:showSafetyCenter(): now uses val usage = permGroupUsage() directly, with no ArrayList wrapper. The filtered list is what gets logged via privacyLogger.logUnfilteredPermGroupUsage(usage) and attached as EXTRA_PERMISSION_USAGES to the ACTION_VIEW_SAFETY_CENTER_QS intent.",
      "HeaderPrivacyIconsControllerTest.kt: adds a mocked UserTracker to the constructor call and three tests. testPermGroupUsage_filtersOutInactiveUsers checks that the usage from OTHER_USER_ID is dropped. testPermGroupUsage_alwaysReturnPhoneCallUsage checks that a phone-call usage from another user is kept. testPermGroupUsage_returnsProfileUsages checks that usages from USER_ID and PROFILE_USER_ID are both kept."
     ],
     "completeness": [
      "The filter is applied only in HeaderPrivacyIconsController.permGroupUsage(), which feeds showSafetyCenter(). This covers the Safety Center path reached from the QS header privacy chip.",
      "Phone-call usages (usage.isPhoneCall) are deliberately exempt from the user filter and are still forwarded for any user. The tests treat this as intended behavior.",
      "Filtering depends on userTracker.userProfiles at click time. The material shows no change on the Safety Center receiving side.",
      "This commit does not touch the PrivacyDialogController / PrivacyDialogControllerV2 dialog path referenced in the same class. The material does not show whether that path has a comparable per-user concern.",
      "This commit does not change permissionManager.getIndicatorAppOpUsageData(). The restriction is enforced in SystemUI."
     ],
     "behavior_changes": [
      "The Safety Center QS intent now carries only usages for the current user and its profiles, plus any phone-call usages. Usages of other users are left out.",
      "The HeaderPrivacyIconsController constructor signature changes (new UserTracker parameter). This affects Dagger injection and any direct instantiation.",
      "permGroupUsage() becomes a public method returning ArrayList<PermissionGroupUsage>.",
      "privacyLogger.logUnfilteredPermGroupUsage() now receives the user-filtered list instead of the raw list from PermissionManager."
     ],
     "preconditions": [
      "A device with more than one user, where a uid outside the current user's profile group has a mic/camera usage in permissionManager.getIndicatorAppOpUsageData().",
      "The mic/camera privacy indicator in the QS header is clicked and the showSafetyCenter() path is taken."
     ],
     "per_major": {
      "14": "adapted fix: RESTRICT AUTOMERGE commit ec11eca03974 in android-security-14.0.0_r24. Same permGroupUsage() filtering logic. UserTracker is added after deviceProvisionedController (this branch has no featureFlags or shadeDialogContextInteractor parameters), and the file imports ShadeModule.Companion.SHADE_HEADER. The test lives in packages/SystemUI/tests/ and uses a 9-argument PermissionGroupUsage constructor. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "adapted fix: RESTRICT AUTOMERGE commit e086bd9b0fa8 in android-security-15.0.0_r12. Same filtering logic. UserTracker is added after featureFlags (no shadeDialogContextInteractor parameter). The test lives in packages/SystemUI/tests/ and uses a 10-argument PermissionGroupUsage constructor. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 2aeba76a58c1 in android-security-16.0.0_r3 (cherry-pick of ec99b5a699f1). UserTracker is added after shadeDialogContextInteractor. The test lives in packages/SystemUI/multivalentTests/. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The only branch-specific differences are the HeaderPrivacyIconsController constructor parameter list (featureFlags and shadeDialogContextInteractor exist only on newer branches) and the test location and PermissionGroupUsage constructor arity. The permGroupUsage() body is identical on 14, 15 and 16. None of the fix commits change a Dagger module.",
      "If a fork has customized HeaderPrivacyIconsController, apply the filter inside permGroupUsage() and make sure every path that builds EXTRA_PERMISSION_USAGES uses the filtered list."
     ],
     "confidence": "high",
     "unknowns": [
      "How the Safety Center side consumes EXTRA_PERMISSION_USAGES and what it displays. The material only shows the SystemUI sender.",
      "Whether the PrivacyDialogController / PrivacyDialogControllerV2 path performs its own per-user filtering.",
      "Which conditions select showSafetyCenter() over the privacy dialog path.",
      "What getIndicatorAppOpUsageData() returns across users on a real device. The tests mock it.",
      "The exact EoP impact chain behind the bulletin's High/EoP rating."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48590": {
     "cve": "CVE-2025-48590",
     "summary": "Removes the platform-compat exemption in AppOpsService that treated an invalid (undeclared) attribution tag as valid when SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE was not enabled for the package or the calling uid (\"Q- apps\"), so isAttributionTagValid is no longer forced to true whatever the caller's compat state.",
     "component": "platform/frameworks/base, AppOps (services/core/java/com/android/server/appop/AppOpsService.java)",
     "weakness": "Improper input validation: a compat-gated bypass of attribution tag validation let undeclared attribution tags be treated as valid. The bulletin type is DoS, and the test name AppOpMemoryUsageTest suggests a memory consumption impact (inferred).",
     "root_cause": "In AppOpsService.java, the attribution tag validation block (reached when the tag is not found in the package, or when the package is not found) had a fallback. If mPlatformCompat.isChangeEnabledByPackageName(SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName, userId) or mPlatformCompat.isChangeEnabledByUid(..., callingUid) returned false, it set isAttributionTagValid = true. For such callers (the commit calls them \"Q- apps\"), attribution tags the package did not declare were treated as valid. The bulletin's DoS classification and the test name AppOpMemoryUsageTest suggest that the consequence is growth of system_server app op memory driven by arbitrary tags. The diff does not show this directly.",
     "fix_analysis": [
      "services/core/java/com/android/server/appop/AppOpsService.java (attribution tag validation block, hunk near line 5017 on 16): deletes the try/catch that queried mPlatformCompat for SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE and forced isAttributionTagValid = true when the change was not enabled for the package or the calling uid. Only Slog.e(TAG, msg) is kept, so the block no longer overrides the validation result.",
      "services/core/java/com/android/server/appop/AppOpsService.java (imports): removes the now unused static import of android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE.",
      "Single commit with no new checks: the fix only removes the exemption, so the existing validation result applies to every caller."
     ],
     "completeness": [
      "Removes the compat override of the validation result in AppOpsService.java. Because the import is also removed, the constant is no longer used anywhere in that file.",
      "The constant SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE itself (in AppOpsManager) is not removed by this diff. The material does not show whether that change id has other consumers.",
      "The diff adds no cap or eviction on per-tag app op state. How callers handle isAttributionTagValid == false is outside the shown hunk."
     ],
     "behavior_changes": [
      "Callers whose package or uid had SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE disabled are now treated like other apps when they pass an attribution tag their package does not declare: the tag is considered invalid instead of being accepted.",
      "The Slog.e(TAG, msg) error log is still emitted for invalid tags or missing packages. The RemoteException catch is gone because the compat query was removed.",
      "No new flags are introduced and the change is unconditional (the commit marks Flag: EXEMPT)."
     ],
     "preconditions": [
      "A caller reaches an AppOpsService path that validates attribution tags, passing a tag the named package does not declare (or naming a package that cannot be found).",
      "Before the fix, the exemption applied only when the compat change SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE was not enabled for the package name or for the calling uid (the commit calls these Q- apps).",
      "The exact binder entry points that lead to this block are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 0c46a5ec453b in android-security-14.0.0_r24, the same hunk at a different line offset (~3937). At android-security-14.0.0_r29, 7 of the 8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 529f24023e15 in android-security-15.0.0_r12, the same hunk at ~4692. At android-security-15.0.0_r17, 7 of the 8 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/base 848f94492175 in android-security-16.0.0_r3 (cherry-pick of d868e9c0ae36, Change-Id I6f3d47bc2416d3a00d0b13f65370f4de6efe89db). At android-security-16.0.0_r8, 7 of the 8 lines of the patched region are present.",
      "17": "not affected (the bulletin does not list 17). At android-security-17.0.0_r1, 7 of the 8 lines of the patched region are present."
     },
     "backport_notes": [
      "Google applied the same deletion on 14, 15 and 16; only the line offsets differ. On a fork, find the try/catch around mPlatformCompat.isChangeEnabledByPackageName/isChangeEnabledByUid with SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE in AppOpsService.java, delete it so only Slog.e(TAG, msg) remains, and drop the unused import. The bulletin lists Android 13 as affected, but the material has no 13 commit."
     ],
     "confidence": "high",
     "unknowns": [
      "The name of the enclosing method, and how its callers use isAttributionTagValid, are not shown in the material.",
      "Which resource grows (which app op data structure is keyed by attribution tag) is inferred only from the test name AppOpMemoryUsageTest and the bulletin's DoS type.",
      "Whether the Android 13 branch received an equivalent commit is not shown.",
      "Which line of the patched region is missing at each latest tag is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48592": {
     "cve": "CVE-2025-48592",
     "summary": "The dav1d-based Codec2 software AV1 decoder (C2SoftDav1dDec) now sets Dav1dSettings.all_layers = 0 before dav1d_open. For scalable (SVC) AV1 clips, the decoder therefore outputs only one layer instead of every layer, which the plugin was not designed to handle.",
     "component": "platform/frameworks/av, media/codec2/components/dav1d (C2SoftDav1dDec software AV1 decoder)",
     "weakness": "Not evident from the one-line diff. The bulletin classifies it as information disclosure (ID). The commit message says the plugin received all-layer SVC output it was not designed to handle.",
     "root_cause": "C2SoftDav1dDec::initDecoder() filled Dav1dSettings with dav1d_default_settings() and overrode only n_threads and max_frame_delay. Per the commit message, the dav1d defaults output all layers for SVC clips, and the Codec2 plugin is not designed to handle this. The bulletin rates the result as a High severity information disclosure.",
     "fix_analysis": [
      "media/codec2/components/dav1d/C2SoftDav1dDec.cpp:C2SoftDav1dDec::initDecoder(): the fix adds lib_settings.all_layers = 0; right after dav1d_default_settings(&lib_settings). The setting is therefore in place when dav1d_open(&mDav1dCtx, &lib_settings) creates the decoder context, so the decoder outputs only one layer for SVC clips.",
      "No other change: the plugin's output handling code is not modified. The fix works by limiting what the library outputs, not by adding validation in the plugin."
     ],
     "completeness": [
      "Covers every dav1d decoder instance created through initDecoder(), because the setting is applied to lib_settings before dav1d_open. The shown context has a single dav1d_open call site.",
      "The fix prevents the multi-layer output condition but adds no defensive handling in the plugin's output path. Whether such handling exists elsewhere is not shown in the material.",
      "The sibling libaom-based decoder (media/codec2/components/aom/C2SoftAomDec.cpp, per index search) is untouched. The material does not show whether it has an equivalent layer-output setting or issue."
     ],
     "behavior_changes": [
      "For scalable (SVC) AV1 clips, the dav1d-backed decoder now outputs only one layer instead of all layers.",
      "Thread count and frame delay configuration are unaffected."
     ],
     "preconditions": [
      "An AV1 bitstream with multiple layers (SVC) decoded by the dav1d-based Codec2 software decoder C2SoftDav1dDec.",
      "The dav1d component must be the one used for decoding (the libaom-based C2SoftAomDec is a separate component, per index search)."
     ],
     "per_major": {
      "14": "not affected (not listed by the bulletin); media/codec2/components/dav1d/C2SoftDav1dDec.cpp is absent at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/av 9d74d91a39f3 in android-security-15.0.0_r12, the same one-line change (hunk at line 542 vs 547); 4/4 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/av 8febdebcb5e8 in android-security-16.0.0_r3; 4/4 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region, including lib_settings.all_layers = 0, is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-line, self-contained change with no dependencies: insert lib_settings.all_layers = 0; right after dav1d_default_settings(&lib_settings) in C2SoftDav1dDec::initDecoder() wherever C2SoftDav1dDec.cpp exists."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact failure in the plugin's output path when all layers were returned (which buffer or dimension assumption broke) is not shown. The material only states the plugin was not designed to handle all-layer output.",
      "Which layer dav1d outputs with all_layers = 0 is not stated in the material.",
      "Whether initDecoder() is called again on flush/reset (and so whether the setting is re-applied) is not visible in the provided context.",
      "Details of bugs 424384335 and 427113482 and the test clip are not provided (Test: Manual)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48594": {
     "cve": "CVE-2025-48594",
     "summary": "The CompanionDeviceManager disassociation path now uses PackageManager.getPackagesForUid() to resolve a UID to every package that shares it, and processes revoked associations for each of those packages. Before, it handled only the single name returned by PackageManagerInternal.getNameForUid().",
     "component": "platform/frameworks/base, CompanionDeviceManager service (services/companion; association/DisassociationProcessor on 15/16, CompanionDeviceManagerService on 14)",
     "weakness": "Incorrect UID-to-package resolution (shared UID not handled), leading to incomplete revocation",
     "root_cause": "In services/companion/java/com/android/server/companion/association/DisassociationProcessor.java, a UID-driven callback completes deferred disassociations. It is the block around line 287 that returns early with \"Not interested in this uid\". The callback mapped the UID to one name with mPackageManagerInternal.getNameForUid(uid) and then called mAssociationStore.getRevokedAssociations(userId, packageName) for that name only. A UID can be shared by several packages (android:sharedUserId), so revoked associations belonging to other packages in the shared UID were not found and were never passed to disassociate().",
     "fix_analysis": [
      "DisassociationProcessor.java: adds the import android.content.pm.PackageManager and a new @NonNull field mPackageManager, which the constructor initializes via context.getPackageManager().",
      "DisassociationProcessor.java (UID callback, around line 291): replaces `final String packageName = mPackageManagerInternal.getNameForUid(uid)` with `final String[] packageNames = mPackageManager.getPackagesForUid(uid)`. It returns early if the array is null or empty.",
      "DisassociationProcessor.java (same block): wraps the existing loop in `for (String packageName : packageNames)`. As a result, mAssociationStore.getRevokedAssociations(userId, packageName) is queried, and disassociate(association.getId(), REASON_REVOKED) is called, for every package that shares the UID. The trailing check `mAssociationStore.getRevokedAssociations().isEmpty()` is unchanged."
     ],
     "completeness": [
      "Covers the single UID-driven revocation callback in DisassociationProcessor. Every package in a shared UID is now iterated.",
      "The fix does not change how associations become 'revoked' or how disassociate() works. It only widens the UID-to-package mapping at this call site.",
      "mPackageManagerInternal remains a field of the class. The material does not show whether other getNameForUid-based lookups remain elsewhere in the companion service."
     ],
     "behavior_changes": [
      "For apps that share a UID, the callback now disassociates the revoked associations of every package in that UID, not just one package's.",
      "The lookup now uses the public PackageManager (context.getPackageManager().getPackagesForUid) instead of PackageManagerInternal.getNameForUid. As before, a null or empty result is treated as 'not interested'.",
      "No new error codes or API surface. The change is internal to the companion service."
     ],
     "preconditions": [
      "Two or more packages share a UID via android:sharedUserId, and at least one of them holds a CompanionDeviceManager association that has been revoked (it appears in mAssociationStore.getRevokedAssociations for that user).",
      "The UID-related event that triggers the DisassociationProcessor callback. The trigger condition is not shown in the diff."
     ],
     "per_major": {
      "14": "Adapted fix: platform/frameworks/base 6b8c659a8cb3 (android-security-14.0.0_r24) patches CompanionDeviceManagerService.java instead. It replaces getPackageNameByUid(uid) with mPackageManager.getPackagesForUid(uid) and loops over every package through getPendingRoleHolderRemovalAssociationsForUser(userId), maybeRemoveRoleHolderForAssociation and removeFromPendingRoleHolderRemoval. A new mPackageManager field is initialized from context.getPackageManager(). 8/8 lines present at android-security-14.0.0_r29.",
      "15": "Same fix with one difference: platform/frameworks/base 9c8c99ba63bb (android-security-15.0.0_r12) patches DisassociationProcessor.java identically, except that the call is disassociate(association.getId()) with no reason argument. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: platform/frameworks/base ea2bcc665342 in android-security-16.0.0_r3 (cherry-pick of 5d85254e025c, Change-Id I3cccad4e85e77916cff020eb7087ac2c355e3e23). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Each branch gets a single commit. On branches where the logic is still in CompanionDeviceManagerService.java (the Android 14 layout, with a pending role-holder removal list), use the 14 variant. On branches that have DisassociationProcessor.java, use the 15/16 variant and match the local disassociate() signature (with or without REASON_REVOKED).",
      "In forked companion services, check every UID-to-package mapping used for revocation cleanup. Each one should iterate getPackagesForUid(uid) rather than rely on a single getNameForUid result."
     ],
     "confidence": "high",
     "unknowns": [
      "The diff hunk does not show the name of the callback method that contains the patched block, the condition in the early return above it, or what triggers it.",
      "The material does not say what disassociate() cleans up on 15/16 (for example role holders), so the exact privilege kept by the sibling packages that were not disassociated is not shown.",
      "The material does not show whether other getNameForUid-based lookups exist elsewhere in the companion service."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48596": {
     "cve": "CVE-2025-48596",
     "summary": "Parcel::appendFrom in libbinder now sets mDataSize to mDataPos only when the new position exceeds the current size, instead of always adding len to mDataSize. The old code could inflate the recorded data size beyond the bytes actually written, and possibly beyond what the capacity check ensured.",
     "component": "platform/frameworks/native, libbinder Parcel (libs/binder/Parcel.cpp)",
     "weakness": "Incorrect buffer size accounting (mDataSize not kept consistent with written data)",
     "root_cause": "In libs/binder/Parcel.cpp Parcel::appendFrom, after the memcpy of len bytes at mData + mDataPos, the code did mDataPos += len and then mDataSize += len unconditionally. When the destination parcel's mDataPos is less than mDataSize, the appended bytes overwrite existing data instead of extending it, but mDataSize still grew by len. The capacity check just above (if (mDataPos + len) > mDataCapacity then growData(len)) only ensures room for mDataPos + len, so the inflated mDataSize could exceed that extent. The material does not show which later path relies on the inflated value.",
     "fix_analysis": [
      "libs/binder/Parcel.cpp : Parcel::appendFrom - the line `mDataSize += len;` is replaced with `if (mDataPos > mDataSize) mDataSize = mDataPos;`, so the data size becomes max(old mDataSize, new mDataPos). This keeps mDataSize at the actual extent of written data, within the range the preceding capacity check covered.",
      "No other lines change. The existing source range checks (offset/len against parcel->mDataSize), validateReadData(mDataPos + len) and the object-table handling are untouched. Test reference: binder_parcel_fuzzer."
     ],
     "completeness": [
      "Covers the data-size accounting in Parcel::appendFrom, which is shared by all callers (JNI android_os_Parcel_appendFrom, NDK AParcel_appendFrom, native, per index search).",
      "The fix only touches mDataSize. The object (flat_binder_object offset) handling later in the same function is unchanged, and the material does not show whether that path depended on the old mDataSize value.",
      "No change to other Parcel write paths. The material does not show them, so their behavior cannot be judged here."
     ],
     "behavior_changes": [
      "After appendFrom into a parcel whose data position is before its end, dataSize() now reflects the actual extent of data (unchanged if the append stayed within existing data) instead of growing by len on every call.",
      "No new error codes. The return values of appendFrom are unchanged."
     ],
     "preconditions": [
      "A caller invokes Parcel::appendFrom on a destination Parcel whose mDataPos is less than mDataSize (e.g. after setDataPosition to a non-end position), so the appended range overlaps existing data.",
      "Callers include frameworks/base/core/jni/android_os_Parcel.cpp android_os_Parcel_appendFrom and frameworks/native/libs/binder/ndk/parcel.cpp AParcel_appendFrom, per index search.",
      "The source parcel range (offset, len) must pass the existing checks against parcel->mDataSize, and len must be <= INT32_MAX."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/native aa67ec623b7d in android-security-14.0.0_r24, the same one-line change in Parcel::appendFrom (hunk at line 509). At android-security-14.0.0_r29, 2/3 lines of the patched region are present per the per-major facts.",
      "15": "same fix: platform/frameworks/native de36dac70667 in android-security-15.0.0_r12, the same one-line change (hunk at line 553). At android-security-15.0.0_r17, 2/3 lines of the patched region are present per the per-major facts.",
      "16": "same fix: platform/frameworks/native 6ffdde944d4e in android-security-16.0.0_r3, a one-line change. The patched line is visible at line 561 in the code at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 2/3 lines of the patched region are present per the per-major facts."
     },
     "backport_notes": [
      "Single-line, self-contained change in Parcel::appendFrom with no visible dependencies. Android 13 is listed as affected, but its code and patch are not in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream consequence (which read/write path misuses the inflated mDataSize, and whether it can exceed mDataCapacity in practice) is not shown in the material.",
      "The Android 13 fix commit is not included in the material, though 13 is listed as affected.",
      "Whether the referenced binder_parcel_fuzzer gained a regression case is not shown.",
      "Which line of the patched region is missing at the latest 14, 15 and 17 tags (per-major facts report 2/3) is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48597": {
     "cve": "CVE-2025-48597",
     "summary": "Moves the TRUSTED_OVERLAY grant for a PiP task's surface out of WindowManager's RootWindowContainer.notifyActivityPipModeChanged, which set it on every PiP mode change based on inPip, into WMShell's PipTransition.onFinishResize. The flag is now granted only once Shell handles the enter-PiP transition, and WM keeps only the revoke on exit from pinned mode.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm) and WindowManager Shell PiP (libs/WindowManager/Shell)",
     "weakness": "Improper privilege grant / insufficient state validation: a trust flag (TRUSTED_OVERLAY) was applied to an app task surface based only on its pinned state, without confirming that Shell had handled the enter-PiP transition.",
     "root_cause": "In RootWindowContainer.notifyActivityPipModeChanged, WM applied setTrustedOverlay(task.getSurfaceControl(), inPip) on every PiP mode change. When inPip was true (a non-null ActivityRecord r), the task's surface was marked TRUSTED_OVERLAY right away, whether or not WMShell's PipTransition had received and finished the enter-PiP transition. The commit message says the flag must only be granted after WMShell has successfully handled the entering PiP transition. Before the fix, a task that reached pinned mode without that transition completing in Shell still had its surface marked as a trusted overlay. TRUSTED_OVERLAY windows are exempt from untrusted-touch occlusion protections (general platform knowledge). The bulletin classifies the issue as EoP.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java:onFinishResize: the leash lookup (mPipOrganizer.getSurfaceControl()) and the hasValidLeash check move above the enteringPip block. When enteringPip is true, after setTransitionState(ENTERED_PIP), the code calls tx.setTrustedOverlay(leash, true) on the transaction passed into onFinishResize if the leash is valid, and logs through ProtoLog (WM_SHELL_PICTURE_IN_PICTURE). A comment states that TRUSTED_OVERLAY is granted iff Shell receives the transition and is revoked in RootWindowContainer#notifyActivityPipModeChanged.",
      "services/core/java/com/android/server/wm/RootWindowContainer.java:notifyActivityPipModeChanged: the setTrustedOverlay(task.getSurfaceControl(), inPip) call after setPipVisibilityLw is removed. A revoke-only call, setTrustedOverlay(task.getSurfaceControl(), false).apply(), guarded by task.getSurfaceControl() != null, is added in the else branch (r == null, task leaving pinned mode). WM no longer grants the flag; it only clears it.",
      "Net effect: the grant moves from the WM side (triggered by the PiP mode change notification) to the Shell side (onFinishResize for the entering direction). Revocation stays a blanket WM policy on unpin."
     ],
     "completeness": [
      "The only grant site in the diff is PipTransition.onFinishResize, for isInPipDirection(direction) with a valid leash. WM no longer grants, so a pinned task whose enter transition does not go through this code path never gets TRUSTED_OVERLAY.",
      "Revocation happens only when notifyActivityPipModeChanged is called with r == null (task unpinned). The grant is applied on Shell's tx, while the revoke is a separate WM transaction from mTransactionFactory. The material does not show any ordering guarantee between the two if an exit follows an enter immediately.",
      "The grant is skipped silently when hasValidLeash is false (leash null or invalid). The task then stays in PiP without TRUSTED_OVERLAY.",
      "Other PiP implementations or organizer paths are not touched by this diff. The material does not show whether any of them relied on the removed WM grant."
     ],
     "behavior_changes": [
      "TRUSTED_OVERLAY on a PiP task surface is now set later, in Shell's onFinishResize for the entering direction, instead of when WM is notified that the task is pinned.",
      "If Shell never finishes the enter-PiP transition, or the leash is invalid, the pinned task surface no longer receives TRUSTED_OVERLAY.",
      "New ProtoLog debug line 'Set TRUSTED_OVERLAY for Task#%d' in WM_SHELL_PICTURE_IN_PICTURE.",
      "The unpin revoke is kept: WM still applies setTrustedOverlay(false) when the task leaves pinned mode, now only in the else branch."
     ],
     "preconditions": [
      "An activity's task enters pinned (PiP) windowing mode, so RootWindowContainer.notifyActivityPipModeChanged is called with a non-null ActivityRecord.",
      "Before the fix, the WM grant did not depend on WMShell completing the PipTransition. The material does not show the exact sequence by which a task reaches pinned mode without the Shell transition finishing. The nearby mMaybeAbortPipEnterRunnable / abortPipEnter code shows that aborted-enter states exist."
     ],
     "per_major": {
      "14": "Adapted fix (platform/frameworks/base 3972f55646f4, android-security-14.0.0_r24). Same two hunks. On this branch the PipTransition.onFinishResize condition is (mExitTransition == null || isAnimatingLocally()), without mMoveToBackTransition, and the removed WM grant had no task.getSurfaceControl() null check. Patched region present (8/8 lines) at android-security-14.0.0_r29.",
      "15": "Same logic, different context (platform/frameworks/base cfcef3f38c2f, android-security-15.0.0_r12). The PipTransition finish condition lacks mMoveToBackTransition. Patched region present (8/8 lines) at android-security-15.0.0_r17.",
      "16": "Same fix (platform/frameworks/base 68170bad5225, android-security-16.0.0_r3, cherry-pick of 492b6ca4f31e). Patched region present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, two files that must go together. Removing the WM grant without adding the Shell grant would leave PiP tasks without TRUSTED_OVERLAY, and adding the Shell grant without removing the WM grant leaves the original issue. Where RootWindowContainer lacks the task.getSurfaceControl() null guard (as on 14), the fix adds it in the revoke branch.",
      "The PipTransition hunk moves the existing leash/hasValidLeash locals up. On branches with a different onFinishResize finish condition (for example without mMoveToBackTransition), the change is the same but a context line differs."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the concrete sequence that produced a pinned task without a Shell-handled enter transition (the triggering scenario).",
      "Whether other PiP code paths or implementations depended on the removed WM-side grant.",
      "Whether there is any ordering guarantee between Shell's grant transaction and WM's revoke transaction on rapid enter/exit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48598": {
     "cve": "CVE-2025-48598",
     "summary": "Settings biometric enrollment now only honors Intent.EXTRA_USER_ID when the activity was launched by the Settings package itself, and the FaceEnroll trampoline strips EXTRA_USER_ID before forwarding extras from external callers, so a third-party app can no longer steer face enrollment to another user id.",
     "component": "platform/packages/apps/Settings, biometrics (face enrollment: BiometricEnrollBase, FaceEnroll trampoline, FaceEnrollIntroduction)",
     "weakness": "Missing caller validation on an intent extra: untrusted EXTRA_USER_ID accepted from external packages and passed on through a same-package trampoline activity",
     "root_cause": "BiometricEnrollBase.onCreate read mUserId directly from getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()) with no check on who supplied the intent. The FaceEnroll trampoline (FaceEnroll.kt; the commit message refers to the trampoline added in change Ie469c47005afb941f5646a2f790736362c23c697) copies all extras of its incoming intent into nextIntent via putExtras(intent) and starts the next enrollment activity, so the next activity is started by Settings while carrying extras chosen by an external caller. FaceEnrollIntroduction.launchFaceSettingsActivity also re-read EXTRA_USER_ID from the raw intent and forwarded it to Settings.FaceSettingsInternalActivity. Together these let a caller outside Settings control the user id used in face enrollment flows (bulletin: High, EoP).",
     "fix_analysis": [
      "src/com/android/settings/biometrics/BiometricEnrollBase.java:onCreate: calls getLaunchedFromPackage(); if it is null or not equal to getPackageName(), mUserId is forced to UserHandle.myUserId(). Only when the launcher is the Settings package itself is Intent.EXTRA_USER_ID read from the intent (defaulting to myUserId). This applies to every BiometricEnrollBase subclass.",
      "src/com/android/settings/biometrics/face/FaceEnroll.kt:onCreate: after nextIntent.putExtras(intent), if launchedFromProvider() (default: launchedFromPackage) is not packageName, nextIntent.removeExtra(Intent.EXTRA_USER_ID) is called before startActivityForResult, so the trampoline no longer passes an external EXTRA_USER_ID into a Settings-originated intent. launchedFromProvider is a @VisibleForTesting lambda that tests can replace.",
      "src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java:launchFaceSettingsActivity: replaces the re-read of EXTRA_USER_ID from getIntent() with the already validated mUserId field when building the intent for Settings.FaceSettingsInternalActivity.",
      "Tests: FaceEnrollTest.kt adds testFinishAndLaunchDefaultActivity_fromExternal_dropUser (external launcher 'com.foo.bar': EXTRA_USER_ID absent, only 1 extra) and asserts that the same-package case keeps EXTRA_USER_ID. FaceEnrollIntroductionTest and MultiBiometricEnrollHelperTest (via a new TestFaceEnrollIntroduction subclass) override getLaunchedFromPackage() to return getPackageName() so the existing user-id assertions still pass."
     ],
     "completeness": [
      "Covers: the base-class check in BiometricEnrollBase.onCreate gates EXTRA_USER_ID for all enrollment activities deriving from BiometricEnrollBase, not just the face ones changed here. The FaceEnroll trampoline drop and the FaceEnrollIntroduction field use add defense in depth on the face path.",
      "The trampoline only strips EXTRA_USER_ID. All other extras from an external caller (e.g. EXTRA_KEY_CHALLENGE_TOKEN, EXTRA_KEY_CHALLENGE, EXTRA_KEY_SENSOR_ID, EXTRA_KEY_GK_PW_HANDLE, multi-biometric extras) are still forwarded unchanged via putExtras(intent). This change does not validate them.",
      "FaceEnrollIntroduction.launchFaceSettingsActivity still copies EXTRA_KEY_CHALLENGE_TOKEN, EXTRA_KEY_CHALLENGE and EXTRA_KEY_SENSOR_ID straight from getIntent() into the intent for FaceSettingsInternalActivity. Only the user id was switched to the validated field.",
      "BiometricEnrollBase.onCreate reads mChallenge, mSensorId, mToken and mFromSettingsSummary from the intent without a caller check. This fix only guards mUserId.",
      "The caller check relies on getLaunchedFromPackage() returning the real launcher. A null result is treated as untrusted (falls back to myUserId), which fails safe."
     ],
     "behavior_changes": [
      "Enrollment activities launched by any package other than Settings (or with an unknown launcher) now ignore Intent.EXTRA_USER_ID and operate on UserHandle.myUserId().",
      "FaceEnroll, when launched from outside Settings, forwards the incoming extras minus EXTRA_USER_ID to the next enrollment activity.",
      "FaceEnrollIntroduction passes the validated mUserId rather than the raw intent value to FaceSettingsInternalActivity.",
      "Robolectric tests of BiometricEnrollBase subclasses must now override getLaunchedFromPackage() to return the Settings package name to exercise EXTRA_USER_ID handling."
     ],
     "preconditions": [
      "A caller outside the Settings package can start the FaceEnroll trampoline (or another BiometricEnrollBase subclass) with an intent carrying Intent.EXTRA_USER_ID. Whether and how those activities are exported is not shown in the material.",
      "The trampoline path requires FaceEnroll.onCreate to forward to enrollActivityProvider.next via putExtras(intent), so Settings appears as the launching package of the next activity."
     ],
     "per_major": {
      "14": "not affected per the bulletin (not listed); at android-security-14.0.0_r29, 7/8 lines of the patched region are present. Which line is missing and whether the fix is effectively present is not shown.",
      "15": "not affected per the bulletin (not listed); at android-security-15.0.0_r17, 8/8 lines of the patched region are present",
      "16": "same fix: commit 83447688f8e3 in android-security-16.0.0_r3 (cherry-pick of internal c07d03e5/e0476f87, Change-Id I49f4a980a0c9ed80cc2e370a42bd60425d018a4c); 8/8 lines present at android-security-16.0.0_r8",
      "17": "not affected per the bulletin (not listed); at android-security-17.0.0_r1, 8/8 lines of the patched region are present"
     },
     "backport_notes": [
      "The fix depends on Activity.getLaunchedFromPackage(), a public Activity API since API 34 (general platform knowledge). Trees without FaceEnroll.kt only need the BiometricEnrollBase.onCreate and FaceEnrollIntroduction.launchFaceSettingsActivity hunks, plus the getLaunchedFromPackage() overrides in the robolectric test subclasses.",
      "Apply as a single commit. The FaceEnrollIntroduction hunk relies on mUserId being set by the new BiometricEnrollBase.onCreate logic, so do not take it without the base-class hunk."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether FaceEnroll or the other enrollment activities are exported and which intent filters expose them to third-party apps.",
      "Which other enrollment activities (e.g. fingerprint) derive from BiometricEnrollBase and so get the base-class check.",
      "The concrete privileged action possible with a controlled user id (the commit only says privilege escalation via the trampoline).",
      "Why Android 14/15/17 are not listed while the patched region is present there (fix merged earlier or code differs), and which line of the patched region is missing in 14."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48601": {
     "cve": "CVE-2025-48601",
     "summary": "Stops NotificationManagerService/PreferencesHelper from lazily creating PackagePreferences for a package that is not installed when an API (notably the fromPrivilegedListener methods) supplies a package that resolves to an invalid uid. It does this by unifying the invalid-uid sentinel on Process.INVALID_UID, rejecting it in the creating path and making most getters non-creating.",
     "component": "platform/frameworks/base, services/core notification (NotificationManagerService, PreferencesHelper)",
     "weakness": "Missing validation of an invalid-uid sentinel (inconsistent sentinel constants), which allowed per-package notification preference state to be created for non-existent packages",
     "root_cause": "PreferencesHelper lazily creates a PackagePreferences entry in getOrCreatePackagePreferencesLocked(pkg, uid) for any pkg/uid pair, and many getters (canShowBadge, getBubblePreference, isImportanceLocked, getNotificationChannel via getConversationNotificationChannel, ...) went through this creating path. The helper's own missing-package sentinel was UNKNOWN_UID = UserHandle.USER_NULL. NotificationManagerService used its own INVALID_UID = -1, and per the commit message NMS passed along the result of getUidForPackageAndUser for a missing package. The fromPrivilegedListener methods had no existence check like canNotifyAsPackage, so that value reached PreferencesHelper. Because -1 did not equal UNKNOWN_UID, the old code did not take the mRestoredWithoutUids branch. It stored the new entry in mPackagePreferences keyed on pkg/uid, and createDefaultChannelIfNeededLocked did not short-circuit for it. RoleObserver also added Pair(pkg, -1) to updateDefaultApps when a role holder's uid could not be resolved.",
     "fix_analysis": [
      "NotificationManagerService.java: remove the local `static final int INVALID_UID = -1` and import android.os.Process.INVALID_UID. The literal -1 uses (two targetUid initializations, updateTrampoline's check, getUidForPackage's return) are replaced by INVALID_UID.",
      "NotificationManagerService.java:getUidForPackageAndUser now returns mPackageManager.getPackageUid(...) directly. In the NMS method near line 7093 that looks up a parent channel, the explicit `if (uid == INVALID_UID) return null` before mPreferencesHelper.getNotificationChannel is dropped, because getNotificationChannel itself now returns null for INVALID_UID.",
      "NotificationManagerService.java RoleObserver init and onRoleHoldersChanged: only add Pair(pkg, uid) to the set passed to mPreferencesHelper.updateDefaultApps when getUidForPackage != INVALID_UID, otherwise Slog.e. The updateTrampoline log message is updated.",
      "PreferencesHelper.java: delete UNKNOWN_UID and replace every use with Process.INVALID_UID. This covers readXml uid/delegate parsing, pkgInstalled, createDefaultChannelIfNeededLocked, XML write, dump, the permission-migration loop, the PackagePreferences.uid default and Delegate isAllowed.",
      "PreferencesHelper.java:getOrCreatePackagePreferencesLocked(pkg, uid) (2-arg) now does Objects.requireNonNull(pkg) and Preconditions.checkArgument(uid != INVALID_UID, \"Valid uid required to get settings of %s\", pkg), and is annotated @NonNull. Setters reaching it with an invalid uid now throw IllegalArgumentException. The `if (r == null) throw` blocks after it in the channel-group create, channel create and two channel-update methods are removed.",
      "PreferencesHelper.java: the multi-arg creating overload is renamed getOrCreatePackagePreferencesSupportingInvalidUidLocked and documented as the only path accepting INVALID_UID (storing into mRestoredWithoutUids). In the diff it is called from readXml and from the 2-arg method after the check.",
      "PreferencesHelper.java: these getters switch from getOrCreatePackagePreferencesLocked to the non-creating getPackagePreferencesLocked (@Nullable) and return a DEFAULT_* value or false when no entry exists: getBubblePreference, getAppLockedFields, canShowBadge, isInInvalidMsgState, hasUserDemotedInvalidMsgApp, hasSentInvalidMsg, hasSentValidMsg, didUserEverDemoteInvalidMsgApp, hasSentValidBubble, isImportanceLocked, a group check near line 998 (name not shown), getPackagePriority and getPackageVisibility.",
      "PreferencesHelper.java: getNotificationChannel and getConversationNotificationChannel return null, and getNotificationChannels returns ParceledListSlice.emptyList(), when uid == INVALID_UID. On 16, one bundle-type channel lookup method also gets an INVALID_UID early return. Another bundle-type method that calls addReservedChannelLocked has its creating lookup wrapped in synchronized (mLock). Method names are not shown in the hunks.",
      "PreferencesHelper.java: getNotificationChannelsByConversationId is removed. setAppImportanceLocked is made package-private and test-only on 16.",
      "Tests: NotificationManagerServiceTest adds MISSING_PACKAGE mocks and three privileged-listener tests (channels empty, groups empty, update throws IllegalArgumentException with the new message). PreferencesHelperTest makes restore mocks throw NameNotFoundException instead of returning UNKNOWN_UID, and creates prefs via setShowBadge rather than canShowBadge. RoleObserverTest adds testInit_withInvalidPackages_invalidIgnored."
     ],
     "completeness": [
      "The creating 2-arg getOrCreatePackagePreferencesLocked entry point is covered centrally, so setters that use it reject INVALID_UID whichever NMS API reaches them.",
      "The listed getters are made non-creating, and getNotificationChannel, getConversationNotificationChannel and getNotificationChannels explicitly short-circuit on INVALID_UID.",
      "The readXml path still creates INVALID_UID entries on purpose, only in mRestoredWithoutUids, via the renamed getOrCreatePackagePreferencesSupportingInvalidUidLocked.",
      "The check is specific to the INVALID_UID (-1) sentinel. PreferencesHelper itself does not validate a well-formed uid for a package that does not exist; callers remain responsible for resolving the uid.",
      "PreferencesHelper methods not in the diff (e.g. getNotificationChannelGroups, deleteNotificationChannel, onPackagesChanged) are not shown, so whether they use the non-creating lookup is not visible."
     ],
     "behavior_changes": [
      "Setters that reach getOrCreatePackagePreferencesLocked with uid == Process.INVALID_UID now throw IllegalArgumentException(\"Valid uid required to get settings of <pkg>\"). For example, updateNotificationChannelFromPrivilegedListener for a missing package now throws, per the new test.",
      "getNotificationChannel and getConversationNotificationChannel return null, and getNotificationChannels returns an empty ParceledListSlice, for INVALID_UID.",
      "These getters return DEFAULT_* values without creating a PackagePreferences entry: canShowBadge, getBubblePreference, getPackagePriority, getPackageVisibility, isImportanceLocked and getAppLockedFields. The boolean has*/is* helpers return false in the same case. Getters like getNotificationChannel still create an entry for valid uids.",
      "RoleObserver init and onRoleHoldersChanged skip role-holder packages whose uid cannot be resolved and log Slog.e, instead of passing Pair(pkg, -1) to updateDefaultApps.",
      "Dump output prints \"INVALID_UID\" instead of \"UNKNOWN_UID\". The readXml default uid and the PackagePreferences.uid default change from USER_NULL to Process.INVALID_UID."
     ],
     "preconditions": [
      "Per the new tests' setup, the fromPrivilegedListener NMS methods are reached with a listener token accepted by mListeners.checkServiceTokenLocked and a CompanionDeviceManager association.",
      "The target package passed to a *FromPrivilegedListener method is not installed for the given user, so getUidForPackageAndUser yields INVALID_UID (mocked this way in the tests).",
      "For the RoleObserver path: a role holder whose uid cannot be resolved (system-internal, not caller-triggered)."
     ],
     "per_major": {
      "14": "Adapted fix (30b11a3523d6 in android-security-14.0.0_r24). Same logic, but it locks on mPackagePreferences instead of mLock, and the multi-arg overload has no creationTime parameter. getNotificationChannels has the 3-arg signature, and the diff has no reserved/bundle-channel changes. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Adapted fix (ed9af8192664 in android-security-15.0.0_r12). Uses mLock and creationTime like 16, but has no bundle-type reserved-channel changes and uses the 3-arg getNotificationChannels. setAppImportanceLocked stays public with @VisibleForTesting. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix (93629e575f69 in android-security-16.0.0_r3, cherry-pick of 236cff6879b2). Includes an INVALID_UID early exit in a bundle-type channel lookup and synchronized (mLock) around a bundle-type creating call. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Not listed by the bulletin. The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Core pieces: in both files, import Process.INVALID_UID and delete UNKNOWN_UID and the NMS-local INVALID_UID. Add the Preconditions.checkArgument in the 2-arg getOrCreatePackagePreferencesLocked and rename the multi-arg overload. Switch the getters to getPackagePreferencesLocked with defaults. Add INVALID_UID early exits in getNotificationChannel, getConversationNotificationChannel and getNotificationChannels. Filter INVALID_UID in RoleObserver. Match branch-specific pieces (lock object, creationTime, bundle channels, getNotificationChannels signature) to the local PreferencesHelper; the 14 and 15 commits show the adaptations.",
      "Tests that create prefs via canShowBadge(...) need setShowBadge(pkg, uid, true) instead. mPm.getPackageUidAsUser mocks that returned UNKNOWN_UID should throw NameNotFoundException, as in PreferencesHelperTest."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete privilege-escalation consequence of a stray PackagePreferences entry for a missing package is not shown; only the bulletin's High/EoP rating is known.",
      "Whether the old multi-arg getOrCreate could return null (its tail is not fully shown).",
      "Names of several methods touched in the 16 hunks (the bundle-type channel methods, the group check near line 998).",
      "Whether getNotificationChannelGroups and other PreferencesHelper methods not in the diff already used the non-creating lookup.",
      "Why Android 17 is not listed by the bulletin although the patched region is present at android-security-17.0.0_r1."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48603": {
     "cve": "CVE-2025-48603",
     "summary": "Adds a 200 KB size limit on an input method's metadata XML resource (InputMethod.SERVICE_META_DATA). The limit is checked in InputMethodInfo before the XML is loaded and parsed, so an oversized IME metadata file causes an XmlPullParserException instead of being parsed in the system process.",
     "component": "platform/frameworks/base, input method framework (core/java/android/view/inputmethod/InputMethodInfo.java, used by InputMethodManagerService per the commit message)",
     "weakness": "Missing size bound on attacker-controlled input (uncontrolled resource consumption leading to OutOfMemoryError / DoS)",
     "root_cause": "The InputMethodInfo constructor called si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA) and parsed the resulting XML with no limit on the size of the metadata resource. Per the commit message, a malicious IME could ship an extremely large inputmethod XML, and parsing it while constructing InputMethodInfo could make IMMS (system process) run out of memory.",
     "fix_analysis": [
      "core/java/android/view/inputmethod/InputMethodInfo.java: adds the constant MAX_METADATA_SIZE_BYTES = 200 * 1024 and imports android.os.Bundle and java.io.InputStream.",
      "InputMethodInfo constructor: 'Resources res = pm.getResourcesForApplication(si.applicationInfo)' moves before the metadata load, and the new validateXmlMetaData(si, res) is called before si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA). The rest of the parsing code is unchanged.",
      "New private static validateXmlMetaData(ServiceInfo si, Resources res): reads si.metaData and returns without validating if the Bundle is null or metaData.getInt(InputMethod.SERVICE_META_DATA) is 0. Otherwise it opens the resource with res.openRawResource(resourceId) in a try-with-resources and calls InputStream.skip() in a loop until MAX_METADATA_SIZE_BYTES bytes are skipped or the stream ends. If exactly MAX_METADATA_SIZE_BYTES were skipped and is.read() != -1, it throws XmlPullParserException(\"Input method metadata exceeds maximum allowed limit of 200KB for <package>. InputMethod will not be loaded.\"). The exception is thrown from inside the constructor's existing try block before parsing starts."
     ],
     "completeness": [
      "Covers the InputMethodInfo constructor path that loads InputMethod.SERVICE_META_DATA. The check measures the raw byte size of the resource, not element count or parse-time allocation, so anything up to 200 KB is still parsed as before.",
      "The check is skipped when si.metaData is null or the resource id is 0. Whether this early return could let an oversized file through depends on loadXmlMetaData returning null in those cases, which would hit the existing 'No ... meta-data' exception. The material does not show loadXmlMetaData's behavior.",
      "The bound applies only to the IME metadata resource. This commit does not touch other meta-data XML resources the framework loads from third-party packages, and the material does not show whether those have similar limits."
     ],
     "behavior_changes": [
      "IMEs whose metadata XML resource is larger than 200 KB (200 * 1024 bytes) are now rejected: the constructor throws XmlPullParserException with a message naming the package, and the IME is not loaded.",
      "pm.getResourcesForApplication(si.applicationInfo) is now called before si.loadXmlMetaData, so a NameNotFoundException for the package resources is raised before the metadata load rather than after it.",
      "Every IME with a metadata resource id now gets an extra read of that resource (a skip of up to 200 KB plus one byte) before parsing."
     ],
     "preconditions": [
      "An installed package declares an InputMethod service whose InputMethod.SERVICE_META_DATA meta-data references an XML resource (the commit message describes a malicious IME).",
      "The system (IMMS) constructs InputMethodInfo for that service."
     ],
     "per_major": {
      "14": "same fix: commit ea309e799b21 in android-security-14.0.0_r24, identical logic. Only the surrounding context differs (no Collections import, mIsVrOnly instead of mIsVirtualDeviceOnly). Present 8/8 at android-security-14.0.0_r29.",
      "15": "same fix: commit e7824e85ad70 in android-security-15.0.0_r12, textually identical to the 16 change. Present 8/8 at android-security-15.0.0_r17.",
      "16": "same fix: commit b4c6786312a2 in android-security-16.0.0_r3 (cherry-pick of 5e31d9c067f0, Change-Id Ic90e1bd6615f5954577ed29c3daf7b44596fb137). Present 8/8 at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. The only parts likely to conflict on merge are the import block and the line 'Resources res = pm.getResourcesForApplication(...)', which must move above loadXmlMetaData so res is available to validateXmlMetaData. The bulletin lists 13 as affected, but the material has no 13 commit."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the exact IMMS call site or when construction is triggered (for example on package install or on IME enumeration).",
      "Whether the IME has to be enabled by the user, and which apps are able to declare an IME service, is not shown.",
      "How loadXmlMetaData behaves when metaData is null or the resource id is 0 is not shown.",
      "No commit for Android 13 is provided even though the bulletin lists 13 as affected.",
      "The material does not say whether an XML resource under 200 KB can still cause significant allocation during parsing."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48604": {
     "cve": "CVE-2025-48604",
     "summary": "MmsService.writePduToContentUri now receives the calling user id from DownloadRequest and refuses to write the downloaded MMS PDU when the user id encoded in the caller-supplied contentUri does not match the calling user.",
     "component": "platform/packages/services/Mms, MmsService (MMS download path: DownloadRequest / RequestManager.writePduToContentUri)",
     "weakness": "Missing authorization check on a cross-user content URI (caller-controlled URI user id not validated against the calling user)",
     "root_cause": "In MmsService.downloadMessage the caller supplies a contentUri into which the downloaded MMS PDU is written. DownloadRequest stored that URI and, in transferResponse, called RequestManager.writePduToContentUri(contentUri, response), which only null-checked its arguments before writing the PDU to the URI. Nothing compared the user id embedded in the content URI with the user that made the download request, so a request from one user could target a content URI belonging to a different user. The sibling read path (readPduFromContentUri) already carried a callingUser parameter in the interface, but the write path did not.",
     "fix_analysis": [
      "src/com/android/mms/service/MmsRequest.java:RequestManager.writePduToContentUri: interface signature changed to writePduToContentUri(Uri contentUri, byte[] pdu, int callingUser), mirroring the existing readPduFromContentUri(contentUri, maxSize, callingUser).",
      "src/com/android/mms/service/MmsService.java:writePduToContentUri: after the existing null checks, computes contentUriUserID = ContentProvider.getUserIdFromUri(contentUri, UserHandle.myUserId()) and, if callingUser != contentUriUserID, logs an error ('Uri belongs to a different user...') and returns false before any output stream is opened. Otherwise behaviour is unchanged.",
      "src/com/android/mms/service/DownloadRequest.java: new final field mCallingUser; constructor gains an int callingUser parameter (inserted between downloadedIntent and creator) and stores it; transferResponse now calls mRequestManager.writePduToContentUri(mContentUri, response, mCallingUser).",
      "src/com/android/mms/service/MmsService.java:downloadMessage: the DownloadRequest construction passes the already-available callingUser variable to the new constructor parameter.",
      "tests/robotests/src/com/android/mms/service/MmsRequestRoboTest.java:downloadRequest_noSatellite_downloadSuccessful: updated to the new constructor signature with CALLING_USER (test-only, present on 15/16, not in the 14 commit)."
     ],
     "completeness": [
      "Covers the MmsService-side write of a downloaded PDU: the path DownloadRequest.transferResponse -> MmsService.writePduToContentUri is now gated on the URI's user id matching the calling user.",
      "SendRequest.transferResponse does not use writePduToContentUri (it puts the response into the intent, per index search), so the send path is unaffected by this change; readPduFromContentUri already had a callingUser parameter.",
      "The check is enforced in writePduToContentUri, not at request admission in downloadMessage; a mismatching request is still accepted and enqueued, and the rejection happens when the response is written (the exact ordering relative to the HTTP transfer is not shown in the material).",
      "The carrier-app download path (downloadMessage calls request.tryDownloadingByCarrierApp(...) and returns before addSimRequest) does not go through MmsService.writePduToContentUri in the shown code; whether that path performs an equivalent user check is not shown in the material.",
      "The comparison uses ContentProvider.getUserIdFromUri with UserHandle.myUserId() as the default for URIs with no explicit user id, so URIs without a user component are treated as belonging to the MmsService's own user."
     ],
     "behavior_changes": [
      "writePduToContentUri returns false when the contentUri's user id differs from the calling user; previously the write was attempted.",
      "A new error log line 'Uri belongs to a different user. contentUriUserId is: ... and calling User ID is: ...' is emitted on rejection.",
      "Public constructor signature of DownloadRequest and the RequestManager.writePduToContentUri interface changed (extra int callingUser parameter); any out-of-tree implementer or test must be updated."
     ],
     "preconditions": [
      "A caller invokes the MmsService download API (MmsService.downloadMessage) supplying a contentUri whose embedded user id differs from the calling user's id.",
      "The request must take the in-service path (no CarrierMessagingService package for the subId, MMS enabled and data enabled for the MMS APN) so that DownloadRequest.transferResponse -> MmsService.writePduToContentUri is reached.",
      "Device has more than one user for the mismatch to be meaningful."
     ],
     "per_major": {
      "14": "adapted fix: same logic in android-security-14.0.0_r24 (ed4355456181); DownloadRequest constructor and super() call lack the TelephonyManager parameter present on 15/16, and the robotest file is not touched. 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: android-security-15.0.0_r12 (ad9bf77e4577), identical diff to 16 including the robotest update. 7/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: android-security-16.0.0_r3 (c60a828b9fa1), cherry-pick of 0b8c94aa0a37. 7/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit; the callingUser local in MmsService.downloadMessage already exists and the diff adds no imports (so ContentProvider/UserHandle are presumably already imported), and readPduFromContentUri already carried the user id; only the DownloadRequest constructor arity (with or without TelephonyManager, see the 14 vs 15/16 diffs) and any robotest callers need adjusting.",
      "No 'Fix in fork' comparison was provided in the material, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether the carrier-app download path (tryDownloadingByCarrierApp) forwards the contentUri to CarrierMessagingService.onDownloadMms and whether any user validation exists there; the material does not show it.",
      "How callingUser is derived in downloadMessage (the variable pre-exists and its computation is outside the diff).",
      "The process identity and privileges under which MmsService opens the content URI.",
      "How a false return from writePduToContentUri is reported to the calling app.",
      "Which commit/CVE added the callingUser parameter to readPduFromContentUri; the material only shows it already present.",
      "Why the per-major resolver reports 7/8 patched lines present even on branches that received the commit; not explained by the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48607": {
     "cve": "CVE-2025-48607",
     "summary": "Changes the IAppOpsService.getPackagesForOpsForDevice binder method to return a ParceledListSlice<AppOpsManager.PackageOps> instead of a plain List. A very large result (op entries for hundreds of packages) is then paginated across binder transactions instead of raising TransactionTooLargeException. Per the commit message, that exception crashed SystemUI and stopped the device from booting.",
     "component": "platform/frameworks/base, AppOps (AppOpsService / AppOpsManager / IAppOpsService.aidl)",
     "weakness": "Unbounded binder reply size (missing pagination / resource limit on returned list), leading to TransactionTooLargeException and denial of service",
     "root_cause": "AppOpsService.getPackagesForOpsForDevice built a List<AppOpsManager.PackageOps> and returned it directly over binder. A previous fix (ag/31434165, referenced in the commit message) capped the number of attributed op entries per package, but nothing bounded the number of packages. Per the commit message, an app that created attributed op entries for hundreds of packages made the reply too large. The callers (SystemUI per the commit message) then hit TransactionTooLargeException and crashed, and the device could not boot.",
     "fix_analysis": [
      "core/java/com/android/internal/app/IAppOpsService.aidl: the return type of getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId) changes from List<AppOpsManager.PackageOps> to ParceledListSlice<AppOpsManager.PackageOps>. ParceledListSlice writes part of the list inline and fetches the rest over additional transactions when the receiver unparcels it (general platform knowledge).",
      "services/core/java/com/android/server/appop/AppOpsService.java:getPackagesForOpsForDevice: the signature changes to return ParceledListSlice. The only change in the body is the final return, which becomes `res == null ? null : new ParceledListSlice<>(res)`. The permission / hasAllPackageAccess logic is untouched.",
      "services/core/java/com/android/server/appop/AppOpsService.java:getPackagesForOps(int[] ops): still an @Override returning List. It now calls getPackagesForOpsForDevice(ops, PERSISTENT_DEVICE_ID_DEFAULT), unwraps the result with getList(), and returns null when the slice is null.",
      "core/java/android/app/AppOpsManager.java:getPackagesForOps(String[] ops, String persistentDeviceId): receives the ParceledListSlice from mService.getPackagesForOpsForDevice and returns packageOps.getList(), or Collections.emptyList() when it is null.",
      "core/java/android/app/AppOpsManager.java:getPackagesForOps(int[] ops) (@UnsupportedAppUsage): unwraps the slice the same way and returns null when the service returned null, as before.",
      "The commit also adds the imports android.content.pm.ParceledListSlice and java.io.StringWriter to AppOpsService.java (the diff does not use StringWriter). On Android 16 it also re-indents the noteOperationsInBatch line in the AIDL (whitespace only)."
     ],
     "completeness": [
      "Covers the getPackagesForOpsForDevice binder path and both AppOpsManager wrappers that call it. This is the path the commit message says crashed SystemUI.",
      "Does not reduce the amount of data produced, only paginates its transport. The server still builds the full list, and the client still holds the whole list after unparceling.",
      "AppOpsService.getPackagesForOps(int[]) remains an @Override returning a plain List<PackageOps>. The AIDL excerpt does not show whether it is still exposed on IAppOpsService. The AppOpsManager wrappers no longer use it.",
      "The per-package cap from the earlier fix (ag/31434165) is referenced but not shown."
     ],
     "behavior_changes": [
      "Hidden binder interface change: IAppOpsService.getPackagesForOpsForDevice now returns ParceledListSlice<AppOpsManager.PackageOps>. Out-of-tree code that calls this hidden method directly must be updated. The AppOpsManager wrappers keep their List return types and null/empty semantics.",
      "Large results go over multiple binder transactions instead of one, so a result that used to throw TransactionTooLargeException is now delivered, with extra round trips.",
      "No change to permission checks, filtering, or the content of the returned PackageOps."
     ],
     "preconditions": [
      "An app able to create attributed op entries for a large number of packages (hundreds, per the commit message), so that the combined PackageOps list exceeds the binder transaction size.",
      "A privileged client that calls AppOpsManager.getPackagesForOps. The @RequiresPermission annotation requires GET_APP_OPS_STATS, and the commit message names SystemUI as the client that crashed."
     ],
     "per_major": {
      "14": "Not affected (the bulletin does not list Android 14). The resolver finds 5/8 patched-region lines at android-security-14.0.0_r29. The material does not show whether getPackagesForOpsForDevice exists on this branch.",
      "15": "Same fix: commit 2114590c98f3 in android-security-15.0.0_r12. Content is identical except that the AIDL hunk has no noteOperationsInBatch line, and line offsets differ.",
      "16": "Same fix: commit 03d704069914 in android-security-16.0.0_r3 (cherry-pick of aff2a8f38eb4 / 2c637955ab38). At android-security-16.0.0_r8 the AIDL imports ParceledListSlice.",
      "17": "Not affected (the bulletin does not list Android 17). The resolver finds 5/8 patched-region lines at android-security-17.0.0_r1. The material does not show the fix state on this branch."
     },
     "backport_notes": [
      "The changes to the AIDL return type, the AppOpsService override and both AppOpsManager wrappers depend on each other and must be applied together. The change alters a return type, not method ordering, and getPackagesForOpsForDevice is outside the native-ordered block. Vendor code that calls the hidden method directly must be updated. The java.io.StringWriter import is unused."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether IAppOpsService still declares getPackagesForOps(int[]) as a binder method, and so whether a direct caller of it still gets an unpaginated reply.",
      "The content of the earlier fix ag/31434165 and the exact mechanism the POC used to create entries for hundreds of packages.",
      "Fix state and code presence on Android 14 and 17 (the resolver shows only partial line matches).",
      "How the pattern would be applied on branches without getPackagesForOpsForDevice."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48614": {
     "cve": "CVE-2025-48614",
     "summary": "RecoverySystem.rebootWipeUserData now queries DynamicSystemManager and throws a SecurityException instead of proceeding with the user-data wipe when the device is running in DSU (Dynamic System Update) mode.",
     "component": "platform/frameworks/base, core/java/android/os/RecoverySystem (factory reset / recovery), interacting with android.os.image.DynamicSystemManager",
     "weakness": "Missing state check (wipe allowed in an unsupported system state); bulletin classifies it as DoS",
     "root_cause": "Before sending MASTER_CLEAR_NOTIFICATION and continuing to the wipe, RecoverySystem.rebootWipeUserData only checked the UserManager.DISALLOW_FACTORY_RESET user restriction, and that check is skipped when force=true. It never checked whether the device was booted into a Dynamic System Update image (DynamicSystemManager.isInUse()), so a factory-reset request issued while in DSU mode was carried out. The bulletin rates the outcome as a High-severity DoS. The material does not describe the exact failure that wiping in DSU mode causes.",
     "fix_analysis": [
      "core/java/android/os/RecoverySystem.java:rebootWipeUserData: imports android.os.image.DynamicSystemManager and gets it via context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE), next to the existing UserManager lookup.",
      "core/java/android/os/RecoverySystem.java:rebootWipeUserData: after the existing DISALLOW_FACTORY_RESET check, adds `if (dsuManager.isInUse()) throw new SecurityException(\"Wiping data is not allowed while in DSU mode.\")`. The new check is unconditional. Unlike the user-restriction check, it is not skipped when force is true, so a call into rebootWipeUserData cannot wipe while in DSU mode.",
      "The check runs before the ConditionVariable and the MASTER_CLEAR_NOTIFICATION broadcast, so when it rejects a request the broadcast is never sent."
     ],
     "completeness": [
      "Covers every caller that goes through RecoverySystem.rebootWipeUserData(Context, boolean, String, boolean, boolean[, boolean]), whatever the force flag.",
      "Only this one entry point is changed. The diff does not touch other wipe-related paths visible in the material: RecoverySystem.rebootPromptAndWipeUserData (used by RescueParty) and the wipePartitionToExt4 service call (used by Settings Enable16kPagesPreferenceController). The material does not show whether these go through rebootWipeUserData or need their own guard.",
      "The new code dereferences dsuManager without a null check. If Context.DYNAMIC_SYSTEM_SERVICE were unavailable, the call would throw a NullPointerException before the wipe instead of being handled explicitly.",
      "Single-commit fix. The added lines are identical on 14, 15 and 16; only the rebootWipeUserData signature differs on 14."
     ],
     "behavior_changes": [
      "rebootWipeUserData throws SecurityException(\"Wiping data is not allowed while in DSU mode.\") whenever DynamicSystemManager.isInUse() is true, including for callers that pass force=true.",
      "Callers that previously expected only IOException or the user-restriction SecurityException may now get this new SecurityException. RescueParty catches Throwable around rebootPromptAndWipeUserData, so if that path reaches rebootWipeUserData the exception would be logged there."
     ],
     "preconditions": [
      "Device booted into a Dynamic System Update image, so that DynamicSystemManager.isInUse() returns true.",
      "A factory-reset request reaching RecoverySystem.rebootWipeUserData. The material does not show which callers reach it.",
      "The material does not show which caller or privilege level the bulletin considered the trigger."
     ],
     "per_major": {
      "14": "same fix (only the context differs): the same two hunks were applied in android-security-14.0.0_r24 (ed10409eb6de). The only difference is that rebootWipeUserData's signature lacks the keepMemtagMode parameter. 8/8 lines present at 14.0.0_r29.",
      "15": "same fix: identical diff in android-security-15.0.0_r12 (aa4b01792c25). 8/8 lines present at 15.0.0_r17.",
      "16": "same fix: ec0c32ea736b in android-security-16.0.0_r3. 8/8 lines present at 16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region (8/8 lines) is already present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Self-contained 7-line change. It needs android.os.image.DynamicSystemManager and Context.DYNAMIC_SYSTEM_SERVICE in the target branch: the applied diffs show them on 14 to 16, and the patched region is present on 17. On trees with a different rebootWipeUserData signature, insert the two new blocks around the existing DISALLOW_FACTORY_RESET check, as done on Android 14.",
      "The bulletin lists Android 13 as affected, but the material contains no 13 commit. The same insertion would be needed there."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe the concrete DoS mechanism when wiping in DSU mode (for example, what state is left unbootable).",
      "The material does not show whether rebootPromptAndWipeUserData and wipePartitionToExt4 go through rebootWipeUserData or remain unguarded.",
      "Whether the DevicePolicyManagerService wipe path (seen only through test mocks with a different signature) reaches the patched static method.",
      "Which caller or privilege level is the attack surface the bulletin rated High.",
      "The material does not include the Android 13 fix commit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48618": {
     "cve": "CVE-2025-48618",
     "summary": "CommandParamsFactory.processLaunchBrowser now rejects the SIM Toolkit LAUNCH BROWSER proactive command with ResultCode.LAUNCH_BROWSER_ERROR when KeyguardManager.isDeviceLocked() is true. ResultException's single-argument constructor no longer asserts on LAUNCH_BROWSER_ERROR, so that error can be raised.",
     "component": "platform/frameworks/opt/telephony, CAT/SIM Toolkit (com.android.internal.telephony.cat)",
     "weakness": "Missing lock-state (authorization) check before acting on a modem/SIM proactive command",
     "root_cause": "processLaunchBrowser in CommandParamsFactory.java decoded a LAUNCH BROWSER proactive command (URL TLV, confirmation text, icon) without checking whether the device was locked. Per index search, the Stk app (packages/apps/Stk StkAppService.launchBrowser) turns the resulting BrowserSettings into an ACTION_VIEW intent using the SIM-supplied or default URL. Separately, ResultException(ResultCode) threw an AssertionError for LAUNCH_BROWSER_ERROR (0x26). ResultException(ResultCode, String) delegates to it via this(result), so neither constructor could raise that result code.",
     "fix_analysis": [
      "CommandParamsFactory.java: imports android.app.KeyguardManager and adds a private Context mContext field. The field is set from the existing Context parameter of the private constructor CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh, Context context).",
      "CommandParamsFactory.java:processLaunchBrowser: at method entry, gets KeyguardManager via mContext.getSystemService(KeyguardManager.class). If it is non-null and isDeviceLocked() returns true, the method logs and throws new ResultException(ResultCode.LAUNCH_BROWSER_ERROR, \"The device is locked, unable to process the command.\") before any TLV parsing.",
      "ResultException.java:ResultException(ResultCode): removes case LAUNCH_BROWSER_ERROR (0x26) from the switch that throws AssertionError when no additional info is given, and drops '26' from the comment. This is needed because the two-argument constructor used by the new check calls this(result) first and would otherwise hit the AssertionError."
     ],
     "completeness": [
      "The check sits at the top of processLaunchBrowser, so it gates every LAUNCH BROWSER command decoded through CommandParamsFactory, whatever its TLV content.",
      "The decision is made once, at decode time. This commit adds no lock-state check anywhere else, for example in the Stk app.",
      "If KeyguardManager is null, the check is skipped and processing continues as before.",
      "The ResultException change relaxes the assertion for LAUNCH_BROWSER_ERROR for all callers, not just this one. No other result codes are changed.",
      "No other proactive command handlers in CommandParamsFactory are changed by this commit."
     ],
     "behavior_changes": [
      "When the device is locked, decoding a LAUNCH BROWSER proactive command throws ResultException(LAUNCH_BROWSER_ERROR) instead of producing command parameters.",
      "New debug log line: \"The device is locked, cannot launch the Browser\".",
      "ResultException(ResultCode.LAUNCH_BROWSER_ERROR) without additional info no longer throws AssertionError.",
      "CommandParamsFactory now keeps a Context reference (mContext)."
     ],
     "preconditions": [
      "A LAUNCH BROWSER proactive command must arrive from the SIM/modem and be decoded by CommandParamsFactory.processLaunchBrowser (RilMessageDecoder path).",
      "Before the fix, the command was processed whatever the lock state. Per index search, the consumer is StkAppService.launchBrowser in packages/apps/Stk, which issues an ACTION_VIEW intent."
     ],
     "per_major": {
      "14": "same fix (different hunk context; the file lacks the UUID/NPE constants present in 15/16): commit cc7d88a59cbd in android-security-14.0.0_r24; 8/8 lines present at android-security-14.0.0_r29",
      "15": "same fix: commit 8e7b0146d56b in android-security-15.0.0_r12, diff identical to the 16 commit; 8/8 lines present at android-security-15.0.0_r17",
      "16": "same fix: commit fee68bcdcf02 in android-security-16.0.0_r3; 8/8 lines present at android-security-16.0.0_r8",
      "17": "not affected (not listed by the bulletin); patched region fully present (8/8 lines) at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Apply both files together. Without the ResultException.java change, the new throw of ResultException(LAUNCH_BROWSER_ERROR, String) triggers the AssertionError in the single-argument constructor it delegates to.",
      "In all shown branches the private CommandParamsFactory constructor already receives a Context (used for mNoAlphaUsrCnf), so only the new mContext field and its assignment are needed. A fork with a different constructor signature must supply a Context some other way."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how the thrown ResultException is turned into the terminal response, whether the Stk app receives anything in that case, or whether other ResultException constructors (e.g. with int additionalInfo) exist.",
      "The material does not show whether StkAppService or later code checks lock state on its own.",
      "The material does not show how isDeviceLocked() behaves on devices without a secure lock screen, i.e. whether the guard applies there.",
      "The material does not show the original report or what exactly was reachable from the lock screen beyond launching the browser via ACTION_VIEW."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48620": {
     "cve": "CVE-2025-48620",
     "summary": "Removes an early `return` in VoiceInteractionManagerService's package-change handler so that, when a voice interaction service is set, the handler still falls through to the logic that resets the current voice recognition service when its package is uninstalled.",
     "component": "platform/frameworks/base, services/voiceinteraction (VoiceInteractionManagerService, system_server)",
     "weakness": "Control-flow logic error: early return skips cleanup of a stale component reference (voice recognition service setting) after its package is removed",
     "root_cause": "In VoiceInteractionManagerService.java, the package-change handler (onSomePackagesChanged per the commit message) handles the case where curInteractor != null (with a path that calls switchImplementationIfNeededLocked(true)) and then unconditionally executed `return;`. Because of this, whenever a voice interaction service was configured, the remaining code in the handler never ran. That code includes the curAssistant check and, per the commit message, the logic that resets the current voice recognizer (the voice_recognition_service secure setting) when its package is uninstalled. The setting could therefore keep pointing at a component whose package had been uninstalled.",
     "fix_analysis": [
      "services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java (package-change handler, onSomePackagesChanged per commit message): the `return;` at the end of the `curInteractor != null` block is removed, so execution continues to the rest of the handler, including the voice recognition service reset logic.",
      "Same file/function: the following check is changed from `if (curAssistant != null)` to `if (curInteractor == null && curAssistant != null)`, with a comment saying the interactor branch already did the needed checks. When an interactor is set, the assistant is still not re-evaluated (as before), but execution can now reach the later code."
     ],
     "completeness": [
      "The fix only changes control flow in the package-change handler. The recognizer reset logic itself (not shown in the diff) is unchanged and is now reachable when an interactor is set.",
      "The handleUserStop path shown in the context only handles the interactor (switchImplementationIfNeededLocked) and is not touched by this fix. The material does not show whether it needs similar recognizer handling.",
      "The diff is a small control-flow change with no new validation. It ensures the setting is reset when the package is removed, but does not change how a stale voice_recognition_service value is treated when it is used."
     ],
     "behavior_changes": [
      "When a voice interaction service is configured and the package of the current voice recognition service is uninstalled, the voice_recognition_service secure setting is now reset instead of keeping the removed component. The commit's test checks this with `adb shell settings get secure voice_recognition_service`.",
      "With an interactor set, the curAssistant disappearance check is still skipped (explicit `curInteractor == null` guard), so assistant handling is unchanged.",
      "Any other code after the former `return;` in the handler now also runs when curInteractor != null."
     ],
     "preconditions": [
      "A voice interaction service (curInteractor) is set for the user, so the handler enters the `curInteractor != null` block.",
      "A package change event reaches VoiceInteractionManagerService's package-change handler (onSomePackagesChanged), specifically the uninstall of the package hosting the current voice recognition service.",
      "The code runs in system_server. The material does not show how the EoP consequence is reached."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 66a76238a130 in android-security-14.0.0_r24, identical diff at a different line offset (@@ -2457). At android-security-14.0.0_r29, 4/5 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 0925826a6ad8 in android-security-15.0.0_r12, identical diff (@@ -2669). At android-security-15.0.0_r17, 4/5 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/base db86972777c8 in android-security-16.0.0_r3, identical diff (@@ -2676). At android-security-16.0.0_r8, 4/5 lines of the patched region are present.",
      "17": "not affected per the bulletin (not listed). At android-security-17.0.0_r1, 4/5 lines of the patched region are present. The material does not confirm that the fix itself is in that branch."
     },
     "backport_notes": [
      "Single commit with a three-line change in one function. The diff is identical on 14, 15 and 16 apart from line offsets. Forks with the same `return;` / `if (curAssistant != null)` structure can take it directly. Check that the fork's handler still has the recognizer reset logic after the curAssistant block, because otherwise removing the return has no effect."
     ],
     "confidence": "high",
     "unknowns": [
      "The diff does not show the recognizer reset code after the modified block, or the structure of onSomePackagesChanged beyond the hunk.",
      "The material does not show how a stale voice_recognition_service setting leads to the EoP that the bulletin rates High (for example, which component reads the setting).",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 commit.",
      "The per-major facts report only 4/5 lines of the patched region at the latest tags. The material does not say which line differs."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48621": {
     "cve": "CVE-2025-48621",
     "summary": "Two-part tapjacking mitigation: InputDispatcher stops delivering motion events to a window whose touchOcclusionMode is USE_OPACITY and whose alpha is below 0.5, and the WM Shell DefaultTransitionHandler halves the maximum default transition animation duration from 3000 ms to 1500 ms.",
     "component": "platform/frameworks/native (inputflinger InputDispatcher) and platform/frameworks/base (libs/WindowManager/Shell transitions)",
     "weakness": "Missing input-eligibility check on the touched window's own opacity (tapjacking / UI redress during animation), plus an overly long animation window",
     "root_cause": "In InputDispatcher::DispatcherTouchState::canWindowReceiveMotion (services/inputflinger/dispatcher/InputDispatcher.cpp), the target window's own alpha was never considered. A window declared opaque for occlusion purposes (TouchOcclusionMode::USE_OPACITY) kept receiving touches even when its alpha was low, which the commit message says is likely a fade-out animation or another visual change. Separately, DefaultTransitionHandler.MAX_ANIMATION_DURATION was 3000 ms, which the commit message calls way too long, especially for tapjacking during an animation.",
     "fix_analysis": [
      "frameworks/native services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::DispatcherTouchState::canWindowReceiveMotion adds `if (info.touchOcclusionMode == TouchOcclusionMode::USE_OPACITY && info.alpha < 0.5f)` as its first check. When the condition holds it logs at INFO and returns false. The check comes before verifyTargetedInjection, PAUSE_DISPATCHING, NO_INPUT_CHANNEL and the connection lookup.",
      "frameworks/native services/inputflinger/tests/InputDispatcher_test.cpp : InputDispatcherUntrustedTouchesTest gains mBottomWindow (BOTTOM_APP_UID) and four tests. UseOpacity_BlocksWhenAlphaIsLow: alpha 0.4, neither the touched window nor the bottom window gets events. UseOpacity_AllowsWhenAlphaIsSufficient: alpha 0.6, the touch is delivered. UseOpacity_AllowsWhenAlphaIsAtThreshold: alpha 0.5, the touch is delivered; the strict `<` allows 0.5 even though the test comment says otherwise. OtherOcclusionMode_AllowsWhenAlphaIsLow: BLOCK_UNTRUSTED with alpha 0.4, the touch is delivered.",
      "frameworks/base libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java : the constant MAX_ANIMATION_DURATION changes from 3000 to 1500."
     ],
     "completeness": [
      "The opacity gate applies only when touchOcclusionMode == USE_OPACITY. It does not apply in other modes: the OtherOcclusionMode_AllowsWhenAlphaIsLow test delivers the touch to a BLOCK_UNTRUSTED window with alpha 0.4.",
      "The 0.5 threshold is hard-coded. It does not use the configurable maximum obscuring opacity set with setMaximumObscuringOpacityForTouch. Alpha of exactly 0.5 is allowed (strict less-than).",
      "When the check fires, the window underneath does not receive the event either (mBottomWindow->assertNoEvents in the blocking test).",
      "The check lives in DispatcherTouchState::canWindowReceiveMotion. The material does not show whether every motion-targeting path in the dispatcher goes through this function.",
      "The duration change only touches DefaultTransitionHandler.MAX_ANIMATION_DURATION. SIZE_CHANGE_ANIMATION_DURATION (400) and other transition handlers are unchanged. A lower cap shortens the exposure window but does not remove the condition."
     ],
     "behavior_changes": [
      "Motion events are no longer delivered to a window with USE_OPACITY and alpha < 0.5. Per the new test, windows beneath it do not receive the event either. A new LOG(INFO) line is emitted: 'Not sending motion to <name>, window opacity=<alpha> is below the threshold'.",
      "The maximum duration for default shell transition animations drops from 3000 ms to 1500 ms (MAX_ANIMATION_DURATION). How the constant is applied is not shown in the material."
     ],
     "preconditions": [
      "A window whose WindowInfo has touchOcclusionMode == TouchOcclusionMode::USE_OPACITY and alpha < 0.5 is in the dispatcher's window list and would otherwise be chosen as a touch target (canWindowReceiveMotion in InputDispatcher.cpp, per index search).",
      "For the shell change: a transition animated by DefaultTransitionHandler."
     ],
     "per_major": {
      "14": "frameworks/base fix applied as the same one-line change (848e575f07ff, android-security-14.0.0_r24, RESTRICT AUTOMERGE backport). This file has no SIZE_CHANGE_ANIMATION_DURATION, but the changed line is identical. The material shows no frameworks/native commit for 14, so its status there is unknown. At android-security-14.0.0_r29, 4/5 lines of the patched region are present.",
      "15": "frameworks/base fix applied as the same one-line change (06e697583c87, android-security-15.0.0_r12, RESTRICT AUTOMERGE backport). The material shows no frameworks/native commit for 15, so its status there is unknown. At android-security-15.0.0_r17, 4/5 lines of the patched region are present.",
      "16": "Both commits applied in android-security-16.0.0_r3 (RESTRICT AUTOMERGE backports): frameworks/base 6d1697c96c5c and frameworks/native cc34c7b416b9. At android-security-16.0.0_r8 both changes are visible in the code context (MAX_ANIMATION_DURATION = 1500; the USE_OPACITY alpha check is present).",
      "17": "Not affected per the bulletin (Android 17 is not listed). At android-security-17.0.0_r1, 4/5 lines of the patched region are present."
     },
     "backport_notes": [
      "The fix is two independent commits in two projects (frameworks/native InputDispatcher and frameworks/base WM Shell); take both. The test fixture change (mBottomWindow / BOTTOM_APP_UID) must go in with the new tests.",
      "On branches where the window eligibility logic is not a DispatcherTouchState method, the check must be adapted to the equivalent function. The material only shows the Android 16 layout."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether the frameworks/native InputDispatcher commit was applied to Android 13, 14 and 15. Only the frameworks/base commit is shown for 14 and 15, and no commit is shown for 13.",
      "How MAX_ANIMATION_DURATION is used inside DefaultTransitionHandler (clamping vs. other handling).",
      "Which window types or transitions produce USE_OPACITY with low alpha in practice. The bulletin and the commits do not describe the concrete trigger.",
      "Why 0.5 was chosen as the threshold and whether it is meant to be configurable."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48622": {
     "cve": "CVE-2025-48622",
     "summary": "Replaces the Android copy of the Adobe DNG SDK in external/dng_sdk (sources still carrying dng_sdk_1_4 $Id tags) with a \"crude\" DNG SDK 1.7.1 import that has JXL and XMP support compiled out. It also updates the two in-tree consumers, Skia's SkRawCodec and the CTS dng-validate JNI, to the new 1.7.1 API.",
     "component": "platform/external/dng_sdk (libdng_sdk), with consumer updates in platform/external/skia (src/codec/SkRawCodec.cpp) and platform/cts (tests/camera/libctscamera2jni/dng-validate-jni.cpp)",
     "weakness": "unknown (the bulletin type is ID; the fix is a wholesale library upgrade and the material does not isolate the defective routine)",
     "root_cause": "The material does not point to a single defective function. The vulnerable code is the old DNG SDK in external/dng_sdk (headers still carry \"dng_sdk_1_4\" $Id tags). libdng_sdk is used by Skia's SkRawCodec to decode DNG/RAW images. Google fixed bug 412662901 by importing DNG SDK 1.7.1 wholesale (159 files, about 61k insertions and 16k deletions, kDNGSDK_VersionString \"1.7.1\", build 2502) instead of writing a targeted patch. As a result, the diff does not show the specific missing check that causes the information disclosure. The Skia commit also notes that leaving dng_stream's offsetInOriginalFile at its default invalid value (-1) sends dng_negative down a crashing unsigned-overflow path, so SkDngStream now passes 0.",
     "fix_analysis": [
      "platform/external/dng_sdk de700ad461e3 (Android 16), 56e64aaa5b59 (14) and c210ca6441c5 (15), 'Crude DNG SDK 1.7.1 upgrade': the source/*.cpp and *.h files are replaced with Adobe DNG SDK 1.7.1 (source/dng_flags.h defines version 1.7.1). New files added to Android.bp srcs: source/dng_big_table.cpp, dng_bmff.cpp, dng_local_string.cpp and dng_lossless_jpeg_shared.cpp. Other new files: dng_jxl.h/.cpp, dng_update_meta, dng_semantic_mask.h, dng_simd_type.h and dng_deprecated_flags.h. The lossless JPEG body now lives in dng_lossless_jpeg_shared.cpp, which dng_lossless_jpeg.cpp #includes. Hardening visible in the new sources: dng_1d_table gets a runtime fTableCount with DNG_REQUIRE power-of-two and minimum-size checks, plus ConvertDoubleToFloat; dng_bad_pixels.cpp computes expectedSize with SafeUint32Add/SafeUint32Mult and uses snprintf instead of sprintf; dng_area_task::FindTileSize clamps tile sizes and counts to avoid division by zero; dng_abort_sniffer gains more priority levels and SupportsPriorityWait.",
      "platform/external/dng_sdk Android.bp: cflags keep -DqDNGUseXMP=0 and add -DqDNGUsingSanitizer=1 and -Wno-implicit-fallthrough. -DqAndroid=1 moves under target.android, and -DqLinux=1 is added for host_linux. libdng_sdk drops vendor_available and sets sdk_version: \"current\" and stl: \"c++_shared\". The windows target is disabled, cc_binary_host dng_validate_host is removed, and the dng_validate binary now links libdng_sdk through static_libs. source/jxl/color_encoding.h is a stub (struct JxlColorEncoding {}) so the code builds without libjxl. Per the commit message, functions in dng_jxl.h were marked [[maybe_unused]] and a uint64 format specifier in dng_jpeg_image.cpp was corrected.",
      "platform/external/skia 40c3f0a50fb9, src/codec/SkRawCodec.cpp: SkDngHost::PerformAreaTask now overrides the 1.7.1 signature, which adds a dng_area_task_progress* parameter. It calls task.Start(numTasks, area, tileSize, &Allocator(), Sniffer()) with the new area argument and task.ProcessOnThread(..., this->Sniffer(), nullptr) with the new progress argument. The SkDngStream constructor calls dng_stream((dng_abort_sniffer*) nullptr, dng_stream::kDefaultBufferSize, 0), so offsetInOriginalFile is 0 instead of the default -1. Android.bp moves libdng_sdk from the generic skia_deps shared_libs to target.android shared_libs, and to static_libs for host_linux and darwin.",
      "platform/cts 1bcf948f5e55, dng-validate-jni.cpp, dng_validate(): RawJPEGImage() becomes RawLossyCompressedImage() in both preview checks. fImage.Reset(...) becomes SetImage(host, ...). WriteDNG uses dngVersion_SaveDefault. The XMP cleanup also removes XMP_NS_CRD and XMP_NS_CRLCP. WriteTIFF now takes &negative->Metadata() and &render.FinalSpace(profilePtr), where profilePtr comes from GetProfileByID for non-monochrome negatives. In validateDngNative, the code now closes pipeFds[0] instead of pipeFds[1], and the error check changes from lineCount < 3 to lineCount > 0 ('Unexpected stderr output!')."
     ],
     "completeness": [
      "The fix replaces the whole library rather than patching one spot, so every DNG parsing path in libdng_sdk moves to the 1.7.1 code. Whether the undisclosed defect is actually covered depends on upstream 1.7.1 having fixed it, which the material cannot confirm.",
      "JXL and XMP support are deliberately compiled out (-DqDNGUseXMP=0, a stub jxl/color_encoding.h, no libjxl dependency). dng_jxl.cpp is not among the srcs added in the Android.bp diff.",
      "Both consumers the material shows (Skia SkRawCodec and the CTS dng-validate JNI) were updated. libdng_sdk lost vendor_available, so any out-of-tree consumer of the old API is not covered by these commits.",
      "The SkDngStream offsetInOriginalFile change works around a crash path in dng_negative from the caller side. The material shows no corresponding change inside dng_sdk."
     ],
     "behavior_changes": [
      "libdng_sdk API/ABI changes: dng_area_task::Start takes a dstArea argument. ProcessOnThread, Perform and dng_host::PerformAreaTask take a dng_area_task_progress*. dng_1d_table has a sized constructor. dng_priority gains background and very_high levels, and dng_set_minimum_priority takes a name. RawJPEGImage is replaced by RawLossyCompressedImage, and WriteTIFF takes dng_metadata.",
      "Build changes: libdng_sdk is no longer vendor_available and is built with sdk_version current and the c++_shared STL. The windows variant is disabled, dng_validate_host is removed, and dng_validate links libdng_sdk statically. Skia links libdng_sdk as a shared library on android and statically on host_linux and darwin.",
      "The new sources add DNG_REQUIRE and safe-arithmetic checks that can throw exceptions. The material does not show which file inputs are newly rejected.",
      "CTS DngCreatorTest.validateDngNative now fails if any stderr output is produced; before, it failed when there were fewer than 3 lines. WriteDNG now uses dngVersion_SaveDefault."
     ],
     "preconditions": [
      "A component decodes an attacker-supplied DNG/RAW image through Skia's SkRawCodec, the in-tree production consumer of libdng_sdk. The dng_sdk commit was tested with android.graphics.cts.BitmapFactoryTest, which points to the BitmapFactory decode path.",
      "The CTS dng-validate JNI path runs only under CTS and is not a production entry point.",
      "The material does not show the malformed-DNG condition that triggers the disclosure."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 56e64aaa5b59 'Crude DNG SDK 1.7.1 upgrade' in android-security-14.0.0_r24. The visible diff matches the 16 commit. 8/8 lines of the patched region are present at android-security-14.0.0_r29. Only the dng_sdk commit is shown for 14.",
      "15": "same fix: platform/external/dng_sdk c210ca6441c5 in android-security-15.0.0_r12. The visible diff matches the 16 commit, but the truncated remainder differs slightly in length. 8/8 lines are present at android-security-15.0.0_r17. Only the dng_sdk commit is shown for 15.",
      "16": "same fix: platform/external/dng_sdk de700ad461e3 (cherry-pick of ag/34978264), platform/external/skia 40c3f0a50fb9 (changes from ag/34978263) and platform/cts 1bcf948f5e55, all in android-security-16.0.0_r3. 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Land the three commits together, dng_sdk first. The skia changes (PerformAreaTask, Start and ProcessOnThread signatures, SkDngStream offset) and the cts API renames depend on the 1.7.1 API. Also carry the skia Android.bp change that moves libdng_sdk to target.android shared_libs and to host static_libs.",
      "Any other module that links libdng_sdk must be rebuilt against the 1.7.1 headers. Dropping vendor_available makes the library unavailable to vendor-partition modules (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which DNG SDK 1.4 routine contains the information-disclosure bug, and what input triggers it.",
      "Whether the SkDngStream unsigned-overflow crash path is related to this CVE.",
      "Why the dng_sdk commit's Test line references 'atest CVE_2025_22423'.",
      "Whether the skia and cts companion commits were also applied on the 14 and 15 security branches.",
      "Whether the 14 and 15 dng_sdk commits are byte-identical to the 16 commit beyond the visible portion."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48626": {
     "cve": "CVE-2025-48626",
     "summary": "Launcher3's OverviewCommandHelper HOME command no longer calls startActivity(homeIntent) directly; it asks SystemUI (new ISystemUiProxy.onKeyEvent) to inject a KEYCODE_HOME down/up key event via InputManagerGlobal, and onBackEvent/sendEvent now carry an explicit displayId.",
     "component": "platform/packages/apps/Launcher3 (quickstep OverviewCommandHelper / SystemUiProxy / Taskbar) and platform/frameworks/base (SystemUI LauncherProxyService, ISystemUiProxy.aidl)",
     "weakness": "Bypass of platform HOME-key handling: Launcher launched the home intent directly with startActivity instead of routing the action through the system's key event path (the exact policy check that was bypassed is not shown in the material)",
     "root_cause": "In quickstep/src/com/android/quickstep/OverviewCommandHelper.kt (OverviewCommandHelper.java on 14/15), the HOME branch of the command-type handling called `touchInteractionService.startActivity(overviewComponentObserver.homeIntent)` from the Launcher process. This started the home activity directly instead of delivering a KEYCODE_HOME key event that the system handles. The commit message (\"Send KEYCODE_HOME for home button instead of startActivity directly\") and the diff identify this direct launch as what changed. Before the fix, ISystemUiProxy exposed only onBackEvent for key injection. sendEvent in LauncherProxyService used SystemUI's own context display (mContext.getDisplay().getDisplayId()) rather than a caller-supplied display.",
     "fix_analysis": [
      "frameworks/base, packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl: adds `oneway void onKeyEvent(int keycode, int displayId) = 60` (Next id bumped to 61). Changes `onBackEvent(in KeyEvent keyEvent)` to `onBackEvent(in KeyEvent keyEvent, int displayId)` (same id 44).",
      "frameworks/base, packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java: new `onKeyEvent(int keycode, int displayId)` wraps the work in `verifyCallerAndClearCallingIdentityPostMain(...)` (body not shown; by its name it verifies the caller and posts to main). Inside, it calls `sendEvent(ACTION_DOWN, keycode, displayId)` then `sendEvent(ACTION_UP, keycode, displayId)`. `sendEvent` now takes displayId and does `ev.setDisplayId(displayId)` instead of `mContext.getDisplay().getDisplayId()`. It then calls `InputManagerGlobal.getInstance().injectInputEvent(ev, INJECT_INPUT_EVENT_MODE_ASYNC)` with FLAG_FROM_SYSTEM | FLAG_VIRTUAL_HARD_KEY and SOURCE_KEYBOARD. The non-predictive branch of `onBackEvent` now just calls `onKeyEvent(KeyEvent.KEYCODE_BACK, displayId)`.",
      "Launcher3, quickstep/src/com/android/quickstep/SystemUiProxy.kt: adds `fun onKeyEvent(keycode: Int, displayId: Int)`, which forwards to `systemUiProxy?.onKeyEvent(keycode, displayId)` under executeWithErrorLog. `onBackEvent` gains the displayId parameter.",
      "Launcher3, quickstep/src/com/android/quickstep/OverviewCommandHelper.kt: the constructor takes a new `systemUiProxy: SystemUiProxy`. The HOME branch replaces `touchInteractionService.startActivity(overviewComponentObserver.homeIntent)` with `systemUiProxy.onKeyEvent(KeyEvent.KEYCODE_HOME, command.displayId)`, and the comment justifying a main-thread startActivity is removed.",
      "Launcher3, quickstep/src/com/android/quickstep/TouchInteractionService.java onUserUnlocked: obtains `SystemUiProxy.INSTANCE.get(this)` once and passes it as the new last constructor argument of OverviewCommandHelper.",
      "Launcher3, quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java: the constructor now takes `int displayId` instead of `Context`. The back path calls `mSystemUiProxy.onBackEvent(keyEvent, mDisplayId)` and the a11y button uses `mDisplayId`. TaskbarManager.java passes `displayId` at both constructor call sites. TaskbarScrimViewController.onClick passes `mActivity.getDisplayId()` to onBackEvent. Tests (TaskbarNavButtonControllerTest, TaskbarScrimViewControllerTest, OverviewCommandHelperTest) are updated for the new signatures."
     ],
     "completeness": [
      "Covers only the HOME command path in OverviewCommandHelper, the single startActivity(homeIntent) call replaced in the diff. The taskbar home button path (TaskbarNavButtonController.navigateHome -> mCallbacks.onNavigateHome(mDisplayId), per index search) is not changed by this commit, and the material does not show where that callback ends up.",
      "The commit message names flag com.android.launcher3.home_button_uses_keycode_home, but the diff contains no flag check, so the new behaviour is unconditional in the patched code.",
      "On the SystemUI side, onKeyEvent accepts any keycode and any displayId from a caller that passes verifyCallerAndClearCallingIdentityPostMain. The diff adds no keycode allow-list.",
      "With the displayId plumbing, back and home key injection on 16 target the caller-supplied display rather than SystemUI's context display. The 14/15 adapted Launcher3 fixes call onKeyEvent(keycode) with no displayId."
     ],
     "behavior_changes": [
      "Home from the OverviewCommandHelper HOME command is now an injected KEYCODE_HOME ACTION_DOWN/ACTION_UP pair (asynchronous injection) instead of a direct activity start. If the SystemUI proxy is null the call does nothing. A RemoteException is only logged.",
      "The ISystemUiProxy.onBackEvent signature changed (transaction 44 now carries displayId) and transaction 60 was added. SystemUI and Launcher must use matching interface definitions.",
      "Injected back/home key events carry the caller-supplied displayId instead of SystemUI's own display.",
      "The TaskbarNavButtonController public constructor changed from (Context, ...) to (int displayId, ...). The OverviewCommandHelper constructor gained a SystemUiProxy parameter."
     ],
     "preconditions": [
      "A HOME command must reach OverviewCommandHelper in the Launcher3 quickstep TouchInteractionService. The helper is created in TouchInteractionService.onUserUnlocked. How a HOME command is enqueued is not shown in the material.",
      "On the fixed path, the Launcher must be bound to SystemUI's ISystemUiProxy and pass verifyCallerAndClearCallingIdentityPostMain in LauncherProxyService for the key injection to happen."
     ],
     "per_major": {
      "14": "adapted fix: Launcher3 9f5688403e25 in android-security-14.0.0_r24. In Java OverviewCommandHelper.java, TYPE_HOME now calls mSystemUiProxy.onKeyEvent(KeyEvent.KEYCODE_HOME) with no displayId. SystemUiProxy.java gets @Override onKeyEvent(int keycode). TouchInteractionService passes SystemUiProxy.INSTANCE.get(this). 8/8 patched lines are present at android-security-14.0.0_r29. The frameworks/base companion for 14 is not in the material.",
      "15": "adapted fix: Launcher3 db8146473259 in android-security-15.0.0_r12, same shape as 14 (onKeyEvent(int keycode) without displayId, in the TYPE_HOME case of the switch). 8/8 lines are present at android-security-15.0.0_r17. The frameworks/base companion is not in the material.",
      "16": "same fix: frameworks/base 9fb37191609f and Launcher3 7628af9bf77f in android-security-16.0.0_r3 (Kotlin OverviewCommandHelper, displayId plumbing). At android-security-16.0.0_r8, 5/8 patched-region lines match. The r8 code context shows the fix content in LauncherProxyService, ISystemUiProxy.aidl, SystemUiProxy.kt, TaskbarScrimViewController and TouchInteractionService. The r8 OverviewCommandHelper HOME branch itself is not shown.",
      "17": "not affected: not listed by the bulletin. 4/8 lines of the patched region are present at android-security-17.0.0_r1; the actual 17 code is not shown."
     },
     "backport_notes": [
      "The frameworks/base commit (ISystemUiProxy.aidl + LauncherProxyService) and the Launcher3 commit must ship together. The Launcher3 change calls the new onKeyEvent transaction and the changed onBackEvent(KeyEvent, int) signature. The 14/15 adapted Launcher3 fixes instead expect a one-argument onKeyEvent(int keycode) on the SystemUI side. On those branches SystemUiProxy implements ISystemUiProxy directly with @Override, so the AIDL must declare that method.",
      "In trees where TaskbarNavButtonController construction lives in TaskbarManagerImpl.createNavButtonController (seen in the android-16.0.0_r4 index), apply the displayId constructor change there."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which system-side check or policy the direct startActivity(homeIntent) bypassed (the concrete EoP consequence) is not shown in the material.",
      "How a HOME command is delivered to OverviewCommandHelper (the entry point and any caller checks) is not in the diff or context.",
      "The behaviour of verifyCallerAndClearCallingIdentityPostMain is not shown.",
      "The frameworks/base companion commits for Android 14 and 15 (adding onKeyEvent(int) to ISystemUiProxy on the SystemUI side) are not in the material.",
      "Whether the flag com.android.launcher3.home_button_uses_keycode_home gates anything elsewhere; the shown diff has no flag check.",
      "The exact state of the patched region at android-security-16.0.0_r8 (OverviewCommandHelper HOME branch) and at android-security-17.0.0_r1; only line-match counts are given."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48628": {
     "cve": "CVE-2025-48628",
     "summary": "PrintManagerService.validateIconUserBoundary now derives the icon URI's user id with ContentProvider.getUserIdFromAuthority(authority, resolvedCallingId) and always enforces the current-user-group check, instead of trusting Uri.getEncodedUserInfo and skipping the check when it returned null.",
     "component": "platform/frameworks/base, services/print (PrintManagerService, system_server)",
     "weakness": "Improper validation of the user id embedded in a content URI, which bypasses a cross-user boundary check (incorrect authorization check)",
     "root_cause": "In services/print/java/com/android/server/print/PrintManagerService.java, validateIconUserBoundary(Icon) worked out which user a URI-backed printer icon (Icon.TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP) belonged to by calling icon.getUri().getEncodedUserInfo(). If that returned null, the method assumed the URI targeted the calling user and did no check at all. Otherwise it ran Integer.parseInt on the value, resolved it with resolveCallingUserEnforcingPermissions, and compared the profile parent with getCurrentUserId(). The commit message says a specially crafted URI could make Uri.getEncodedUserInfo return an incorrect value. The user id used for the boundary check could then differ from the user the content URI refers to, so an icon pointing into another user's space could be returned across the user boundary (information disclosure, matching the bulletin's ID type).",
     "fix_analysis": [
      "services/print/java/com/android/server/print/PrintManagerService.java: the binder path that calls userState.getCustomPrinterIcon(printerId) now passes its already-resolved user id into validateIconUserBoundary(icon, resolvedUserId).",
      "PrintManagerService.java:validateIconUserBoundary(Icon, int resolvedCallingId): the getEncodedUserInfo / Integer.parseInt / resolveCallingUserEnforcingPermissions sequence is replaced by ContentProvider.getUserIdFromAuthority(icon.getUri().getAuthority(), resolvedCallingId), with resolvedCallingId as the second (default) argument. The commit message describes this as the more robust method.",
      "The early exit for a null user-info is removed. For every URI-type icon the code now takes mLock and returns null unless resolveCallingProfileParentLocked(iconUserId) == getCurrentUserId(), so the check always runs for URI icons.",
      "Adds import android.content.ContentProvider."
     ],
     "completeness": [
      "Covers the single call site shown (the getCustomPrinterIcon path into validateIconUserBoundary) for icons of type TYPE_URI and TYPE_URI_ADAPTIVE_BITMAP. Other Icon types are passed through unchanged, as before.",
      "The check now relies on how ContentProvider.getUserIdFromAuthority parses the authority, not on Uri.getEncodedUserInfo. The diff does not change Uri itself, so any other code that uses getEncodedUserInfo for user-boundary decisions is outside this fix.",
      "The fix only controls whether the Icon object is returned to the caller. It does not validate the URI's authority or path beyond the user id."
     ],
     "behavior_changes": [
      "URI-backed printer icons with no encoded user id now go through the current-user-group check (using the resolved calling user) instead of being returned unchecked. When the profile parent of the resolved user is not the current user, the caller receives null.",
      "Integer.parseInt is no longer applied to the user-info component in this method.",
      "resolveCallingUserEnforcingPermissions is no longer called on the user id embedded in the URI, so this path no longer enforces cross-user permissions for that id. The decision is now only the current-user-group comparison."
     ],
     "preconditions": [
      "A caller reaches the PrintManagerService binder path that calls userState.getCustomPrinterIcon(printerId) for a printer whose custom icon is of type Icon.TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP.",
      "The icon's URI is crafted so that Uri.getEncodedUserInfo does not reflect the user id the URI really refers to (per the commit message). The exact URI shape is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 0d6bfc8a1f50 in android-security-14.0.0_r24, same diff content; 8/8 patched lines present at android-security-14.0.0_r29",
      "15": "same fix: platform/frameworks/base 3723e843b659 in android-security-15.0.0_r12, same diff content; 8/8 patched lines present at android-security-15.0.0_r17",
      "16": "same fix: platform/frameworks/base 9489a5dcd3cd in android-security-16.0.0_r3 (cherry-pick of 8d40513846ee); 8/8 patched lines present at android-security-16.0.0_r8",
      "17": "not affected per the bulletin; the patched region is already present (8/8 lines) at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Single self-contained commit touching one file; the same diff content landed on 14, 15 and 16. It needs ContentProvider.getUserIdFromAuthority and the existing resolveCallingProfileParentLocked and getCurrentUserId helpers in PrintManagerService. On a customized tree, make sure the caller still computes resolvedUserId before the validateIconUserBoundary call so it can be passed in."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact URI form that makes Uri.getEncodedUserInfo return an incorrect value is not shown in the material; the commit message only states that it can happen.",
      "How ContentProvider.getUserIdFromAuthority handles a missing or malformed user prefix is not shown in the material.",
      "The name of the binder method enclosing the call site is not visible in the diff hunks.",
      "Android 13 is listed as affected by the bulletin, but no Android 13 commit is included in the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48629": {
     "cve": "CVE-2025-48629",
     "summary": "VoiceInteractionManagerService no longer auto-selects the first available RecognitionService when the preferred/default recognizer is not found; it now only auto-selects a service whose application has ApplicationInfo.FLAG_SYSTEM and returns null otherwise.",
     "component": "platform/frameworks/base, services/voiceinteraction (VoiceInteractionManagerService, default voice recognizer selection)",
     "weakness": "Missing trust check on automatic default-component selection (an untrusted third-party app could be chosen as the default voice recognizer without user selection)",
     "root_cause": "In the recognizer selection logic of services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java, when the preferred package (prefPackage: the current or default recognizer) was not found among the resolved recognition services, the code fell back to `available.get(0).getServiceInfo()` and returned that component, logging only a warning if more than one was available. Any installed third-party app declaring a recognition service could therefore become the default voice recognizer automatically, with no check on whether the app was a system app and no user choice (per the commit message, this happens when no default recognizer is set via config_systemSpeechRecognizer).",
     "fix_analysis": [
      "services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java (recognizer auto-selection code after the prefPackage lookup; method name not shown in the hunk header): the unconditional `available.get(0)` fallback and the 'more than one voice recognition service found, picking first' warning are removed.",
      "A new loop iterates over all `numAvailable` entries in `available`; for each `ServiceInfo`, entries whose `applicationInfo.flags` lack `ApplicationInfo.FLAG_SYSTEM` are skipped, and the first system-app service is returned as `new ComponentName(serviceInfo.packageName, serviceInfo.name)`.",
      "If no system recognizer is found, the method logs 'no auto selectable voice recognition services found for user <userHandle>' and returns null instead of a third-party component."
     ],
     "completeness": [
      "The fix covers only the automatic fallback path taken when prefPackage is not matched; selection of prefPackage itself (current or default recognizer) is unchanged by this diff.",
      "The trust criterion is solely ApplicationInfo.FLAG_SYSTEM; no check for FLAG_UPDATED_SYSTEM_APP, privileged status, or signature is introduced (whether such a check is needed is not shown in the material).",
      "The diff does not show how the null return is handled by callers; the material does not show whether null was already a possible return value before the fix.",
      "Single-commit fix, same hunk on 14, 15 and 16; no automated test added (Test: Manual)."
     ],
     "behavior_changes": [
      "When the preferred recognizer is absent, only system apps (FLAG_SYSTEM) can be auto-selected as the voice recognizer; a non-system RecognitionService is never chosen automatically by this path.",
      "If no system recognizer exists, the method returns null and logs 'no auto selectable voice recognition services found for user ...'; how callers react to null is not shown.",
      "The warning 'more than one voice recognition service found, picking first' is removed; if multiple system recognizers exist, the first FLAG_SYSTEM entry in the `available` list is chosen."
     ],
     "preconditions": [
      "The preferred recognizer package (current or default recognizer) is not set or not found among the resolved recognition services for the user (per commit message: no default recognizer configured via config_systemSpeechRecognizer).",
      "A third-party (non-system) app declaring a RecognitionService is installed for that user.",
      "The recognizer selection logic in VoiceInteractionManagerService runs for that user (exact trigger points are not shown in the material)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 6c030f1f30b4 in android-security-14.0.0_r24, identical hunk at a different offset (@@ -852); resolved by bug_id. Per-major fact reports 1/4 patched-region lines present at android-security-14.0.0_r29 (see unknowns).",
      "15": "same fix: platform/frameworks/base d8faf81970e4 in android-security-15.0.0_r12, identical hunk (@@ -933); resolved by bug_id. 1/4 patched-region lines present at android-security-15.0.0_r17 (see unknowns).",
      "16": "same fix: platform/frameworks/base 84dd2b90f4a2 in android-security-16.0.0_r3, identical hunk (@@ -933); resolved by bug_id. 1/4 patched-region lines present at android-security-16.0.0_r8 (see unknowns).",
      "17": "not affected (not listed by the bulletin); at android-security-17.0.0_r1 the per-major fact reports 1/4 patched-region lines present, so the exact form of the code there is not shown."
     },
     "backport_notes": [
      "The change is self-contained: replace the `available.get(0)` fallback with a loop that requires `serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM`, and return null otherwise. Confirm ApplicationInfo is imported and that the caller of this selection method tolerates a null ComponentName on the target branch."
     ],
     "confidence": "high",
     "unknowns": [
      "The name of the method containing the patched code and its callers are not shown in the material (the hunk header shows only the class).",
      "How callers handle the new null return, and whether null was already a possible return before the fix.",
      "Why the per-major facts report only 1/4 patched-region lines present at the latest security tags (14.0.0_r29, 15.0.0_r17, 16.0.0_r8, 17.0.0_r1): whether the region was later refactored or the match is imprecise is not shown.",
      "The concrete runtime triggers (boot, user switch, package install) for the recognizer selection path.",
      "The ordering of the `available` list of resolved recognition services."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48632": {
     "cve": "CVE-2025-48632",
     "summary": "Adds a shared validateDisplayName() length check to AssociationRequest so that the private constructor and the @hide setDisplayName() enforce DISPLAY_NAME_LENGTH_LIMIT, not only Builder.setDisplayName().",
     "component": "platform/frameworks/base, CompanionDeviceManager (core/java/android/companion/AssociationRequest.java)",
     "weakness": "Missing input validation (display name length limit enforced on only one of several setters)",
     "root_cause": "Per the javadoc of the constructor parameter and of getDisplayName(), AssociationRequest's displayName is \"The Display name of the device to be shown in the CDM confirmation UI\". The DISPLAY_NAME_LENGTH_LIMIT check existed only inside Builder.setDisplayName(). The private 8-argument constructor AssociationRequest(...) and the @hide public setDisplayName(CharSequence) stored mDisplayName with no length check. Through those paths an AssociationRequest could be built or changed to carry an over-long display name, bypassing the limit.",
     "fix_analysis": [
      "core/java/android/companion/AssociationRequest.java: a new private static helper, validateDisplayName(@Nullable CharSequence), throws IllegalArgumentException when displayName != null and displayName.length() > DISPLAY_NAME_LENGTH_LIMIT. Null is accepted, matching the @Nullable constructor parameter.",
      "AssociationRequest.AssociationRequest(boolean, List, String, CharSequence, boolean, boolean, boolean, Icon) (private constructor): now calls validateDisplayName(displayName) as its first statement, before any field is assigned.",
      "AssociationRequest.setDisplayName(CharSequence) (@hide): now calls validateDisplayName(displayName) before assigning mDisplayName.",
      "AssociationRequest.Builder.setDisplayName(CharSequence): the inline length check is replaced by a call to validateDisplayName(displayName). checkNotUsed() and requireNonNull are kept, so behavior there is unchanged.",
      "Single commit, single file. The material shows no service-side changes."
     ],
     "completeness": [
      "Covers the three display name setters visible in the diff: the private constructor, the @hide setDisplayName(), and Builder.setDisplayName().",
      "The Parcel path is not touched. CREATOR.createFromParcel still calls new AssociationRequest(in), and the diff does not change that constructor. The material does not show whether the Parcel constructor delegates to the validated 8-arg constructor, so it is unknown whether an unparceled AssociationRequest with an over-long name is rejected.",
      "The check is length only. No other content checks on displayName are added."
     ],
     "behavior_changes": [
      "The private constructor and the @hide setDisplayName() now throw IllegalArgumentException (\"Length of the display name must be at most N characters\") for over-long names. Before the fix they accepted any length.",
      "A null displayName is still accepted by the constructor and the @hide setter, because the helper tolerates null. Builder.setDisplayName() still rejects null via requireNonNull.",
      "No API signature changes. The helper is private static."
     ],
     "preconditions": [
      "An AssociationRequest has to be created or changed through a path other than Builder.setDisplayName(): either the private constructor or the @hide setDisplayName(CharSequence). The material does not show which callers use these paths.",
      "Per the getDisplayName() javadoc, the displayName is shown in the CDM confirmation UI. The material does not show the concrete consumer or how an over-long value leads to the bulletin's EoP.",
      "Index search returned only unrelated setDisplayName methods (Contacts ValuesDelta, SubscriptionManager, Telecom), so the material does not show the real callers of AssociationRequest.setDisplayName."
     ],
     "per_major": {
      "14": "adapted fix: platform/frameworks/base a100e23a3fda in android-security-14.0.0_r24. Same helper and same three call sites, but the private constructor takes fewer parameters (it ends at forceConfirmation, with no skipRoleGrant or deviceIcon) and the hunk offsets differ. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "adapted fix: platform/frameworks/base e72cdc5c770d in android-security-15.0.0_r12. Same logic and constructor signature as the 14 patch. It differs only in whitespace: it also removes the blank line after the call in Builder.setDisplayName. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base de27b16b1af8 in android-security-16.0.0_r3 (cherry-pick of 110f3e5843d8, Change-Id I41dc3804c35328a5968716f502c4320a58b75158). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 6/8 lines of the patched region are present. The material does not show which lines differ or the exact state of the code."
     },
     "backport_notes": [
      "Self-contained, single-file change. If the private constructor signature differs in your tree, add validateDisplayName(displayName) as the first statement of the constructor that assigns mDisplayName, and replace the inline check in Builder.setDisplayName with the helper call.",
      "No 'Fix in fork' section was provided, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "Which component calls the @hide setDisplayName() or the private constructor with input of untrusted length, and how an over-long name leads to EoP (the material has only the bulletin rating).",
      "Whether the Parcel constructor AssociationRequest(Parcel) delegates to the validated constructor or assigns mDisplayName directly.",
      "The value of DISPLAY_NAME_LENGTH_LIMIT (not shown in the material).",
      "Which 2 of the 8 patched lines are missing on Android 17, and why the bulletin lists 17 as not affected."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48633": {
     "cve": "CVE-2025-48633",
     "summary": "DevicePolicyManagerService.hasAccountsOnAnyUser() is changed to query AccountManagerService.getSingleton().getAllAccountsForSystemProcess() instead of per-user AccountManager.getAccounts(), so that, per the commit title, accounts are counted regardless of their visibility when deciding whether any account exists.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy)",
     "weakness": "Incomplete check: per the commit title, the account-presence check relied on a visibility-dependent account list",
     "root_cause": "hasAccountsOnAnyUser() in DevicePolicyManagerService.java iterated over mUserManagerInternal.getUsers(true), created a Context as each user and called AccountManager.getAccounts() after Binder.clearCallingIdentity(). The commit title ('Get all accounts no matter the visibility') indicates that this list was affected by account visibility, so some existing accounts could be missed and the method could report that no accounts exist when accounts were present. The material does not show which decision depends on this result.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java:hasAccountsOnAnyUser(): the per-user loop, the createContextAsUser/AccountManager.getAccounts() calls and the clearCallingIdentity/restoreCallingIdentity wrapper are removed.",
      "The method now obtains AccountManagerService via AccountManagerService.getSingleton() and returns getAllAccountsForSystemProcess().length != 0; per the commit message this returns accounts no matter their visibility (the method body, including cross-user coverage, is not in the material).",
      "A new import of com.android.server.accounts.AccountManagerService is added; no other file is changed."
     ],
     "completeness": [
      "Covers the single helper hasAccountsOnAnyUser(); its signature is unchanged, so any callers get the new behavior without changes.",
      "The fix relies on AccountManagerService.getAllAccountsForSystemProcess() covering all users (the previous explicit per-user loop is dropped); the material does not show that method's body.",
      "No other account-presence checks in DevicePolicyManagerService are touched by this commit; the material does not show whether other such checks exist."
     ],
     "behavior_changes": [
      "hasAccountsOnAnyUser() can now return true in cases where accounts were previously omitted because of visibility; previously it could return false in those cases.",
      "The per-user AccountManager calls and identity clearing are replaced by a direct call to the AccountManagerService singleton.",
      "Dying users are no longer explicitly excluded by the DPMS code; inclusion now depends on getAllAccountsForSystemProcess()."
     ],
     "preconditions": [
      "Device has at least one account that the previous visibility-dependent AccountManager.getAccounts() query omitted.",
      "A code path in DevicePolicyManagerService that calls hasAccountsOnAnyUser() is reached; the actual callers are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 527b25ca541f in android-security-14.0.0_r24, identical hunk at a different line offset; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit e74b3376e899 in android-security-15.0.0_r12, identical hunk; 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit d00bcda9f42d in android-security-16.0.0_r3 (cherry-pick of a8819d3bdc49); 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched code (8/8 lines) is already present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk plus one import; it applies identically on 14, 15 and 16. The target branch must have com.android.server.accounts.AccountManagerService with getSingleton() and getAllAccountsForSystemProcess(); Android 13 is listed as affected but no 13 commit is in the material, so verify those methods exist there before backporting."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which DevicePolicyManagerService code paths call hasAccountsOnAnyUser() and what decision its result gates (the index search returned no callers).",
      "The body of AccountManagerService.getAllAccountsForSystemProcess(), including whether it spans all users and how it treats dying users.",
      "Exactly how account visibility affected the previous AccountManager.getAccounts() result from system_server.",
      "No fix commit for Android 13 is included in the material although the bulletin lists 13 as affected.",
      "The concrete information-disclosure consequence rated High by the bulletin is not shown in the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48639": {
     "cve": "CVE-2025-48639",
     "summary": "Lowers the MAX_ANIMATION_DURATION constant in WindowManager Shell's DefaultTransitionHandler from 3000 to 1500. Per the commit message, this shortens the time during which a transition animation can be abused for tapjacking.",
     "component": "platform/frameworks/base, libs/WindowManager/Shell (shell transitions, DefaultTransitionHandler)",
     "weakness": "Transition animation window long enough to be used for tapjacking (UI redress). The fix is a mitigation that tightens a duration limit; it does not remove the condition.",
     "root_cause": "DefaultTransitionHandler is the shell transition handler \"that handles anything not already handled\". It defined MAX_ANIMATION_DURATION = 3000. Per the commit message, 3 seconds is long enough to be exploited for tap jacking during the animation. The limit value itself was the problem; the diff shows no logic defect.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java (class constant, no function): MAX_ANIMATION_DURATION changed from 3000 to 1500. This is the only change in the commit (1 insertion, 1 deletion).",
      "The commit is a RESTRICT AUTOMERGE backport (Cherrypick-From 71d67882398e9a28c268e19d5a5d66ff7632c4ed, Change-Id I691b7746a8b413e956fdb75c7809904c70853552). The same one-line change is applied on Android 14 (848e575f07ff), 15 (06e697583c87) and 16 (6d1697c96c5c).",
      "The material does not show the code that uses MAX_ANIMATION_DURATION. The fix only changes the constant's value."
     ],
     "completeness": [
      "Covers only the default transition handler's MAX_ANIMATION_DURATION constant. The value is halved (3000 to 1500), so the window is shortened, not eliminated.",
      "SIZE_CHANGE_ANIMATION_DURATION (400) is untouched. It appears in the Android 16 code but not in the Android 14 and 15 diff context.",
      "This commit does not change any other transition handler or the legacy (non-shell-transition) app transition path. The material does not show whether they have their own duration limits.",
      "No test was added (Test: manual)."
     ],
     "behavior_changes": [
      "Transition animations handled by DefaultTransitionHandler are expected to be limited to 1500 instead of 3000, judging by the constant name. The clamping code itself is not shown.",
      "The diff shows no API, error code or permission change."
     ],
     "preconditions": [
      "A transition is handled by DefaultTransitionHandler, the fallback shell transition handler. Presumably the animation's duration must exceed the limit (inferred from the constant name).",
      "The material does not show how the animation duration is supplied or how the limit is applied."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 848e575f07ff in android-security-14.0.0_r24, identical one-line change MAX_ANIMATION_DURATION 3000 -> 1500 (no SIZE_CHANGE_ANIMATION_DURATION line in the diff context). At android-security-14.0.0_r29, 4/5 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 06e697583c87 in android-security-15.0.0_r12, identical one-line change. At android-security-15.0.0_r17, 4/5 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/base 6d1697c96c5c in android-security-16.0.0_r3 (matched by change_id). android-security-16.0.0_r8 shows MAX_ANIMATION_DURATION = 1500 at line 129.",
      "17": "not affected per the bulletin (not listed). At android-security-17.0.0_r1, 4/5 lines of the patched region are present. The material does not show the value of MAX_ANIMATION_DURATION on 17."
     },
     "backport_notes": [
      "Single self-contained constant change with no dependencies. On a branch whose DefaultTransitionHandler.java has MAX_ANIMATION_DURATION = 3000, apply the same one-line edit. The bulletin lists Android 13 as affected, but the material includes no 13 commit."
     ],
     "confidence": "high",
     "unknowns": [
      "Where and how MAX_ANIMATION_DURATION is applied (the clamping code) is not in the provided context.",
      "Whether other transition handlers or the legacy app transition path have comparable duration limits.",
      "The Android 13 fix commit is not included in the material.",
      "The value of MAX_ANIMATION_DURATION on Android 17, and which line of the patched region differs on the 14, 15 and 17 latest tags.",
      "The content of the original change 71d67882398e9a28c268e19d5a5d66ff7632c4ed and whether it carried changes beyond this constant."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "17": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "coverage": {
    "n_ingested_tags": 2,
    "first_ingested_tag": "android-security-17.0.0_r1",
    "latest_ingested_tag": "android-security-17.0.0_r2",
    "window_base_tag": "android-17.0.0_r1",
    "window_start": "2026-06-16",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {}
  }
 },
 "bulletin": {
  "url": "https://source.android.com/docs/security/bulletin/2025-12-01",
  "latest_version": "1.5",
  "latest_revision": {
   "version": "1.5",
   "date": "March 6, 2026",
   "notes": "CVE-2025-48566 and CVE-2025-48564 were removed as they share a fix with CVE-2025-48565."
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "December 1, 2025",
    "notes": "Bulletin Published"
   },
   {
    "version": "1.1",
    "date": "December 4, 2025",
    "notes": "Bulletin revised to include AOSP links"
   },
   {
    "version": "1.2",
    "date": "December 17, 2025",
    "notes": "CVE-2025-48600 and CVE-2025-48615 have been removed from ASB#2025-12 due to an incomplete fix. Partners may keep these fixes. An updated fix will be required as part of a future Android Security Bulletin."
   },
   {
    "version": "1.3",
    "date": "January 8, 2026",
    "notes": "CVE-2025-48631 has been removed due to an incomplete fix for Android 16 25Q4, all other Android versions are fine. Partners may keep this fix. An updated fix will be required as part of ASB#2026-03 Android Security Bulletin."
   },
   {
    "version": "1.3",
    "date": "February 10, 2026",
    "notes": "CVE-2025-48612 A-429417453 will be removed from ASB#2025-12 due to a possible regression that may limit the ability to switch default payment apps for contactless payments. Partners may keep this fix. An updated fix will be required as part of a future Android Security Bulletin."
   },
   {
    "version": "1.4",
    "date": "March 3, 2026",
    "notes": "CVE-2025-48565 A-407763772 will be removed from ASB#2025-12 due to an incomplete fix. Partners may keep this fix. An updated fix will be required as part of a future Android Security Bulletin."
   },
   {
    "version": "1.5",
    "date": "March 6, 2026",
    "notes": "CVE-2025-48566 and CVE-2025-48564 were removed as they share a fix with CVE-2025-48565."
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [],
  "out_of_scope_rows": {
   "Arm components": 2,
   "Imagination Technologies": 4,
   "Kernel": 9,
   "MediaTek components": 17,
   "Qualcomm closed-source components": 8,
   "Qualcomm components": 3,
   "Unisoc components": 13
  },
  "note": "changes are only visible from `tracked_since`; earlier revisions of the bulletin cannot be reconstructed. Out-of-scope rows (vendor closed-source, kernel) have no public AOSP code to resolve."
 }
}