{
 "month": "2024-06",
 "generated": "2026-10-06T12:05+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 17,
    "certain": 17,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2024-31310",
     "osv_id": "ASB-A-324874908",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AutofillManagerServiceImpl.newServiceInfoLocked now uses PackageManager.queryIntentServicesAsUser to check that the component being set as the autofill service declares the AutofillService.SERVICE_INTERFACE intent filter, and throws SecurityException if it does not.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit: 27 added lines in one file. It uses AutofillService.SERVICE_INTERFACE, PackageManager.queryIntentServicesAsUser, getContext() and mUserId. How it applies to 12/12L/13 is not shown in the material. Keep the MATCH_INSTANT flag if CtsAutoFillServiceTestCases runs in instant mode on the target branch."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "96bfc01a491bafc495dd1e8b4bbd41afb065fb56",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31311",
     "osv_id": "ASB-A-330054251",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a bounds check in libstatssocket's increment_annotation_count() so that an annotation added after the AStatsEvent buffer has reached its maximum capacity no longer reads and writes the byte at event->buf[lastFieldPos] when lastFieldPos is at or beyond event->bufSize.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained 3-line change in a static helper. It depends only on the AStatsEvent fields lastFieldPos and bufSize, which stats_event.c already uses, so it should apply to branches that have increment_annotation_count (the bulletin also lists 12, 12L and 13 as affected). StatsD is a mainline module, so the fix may also ship via a module update (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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/StatsD",
       "sha": "2c6cc7816da32a1b8d64123e65dcbc14afdd683c",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/StatsD"
     ]
    },
    {
     "cve": "CVE-2024-31312",
     "osv_id": "ASB-A-314333719",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Moves enforcement of the Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN user setting from the keyguard-only KeyguardMediaController into MediaHierarchyManager/MediaHost so that, when the setting is disabled, every media host hides the media carousel while the device is in the KEYGUARD or SHADE_LOCKED state.",
      "confidence": "high",
      "backport_notes": [
       "The fix assumes MediaHierarchyManager already has SecureSettings and a @Main Handler injected, a lockScreenMediaPlayerUri ContentObserver, a statusbarState field and the mediaCarouselController.updateHostVisibility lambda. On branches lacking these pieces (the bulletin lists 12/12L/13 as affected, but no commit for them is in the material), they must be introduced first. Dagger wiring and KeyguardMediaControllerTest must also be updated for the removed KeyguardMediaController constructor parameters."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "76d298d2e82cbc8ee48b4fa42702f5dfc09cbab0",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31313",
     "osv_id": "ASB-A-321341508",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds underflow checks to MessageQueueBase::availableToReadBytes() and availableToWriteBytes() in system/libfmq, so that inconsistent read/write counters yield 0 (with an error log) instead of a wrapped size_t.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained change to include/fmq/MessageQueueBase.h. It uses hardware::details::logError, which must be available in the target branch.",
       "No fork comparison was provided in the material, so no per-file fork verdict can be given."
      ],
      "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_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/system/libfmq",
       "sha": "7903ae2f7f31a485d6a7ea24bed5cf9fa61e55ed",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/system/libfmq"
     ]
    },
    {
     "cve": "CVE-2024-31314",
     "osv_id": "ASB-A-304290201",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a per-package, time-windowed throttle in ShortcutPackage so that ShortcutService forwards at most REPORT_USAGE_BUFFER_SIZE (3) shortcut-usage reports per mSaveDelayMillis window to UsageStatsManagerInternal.reportShortcutUsage, instead of forwarding every reportShortcutUsed/pushDynamicShortcut call unconditionally.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. It relies on ShortcutService.mSaveDelayMillis, a ShortcutPackage mLock, and mShortcutUser.mService. Check that these exist on the target branch, and keep the throttle list per package.",
       "Add KEY_SAVE_DELAY_MILLIS=1 to ShortcutManagerTest1.testPushDynamicShortcut and ShortcutManagerTest2.testReportShortcutUsed as the fix does, so their existing expectations are not throttled."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6dc0f712dbe795fdd8b563b1fddbd1c0efe17e91",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31315",
     "osv_id": "ASB-A-321707289",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ManagedServices now checks that a managed service component (e.g. a NotificationListenerService) still declares the required bind permission before rebinding it, and removes approved component entries that lost the permission when their package changes.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. It depends on existing helpers: getServiceInfo(ComponentName, int), mConfig.bindPermission, getPackageName(String), and the mApproved ArrayMap<Boolean, ArraySet<String>> structure. Check that the target branch's package-change loop has the uidList/isPackageAllowed structure needed to hook in trimApprovedListsForInvalidServices.",
       "The existing ManagedServicesTest reregisterService tests will fail after the backport unless getServiceInfo is mocked. The patch adds mockServiceInfoWithMetaData calls for this."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0a645bd7c46ffdff58a71e18b775e9816d69e1d2",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31316",
     "osv_id": "ASB-A-321941232",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a second checkKeyIntent() check of the result Bundle returned by the authenticator in two result-handling paths of AccountManagerService. The check runs after the Bundle has been modified (KEY_AUTHTOKEN removed) and rejects the response with ERROR_CODE_INVALID_RESPONSE if the intent fails it.",
      "confidence": "medium",
      "backport_notes": [
       "Single-file change that only adds code. It requires checkKeyIntent(int, Bundle) to already exist in AccountManagerService on the target branch, because the fix adds calls only, not the method. The bulletin lists 12, 12L and 13 as affected, but the material has no commit or branch facts for those versions."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3c59b877099d71494121ef9c4d3304fdd055c093",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31317",
     "osv_id": "ASB-A-316153291",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds character validation in core/java/android/os/ZygoteProcess.java so that hidden API denylist exemption strings containing '\\n', '\\r' or '\\u0000' are rejected (list cleared, nothing written) before reaching the zygote command socket, and process start arguments containing '\\u0000' now throw ZygoteStartFailedEx alongside the existing newline and carriage return checks.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, self-contained change that anchors on the existing '\\n'/'\\r' loop in zygoteSendArgsAndGetResult and the existing mApiDenylistExemptions write to state.mZygoteOutputWriter. The diff adds no imports, so Slog and Collections are already available in the Android 14 file. The bulletin lists 12, 12L and 13 as affected, but no per-branch material for them is provided here."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c0c2c1ef78f213b413d0ec129aae2e27f33aa698",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31318",
     "osv_id": "ASB-A-313428840",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The CompanionDeviceManagerService binder implementation no longer overrides onShellCommand and instead overrides handleShellCommand. The base Binder.onShellCommand, which per the commit message checks the caller's permissions, therefore runs before CompanionDeviceShellCommand is executed.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, single-commit change. The bulletin lists 12, 12L and 13 as affected, but the material shows no commits for them. When backporting, check that the CompanionDeviceShellCommand constructor arguments match that branch's fields. The essential change is to move the body from an onShellCommand override into a handleShellCommand(ParcelFileDescriptor x3, String[]) override that returns int. Binder.handleShellCommand with ParcelFileDescriptor parameters exists from Android 11 onward (general platform knowledge)."
      ],
      "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_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2442fb3428e76822bfe3266f7a864110bab37932",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31319",
     "osv_id": "ASB-A-317357401",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a URI read-permission check in NotificationManagerService.updateNotificationChannelFromPrivilegedListener so a privileged (CDM) NotificationListenerService can only set a new channel sound URI that its own UID is allowed to read.",
      "confidence": "high",
      "backport_notes": [
       "Single commit; it relies on the existing mUgmInternal field in NotificationManagerService, ContentProvider.getUriWithoutUserId/getUserIdFromUri and Binder.withCleanCallingIdentity. The bulletin also lists 12, 12L and 13 as affected but the material only shows the 14 commit; for those branches confirm mUgmInternal exists in NMS and that the test fixture exposes the mUgmInternal mock.",
       "When adapting, ensure verifyPrivilegedListenerUriPermission is called before updateNotificationChannelInt."
      ],
      "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_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f428043dbaafa5437d69ef5fa63d9fba39b851e8",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31322",
     "osv_id": "ASB-A-326485767",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AccessibilityManagerService.updateServicesLocked now removes components that are no longer installed from the user's enabled accessibility services list and touch-exploration-granted list and persists both settings, so a service that disappears after a service update does not stay enabled.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, additive change inside updateServicesLocked. It relies on the existing members mTempComponentNameSet and persistComponentNamesToSettingLocked and on the two Settings.Secure keys, which must exist in the target branch. When backporting, decide whether to keep the '||' short-circuit between the two removeIf calls or to evaluate both (see completeness)."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c1bc907a649addd5b97d489fd39afb956164a46c",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31323",
     "osv_id": "ASB-A-313425281",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds the HIDE_OVERLAY_WINDOWS permission to the Health Connect controller APK manifest and sets SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS in onCreate of seven Health Connect activities so non-system apps cannot show overlay windows on the Health Connect UI.",
      "confidence": "high",
      "backport_notes": [
       "Single commit, additions only (28 inserted lines across 8 files, no deletions). The manifest permission and the eight addSystemFlags calls are meant to go together."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/HealthFitness",
       "sha": "c4e13d15e8dd1df1bd827117d1a74c187ed2b3c2",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref c4e13d15e8dd",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/HealthFitness"
     ]
    },
    {
     "cve": "CVE-2024-31324",
     "osv_id": "ASB-A-302431573",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowState.hide() now decides whether to defer the visibility change based on whether an exit animation was actually applied to the window itself (return value of mWinAnimator.applyAnimationLocked), instead of checking isAnimating(TRANSITION | PARENTS), so a window with no hide animation is committed invisible immediately even when its parent container is animating.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit, two-line logic change in WindowState.hide(); it uses the boolean return of mWinAnimator.applyAnimationLocked to indicate whether an animation was applied, so that return value must exist on the target branch.",
       "The test addition uses window.getParent().startAnimation(mTransaction, mock(AnimationAdapter.class), false, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION), isSelfAnimating and cancelAnimation; these helpers must exist in the target branch's wmtests."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1ec80672496e40c7e07cef3f517c702e7ba50c36",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31325",
     "osv_id": "ASB-A-317503801",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI now constructs each ExpandableNotificationRow's NotificationInlineImageResolver with a Context bound to the notification's own user (via createContextAsUser when it differs from SystemUI's user) instead of SystemUI's application context, so inline message/conversation image Uris are resolved with the notification's user id.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit fix. It depends on the AsyncLayoutInflater(Context, AsyncLayoutFactory) constructor and AsyncLayoutFactory from androidx asynclayoutinflater, plus Context.createContextAsUser and StatusBarNotification.getNormalizedUserId, as used in the diff.",
       "Any downstream code or test that inflates status_bar_notification_row directly must install RowInflaterTask.RowAsyncLayoutInflater (as NotificationTestHelper now does). Otherwise rows fall back to the SystemUI-user resolver and log wtf."
      ],
      "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-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d147a331979353639688fbd2ad478ea0ec320842",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31326",
     "osv_id": "ASB-A-318497672",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a one-time, persisted \"post upgrade\" migration step in DevicePolicyManagerService that pushes screen-capture-disabled, lock task, and legacy user restriction policies held by device/profile owner ActiveAdmins into the DevicePolicyEngine, because the initial Android T to U migration did not carry them over correctly.",
      "confidence": "medium",
      "backport_notes": [
       "Apply 736462a777d0 first, then f1eb8e719dfb: the second commit's diff is based on the first commit's output (index 5e67708b1adb) and inserts migrateUserRestrictionsLocked() into the method the first commit adds. Per the fix's own comment, it repairs the Android T to U migration.",
       "If the target branch has customized migratePoliciesToDevicePolicyEngine(), keep the removal of migrateScreenCapturePolicyLocked() from it in sync with adding the post-upgrade step, since screen capture migration now happens only in the post-upgrade step."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "736462a777d0a0e1258bd7ab80d6e352ef797669",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "f1eb8e719dfbe15a38d40af0a73ead207eba9389",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-31327",
     "osv_id": "ASB-A-321326147",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds consistency checks to MessageQueueBase::availableToReadBytes() and availableToWriteBytes() in libfmq so that inconsistent read/write pointer values (readPtr > writePtr, or available > queue size) yield 0 with an error log instead of an unsigned underflow.",
      "confidence": "medium",
      "backport_notes": [
       "This is a single-file, header-only change to MessageQueueBase.h. Because it is a template header, modules that include it must be rebuilt to pick up the fix (general platform knowledge)."
      ],
      "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_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r9",
      "date": "2024-06-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/system/libfmq",
       "sha": "7903ae2f7f31a485d6a7ea24bed5cf9fa61e55ed",
       "tag": "android-security-14.0.0_r9",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/system/libfmq"
     ]
    },
    {
     "cve": "CVE-2024-32896",
     "osv_id": "PUB-A-324321147",
     "source": "osv",
     "announced": "2024-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "RecoverySystemService.rebootRecoveryWithCommand() now destroys all KeyMint keys via the new AndroidKeyStoreMaintenance.deleteAllKeys() (IKeystoreMaintenance.deleteAllKeys) before rebooting to recovery when the command contains \"--wipe_data\", with a matching SELinux allow for system_server, so FBE-encrypted data becomes unrecoverable even if the recovery data wipe is interrupted or skipped.",
      "confidence": "high",
      "backport_notes": [
       "Apply the sepolicy change (delete_all_keys in allow system_server keystore:keystore2, in both private/system_server.te and the matching prebuilts/api/<level>/private/system_server.te) together with the frameworks/base change; otherwise the deleteAllKeys() call can be expected to fail and would only be logged via Log.wtf. The Java side depends on IKeystoreMaintenance.deleteAllKeys() in the android.security.maintenance AIDL and on com.android.server.utils.Slogf.",
       "The Android 15 entry points to a soong change whose content does not match this fix. To verify, look directly for RECOVERY_WIPE_DATA_COMMAND / deleteSecrets() in RecoverySystemService and deleteAllKeys() in AndroidKeyStoreMaintenance."
      ],
      "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-14.0.0_r12",
      "date": "2024-09-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r12",
      "date": "2024-09-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8b7b2c66ca96d711fb364cbcc9d655197d9743e0",
       "tag": "android-security-14.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/system/sepolicy",
       "sha": "92cbf8d4f18ccb2c488f50b7300bd6bfc33023aa",
       "tag": "android-security-14.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/system/sepolicy"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r9"
   ],
   "unannounced": [],
   "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-2024-31310": {
     "cve": "CVE-2024-31310",
     "summary": "AutofillManagerServiceImpl.newServiceInfoLocked now uses PackageManager.queryIntentServicesAsUser to check that the component being set as the autofill service declares the AutofillService.SERVICE_INTERFACE intent filter, and throws SecurityException if it does not.",
     "component": "platform/frameworks/base, Autofill framework (services/autofill, AutofillManagerServiceImpl)",
     "weakness": "Missing validation of the target service component: there was no check that the configured autofill service declares the AutofillService intent filter.",
     "root_cause": "In services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java, newServiceInfoLocked(ComponentName) built an AutofillServiceInfo directly for whatever ComponentName it was given when the autofill service was set or updated. It did not check that the component declares the AutofillService.SERVICE_INTERFACE intent filter, so a service component that was never declared as an autofill service could be accepted.",
     "fix_analysis": [
      "AutofillManagerServiceImpl.java:newServiceInfoLocked: before building AutofillServiceInfo, the code calls getContext().getPackageManager().queryIntentServicesAsUser(new Intent(AutofillService.SERVICE_INTERFACE), GET_META_DATA | MATCH_INSTANT, mUserId). This lists the services that declare the autofill intent filter for the user.",
      "It loops over the ResolveInfo list and compares resolveInfo.serviceInfo.getComponentName() with the requested serviceComponent. On a match it sets serviceHasAutofillIntentFilter.",
      "If nothing matches, it logs a warning (Slog.w) and throws SecurityException(\"Service does not declare intent filter \" + AutofillService.SERVICE_INTERFACE), so mInfo is never created for a component without the filter.",
      "MATCH_INSTANT is included on purpose so the CTS test autofill service still resolves when the CTS module runs as an instant app (per the in-code comment and TODO).",
      "Imports added: android.content.Intent and android.content.pm.ResolveInfo."
     ],
     "completeness": [
      "The check is added in newServiceInfoLocked. The commit title says it covers setting and updating the service. The material does not show whether other code paths build the service info.",
      "The check only verifies that the intent filter is declared. This diff adds no permission check (BIND_AUTOFILL_SERVICE) and no exported-ness check. The material does not show whether the AutofillServiceInfo constructor performs such checks.",
      "This commit does not touch the sibling PerUserSystemService implementations (TranslationManagerServiceImpl, ContentCapturePerUserService, SearchUiPerUserService); see potential_variants.",
      "The exception is thrown from newServiceInfoLocked. The material does not show how the caller in PerUserSystemService handles a SecurityException (as opposed to NameNotFoundException)."
     ],
     "behavior_changes": [
      "Setting or updating the autofill service to a component that does not declare the AutofillService.SERVICE_INTERFACE intent filter now throws SecurityException and logs a warning. Before the fix, such a component was accepted.",
      "Each run of newServiceInfoLocked now makes an extra PackageManager.queryIntentServicesAsUser call (with GET_META_DATA | MATCH_INSTANT).",
      "Because of MATCH_INSTANT, instant-app services that declare the filter are matched. The TODO says this flag should be removed once the CTS module is refactored."
     ],
     "preconditions": [
      "The autofill service component for a user is set or updated, which makes AutofillManagerServiceImpl.newServiceInfoLocked run. The method overrides PerUserSystemService; the exact caller is not shown in the material.",
      "The supplied ComponentName refers to a service that does not declare the AutofillService.SERVICE_INTERFACE intent filter.",
      "Per the bulletin, the vulnerability affects Android 12, 12L, 13, 14 and 14-next."
     ],
     "per_major": {
      "14": "same fix: commit 96bfc01a491b in android-security-14.0.0_r9, a [DO NOT MERGE] cherry-pick of internal changes 141d9d05 / e8a448f8. At android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "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. 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 self-contained commit: 27 added lines in one file. It uses AutofillService.SERVICE_INTERFACE, PackageManager.queryIntentServicesAsUser, getContext() and mUserId. How it applies to 12/12L/13 is not shown in the material. Keep the MATCH_INSTANT flag if CtsAutoFillServiceTestCases runs in instant mode on the target branch."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether the AutofillServiceInfo constructor already did other validation (for example the BIND_AUTOFILL_SERVICE permission) is not shown.",
      "How the caller of newServiceInfoLocked in PerUserSystemService handles the new SecurityException is not shown.",
      "The content of the original internal changes 141d9d05 and e8a448f8, and whether they differ from this cherry-pick, is not shown.",
      "The material does not describe the concrete effect of accepting a non-autofill service as the autofill service, which is the basis for the High EoP rating.",
      "The Android 12/12L/13 code and how the fix applies there are not shown."
     ],
     "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-2024-31311": {
     "cve": "CVE-2024-31311",
     "summary": "Adds a bounds check in libstatssocket's increment_annotation_count() so that an annotation added after the AStatsEvent buffer has reached its maximum capacity no longer reads and writes the byte at event->buf[lastFieldPos] when lastFieldPos is at or beyond event->bufSize.",
     "component": "platform/packages/modules/StatsD, lib/libstatssocket (AStatsEvent native API)",
     "weakness": "Missing bounds check leading to a heap out-of-bounds read/write of one byte in stats_event.c",
     "root_cause": "increment_annotation_count() in lib/libstatssocket/stats_event.c always read event->buf[event->lastFieldPos] to get the field type and the current annotation count, then wrote the updated count back to the same byte. It did not check that lastFieldPos was inside the buffer. The new check (lastFieldPos >= bufSize) shows that lastFieldPos could be at or beyond bufSize once the buffer had grown to its maximum size (50*1024 bytes per the new test) and a further write had been rejected with ERROR_OVERFLOW. A following AStatsEvent_addBoolAnnotation then reached increment_annotation_count and accessed buf[lastFieldPos] out of bounds. The new test TestHeapBufferOverflowError reproduces this sequence: it fills the buffer to the last byte, makes one rejected write that sets ERROR_OVERFLOW, then calls AStatsEvent_addBoolAnnotation.",
     "fix_analysis": [
      "lib/libstatssocket/stats_event.c:increment_annotation_count: adds an early return when event->lastFieldPos >= event->bufSize. The return comes before the read of event->buf[event->lastFieldPos] and before the write back of the new annotation count, so one check guards both the read and the write.",
      "lib/libstatssocket/tests/stats_event_test.cpp: adds TestHeapBufferOverflowError. The test writes a 4039-byte string (buffer size 4060), then a 47135-byte string (buffer size 50*1024), then makes one more rejected write (ERROR_OVERFLOW expected). It then calls AStatsEvent_addBoolAnnotation and AStatsEvent_write and asserts that ERROR_OVERFLOW is still set.",
      "Single-commit fix. The commit is a cherry-pick of two internal changes (4f6c56889356 and e440696c4fd6), with Merged-In I27c69d9875b494f98a2a961cdd4fe139cd809387."
     ],
     "completeness": [
      "The guard is inside the shared helper increment_annotation_count, which AStatsEvent_addBoolAnnotation calls (line 360). Other annotation entry points are not shown in the material, so whether they all go through this helper is not shown.",
      "The check is purely positional (lastFieldPos >= bufSize). It does not look at event->errors or ERROR_OVERFLOW, and it does not reset lastFieldPos when a write is rejected. The material does not show where lastFieldPos is assigned, so the diff cannot tell whether a rejected write could leave lastFieldPos at a stale position inside the buffer.",
      "The early return sets no error flag, so the annotation count update is silently skipped. The ERROR_OVERFLOW set by the rejected write stays set, and that is what the test asserts.",
      "The diff does not change the append_* helpers or AStatsEvent_write/build. It touches only increment_annotation_count and the test."
     ],
     "behavior_changes": [
      "When lastFieldPos is at or beyond bufSize, increment_annotation_count returns without touching buf and without adding a new error bit.",
      "No new error codes and no API signature changes. Nothing changes when lastFieldPos is inside the buffer."
     ],
     "preconditions": [
      "Code must build an AStatsEvent through libstatssocket (AStatsEvent_obtain / write* / add*Annotation). The flaw is in the client-side encoder.",
      "The event buffer must have grown to its maximum size and a later write must have been rejected (ERROR_OVERFLOW set), leaving lastFieldPos >= bufSize. This follows the sequence in TestHeapBufferOverflowError (strings of 4039 and 47135 bytes, buffer at 50*1024 bytes).",
      "An annotation call (for example AStatsEvent_addBoolAnnotation) must follow that rejected write, with numElements >= 2 so that the ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD early return is not taken."
     ],
     "per_major": {
      "14": "same fix: commit 2c6cc7816da3 in android-security-14.0.0_r9 (linked by bug_id 330054251). At android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin (not listed). The patched region is present 8/8 at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin (not listed). The patched region is present 8/8 at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed). The patched region is present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Self-contained 3-line change in a static helper. It depends only on the AStatsEvent fields lastFieldPos and bufSize, which stats_event.c already uses, so it should apply to branches that have increment_annotation_count (the bulletin also lists 12, 12L and 13 as affected). StatsD is a mainline module, so the fix may also ship via a module update (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "Where and how event->lastFieldPos is assigned on a rejected write (not in the provided diff or context). So it is not shown whether it can equal bufSize, exceed it, or point to a stale in-bounds position.",
      "Whether annotation APIs other than AStatsEvent_addBoolAnnotation call increment_annotation_count.",
      "What the two internal cherry-picked changes contained beyond what landed in 2c6cc7816da3.",
      "Which privileged processes using libstatssocket the bulletin's EoP rating refers to."
     ],
     "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-2024-31312": {
     "cve": "CVE-2024-31312",
     "summary": "Moves enforcement of the Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN user setting from the keyguard-only KeyguardMediaController into MediaHierarchyManager/MediaHost so that, when the setting is disabled, every media host hides the media carousel while the device is in the KEYGUARD or SHADE_LOCKED state.",
     "component": "platform/frameworks/base, SystemUI media controls (packages/SystemUI/src/com/android/systemui/media/controls/ui)",
     "weakness": "Incomplete enforcement of a user privacy setting (lock screen media controls visibility check applied to only one of several display paths), leading to information disclosure",
     "root_cause": "Before the fix, the MEDIA_CONTROLS_LOCK_SCREEN setting was read and observed inside KeyguardMediaController. Its allowMediaPlayerOnLockScreen flag was consulted only in KeyguardMediaController's visibility computation for the media container in the keyguard layout. MediaHierarchyManager kept its own allowMediaPlayerOnLockScreen field, hard-initialised to true and updated by a content observer, but MediaHost.updateViewVisibility did not consult it: a host's visibility depended only on whether active or any media (or a recommendation) existed. As a result, when the user disabled media controls on the lock screen, the carousel was hidden only in the keyguard layout and not everywhere while the device was on the lockscreen (per the commit message).",
     "fix_analysis": [
      "MediaHierarchyManager.kt: allowMediaPlayerOnLockScreen is now initialised from a new private helper getMediaLockScreenSetting() (secureSettings.getBoolForUser(MEDIA_CONTROLS_LOCK_SCREEN, true, USER_CURRENT)) instead of a hard-coded true.",
      "MediaHierarchyManager.kt: a new public fun isLockedAndHidden() returns true when !allowMediaPlayerOnLockScreen and statusbarState is StatusBarState.SHADE_LOCKED or StatusBarState.KEYGUARD.",
      "MediaHost.kt:updateViewVisibility: state.visible is forced to false when mediaHierarchyManager.isLockedAndHidden() is true. This check runs before the existing showsOnlyActiveMedia / hasActiveMediaOrRecommendation / hasAnyMediaOrRecommendation logic. hostView.visibility then becomes GONE and visibleChangedListeners are notified if the visibility changed. This applies to every MediaHost, not only the keyguard one.",
      "MediaHierarchyManager.kt: in the status bar state callback, mediaCarouselController.updateHostVisibility() is now called after updateUserVisibility(), so all hosts recompute visibility on state changes. The tests exercise this through onStateChanged. The content observer for lockScreenMediaPlayerUri now updates allowMediaPlayerOnLockScreen via getMediaLockScreenSetting() and also calls mediaCarouselController.updateHostVisibility().",
      "KeyguardMediaController.kt: removes the setting read, the ContentObserver registration, the allowMediaPlayerOnLockScreen field, lockScreenMediaPlayerUri, and the SecureSettings and @Main Handler constructor parameters. Its visibility condition no longer checks allowMediaPlayerOnLockScreen and relies on mediaHost.visible, which now reflects isLockedAndHidden().",
      "Tests: KeyguardMediaControllerTest drops the two setting-based tests and the FakeSettings/FakeHandler constructor args. MediaHierarchyManagerTest adds keyguardState_allowedOnLockscreen_updateVisibility, keyguardState_notAllowedOnLockscreen_updateVisibility, keyguardGone_updateVisibility and lockscreenSettingChanged_updateVisibility, and stubs mediaCarouselController.updateHostVisibility."
     ],
     "completeness": [
      "The check is centralised in MediaHost.updateViewVisibility, so it covers every MediaHost whose visibility is refreshed through that method, including via the mediaCarouselController.updateHostVisibility lambda that iterates over all registered mediaHosts.",
      "isLockedAndHidden() treats only StatusBarState.KEYGUARD and StatusBarState.SHADE_LOCKED as locked, and it relies on MediaHierarchyManager.statusbarState being kept current. The material does not show whether other locked surfaces (for example the dream overlay) report one of these states.",
      "Host visibility is recomputed on status bar state changes and on setting changes. Any other trigger that calls updateViewVisibility also goes through the check.",
      "The KeyguardMediaController visibility condition still uses mediaHost.visible, which now includes the lock check indirectly."
     ],
     "behavior_changes": [
      "When MEDIA_CONTROLS_LOCK_SCREEN is disabled and the device is in KEYGUARD or SHADE_LOCKED, all media host views become GONE, not only the keyguard layout container. Outside those states, visibility goes back to the existing media-availability logic.",
      "MediaHierarchyManager now reads the setting at construction instead of assuming true until the observer fires.",
      "mediaCarouselController.updateHostVisibility() is now invoked on status bar state changes and on every change of the MEDIA_CONTROLS_LOCK_SCREEN setting.",
      "The KeyguardMediaController constructor signature changed: the SecureSettings and @Main Handler parameters were removed. This affects Dagger injection and any code or tests that construct it directly."
     ],
     "preconditions": [
      "The user has disabled Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN (the getBoolForUser default is true).",
      "The device is in StatusBarState.KEYGUARD or SHADE_LOCKED, and there is media or a recommendation, so a MediaHost would otherwise be visible.",
      "A media host other than the keyguard layout one is shown while locked. The material does not say which hosts were affected."
     ],
     "per_major": {
      "14": "same fix: commit 76d298d2e82c in android-security-14.0.0_r9 (cherry-pick of a441f4acd62c, Change-Id I4f618e4013db894291e6fca9d49bceb1cb7e4bd9); at android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin; the three patched files are absent at these paths in android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; the patched files are absent at these paths in android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; the patched files are absent at these paths in android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix assumes MediaHierarchyManager already has SecureSettings and a @Main Handler injected, a lockScreenMediaPlayerUri ContentObserver, a statusbarState field and the mediaCarouselController.updateHostVisibility lambda. On branches lacking these pieces (the bulletin lists 12/12L/13 as affected, but no commit for them is in the material), they must be introduced first. Dagger wiring and KeyguardMediaControllerTest must also be updated for the removed KeyguardMediaController constructor parameters."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific surface or host showed media content on the lockscreen in the original report (bug 314333719 is not described beyond the commit message), for example whether QS/QQS hosts in SHADE_LOCKED were involved.",
      "Whether and how the fix was applied to Android 12, 12L and 13, which the bulletin lists as affected; no commits for those branches are in the material.",
      "Where the equivalent logic lives in Android 15+ (if anywhere), and whether it carries the same check."
     ],
     "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-2024-31313": {
     "cve": "CVE-2024-31313",
     "summary": "Adds underflow checks to MessageQueueBase::availableToReadBytes() and availableToWriteBytes() in system/libfmq, so that inconsistent read/write counters yield 0 (with an error log) instead of a wrapped size_t.",
     "component": "platform/system/libfmq, Fast Message Queue (include/fmq/MessageQueueBase.h)",
     "weakness": "Integer underflow / missing validation of read/write counter values",
     "root_cause": "The FMQ read and write counters (mReadPtr, mWritePtr, declared as std::atomic<uint64_t>*) were used without consistency checks. availableToReadBytes() returned mWritePtr - mReadPtr, which wraps to a huge value if the write counter is behind the read counter. availableToWriteBytes() returned mDesc->getSize() - availableToReadBytes(), which wraps if the available bytes exceed the queue size. Per the in-code comment, availableToReadBytes() is invoked by implementations of both read() and write().",
     "fix_analysis": [
      "include/fmq/MessageQueueBase.h:availableToReadBytes(): loads mWritePtr and mReadPtr (memory_order_acquire) into local uint64_t writePtr and readPtr. If writePtr < readPtr, it logs 'The write or read pointer has become corrupted...' via hardware::details::logError and returns 0. Otherwise it returns writePtr - readPtr, computed from the same values it checked (commit title: 'Use the values of the ptrs that we check').",
      "include/fmq/MessageQueueBase.h:availableToWriteBytes(): reads queueSizeBytes = mDesc->getSize() and availableBytes = availableToReadBytes(). If queueSizeBytes < availableBytes, it logs the same error and returns 0. Otherwise it returns queueSizeBytes - availableBytes.",
      "Both functions are also marked inline. No other files change. Tested with fmq_fuzzer per the commit message."
     ],
     "completeness": [
      "Covers the two subtraction sites that could underflow: write-minus-read in availableToReadBytes() and size-minus-available in availableToWriteBytes().",
      "availableToReadBytes() does not clamp its result to the queue size. Only availableToWriteBytes() compares against mDesc->getSize(). The material does not show whether other users of availableToReadBytes() separately bound the result.",
      "On detection the functions return 0 and log. The diff does not mark the queue invalid, so later calls recompute from the same counters.",
      "The surrounding context shows MemTransaction::getSlot() uses 'idx > firstRegionLength + secondRegionLength' as its bounds test. This commit does not touch it."
     ],
     "behavior_changes": [
      "When the write counter is below the read counter, availableToReadBytes() now returns 0 (queue appears empty) and logs an error instead of returning a wrapped value.",
      "When available bytes exceed the queue size, availableToWriteBytes() now returns 0 (queue appears full) and logs an error.",
      "availableToReadBytes() and availableToWriteBytes() are now declared inline."
     ],
     "preconditions": [
      "The read/write counters must hold inconsistent values (write counter < read counter, or available bytes > queue size). How an attacker reaches that state is not shown in the material.",
      "The victim must call a path that uses availableToReadBytes() or availableToWriteBytes(). The in-code comment says both read() and write() implementations invoke availableToReadBytes(). Per index search, readBlocking() is exercised by fmq_fuzzer.cpp readerBlocking and by fmq_unit_tests.cpp."
     ],
     "per_major": {
      "14": "same fix: commit 7903ae2f7f31 in android-security-14.0.0_r9 (cherry-pick of 38963310ad57 / da080aa565f0). At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "not affected per the bulletin. At android-security-15.0.0_r17, 5/8 lines of the patched region are present. The material does not show whether the fix is present in another form.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single self-contained change to include/fmq/MessageQueueBase.h. It uses hardware::details::logError, which must be available in the target branch.",
      "No fork comparison was provided in the material, so no per-file fork verdict can be given."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which read/write functions consume the wrapped values and whether they have their own bounds checks (callers of availableToReadBytes/availableToWriteBytes are not shown).",
      "How the counters become inconsistent in practice (shared-memory mapping and peer write access are not shown).",
      "Contents of the upstream commits 38963310ad57 and da080aa565f0, and whether an earlier check existed that re-loaded the pointers.",
      "Whether binaries using this template header must be rebuilt to pick up the fix.",
      "Why 15.0.0_r17 shows only 5/8 lines of the patched region, and whether 16/17 fully contain the fix."
     ],
     "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-2024-31314": {
     "cve": "CVE-2024-31314",
     "summary": "Adds a per-package, time-windowed throttle in ShortcutPackage so that ShortcutService forwards at most REPORT_USAGE_BUFFER_SIZE (3) shortcut-usage reports per mSaveDelayMillis window to UsageStatsManagerInternal.reportShortcutUsage, instead of forwarding every reportShortcutUsed/pushDynamicShortcut call unconditionally.",
     "component": "platform/frameworks/base, system_server ShortcutService (com.android.server.pm.ShortcutService / ShortcutPackage)",
     "weakness": "Missing rate limiting on an app-reachable system_server path (uncontrolled resource consumption leading to DoS, per bulletin type)",
     "root_cause": "ShortcutService.reportShortcutUsed (app-facing via ShortcutManager.reportShortcutUsed) and the pushDynamicShortcut path both called the private helper reportShortcutUsedInternal. That helper cleared the calling identity and invoked mUsageStatsManagerInternal.reportShortcutUsage on every call. On the reportShortcutUsed path, the only gate was that the shortcut id must exist in the caller's package. Nothing limited how often a package could make system_server do this usage-stats work.",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/ShortcutPackage.java: new method reportShortcutUsed(UsageStatsManagerInternal, String shortcutId). Inside synchronized(mLock) it keeps a List<Long> mLastReportedTime of SystemClock.elapsedRealtime() timestamps (new field, @GuardedBy mLock). If the list has fewer than REPORT_USAGE_BUFFER_SIZE (3) entries, the method appends the current timestamp and the report goes ahead. If the list is full and the oldest entry is older than mShortcutUser.mService.mSaveDelayMillis, the method evicts the oldest entry, appends the current timestamp, and the report goes ahead. Otherwise it returns without reporting. The call to usageStatsManagerInternal.reportShortcutUsage(getPackageName(), shortcutId, getUser().getUserId()) is still wrapped in s.injectClearCallingIdentity()/injectRestoreCallingIdentity(token).",
      "services/core/java/com/android/server/pm/ShortcutService.java: mSaveDelayMillis changed from private to package-private so ShortcutPackage can read it. The private helper reportShortcutUsedInternal is deleted.",
      "services/core/java/com/android/server/pm/ShortcutService.java (pushDynamicShortcut path, around line 2285): the call reportShortcutUsedInternal(packageName, shortcut.getId(), userId) is replaced by ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcut.getId()) on the ShortcutPackage ps.",
      "services/core/java/com/android/server/pm/ShortcutService.java reportShortcutUsed(): the ps variable is now declared outside the synchronized(mLock) block. The findShortcutById(shortcutId)==null check still logs a warning and returns early. After the block, the method calls ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcutId) instead of reportShortcutUsedInternal.",
      "Tests: ShortcutManagerTest1.testPushDynamicShortcut and ShortcutManagerTest2.testReportShortcutUsed now set KEY_SAVE_DELAY_MILLIS=1 so their existing expectations are not throttled. The new ShortcutManagerTest1.testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled sets KEY_SAVE_DELAY_MILLIS=500 and checks four things: after the first REPORT_USAGE_BUFFER_SIZE pushes, s1 was reported once; the pushes that follow, up to s10, produce no reports; a second package is throttled independently; and one more report per package goes through after a 505 ms sleep."
     ],
     "completeness": [
      "Both callers of the removed helper are covered: the pushDynamicShortcut path and the public reportShortcutUsed binder entry point now go through ShortcutPackage.reportShortcutUsed.",
      "Throttle state is kept per ShortcutPackage and only in memory. It is not shared across packages, so N packages can each send 3 reports per window. The window length is the existing, configurable mSaveDelayMillis (ConfigConstants.KEY_SAVE_DELAY_MILLIS), not a dedicated constant. Its default is not shown in the material.",
      "Only the forward to UsageStatsManagerInternal.reportShortcutUsage is throttled. The rest of the pushDynamicShortcut work (packageShortcutsChanged, verifyStates) and the findShortcutById lookup in reportShortcutUsed still run on every call. The diff does not change them.",
      "Dropped reports are silent: no log, no return value, no exception."
     ],
     "behavior_changes": [
      "Per ShortcutPackage, at most 3 shortcut-usage reports reach UsageStatsManagerInternal within any mSaveDelayMillis window. Further reportShortcutUsed/pushDynamicShortcut calls in that window are silently dropped and do not update usage stats.",
      "The UsageStatsManagerInternal.reportShortcutUsage call now runs inside the ShortcutPackage synchronized(mLock) block. On the reportShortcutUsed path it used to run after ShortcutService's synchronized(mLock) block.",
      "ShortcutService.mSaveDelayMillis is now package-private instead of private, and reportShortcutUsedInternal is removed.",
      "Tests that expect every push to be reported must set KEY_SAVE_DELAY_MILLIS to a small value. The fix does this in two existing tests."
     ],
     "preconditions": [
      "The caller is an app that publishes shortcuts. ShortcutService.reportShortcutUsed is reached through the public ShortcutManager.reportShortcutUsed API (used, for example, by packages/apps/Contacts DynamicShortcuts.reportShortcutUsed, per index search). It requires the shortcut id to exist in the caller's package (findShortcutById).",
      "Alternatively, the pushDynamicShortcut path, which reports usage of the pushed shortcut.",
      "On the reportShortcutUsed path, the user must not be locked (throwIfUserLockedL)."
     ],
     "per_major": {
      "14": "same fix: commit 6dc0f712dbe7 in android-security-14.0.0_r9 (cherry-pick of bd88f35c6797, Change-Id I96370cbd4f6a55f894c1a93307e5f82dfd394652). At android-security-14.0.0_r29, all 8/8 lines of the patched region are present.",
      "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; 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.",
      "12/12L/13": "listed as affected by the bulletin, but the material has no per-major facts or commits for these branches; status unknown."
     },
     "backport_notes": [
      "Single self-contained commit. It relies on ShortcutService.mSaveDelayMillis, a ShortcutPackage mLock, and mShortcutUser.mService. Check that these exist on the target branch, and keep the throttle list per package.",
      "Add KEY_SAVE_DELAY_MILLIS=1 to ShortcutManagerTest1.testPushDynamicShortcut and ShortcutManagerTest2.testReportShortcutUsed as the fix does, so their existing expectations are not throttled."
     ],
     "confidence": "high",
     "unknowns": [
      "The default value of mSaveDelayMillis, and so the effective throttle window in production, is not shown.",
      "The material does not describe the concrete DoS effect of unthrottled reportShortcutUsage calls in UsageStatsService.",
      "Whether and how the fix was applied to Android 12, 12L and 13, which the bulletin lists as affected.",
      "Whether older branches have the same ShortcutPackage locking structure and mSaveDelayMillis field.",
      "Which lock, if any, was held around the usage report on the pushDynamicShortcut path before the fix."
     ],
     "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-2024-31315": {
     "cve": "CVE-2024-31315",
     "summary": "ManagedServices now checks that a managed service component (e.g. a NotificationListenerService) still declares the required bind permission before rebinding it, and removes approved component entries that lost the permission when their package changes.",
     "component": "platform/frameworks/base, system_server notification service (com.android.server.notification.ManagedServices)",
     "weakness": "missing permission check (the approval list was trusted without re-checking the component's bind permission on rebind)",
     "root_cause": "In services/core/java/com/android/server/notification/ManagedServices.java, two rebind paths only checked that the package or the flattened component name was in the approved list (isPackageOrComponentAllowed): reregisterService, and the enabled branch of the mMutex-guarded path near line 1150. Neither path checked that the resolved ServiceInfo still carried mConfig.bindPermission. As a result, an approved component whose package was updated so that the service no longer declared the bind permission could still be bound. The package-change handling also did not remove such entries from mApproved.",
     "fix_analysis": [
      "ManagedServices.java: adds componentHasBindPermission(ComponentName, int userId). It resolves the component with getServiceInfo(component, userId) and returns true only if info is non-null and mConfig.bindPermission.equals(info.permission).",
      "ManagedServices.java: adds isPackageOrComponentAllowedWithPermission(ComponentName, int userId), which is (approved as flattened component OR approved as package) AND componentHasBindPermission.",
      "ManagedServices.java:reregisterService now calls isPackageOrComponentAllowedWithPermission(cn, userId) before registerService, in place of the two isPackageOrComponentAllowed checks.",
      "ManagedServices.java: the mMutex-guarded path near line 1150 ('if (enabled)') makes the same replacement before registerServiceLocked. If the check fails, it logs 'no longer has permission to be bound'.",
      "ManagedServices.java: adds trimApprovedListsForInvalidServices(packageName, userId). Holding the mApproved lock, it goes through every approval type for the user. Any entry for that package that parses as a ComponentName is removed when componentHasBindPermission is false.",
      "ManagedServices.java: in the package-change loop over uidList (reached from onPackagesChanged in the new test), trimApprovedListsForInvalidServices(pkgName, userId) is called for each uid where isPackageAllowed is true.",
      "ManagedServicesTest.java: the existing reregisterService tests now mock ServiceInfo (mockServiceInfoWithMetaData), because getServiceInfo is now consulted. The new testUpgradeAppNoPermissionNoRebind checks that after onPackagesChanged, a component whose ServiceInfo.permission is not the bind permission is no longer enabled, while a sibling component that keeps the permission stays enabled."
     ],
     "completeness": [
      "Covers the two rebind paths shown in the diff (reregisterService and the mMutex 'enabled' branch), plus the package-change path through trimApprovedListsForInvalidServices.",
      "trimApprovedListsForInvalidServices only removes entries that parse as a ComponentName. Package-level approval entries (plain package names) stay in mApproved. In the two patched paths, the per-component permission check still applies to those entries.",
      "trimApprovedListsForInvalidServices is called only when isPackageAllowed(pkgName, userId) is true in the package-change loop. The material does not show other ways mApproved changes (e.g. XML load, migrateToXml, addApprovedList) re-checking the permission.",
      "The initial binding and discovery paths are not in the diff. The material does not show whether they already filter on the bind permission.",
      "componentHasBindPermission returns false when getServiceInfo returns null, so a component that cannot be resolved is not rebound (fails closed)."
     ],
     "behavior_changes": [
      "reregisterService and the mMutex 'enabled' branch skip binding when the resolved ServiceInfo.permission does not equal the configured bind permission, or when the ServiceInfo cannot be resolved.",
      "On a package change, approved component entries for that package that lack the bind permission are removed from mApproved. With DEBUG on, this logs 'Removing ... from approved list; no bind permission found'.",
      "The patched rebind paths now do an extra getServiceInfo lookup per component.",
      "Tests of reregisterService must mock getServiceInfo, or the rebind no longer happens."
     ],
     "preconditions": [
      "A package has a component or package entry in the ManagedServices approved list (e.g. a user-approved notification listener).",
      "The package is updated so that the approved service component no longer declares the required bind permission (mConfig.bindPermission).",
      "A rebind path is reached: reregisterService (rebinding a package that died), or the mMutex 'enabled' branch."
     ],
     "per_major": {
      "14": "same fix: commit 0a645bd7c46f (cherry-pick of 24b13a64f9f5) in android-security-14.0.0_r9. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "not affected per the bulletin. At android-security-15.0.0_r17, 7/8 lines of the patched region are present.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 5/8 lines of the patched region are present. The reason for the difference is not shown."
     },
     "backport_notes": [
      "Single commit. It depends on existing helpers: getServiceInfo(ComponentName, int), mConfig.bindPermission, getPackageName(String), and the mApproved ArrayMap<Boolean, ArraySet<String>> structure. Check that the target branch's package-change loop has the uidList/isPackageAllowed structure needed to hook in trimApprovedListsForInvalidServices.",
      "The existing ManagedServicesTest reregisterService tests will fail after the backport unless getServiceInfo is mocked. The patch adds mockServiceInfoWithMetaData calls for this."
     ],
     "confidence": "high",
     "unknowns": [
      "The diff context does not show the name of the method that contains the mMutex 'if (enabled)' hunk near line 1150, or what calls it.",
      "Whether the initial discovery and registration paths (not in the diff) already filter components by bind permission.",
      "Which ManagedServices subclasses were the concrete affected surface. The bulletin only gives EoP/High.",
      "Which code calls reregisterService in production.",
      "Why only 5/8 lines of the patched region are 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": 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-2024-31316": {
     "cve": "CVE-2024-31316",
     "summary": "Adds a second checkKeyIntent() check of the result Bundle returned by the authenticator in two result-handling paths of AccountManagerService. The check runs after the Bundle has been modified (KEY_AUTHTOKEN removed) and rejects the response with ERROR_CODE_INVALID_RESPONSE if the intent fails it.",
     "component": "platform/frameworks/base, services/core AccountManagerService (accounts)",
     "weakness": "Insufficient validation of the KEY_INTENT in a Bundle supplied by the authenticator: the intent was not checked again after the Bundle was modified and before it was returned to the caller (check-before-modify gap).",
     "root_cause": "In AccountManagerService.java, result handlers receive a result Bundle from an account authenticator and modify it before forwarding it to the caller's response. Both paths remove KEY_AUTHTOKEN; the first path also removes KEY_PASSWORD when !mIsPasswordForwardingAllowed. The commit message says another verification is needed after Bundle modification: checkKeyIntent() was not run again after these modifications. The location of the original checkKeyIntent call is not in the material.",
     "fix_analysis": [
      "services/core/java/com/android/server/accounts/AccountManagerService.java, first result handler (around line 3612 in the fix, line 3727 at android-security-14.0.0_r29). This handler later processes KEY_ACCOUNT_SESSION_BUNDLE. Right after result.remove(AccountManager.KEY_AUTHTOKEN), the fix adds `if (!checkKeyIntent(Binder.getCallingUid(), result))`. On failure it calls onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, \"invalid intent in bundle returned\") and returns, so the result is not forwarded.",
      "Same file, second result handler (around line 5201): inside the `if (mStripAuthTokenFromResult)` branch, right after result.remove(AccountManager.KEY_AUTHTOKEN), the fix adds the same checkKeyIntent() guard with the same onError and return on failure.",
      "Both changes are pure additions (10 lines). checkKeyIntent itself and the earlier validation are not changed."
     ],
     "completeness": [
      "Covers the two paths where KEY_AUTHTOKEN is removed from the result Bundle before forwarding. In each path the new check sits directly after the modification.",
      "In the second path the new check is only inside the `if (mStripAuthTokenFromResult)` branch. When mStripAuthTokenFromResult is false, no new check is added. The rest of the surrounding code is not shown.",
      "In the first path, the check comes before the later handling of the nested KEY_ACCOUNT_SESSION_BUNDLE (putString of KEY_ACCOUNT_TYPE and encryption at r29 lines 3742-3754). In the shown context the top-level result is not modified again after the check.",
      "The checkKeyIntent() implementation and the original validation site are not in the material, so it cannot be judged whether other forwarding paths lack a check after modification."
     ],
     "behavior_changes": [
      "If an authenticator response's KEY_INTENT fails checkKeyIntent() after the auth token is removed, the caller now gets onError(ERROR_CODE_INVALID_RESPONSE, \"invalid intent in bundle returned\") instead of the result.",
      "No change for responses without an intent or with an intent that passes checkKeyIntent()."
     ],
     "preconditions": [
      "An account authenticator must return a result Bundle to AccountManagerService through one of the patched result handlers.",
      "The result must take one of the two patched paths: the handler that processes KEY_ACCOUNT_SESSION_BUNDLE results, or the handler with mStripAuthTokenFromResult set.",
      "The uid checked is Binder.getCallingUid() when the result handler runs, as in the added code."
     ],
     "per_major": {
      "14": "same fix: commit 3c59b877099d in android-security-14.0.0_r9 (cherry-pick of 36db8a1d61a8, Change-Id I9e45d758a2320328da5664b6341eafe6f285f297); all 7/7 patched lines present at android-security-14.0.0_r29.",
      "15": "not affected (not listed by the bulletin); patched region present (7/7 lines) at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); patched region present (7/7 lines) at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); patched region present (7/7 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file change that only adds code. It requires checkKeyIntent(int, Bundle) to already exist in AccountManagerService on the target branch, because the fix adds calls only, not the method. The bulletin lists 12, 12L and 13 as affected, but the material has no commit or branch facts for those versions."
     ],
     "confidence": "medium",
     "unknowns": [
      "Where the original checkKeyIntent() call is and what it does, and the body of checkKeyIntent() itself.",
      "Why modifying the Bundle makes the earlier check invalid: the commit message states it but the mechanism is not shown.",
      "Whether and how the fix was applied to the Android 12, 12L and 13 branches.",
      "Names of the classes and methods that contain the two patched code paths."
     ],
     "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-2024-31317": {
     "cve": "CVE-2024-31317",
     "summary": "Adds character validation in core/java/android/os/ZygoteProcess.java so that hidden API denylist exemption strings containing '\\n', '\\r' or '\\u0000' are rejected (list cleared, nothing written) before reaching the zygote command socket, and process start arguments containing '\\u0000' now throw ZygoteStartFailedEx alongside the existing newline and carriage return checks.",
     "component": "platform/frameworks/base, core/java/android/os/ZygoteProcess.java (system_server side of the zygote command protocol, hidden API exemption and process start paths)",
     "weakness": "Missing input validation of strings written into a newline-delimited command protocol (embedded control characters in zygote command arguments)",
     "root_cause": "The zygote wire format is a count followed by newline-separated argument strings (see the comment in zygoteSendArgsAndGetResult referencing com.android.internal.os.ZygoteArguments.parseArgs()). The method in ZygoteProcess.java that writes mApiDenylistExemptions to state.mZygoteOutputWriter (HiddenApiSettings.update() per the commit message) did not check the exemption strings for embedded '\\n', '\\r' or NUL before writing the count (size + 1) and the list. Because of this, embedded line breaks could break the newline-based framing. Separately, zygoteSendArgsAndGetResult rejected embedded '\\n' and '\\r' in process start args but not '\\u0000'.",
     "fix_analysis": [
      "core/java/android/os/ZygoteProcess.java:zygoteSendArgsAndGetResult: adds a third branch to the per-argument loop so that any arg with arg.indexOf('\\u0000') >= 0 throws ZygoteStartFailedEx(\"Embedded nulls not allowed\"), before msgStr is built and before either the USAP or the zygote socket path is used.",
      "core/java/android/os/ZygoteProcess.java (method writing mApiDenylistExemptions, HiddenApiSettings.update() per commit message): before writing the count to state.mZygoteOutputWriter, iterates over mApiDenylistExemptions and if any string contains '\\n', '\\r' or '\\u0000' it logs Slog.e(LOG_TAG, \"Failed to set API denylist exemptions: Bad character\"), replaces mApiDenylistExemptions with Collections.emptyList() and returns false, so nothing is written to the zygote for that call and the bad list is not kept."
     ],
     "completeness": [
      "Both places named in the commit message are covered: the exemption write path and the process start argument path.",
      "The exemption validation is all-or-nothing: one bad string discards the entire exemption list rather than filtering the offending entry; the method returns false.",
      "Because the list is replaced with an empty list on failure, a bad list is not re-sent on later calls.",
      "The process start check covers both the USAP path (attemptUsapSendArgsAndGetResult uses the same msgStr) and the regular zygote path, since the loop runs before msgStr is built.",
      "Only '\\n', '\\r' and '\\u0000' are rejected. The material does not show whether the zygote-side parser (com.android.internal.os.ZygoteArguments.parseArgs) validates input independently. It also does not show whether other writers to the zygote socket (e.g. the command around line 394 that calls writer.newLine()/writer.flush()) take caller-controlled strings."
     ],
     "behavior_changes": [
      "Process start requests whose argument strings contain a NUL character now fail early with ZygoteStartFailedEx(\"Embedded nulls not allowed\") instead of being sent to the zygote.",
      "Setting hidden API denylist exemptions containing '\\n', '\\r' or '\\u0000' now logs \"Failed to set API denylist exemptions: Bad character\", clears the stored exemption list to Collections.emptyList(), and the update method returns false; no exemptions are sent to the zygote in that case."
     ],
     "preconditions": [
      "The caller must be able to influence the strings stored in ZygoteProcess.mApiDenylistExemptions; who can set this list is not shown in the material.",
      "For the process start path, the caller must be able to supply process start arguments containing a NUL character to ZygoteProcess.zygoteSendArgsAndGetResult; the material does not show which callers reach this."
     ],
     "per_major": {
      "14": "same fix: commit c0c2c1ef78f2 (cherry-pick of 7ba059e2cf0a) in android-security-14.0.0_r9; all 5 patched lines present at android-security-14.0.0_r29.",
      "15": "not affected per the bulletin; the patched region (5/5 lines) is present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; the patched region (5/5 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; the patched region (5/5 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, self-contained change that anchors on the existing '\\n'/'\\r' loop in zygoteSendArgsAndGetResult and the existing mApiDenylistExemptions write to state.mZygoteOutputWriter. The diff adds no imports, so Slog and Collections are already available in the Android 14 file. The bulletin lists 12, 12L and 13 as affected, but no per-branch material for them is provided here."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact name of the method containing the second hunk is not shown in the diff hunk header; the commit message calls it HiddenApiSettings.update().",
      "Which callers (and with what privilege) can set the hidden API denylist exemptions or supply process start arguments is not shown.",
      "Whether the zygote-side parser (com.android.internal.os.ZygoteArguments.parseArgs) validates input independently is not shown.",
      "How the exemption strings are written after the count, and the concrete impact of the unvalidated characters, are not shown.",
      "Where mApiDenylistExemptions is set is not shown.",
      "How the fix was applied to Android 12, 12L and 13, which the bulletin lists as affected."
     ],
     "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-2024-31318": {
     "cve": "CVE-2024-31318",
     "summary": "The CompanionDeviceManagerService binder implementation no longer overrides onShellCommand and instead overrides handleShellCommand. The base Binder.onShellCommand, which per the commit message checks the caller's permissions, therefore runs before CompanionDeviceShellCommand is executed.",
     "component": "platform/frameworks/base, services/companion (CompanionDeviceManagerService, CDM shell commands)",
     "weakness": "missing permission check (shell command entry point bypassing Binder's caller check)",
     "root_cause": "In services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java, the inner binder class overrode onShellCommand(FileDescriptor, FileDescriptor, FileDescriptor, String[], ShellCallback, ResultReceiver). The override unconditionally constructed a CompanionDeviceShellCommand (with the service's AssociationStore, DevicePresenceMonitor, TransportManager, SystemDataTransferRequestStore and AssociationRequestsProcessor) and called exec on it. Because this replaced Binder.onShellCommand, the caller permission check in Binder.onShellCommand was skipped. The commit message says: \"Binder.onShellCommand checks the necessary permissions of the caller.\"",
     "fix_analysis": [
      "CompanionDeviceManagerService.java (inner binder class): the onShellCommand override is removed and replaced by an override of int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args). The base Binder.onShellCommand is no longer overridden, so its caller permission check applies (per the commit message).",
      "The new handleShellCommand constructs the same CompanionDeviceShellCommand with the same arguments. It calls exec(this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args) and returns the int result. The ShellCallback and ResultReceiver are no longer passed to exec.",
      "The imports of android.os.ResultReceiver and android.os.ShellCallback are removed because nothing uses them any more."
     ],
     "completeness": [
      "The fix covers the single shell-command entry point of the CompanionDeviceManagerService binder. CDM shell commands are now dispatched through the Binder.onShellCommand caller check.",
      "The fix adds no per-command permission checks inside CompanionDeviceShellCommand; it relies on the Binder-level check. No other files are touched.",
      "The material does not show whether other system services in this tree override onShellCommand in the same way."
     ],
     "behavior_changes": [
      "Callers that fail the Binder.onShellCommand permission check can no longer reach CDM shell command execution.",
      "The shell command path now returns an int result through handleShellCommand instead of the void onShellCommand signature. The commit was tested manually on CDM shell commands."
     ],
     "preconditions": [
      "A process must be able to obtain the companion device manager binder and send it a shell command transaction. Before the fix, the Binder.onShellCommand caller check was not applied on that path."
     ],
     "per_major": {
      "14": "same fix: commit 2442fb3428e7 in android-security-14.0.0_r9 (cherry-pick of 1761a0fee9c2). All 8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "not affected per the bulletin; all 8 lines of the patched handleShellCommand region are present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; all 8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; all 8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, single-commit change. The bulletin lists 12, 12L and 13 as affected, but the material shows no commits for them. When backporting, check that the CompanionDeviceShellCommand constructor arguments match that branch's fields. The essential change is to move the body from an onShellCommand override into a handleShellCommand(ParcelFileDescriptor x3, String[]) override that returns int. Binder.handleShellCommand with ParcelFileDescriptor parameters exists from Android 11 onward (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the commits applied to Android 12, 12L and 13, which the bulletin lists as affected.",
      "Exactly which callers Binder.onShellCommand permits is not shown in the material.",
      "How ShellCallback and ResultReceiver are handled after the change is not shown.",
      "The exact name of the inner binder class is not visible in the diff hunk.",
      "The set of CDM shell commands exposed by CompanionDeviceShellCommand, and the impact of each, are not shown.",
      "Whether reaching the code depends on any device feature being present is not shown."
     ],
     "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-2024-31319": {
     "cve": "CVE-2024-31319",
     "summary": "Adds a URI read-permission check in NotificationManagerService.updateNotificationChannelFromPrivilegedListener so a privileged (CDM) NotificationListenerService can only set a new channel sound URI that its own UID is allowed to read.",
     "component": "platform/frameworks/base, services/core notification (NotificationManagerService, privileged listener channel update)",
     "weakness": "Missing permission check (no URI permission verification on caller-supplied content URI)",
     "root_cause": "updateNotificationChannelFromPrivilegedListener only called verifyPrivilegedListener(token, user, false) and then passed the caller-supplied NotificationChannel straight to updateNotificationChannelInt. The channel's sound URI (channel.getSound()) was accepted without checking that the calling UID has read access to that URI, so a privileged listener could store a content URI as a channel sound in another package's channel without holding permission for it.",
     "fix_analysis": [
      "services/core/java/com/android/server/notification/NotificationManagerService.java:updateNotificationChannelFromPrivilegedListener: after verifyPrivilegedListener, looks up the existing channel via mPreferencesHelper.getNotificationChannel(pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true) and calls the new verifyPrivilegedListenerUriPermission(Binder.getCallingUid(), channel, originalChannel) before updateNotificationChannelInt.",
      "services/core/java/com/android/server/notification/NotificationManagerService.java:verifyPrivilegedListenerUriPermission (new): if updateChannel.getSound() is non-null and differs (Objects.equals) from the original channel's sound, it calls mUgmInternal.checkGrantUriPermission(sourceUid, null, ContentProvider.getUriWithoutUserId(soundUri), Intent.FLAG_GRANT_READ_URI_PERMISSION, ContentProvider.getUserIdFromUri(soundUri, UserHandle.getUserId(sourceUid))) inside Binder.withCleanCallingIdentity. The check is made against the caller's UID (sourceUid), so a SecurityException aborts the update before updateNotificationChannelInt runs.",
      "services/tests/uiservicestests/.../NotificationManagerServiceTest.java: adds testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission (expects SecurityException, no updateNotificationChannel, no listener notification) and ..._sameSound (unchanged sound URI is not re-checked and the update proceeds)."
     ],
     "completeness": [
      "Covers only the sound URI of the channel, and only when it is non-null and different from the currently stored channel's sound; an unchanged sound URI is deliberately not re-checked (verified by the _sameSound test).",
      "Only the privileged-listener entry point updateNotificationChannelFromPrivilegedListener is changed. Other channel update entry points in the same service (e.g. updateNotificationChannelForPackage, seen in the test context) are not touched by this commit; the material does not show whether they already perform an equivalent check.",
      "If no original channel exists (originalChannel == null), the check still runs against any non-null new sound URI."
     ],
     "behavior_changes": [
      "updateNotificationChannelFromPrivilegedListener now throws SecurityException (propagated from mUgmInternal.checkGrantUriPermission) when the caller lacks read permission for a newly supplied sound URI; in that case no channel update and no notifyNotificationChannelChanged occur.",
      "One additional mPreferencesHelper.getNotificationChannel lookup and a second getUidForPackageAndUser call per update; the permission check itself runs with cleared calling identity."
     ],
     "preconditions": [
      "Caller must be a NotificationListenerService that passes verifyPrivilegedListener for the target user (the tests satisfy this via a CompanionDeviceManager association, mCompanionMgr.getAssociations).",
      "The supplied NotificationChannel must carry a non-null sound URI that differs from the sound of the existing channel (or no existing channel is found)."
     ],
     "per_major": {
      "14": "same fix: commit f428043dbaaf in android-security-14.0.0_r9 (cherry-pick of 9b7bbbf5ad54); at android-security-14.0.0_r29 all 8/8 lines of the patched region are present.",
      "15": "not affected (not listed by the bulletin); 8/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); 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 commit; it relies on the existing mUgmInternal field in NotificationManagerService, ContentProvider.getUriWithoutUserId/getUserIdFromUri and Binder.withCleanCallingIdentity. The bulletin also lists 12, 12L and 13 as affected but the material only shows the 14 commit; for those branches confirm mUgmInternal exists in NMS and that the test fixture exposes the mUgmInternal mock.",
      "When adapting, ensure verifyPrivilegedListenerUriPermission is called before updateNotificationChannelInt."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how the stored sound URI is later consumed, only that the URI was accepted without a permission check.",
      "Whether sibling channel update/create paths (e.g. updateNotificationChannelForPackage) already validate sound URI access is not visible in the material.",
      "The exact code applied to Android 12/12L/13 is not shown; only the Android 14 commit is provided.",
      "Which line of the patched region differs on Android 16/17 (7/8 match) is not shown.",
      "Whether updateNotificationChannelInt creates a channel when none exists is not shown."
     ],
     "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-2024-31322": {
     "cve": "CVE-2024-31322",
     "summary": "AccessibilityManagerService.updateServicesLocked now removes components that are no longer installed from the user's enabled accessibility services list and touch-exploration-granted list and persists both settings, so a service that disappears after a service update does not stay enabled.",
     "component": "platform/frameworks/base, services/accessibility (AccessibilityManagerService)",
     "weakness": "Stale privileged state not revoked: the enabled and granted accessibility service lists were not cleaned up when the service component left the installed set (improper cleanup / missing state invalidation)",
     "root_cause": "In services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java, updateServicesLocked(AccessibilityUserState) iterated userState.mInstalledServices to manage service connections, but never reconciled userState.mEnabledServices or userState.mTouchExplorationGrantedServices against the installed set. When a service update removed the accessibility service component (test name: testUpdateServiceWithoutIntent_disablesService), its ComponentName stayed in both in-memory lists and in the persisted Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES and TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES values, so the enabled and granted state survived the removal of the service. The bulletin rates this as a High EoP.",
     "fix_analysis": [
      "AccessibilityManagerService.java:updateServicesLocked: mTempComponentNameSet is cleared before the installed-services loop. At the top of the loop, each installed service's ComponentName (ComponentName.unflattenFromString(installedService.getId())) is added to it before any 'continue', so services that are not direct-boot-aware, still binding or crashed are still counted as installed.",
      "AccessibilityManagerService.java:updateServicesLocked: after mActivityTaskManagerService.setAccessibilityServiceUids(mTempIntArray), userState.mEnabledServices.removeIf(comp -> !mTempComponentNameSet.contains(comp)) || userState.mTouchExplorationGrantedServices.removeIf(...) drops components that are not installed.",
      "If anyServiceRemoved is true, persistComponentNamesToSettingLocked is called for Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES with userState.mEnabledServices, and for Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES with userState.mTouchExplorationGrantedServices, both for userState.mUserId. mTempComponentNameSet is then cleared, and updateAccessibilityEnabledSettingLocked(userState) runs as before."
     ],
     "completeness": [
      "Covers both in-memory lists (mEnabledServices and mTouchExplorationGrantedServices) and their persisted Settings.Secure values, for the user whose state is being updated.",
      "The two removeIf calls are joined with '||', which short-circuits. When mEnabledServices.removeIf returns true, mTouchExplorationGrantedServices.removeIf does not run in that pass, so the touch-exploration-granted list is persisted without pruning. That list is pruned only in a pass where nothing is removed from mEnabledServices, for example a later updateServicesLocked pass.",
      "Pruning happens only when updateServicesLocked runs. The diff does not touch other code that reads these lists or settings.",
      "The material shows only the Android 14 frameworks/base commit. The bulletin also lists 12, 12L and 13 as affected."
     ],
     "behavior_changes": [
      "Enabled accessibility services whose component is no longer in userState.mInstalledServices are now removed from the enabled list (and, subject to the '||' short-circuit, from the touch-exploration-granted list), and both Settings.Secure values are rewritten.",
      "Settings are written from within updateServicesLocked through persistComponentNamesToSettingLocked, and only when something was removed."
     ],
     "preconditions": [
      "An accessibility service was enabled (and/or granted touch exploration) for a user, so its ComponentName is in userState.mEnabledServices and/or mTouchExplorationGrantedServices and is persisted in Settings.Secure.",
      "The service is later updated so that its component is no longer in userState.mInstalledServices, and updateServicesLocked runs with the stale entry (the fix's test is testUpdateServiceWithoutIntent_disablesService)."
     ],
     "per_major": {
      "14": "same fix: commit c1bc907a649a ([RESTRICT AUTOMERGE] cherry-pick of f6192d3a7752, Change-Id Ia86857d58ebab925ec6e55f9e5fa64e265326ec0) in android-security-14.0.0_r9; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "not affected (per the bulletin); the patched region is fully present (8/8 lines) at android-security-15.0.0_r17.",
      "16": "not affected (per the bulletin); the patched region is fully present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "not affected (per the bulletin); 6/8 patched lines present at android-security-17.0.0_r1; the material does not show the difference."
     },
     "backport_notes": [
      "Single-file, additive change inside updateServicesLocked. It relies on the existing members mTempComponentNameSet and persistComponentNamesToSettingLocked and on the two Settings.Secure keys, which must exist in the target branch. When backporting, decide whether to keep the '||' short-circuit between the two removeIf calls or to evaluate both (see completeness)."
     ],
     "confidence": "high",
     "unknowns": [
      "Commits for Android 12, 12L and 13 (listed as affected by the bulletin) are not in the material.",
      "What differs in the Android 17 region, where only 6/8 patched lines match.",
      "The body of persistComponentNamesToSettingLocked (for example, how it handles empty sets and calling identity).",
      "The exact update scenario and its consequence, beyond the commit title and test name."
     ],
     "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-2024-31323": {
     "cve": "CVE-2024-31323",
     "summary": "Adds the HIDE_OVERLAY_WINDOWS permission to the Health Connect controller APK manifest and sets SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS in onCreate of seven Health Connect activities so non-system apps cannot show overlay windows on the Health Connect UI.",
     "component": "platform/packages/modules/HealthFitness, Health Connect controller UI (apk/)",
     "weakness": "Missing protection against UI overlay / tapjacking (improper restriction of rendered UI layers); no SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS on sensitive activities",
     "root_cause": "The Health Connect controller activities (main, data management, migration, onboarding, permission request, settings, exercise route request) created their windows without SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, and the APK did not request android.permission.HIDE_OVERLAY_WINDOWS. Because of this, windows drawn by non-system apps could stay visible over Health Connect screens, including the permission request and route request UI. The fix comment states this directly: \"This flag ensures a non system app cannot show an overlay on Health Connect. b/313425281\".",
     "fix_analysis": [
      "apk/AndroidManifest.xml: adds <uses-permission android:name=\"android.permission.HIDE_OVERLAY_WINDOWS\" /> with the comment 'Used to disable non system apps from showing overlays'.",
      "apk/src/com/android/healthconnect/controller/MainActivity.kt:MainActivity.onCreate: calls window.addSystemFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) right after super.onCreate and before setContentView.",
      "apk/src/com/android/healthconnect/controller/data/DataManagementActivity.kt:DataManagementActivity.onCreate: same addSystemFlags call before setContentView.",
      "apk/src/com/android/healthconnect/controller/migration/MigrationActivity.kt:MigrationActivity.onCreate: same addSystemFlags call before setContentView.",
      "apk/src/com/android/healthconnect/controller/onboarding/OnboardingActivity.kt:OnboardingActivity.onCreate: same addSystemFlags call before setContentView.",
      "apk/src/com/android/healthconnect/controller/permissions/request/PermissionsActivity.kt:PermissionsActivity.onCreate: adds window.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) (statically imported constant) after setContentView, still within onCreate and before the EXTRA_PACKAGE_NAME check.",
      "apk/src/com/android/healthconnect/controller/permissions/shared/SettingsActivity.kt:SettingsActivity.onCreate: same addSystemFlags call before setContentView.",
      "apk/src/com/android/healthconnect/controller/route/RouteRequestActivity.kt:RouteRequestActivity.onCreate: same addSystemFlags call after super.onCreate and before the isExerciseRouteEnabled check."
     ],
     "completeness": [
      "The flag is applied to seven activities in the controller APK: MainActivity, DataManagementActivity, MigrationActivity, OnboardingActivity, PermissionsActivity, SettingsActivity and RouteRequestActivity. All calls are in onCreate, which would normally run before the window is first shown.",
      "The manifest excerpt only lists MainActivity. The material does not show whether the APK declares other activities or dialog-style windows not covered by this change.",
      "In PermissionsActivity the call comes after setContentView rather than before, unlike the other six activities. Both are in onCreate, so the ordering difference is probably harmless, but it is an inconsistency in the diff.",
      "The fix only acts at the window level; the diff does not change any intent validation or permission-granting logic in these activities."
     ],
     "behavior_changes": [
      "While any of the seven Health Connect activities is in the foreground, the window manager hides overlay windows belonging to non-system apps (effect of SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) (general platform knowledge).",
      "The controller APK now requests android.permission.HIDE_OVERLAY_WINDOWS.",
      "The diff does not change Health Connect screen content, error codes or intent handling."
     ],
     "preconditions": [
      "A Health Connect controller activity (e.g. PermissionsActivity, RouteRequestActivity, SettingsActivity, MainActivity) is in the foreground on the device.",
      "A non-system app on the device can draw overlay windows (the diff comment names non-system overlays as the issue); the material does not give further trigger details."
     ],
     "per_major": {
      "14": "same fix: commit c4e13d15e8dd in android-security-14.0.0_r9 (cherry-pick of 85c7c82115c8); at android-security-14.0.0_r29 all 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin; at android-security-15.0.0_r17, 8/8 lines of the patched region are present.",
      "16": "not affected per the bulletin; at android-security-16.0.0_r8, 7/8 lines of the patched region are present (the material does not say which line differs).",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1, 7/8 lines of the patched region are present (the material does not say which line differs)."
     },
     "backport_notes": [
      "Single commit, additions only (28 inserted lines across 8 files, no deletions). The manifest permission and the eight addSystemFlags calls are meant to go together."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether the controller APK declares any other activities not touched by this commit (the manifest excerpt only shows MainActivity).",
      "The specific overlay scenario reported in b/313425281 / b/328561789 beyond what the code comment states.",
      "Which line of the patched region differs at the Android 16/17 tags (7/8 rather than 8/8).",
      "Which permission or privilege the framework requires before honouring SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, and whether the controller APK meets it on a given fork."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2024-31324": {
     "cve": "CVE-2024-31324",
     "summary": "WindowState.hide() now decides whether to defer the visibility change based on whether an exit animation was actually applied to the window itself (return value of mWinAnimator.applyAnimationLocked), instead of checking isAnimating(TRANSITION | PARENTS), so a window with no hide animation is committed invisible immediately even when its parent container is animating.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Incorrect condition / state-handling logic error: visibility update deferred to an animation-finish callback that can never fire, leaving a policy-hidden window on screen",
     "root_cause": "In WindowState.hide(doAnimation, requestAnim), after calling mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false), the code only fell back to immediate hiding when !isAnimating(TRANSITION | PARENTS). Per the commit message, commit 9bca6b4 overextended this condition to include the parent container. If the parent was animating but the window itself had no exit animation, doAnimation stayed true and the window waited for onAnimationFinished -> checkPolicyVisibilityChange to hide it. That callback never arrives because the window is not animating, so the window that should have been hidden remained on screen.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowState.java:hide(): replaces the unconditional applyAnimationLocked call followed by `if (!isAnimating(TRANSITION | PARENTS)) doAnimation = false;` with `if (!mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false /* isEntrance */)) doAnimation = false;`. The decision is now tied to whether a hide animation was applied to this window: if yes, onAnimationFinished -> checkPolicyVisibilityChange hides it later; if no, doAnimation is cleared so the window is committed invisible immediately, regardless of parent animation state.",
      "services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java:testIsOnScreen_hiddenByPolicy: adds two cases: (1) parent started animating via window.getParent().startAnimation(...) and window.mAttrs.windowAnimations = 0, then hide(true, true) must yield !isSelfAnimating, !isVisibleByPolicy, !isOnScreen; (2) with mAttrs.windowAnimations = android.R.style.Animation_Dialog, hide(true, true) leaves the window self-animating and still visible by policy until cancelAnimation(), after which it is not visible by policy."
     ],
     "completeness": [
      "Covers the hide() path only: any caller of WindowState.hide(doAnimation=true, ...) now gets immediate hiding when no exit animation is applied to the window itself.",
      "The fix depends on mWinAnimator.applyAnimationLocked returning false exactly when no animation was applied; that method is not in the material, so its return semantics are assumed from the comment and tests.",
      "The show() path is not touched by the diff and its logic is not shown in the material.",
      "The regression test exercises both the no-animation-with-animating-parent case and the with-animation case."
     ],
     "behavior_changes": [
      "A window whose hide() is requested with doAnimation=true but which has no exit animation (e.g. mAttrs.windowAnimations == 0) is now hidden immediately even if its parent container is animating; previously it remained on screen because the animation-finish callback never fired.",
      "A window that does get an exit animation keeps the previous behavior: it remains visible by policy until the animation finishes (onAnimationFinished -> checkPolicyVisibilityChange)."
     ],
     "preconditions": [
      "WindowState.hide() is invoked with doAnimation=true (for example WindowState.setHiddenWhileSuspended(true) calls hide(true /* doAnimation */, true /* requestAnim */), per the test context in WindowStateTests).",
      "The window's parent container is currently animating (isAnimating(TRANSITION | PARENTS) true via the parent).",
      "mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false) applies no animation to the window itself (e.g. no window animation style)."
     ],
     "per_major": {
      "14": "same fix: cherry-pick 1ec80672496e in android-security-14.0.0_r9 (from 9add9281ffc1 / 10a7f0914c87); at android-security-14.0.0_r29 the patched region is fully present (7/7 lines).",
      "15": "not affected (not listed by the bulletin); at android-security-15.0.0_r17 the patched region is present (7/7 lines).",
      "16": "not affected (not listed by the bulletin); at android-security-16.0.0_r8 the patched region is present (7/7 lines).",
      "17": "not affected (not listed by the bulletin); at android-security-17.0.0_r1 6/7 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-commit, two-line logic change in WindowState.hide(); it uses the boolean return of mWinAnimator.applyAnimationLocked to indicate whether an animation was applied, so that return value must exist on the target branch.",
      "The test addition uses window.getParent().startAnimation(mTransaction, mock(AnimationAdapter.class), false, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION), isSelfAnimating and cancelAnimation; these helpers must exist in the target branch's wmtests."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete user-visible scenario and which window/policy path makes this an EoP per the bulletin is not shown in the material.",
      "The content of the referenced regression commit 9bca6b4 and the implementation of mWinAnimator.applyAnimationLocked are not in the material, nor which branches carry the 9bca6b4 form.",
      "Whether WindowState.show() has an analogous parent-animation condition is not shown.",
      "Which line of the patched region differs at android-security-17.0.0_r1 is not shown."
     ],
     "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-2024-31325": {
     "cve": "CVE-2024-31325",
     "summary": "SystemUI now constructs each ExpandableNotificationRow's NotificationInlineImageResolver with a Context bound to the notification's own user (via createContextAsUser when it differs from SystemUI's user) instead of SystemUI's application context, so inline message/conversation image Uris are resolved with the notification's user id.",
     "component": "platform/frameworks/base, SystemUI notification row inflation (ExpandableNotificationRow / NotificationInlineImageResolver / RowInflaterTask)",
     "weakness": "Cross-user confused deputy: content Uris from a notification were resolved under SystemUI's own user id rather than the notification's user (missing user-id scoping of Uri resolution)",
     "root_cause": "ExpandableNotificationRow(Context, AttributeSet) passed its context to NotificationInlineImageResolver. The resolver constructor replaced it with context.getApplicationContext(), SystemUI's own application context, and stored that in mContext for image Uri resolution. Message/conversation image Uris carried in a notification were therefore resolved with SystemUI's user id, whichever user posted the notification. RowInflaterTask inflated the row from R.layout.status_bar_notification_row through a plain AsyncLayoutInflater, which had no way to pass the NotificationEntry (and thus the user) into the row constructor.",
     "fix_analysis": [
      "ExpandableNotificationRow.java: adds a new constructor ExpandableNotificationRow(Context, AttributeSet, NotificationEntry) that delegates to a private constructor (Context sysUiContext, AttributeSet, Context userContext). The view itself is still built with the SystemUI context, but mImageResolver is created with userContext.",
      "ExpandableNotificationRow.java:userContextForEntry: returns the base context when base.getUserId() equals entry.getSbn().getNormalizedUserId(); otherwise returns base.createContextAsUser(UserHandle.of(normalizedUserId), 0). This binds Uri resolution to the notification's user.",
      "ExpandableNotificationRow.java: the two-argument constructor is kept but now logs Log.wtf(TAG, \"This constructor shouldn't be called\") and uses the passed context as the user context. mImageResolver becomes final.",
      "NotificationInlineImageResolver.java:constructor: stores the passed context directly (mContext = context) instead of context.getApplicationContext(), so the user-bound context is not discarded. It also adds a @VisibleForTesting getContext() accessor.",
      "RowInflaterTask.java: constructs AsyncLayoutInflater with a new RowAsyncLayoutInflater (implements androidx AsyncLayoutFactory) that holds the NotificationEntry. Its onCreateView intercepts the tag named ExpandableNotificationRow.class.getName() and creates the row with the new three-argument constructor. All other tags return null.",
      "Tests: ExpandableNotificationRowTest adds imageResolver_sameNotificationUser_usesContext and imageResolver_differentNotificationUser_createsUserContext. NotificationTestHelper.generateRow now builds the NotificationEntry first and installs RowInflaterTask.RowAsyncLayoutInflater as Factory2 before inflating. SysuiTestableContext gains prepareCreateContextAsUser and a createContextAsUser override."
     ],
     "completeness": [
      "Covers rows inflated through RowInflaterTask, the inflation path changed in the diff. The resolver's context is now scoped to the notification's user. NotificationInlineImageResolver uses it for Uri loads, and per index search NotificationInlineImageCache preload tasks use the resolver set through setImageResolver.",
      "The two-argument ExpandableNotificationRow(Context, AttributeSet) constructor remains and falls back to the caller's context, logging Log.wtf. Any other code that inflates status_bar_notification_row without RowAsyncLayoutInflater would still get a resolver bound to SystemUI's user. The material shows no such production caller.",
      "User selection relies on StatusBarNotification.getNormalizedUserId(). How USER_ALL notifications are handled depends on that normalization, which this patch does not change.",
      "Only the inline image resolver path is changed. The material does not show whether other code that loads notification-supplied Uris needed changes."
     ],
     "behavior_changes": [
      "For notifications whose normalized user differs from SystemUI's user, a per-row Context is created with createContextAsUser and used for image Uri resolution.",
      "NotificationInlineImageResolver no longer unwraps to the application context; it keeps whatever Context it is given.",
      "Inflating ExpandableNotificationRow through the two-argument constructor now emits a Log.wtf.",
      "RowInflaterTask now always installs a custom AsyncLayoutFactory on its AsyncLayoutInflater."
     ],
     "preconditions": [
      "A notification carrying image Uris in message/conversation content resolved by NotificationInlineImageResolver is posted, and SystemUI inflates its row via RowInflaterTask.",
      "The notification's normalized user differs from the user SystemUI runs as (multi-user or profile setup).",
      "Exact trigger details and the affected content providers are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit d147a3319793 in android-security-14.0.0_r9 (cherry-pick of 44524f06f9d6); at android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin (not listed); 8/8 lines of the patched region are present at android-security-15.0.0_r17, consistent with the fix being present.",
      "16": "not affected per the bulletin (not listed); 7/8 lines of the patched region are present at android-security-16.0.0_r8; which line differs is not shown.",
      "17": "not affected per the bulletin (not listed); 7/8 lines of the patched region are present at android-security-17.0.0_r1; which line differs is not shown."
     },
     "backport_notes": [
      "Single-commit fix. It depends on the AsyncLayoutInflater(Context, AsyncLayoutFactory) constructor and AsyncLayoutFactory from androidx asynclayoutinflater, plus Context.createContextAsUser and StatusBarNotification.getNormalizedUserId, as used in the diff.",
      "Any downstream code or test that inflates status_bar_notification_row directly must install RowInflaterTask.RowAsyncLayoutInflater (as NotificationTestHelper now does). Otherwise rows fall back to the SystemUI-user resolver and log wtf."
     ],
     "confidence": "high",
     "unknowns": [
      "The fix status for Android 12, 12L and 13, which the bulletin lists as affected, is not shown in the per-major facts.",
      "Whether 12/12L/13 branches have the androidx AsyncLayoutFactory API that the fix needs is not shown.",
      "Which content providers or user data could be reached through the misresolved Uris is not shown in the material.",
      "Whether other SystemUI code paths that load notification-supplied Uris had the same user-id issue is not shown.",
      "Which line of the patched region differs on Android 16 and 17 is not shown."
     ],
     "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-2024-31326": {
     "cve": "CVE-2024-31326",
     "summary": "Adds a one-time, persisted \"post upgrade\" migration step in DevicePolicyManagerService that pushes screen-capture-disabled, lock task, and legacy user restriction policies held by device/profile owner ActiveAdmins into the DevicePolicyEngine, because the initial Android T to U migration did not carry them over correctly.",
     "component": "platform/frameworks/base, services/devicepolicy (DevicePolicyManagerService, Owners, OwnersData)",
     "weakness": "Incomplete data migration leading to missing policy enforcement: admin-set policies (screen capture restriction, lock task, user restrictions) were not properly carried into the DevicePolicyEngine on upgrade from Android 13 to 14.",
     "root_cause": "On upgrade from Android T to Android U, migratePoliciesToDevicePolicyEngine() in DevicePolicyManagerService.java migrated legacy ActiveAdmin state into the DevicePolicyEngine, but three policy groups were not migrated properly: (1) migrateScreenCapturePolicyLocked() returned early when mPolicyCache.getScreenCaptureDisallowedUser() == UserHandle.USER_NULL, skipping the DO / org-owned-PO parent disableScreenCapture flag in that case; (2) lock task packages/features (DevicePolicyData.mLockTaskPackages / mLockTaskFeatures) had no migration; (3) user restrictions stored in ActiveAdmin.ensureUserRestrictions() (own and parent admin) had no migration, so per the second commit message they could not be queried after the OTA (its test notes that DISALLOW_FACTORY_RESET set by a device owner stopped applying). The initial migration is gated by shouldMigrateToDevicePolicyEngine(), which includes !mOwners.isMigratedToPolicyEngine(), so an already-migrated device would not re-run it to repair the gap.",
     "fix_analysis": [
      "Commit 736462a777d0, OwnersData.java: adds persisted boolean mPoliciesMigratedPostUpdate serialized as attribute \"migratedPostUpgrade\" on the existing policy-engine-migration tag (written alongside migratedToPolicyEngine, read in the TAG_POLICY_ENGINE_MIGRATION parser case). Owners.java: adds markPostUpgradeMigration() (sets flag and calls mData.writeDeviceOwner()) and isMigratedPostUpdate(), both under synchronized(mData).",
      "Commit 736462a777d0, DevicePolicyManagerService.java:systemReady(PHASE_ACTIVITY_MANAGER_READY): after the existing shouldMigrateToDevicePolicyEngine()/migratePoliciesToDevicePolicyEngine() block, calls new maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked(), which runs only if mOwners.isMigratedToPolicyEngine() is true and isMigratedPostUpdate() is false, then calls migratePoliciesPostUpgradeToDevicePolicyEngineLocked() and markPostUpgradeMigration().",
      "Commit 736462a777d0, DevicePolicyManagerService.java:migratePoliciesPostUpgradeToDevicePolicyEngineLocked(): new method that runs migrateScreenCapturePolicyLocked() and new migrateLockTaskPolicyLocked() inside try/catch(Exception), logging and returning false on failure.",
      "Commit 736462a777d0, DevicePolicyManagerService.java:migrateScreenCapturePolicyLocked(): removes the early return on mPolicyCache.getScreenCaptureDisallowedUser() == USER_NULL so the DO / org-owned PO parent disableScreenCapture flag is always evaluated; the call is removed from migratePoliciesToDevicePolicyEngine() and now lives only in the post-upgrade step.",
      "Commit 736462a777d0, DevicePolicyManagerService.java:migrateLockTaskPolicyLocked() + setLockTaskPolicyInPolicyEngine(): for the device owner's user and for every user with a profile owner where canDPCManagedUserUseLockTaskLocked(userId), if DevicePolicyData.mLockTaskPackages is non-empty, writes PolicyDefinition.LOCK_TASK with new LockTaskPolicy(new HashSet<>(packages), features) via mDevicePolicyEngine.setLocalPolicy for an EnforcingAdmin.createEnterpriseEnforcingAdmin of that admin.",
      "Commit 736462a777d0, DevicePolicyManagerService.java: the MANAGE_PROFILE_AND_DEVICE_OWNERS-gated forced migration entry point (around line 24151) now executes inside synchronized(getLockObject()) and runs migratePoliciesToDevicePolicyEngine() followed by migratePoliciesPostUpgradeToDevicePolicyEngineLocked(), returning the AND of both results.",
      "Commit f1eb8e719dfb, DevicePolicyManagerService.java:setBackwardCompatibleUserRestriction(): refactored to compute an ownerType (OWNER_TYPE_DEVICE_OWNER / OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE / OWNER_TYPE_PROFILE_OWNER) from the CallerIdentity and delegate to new setBackwardCompatibleUserRestrictionLocked(ownerType, admin, userId, key, enabled, parent), which keeps the previous global-vs-local routing (UserRestrictionsUtils.isGlobal, getProfileParentId for parent).",
      "Commit f1eb8e719dfb, DevicePolicyManagerService.java:migrateUserRestrictionsLocked(): new method added to migratePoliciesPostUpgradeToDevicePolicyEngineLocked() after the lock task migration; for each user from mUserManager.getUsers() with getProfileOwnerOrDeviceOwnerLocked(), determines ownerType (throwing IllegalStateException on invalid DO/PO state), then calls setBackwardCompatibleUserRestrictionLocked(..., enabled=true, parent=false) for every key in admin.ensureUserRestrictions() and (..., enabled=true, parent=true) for every key in admin.getParentActiveAdmin().ensureUserRestrictions().",
      "Commit 736462a777d0, DevicePolicyManagerServiceMigrationTest.java: three COMP migration tests (testCompMigrationUnAffiliated_skipped, testCompMigration_keepSuspendedAppsWhenDpcIsRPlus, testCompMigration_unsuspendAppsWhenDpcNotRPlus) are marked @Ignore(\"b/277916462\"); no new test coverage is added (Test: manual)."
     ],
     "completeness": [
      "Covers: DO and org-owned-PO (parent) disableScreenCapture; DO and PO lock task packages/features; own and parent user restrictions of every DO/PO ActiveAdmin. On boot it runs once, guarded by the new persisted migratedPostUpgrade flag; the forced migration entry point runs it without checking or setting that flag.",
      "maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked() ignores the boolean returned by migratePoliciesPostUpgradeToDevicePolicyEngineLocked() and calls markPostUpgradeMigration() unconditionally, so if any sub-migration throws (caught and logged), the remaining sub-migrations are skipped and the boot path does not retry on later boots.",
      "migrateLockTaskPolicyLocked() skips users whose mLockTaskPackages is empty (so mLockTaskFeatures alone is not migrated) and carries a TODO about persistent preferred activities, which are not migrated.",
      "migrateScreenCapturePolicyLocked() only handles the device owner's own flag and the org-owned PO's parent admin flag; the disableScreenCapture flag of a non-parent profile owner ActiveAdmin is not migrated by this method (whether another path handles it is not shown).",
      "migrateUserRestrictionsLocked() only migrates keys present in ensureUserRestrictions() as enabled=true, only for the DO/PO of each user, and dereferences admin.getParentActiveAdmin() directly; a null would throw, be caught by the outer try/catch, and abort the rest of the step.",
      "The post-upgrade step runs at boot only when mOwners.isMigratedToPolicyEngine() is true; whether it also runs on the same boot as an initial migration depends on whether migratePoliciesToDevicePolicyEngine() sets that flag, which is not shown. Since screen capture migration was removed from the initial migration, it depends on the post-upgrade step running."
     ],
     "behavior_changes": [
      "New persisted attribute migratedPostUpgrade under the policy-engine-migration tag in the owners data, written via mData.writeDeviceOwner().",
      "At PHASE_ACTIVITY_MANAGER_READY, already-migrated devices perform a one-time re-application of screen capture, lock task and user restriction policies into the DevicePolicyEngine.",
      "Screen capture migration no longer depends on the policy cache state; it is driven solely by ActiveAdmin.disableScreenCapture.",
      "The forced migration entry point now holds the DPMS lock across the whole operation, also runs the post-upgrade step, and returns false if either step fails.",
      "Legacy user restrictions are re-set as enabled=true through setBackwardCompatibleUserRestrictionLocked, using the same global/local routing as setBackwardCompatibleUserRestriction().",
      "Three COMP migration unit tests are disabled."
     ],
     "preconditions": [
      "Device with a device owner or profile owner (including org-owned profile owner) that had set screen capture disabled, lock task packages, or user restrictions while on Android 13 (T).",
      "Device upgraded via OTA to an Android 14 build where the initial DevicePolicyEngine migration ran (mOwners.isMigratedToPolicyEngine() true) before this fix; per the second commit's test description, a DO-set factory reset restriction was then no longer applied."
     ],
     "per_major": {
      "14": "same fix: commits 736462a777d0 and f1eb8e719dfb in android-security-14.0.0_r9 (both linked by bug 318497672); at android-security-14.0.0_r29 the patched region is fully present (8/8 lines), including maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked() in systemReady and ATTR_MIGRATED_POST_UPGRADE in OwnersData.",
      "15": "not affected per the bulletin; at android-security-15.0.0_r17 the patched region partially matches (5/8 lines).",
      "16": "not affected per the bulletin; at android-security-16.0.0_r8 the patched region partially matches (5/8 lines).",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1 the patched region partially matches (5/8 lines)."
     },
     "backport_notes": [
      "Apply 736462a777d0 first, then f1eb8e719dfb: the second commit's diff is based on the first commit's output (index 5e67708b1adb) and inserts migrateUserRestrictionsLocked() into the method the first commit adds. Per the fix's own comment, it repairs the Android T to U migration.",
      "If the target branch has customized migratePoliciesToDevicePolicyEngine(), keep the removal of migrateScreenCapturePolicyLocked() from it in sync with adding the post-upgrade step, since screen capture migration now happens only in the post-upgrade step."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact name of the MANAGE_PROFILE_AND_DEVICE_OWNERS-gated forced migration method around line 24151 is not shown in the diff context.",
      "Whether migratePoliciesToDevicePolicyEngine() sets mOwners' migratedToPolicyEngine flag (and thus whether the post-upgrade step runs on the same boot as an initial migration) is not shown.",
      "Why the early return on mPolicyCache.getScreenCaptureDisallowedUser() was incorrect is not explained.",
      "Whether ActiveAdmin.getParentActiveAdmin() can return null for a DO or non-org-owned PO, which would make migrateUserRestrictionsLocked() throw and abort the step, is not shown.",
      "Whether a separate path migrates non-parent profile owner disableScreenCapture is not shown.",
      "The concrete report that triggered the bulletin entry (which of the three policy groups) is not stated; the second commit's test description references DISALLOW_FACTORY_RESET.",
      "Why the three COMP migration tests were disabled (b/277916462) is not explained in the material.",
      "How Android 15 and later handle these migrations is not shown beyond the partial line matches."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2024-31327": {
     "cve": "CVE-2024-31327",
     "summary": "Adds consistency checks to MessageQueueBase::availableToReadBytes() and availableToWriteBytes() in libfmq so that inconsistent read/write pointer values (readPtr > writePtr, or available > queue size) yield 0 with an error log instead of an unsigned underflow.",
     "component": "platform/system/libfmq, Fast Message Queue (include/fmq/MessageQueueBase.h header template)",
     "weakness": "Integer underflow in size computation from read/write pointer values (missing consistency check)",
     "root_cause": "availableToReadBytes() returned mWritePtr->load(std::memory_order_acquire) - mReadPtr->load(std::memory_order_acquire) with no check that writePtr >= readPtr. The pointers are std::atomic<uint64_t>* members. If readPtr exceeds writePtr, the unsigned subtraction wraps to a huge value. availableToWriteBytes() computed mDesc->getSize() - availableToReadBytes(), which also wraps when the available count exceeds the queue size.",
     "fix_analysis": [
      "include/fmq/MessageQueueBase.h:MessageQueueBase::availableToReadBytes(): loads mWritePtr and mReadPtr (memory_order_acquire) into the locals writePtr/readPtr. If writePtr < readPtr, it calls hardware::details::logError(\"The write or read pointer has become corrupted...\") and returns 0. Otherwise it returns writePtr - readPtr, computed from the same values that were checked (matching the commit title \"Use the values of the ptrs that we check\").",
      "include/fmq/MessageQueueBase.h:MessageQueueBase::availableToWriteBytes(): stores mDesc->getSize() in queueSizeBytes and availableToReadBytes() in availableBytes. If queueSizeBytes < availableBytes, it logs the same error and returns 0. Otherwise it returns queueSizeBytes - availableBytes.",
      "Both functions are also marked inline. Only this one file is changed. The commit message lists fmq_fuzzer as the test."
     ],
     "completeness": [
      "Covers the two subtraction sites in availableToReadBytes() and availableToWriteBytes().",
      "The fix does not repair or reset inconsistent counters. It makes both helpers return 0 and log an error. The log text says reading from the queue is no longer possible.",
      "Other code that uses mReadPtr/mWritePtr directly (for example ring offset computations) is not shown in the diff. Whether those uses are bounded elsewhere is not visible in the material."
     ],
     "behavior_changes": [
      "When writePtr < readPtr, availableToReadBytes() now returns 0 and logs an error via hardware::details::logError instead of returning a wrapped value.",
      "When the queue size is smaller than the available-to-read count, availableToWriteBytes() returns 0 and logs an error.",
      "availableToReadBytes()/availableToWriteBytes() are now declared inline."
     ],
     "preconditions": [
      "The read/write pointer values of an FMQ must become inconsistent (writePtr < readPtr, or available bytes > queue size). How this can happen is not shown in the material.",
      "The code is reached through users of MessageQueueBase. readBlocking callers were found in the system/libfmq tests and fmq_fuzzer (per index search). No callers outside libfmq showed up in the index."
     ],
     "per_major": {
      "14": "same fix: commit 7903ae2f7f31 (cherry-pick of 38963310ad57 / da080aa565f0) in android-security-14.0.0_r9. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "not affected per the bulletin. At android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "This is a single-file, header-only change to MessageQueueBase.h. Because it is a template header, modules that include it must be rebuilt to pick up the fix (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the read()/write()/beginRead()/beginWrite() bodies, so the downstream consequence of a wrapped size is not visible.",
      "Who can modify the read/write counters (for example a peer process through shared memory) is not shown.",
      "Whether the 12, 12L and 13 branches received the same cherry-pick is not in the material.",
      "Why 15/16/17 are not listed by the bulletin, and what the partial line matches there mean, is not shown.",
      "What the three referenced bugs (321326147, 321341508, 321383085) each reported is not shown."
     ],
     "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-2024-32896": {
     "cve": "CVE-2024-32896",
     "summary": "RecoverySystemService.rebootRecoveryWithCommand() now destroys all KeyMint keys via the new AndroidKeyStoreMaintenance.deleteAllKeys() (IKeystoreMaintenance.deleteAllKeys) before rebooting to recovery when the command contains \"--wipe_data\", with a matching SELinux allow for system_server, so FBE-encrypted data becomes unrecoverable even if the recovery data wipe is interrupted or skipped.",
     "component": "platform/frameworks/base, RecoverySystemService / android.security.AndroidKeyStoreMaintenance; platform/system/sepolicy, system_server keystore2 policy",
     "weakness": "Incomplete cleanup / insufficient data destruction: keys protecting FBE encryption material were not destroyed before the wipe was deferred to recovery",
     "root_cause": "Before the fix, a factory reset request through RecoverySystemService.rebootRecoveryWithCommand() only wrote the command (including \"--wipe_data\") into the BCB via setupOrClearBcb(true, command) and then called PowerManager.reboot(REBOOT_RECOVERY), leaving data destruction to recovery after the reboot. The Keystore/KeyMint keys that protect the synthetic password blobs (used by LockSettingsService to derive FBE keys) and the keys protecting DE and metadata encryption keys (used by vold) stayed intact until recovery ran. Per the commit messages, if the recovery wipe was interrupted or skipped, the FBE-encrypted data could still be recovered.",
     "fix_analysis": [
      "platform/system/sepolicy private/system_server.te and prebuilts/api/34.0/private/system_server.te: adds delete_all_keys to the existing 'allow system_server keystore:keystore2 { ... }' rule so system_server may invoke IKeystoreMaintenance.deleteAllKeys().",
      "keystore/java/android/security/AndroidKeyStoreMaintenance.java: new static deleteAllKeys() that calls StrictMode.noteDiskWrite(), then getService().deleteAllKeys() on the android.security.maintenance IKeystoreMaintenance binder. RemoteException and NullPointerException are converted to KeyStoreException(SYSTEM_ERROR); ServiceSpecificException is converted to KeyStoreException(e.errorCode).",
      "services/core/java/com/android/server/recoverysystem/RecoverySystemService.java rebootRecoveryWithCommand(): adds constant RECOVERY_WIPE_DATA_COMMAND = \"--wipe_data\" and computes isForcedWipe = command != null && command.contains(RECOVERY_WIPE_DATA_COMMAND). Inside the sRequestLock block, after setupOrClearBcb(true, command) succeeds and before pm.reboot(PowerManager.REBOOT_RECOVERY), it calls deleteSecrets() when isForcedWipe is true.",
      "RecoverySystemService.deleteSecrets(): logs Slogf.w(TAG, \"deleteSecrets\"), calls AndroidKeyStoreMaintenance.deleteAllKeys(), and on android.security.KeyStoreException logs Log.wtf and returns; the reboot still proceeds."
     ],
     "completeness": [
      "Key destruction runs only when the command string contains \"--wipe_data\"; recovery commands without that substring do not trigger deleteSecrets().",
      "Only the RecoverySystemService.rebootRecoveryWithCommand() path is covered; the material shows no other path that writes a wipe command to the BCB.",
      "Failure of deleteAllKeys() is only logged with Log.wtf; the device still reboots to recovery with the wipe deferred, so in that failure case the pre-fix behavior remains.",
      "deleteSecrets() runs only if setupOrClearBcb(true, command) succeeded; if BCB setup fails the method returns early without rebooting.",
      "The sepolicy change grants system_server the delete_all_keys permission the Java call relies on; any failure of the call is logged and not retried."
     ],
     "behavior_changes": [
      "Factory reset via rebootRecoveryWithCommand with \"--wipe_data\" now deletes all keys in all KeyMint devices (including synthetic password protector keys and vold DE/metadata protector keys) before the reboot, intended to make FBE-encrypted data unrecoverable even if recovery does not complete the wipe.",
      "New warning log \"deleteSecrets\" from RecoverySystemService, and Log.wtf \"Failed to delete all keys from keystore.\" on failure.",
      "system_server gains the keystore2 delete_all_keys permission in SELinux policy.",
      "Per the commit test notes, if the wipe is interrupted after the fix, the device reboots into recovery and prompts for a factory reset ('Cannot load Android system. Your data may be corrupt. ...')."
     ],
     "preconditions": [
      "A factory reset / data wipe is initiated through RecoverySystemService.rebootRecoveryWithCommand(), reached from RecoverySystem.bootCommand() in frameworks/base/core/java/android/os/RecoverySystem.java (per index search); the service tests stub enforceCallingOrSelfPermission(RECOVERY) for this call (per index search).",
      "The recovery data wipe is interrupted or skipped, so the keystore keys protecting FBE material are not destroyed (per commit message); the bulletin rates this High EoP."
     ],
     "per_major": {
      "14": "same fix: frameworks/base 8b7b2c66ca96 and sepolicy 92cbf8d4f18c in android-security-14.0.0_r12 (Merged-In I5eb8e97f..., I5be2f9e8...); at android-security-14.0.0_r29 8/8 lines of the patched region present.",
      "15": "resolved at base (android-15.0.0_r1) per per-major facts; 6/6 lines of the patched region present at android-security-15.0.0_r17. The commit listed for 15, platform/build/soong 7ee98928ea1b ('Add secretkeeper-v1-java to platform APIs'), does not contain the deleteSecrets/deleteAllKeys logic.",
      "16": "not affected (not listed by the bulletin); 8/8 lines of the patched region 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": [
      "Apply the sepolicy change (delete_all_keys in allow system_server keystore:keystore2, in both private/system_server.te and the matching prebuilts/api/<level>/private/system_server.te) together with the frameworks/base change; otherwise the deleteAllKeys() call can be expected to fail and would only be logged via Log.wtf. The Java side depends on IKeystoreMaintenance.deleteAllKeys() in the android.security.maintenance AIDL and on com.android.server.utils.Slogf.",
      "The Android 15 entry points to a soong change whose content does not match this fix. To verify, look directly for RECOVERY_WIPE_DATA_COMMAND / deleteSecrets() in RecoverySystemService and deleteAllKeys() in AndroidKeyStoreMaintenance."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether other paths exist that write a wipe command to the BCB or enter recovery without going through rebootRecoveryWithCommand (none shown in the material).",
      "Where the RECOVERY permission check for rebootRecoveryWithCommand is enforced (the diff does not show it; tests imply enforceCallingOrSelfPermission(RECOVERY)).",
      "How a missing sepolicy permission surfaces to the Java caller (error code not shown).",
      "The exact frameworks/base and sepolicy commits that carry this change on Android 15, since the listed 15 commit is a soong change with different content.",
      "Whether IKeystoreMaintenance.deleteAllKeys() was already present in the keystore2 AIDL and daemon on the 14 branch or was added by a separate, unlisted change."
     ],
     "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."
     }
    }
   }
  },
  "15": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "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": {}
  },
  "16": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "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": {}
  },
  "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/2024-06-01",
  "latest_version": "1.0",
  "latest_revision": {
   "version": "1.0",
   "date": "June 3, 2024",
   "notes": "Bulletin published"
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "June 3, 2024",
    "notes": "Bulletin published"
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [],
  "out_of_scope_rows": {
   "Arm components": 2,
   "Imagination Technologies": 5,
   "Kernel": 1,
   "MediaTek components": 5,
   "Qualcomm closed-source components": 5
  },
  "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."
 }
}