{
 "month": "2025-04",
 "generated": "2026-10-06T12:05+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 21,
    "certain": 21,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-22423",
     "osv_id": "ASB-A-346797131",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "dng_ifd::ParseTag in external/dng_sdk now aborts tag parsing (returns false) when CheckTagCount reports an invalid tag count for three tags, instead of ignoring the result and continuing to process the out-of-range count.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit in source/dng_ifd.cpp that only wraps three existing CheckTagCount calls; Android 13 is listed as affected by the bulletin but no 13 commit is in the material.",
       "Verify that CheckTagCount in the target tree returns bool (the diff relies on it) and that ParseTag's caller treats a false return as a rejected tag."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "748dbd7dfcecb19f3a19caaba4285e059f32d2dd",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-22429",
     "osv_id": "ASB-A-373357090",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes Parcel.readArrayMap to report the number of LazyValue entries through an int[] out-parameter that is updated as each value is read. BaseBundle.initializeFromParcelLocked therefore sees the correct count in its finally block even when readArrayMap throws partway, and no longer recycles a Parcel that LazyValue entries still reference.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, two files that must move together: the Parcel.readArrayMap signature change and both callers (BaseBundle passing int[], readArrayMapInternal passing null). Any additional readArrayMap callers in a fork must be updated or the build will fail.",
       "Fork code may differ in the recycle call (recycleParcel(parcelledData) in the diff vs parcelledData.recycle() at 14.0.0_r29); the logic change is the same."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ece83fb425b1e912a036e9985b710910e2e3ca37",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-26416",
     "osv_id": "ASB-A-388480622",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In Skia's BMP decoder, SkBmpStandardCodec::initializeSwizzler now chooses the swizzler's destination format based on xformOnDecode() instead of colorXform(). The swizzler's output pixel format now matches the buffer that decodeRows actually writes into.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with a one-line functional change. It requires that xformOnDecode() is available to SkBmpStandardCodec and that the xform buffer is already keyed on xformOnDecode(). The 14.0.0_r29 context shows this for onPrepareToDecode, and the commit message states it for decodeRows. The test and the binary resource bmp-size-32x32-8bpp.bmp are not needed for the fix itself, only for the regression test."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/skia",
       "sha": "fc2ebb312c5898486776df981a51c2bb90e3756d",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/skia"
     ]
    },
    {
     "cve": "CVE-2024-40653",
     "osv_id": "ASB-A-293458004",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Telecom's ConnectionServiceWrapper now schedules a 15 second timeout when it asks a bound ConnectionService to create a connection or conference. If the app never completes creation, the request is failed with DisconnectCause.ERROR. Per the commit message, this lets Telecom unbind the service instead of keeping it bound indefinitely.",
      "confidence": "high",
      "backport_notes": [
       "Backport the second iteration (12016109c473 on 14 / 4f73f974ca16 on 15), not the DO NOT MERGE first iteration. It contains the 'call != null' guard before ConcurrentHashMap.containsKey, the removeServiceInterface() early return and executor shutdown, and mScheduledFutureMap.clear() in handleConnectionServiceDeath(). These diffs do not add the code it depends on, so the target tree must already have: Call.isCreateConnectionComplete()/setIsCreateConnectionComplete(), AnomalyReporterAdapter/AnomalyReporterAdapterImpl, android.telecom.Logging.Runnable.getRunnableToCancel(), and a tests-side TestScheduledExecutorService.",
       "Adjust the ConnectionServiceWrapper test constructor call to the branch's signature (Android 15 adds a trailing mFeatureFlags argument)."
      ],
      "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_r17",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "b007692a8d529bc2b164b038d81862cddee686b4",
       "tag": "android-security-14.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "12016109c473d8d880333556726b1dcbce041e41",
       "tag": "android-security-14.0.0_r17",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 57,
     "first_commit_before_announcement_days": 210,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2024-49720",
     "osv_id": "ASB-A-355411348",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds PackageManager.FLAG_PERMISSION_ONE_TIME to the set of flags that the role controller treats as a user choice, so role (re-)evaluation no longer overrides a permission the user chose to grant only once, and clears that flag when a role grant gives a permission that was not previously granted.",
      "confidence": "high",
      "backport_notes": [
       "The change is two one-line additions in Permissions.java. Apply both hunks (grant mask and fixed-flags check) together, as Google did on 14 and 15. Permission is a mainline module delivered via PermissionController, so a device may also receive this through a module update rather than the platform tag (general platform knowledge)."
      ],
      "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": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Permission",
       "sha": "e02775c62a81b99ba4cc693dd1885cc70c0bd5cf",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref e02775c62a81",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Permission"
     ]
    },
    {
     "cve": "CVE-2024-49728",
     "osv_id": "ASB-A-296915500",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a check in Bluetooth OPP's BluetoothOppSendFileInfo.generateFileInfo that rejects content URIs whose authority carries a user-id prefix for a user other than the current one. Such URIs now return SEND_FILE_INFO_ERROR and are not opened.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The only branch difference is the Android 15 ContentProfileErrorReportUtils.report call and the 'Next tag value' comment, which the 14 backport leaves out. The 14 context shows neither that comment nor imports for that reporting scheme. When porting to a tree without ContentProfileErrorReportUtils, use the 14 form.",
       "At r29, the Android 14 test file imports TestParameterInjector and has no AndroidJUnit4 import, unlike the diff context. Check that the added tests fit the test runner used in the target tree."
      ],
      "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_r17",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r17",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "339d20053956ec0f92384f0b7cefda4fa7126290",
       "tag": "android-security-14.0.0_r17",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 57,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2024-49730",
     "osv_id": "ASB-A-284989074",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes MediaProvider FuseDaemon's MAX_READ_SIZE from a fixed 128 KiB to FUSE_MAX_MAX_PAGES (256) * getpagesize(). Per the commit message, the goal is to make the zero_addr buffer as large as the largest single request.",
      "confidence": "medium",
      "backport_notes": [
       "Single-hunk change. MAX_READ_SIZE is no longer a compile-time constant, so check that it is not used in a constexpr context elsewhere in FuseDaemon.cpp, and that getpagesize is declared in the translation unit."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "961c8cbd2a489277876aeffa40ebdee5eae29f1f",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/MediaProvider"
     ]
    },
    {
     "cve": "CVE-2025-22416",
     "osv_id": "ASB-A-277207798",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ChooserActivity now checks, through IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, that the launching caller has read access to the URI behind any URI-type Icon in the caller-supplied ChooserTarget array. If the check fails, the icon is removed from that target.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. It depends on IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, Icon.TYPE_URI_ADAPTIVE_BITMAP, ContentProvider.getUriWithoutUserId and getLaunchedFromUid existing on the target branch. The new helpers are appended at the end of the class."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "82aa8527b7da3dbbc2f19c869b4ded106e140452",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22417",
     "osv_id": "ASB-A-332277530",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In Transition.finishTransition (services/core/java/com/android/server/wm/Transition.java), the ActivityRecordInputSink (touch-blocking) state of visible participant activities is now written into each activity's pending transaction and flushed via scheduleAnimationLocked(), instead of being applied immediately through a freshly created standalone SurfaceControl.Transaction, to fix a race that could leave the state stale.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk in Transition.finishTransition; it uses getPendingTransaction(), scheduleAnimationLocked() and ActivityRecord.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(Transaction). On trees where the block already handles task.mTaskInputSink (as at 14.0.0_r29), apply by hand: both the ActivityRecord and Task branches should pass the container's getPendingTransaction() and set scheduleAnimation."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "89e32320207c1f332dd84024a13b001320d8c63d",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22418",
     "osv_id": "ASB-A-333344157",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' RingtonePreference now sets a target package on the ringtone picker intent by calling setPackage() with a new string resource (config_sound_picker_package_name, default com.android.soundpicker), so it no longer sends an implicit ACTION_RINGTONE_PICKER intent.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit touching two files: add the string resource to res/values/config.xml and the setPackage() call in RingtonePreference's constructor. Forks that ship a sound picker under a different package name must make config_sound_picker_package_name match it."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "ad9fb985df470bed5f77da4701f2aebe45af5ff3",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings",
      "platform/vendor/unbundled_google/packages/SettingsGoogle"
     ]
    },
    {
     "cve": "CVE-2025-22419",
     "osv_id": "ASB-A-335387175",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to the activity window in onCreate of 18 Telephony (com.android.phone) settings, SIM PIN, FDN, SIM contacts and emergency dialer activity classes so non-system overlay windows are not shown over them.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit: each hunk inserts the same 3-line getWindow().addSystemFlags(...) call into an onCreate override (directly after super.onCreate() in most files, after other early calls in CallFeaturesSetting, ADNList and BaseFdnContactScreen). Forks that customize these activities must patch every listed onCreate. The same pattern is used in packages/apps/Settings SimDialogActivity.onCreate per index search.",
       "Android 13 is listed as affected by the bulletin but no 13 commit is included in the material."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "92f47baf4505e474376c0550e8b8cd25d19e74e2",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telephony"
     ]
    },
    {
     "cve": "CVE-2025-22421",
     "osv_id": "ASB-A-338024220",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI stops embedding notification title/text/ticker in the accessibility content description of status bar notification icons, passing an empty string instead so only the app name is included.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit fix. Apply it to whichever file formats R.string.accessibility_desc_notification_icon: StatusBarIconView.java on Android 14, NotificationContentDescription.kt (contentDescForNotification) on Android 15.",
       "If the target branch has tests asserting title/text/ticker in the icon description, update them as the Android 15 commit does."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4ec1792148a5d31a890dbe86bde40946404199e8",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22422",
     "osv_id": "ASB-A-339532378",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's AuthController now dismisses a BiometricPrompt owned by Settings' ConfirmDeviceCredentialActivity when the top activity has a different class name. It does this by carrying the owning activity's class name in PromptInfo and comparing it with the top task's component, instead of comparing package names only.",
      "confidence": "high",
      "backport_notes": [
       "Apply frameworks/base 6894ba3da6da first, then Settings 1c0ab9c696d7. The base commit adds the Builder API, the PromptInfo field and the AuthDialog method, and the Settings change calls the new Builder method. Because of the PromptInfo parcel change, framework and SystemUI should ship together.",
       "The Android 15 fix uses a different mechanism (a real-caller ComponentName via setRealCallerForConfirmDeviceCredentialActivity) and replaces an existing setComponentNameForConfirmDeviceCredentialActivity Builder call. The 14 and 15 patches cannot be cherry-picked across branches."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6894ba3da6da99eb0ef63d95bf1b19080d302eda",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "1c0ab9c696d736da0176aa00f7effa31c905430e",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22427",
     "osv_id": "ASB-A-368579654",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the onResume/onPause overrides in Settings' NotificationAccessConfirmationActivity that passed the system-only flag SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to Window.addFlags/clearFlags (public flag namespace), leaving only the correct onCreate handling of that flag.",
      "confidence": "high",
      "backport_notes": [
       "Deletion-only change with no dependencies shown. Before applying it to a fork, confirm that onCreate in NotificationAccessConfirmationActivity already applies SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS as a private flag (the static import at line 21 in the r29 context suggests it does). Otherwise, removing onResume could leave the dialog without the intended overlay protection."
      ],
      "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": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "7255b2eaac4b5cb1bc3bbf23509b33c7355a8657",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22428",
     "osv_id": "ASB-A-372671447",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' AppInfoBase now checks INTERACT_ACROSS_USERS_FULL against the UID that launched the hosting activity (from ActivityManager getLaunchedFromUid) instead of against the package name from SettingsActivity.getInitialCallingPackage().",
      "confidence": "medium",
      "backport_notes": [
       "Single-file, single-commit change. It relies on IActivityManager.getLaunchedFromUid(IBinder) and ActivityManager.checkUidPermission, hidden framework APIs that Settings can use as a platform app (general platform knowledge). The diff applied with only a line offset on 14 and 15.",
       "Android 13 is listed as affected, but the material contains no 13 commit. The same hunk should port if AppInfoBase on 13 still has the getInitialCallingPackage()/mPm.checkPermission pattern."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "4a9cb946e978d9d93f9ee0a2c4c8ca41ba774540",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22431",
     "osv_id": "ASB-A-375623125",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AppOpsService stops treating a proxy-supplied attribution tag as valid when the proxied package is a non-app uid (shell, system, etc.) unless the proxy is a system app or absent, requires the next AttributionSource in a chain to also be trusted, and re-checks remaining restrictions before resuming a paused op when one restriction is lifted.",
      "confidence": "high",
      "backport_notes": [
       "Single commit in AppOpsService.java. When backporting, match local signatures: branches that have startOperationDryRun need the extra proxyUid parameter (as in the Android 15 variant), and branches with virtual-device support need the device id argument in isOpRestrictedLocked. The fix uses mPackageManagerInternal.isSystemPackage."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "79211e094a7363f28a06cea2737aa815339911ad",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22433",
     "osv_id": "ASB-A-376674080",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "IntentForwarderActivity.canForward now runs the cross-profile forwarding check (ACTION_CHOOSER rejection plus IPackageManager.canForwardTo) on both the forwarded intent and its selector, instead of checking only the selector when one is present.",
      "confidence": "high",
      "backport_notes": [
       "Two copies are patched on Android 14: core/java/com/android/internal/app/IntentForwarderActivity.java in frameworks/base and java/src/com/android/intentresolver/IntentForwarderActivity.java in packages/modules/IntentResolver. The IntentResolver commit also makes canForward public.",
       "If the target branch's canForward already takes a resolvedType String rather than a ContentResolver (as at 14.0.0_r29), the code shown at r29 passes that value to both canForwardInner calls. Either way, the essential part is checking both the intent and the selector."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d2216fc62a9e1253828bf4cfdf5395948f2e78c6",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/modules/IntentResolver",
       "sha": "20cef18d8d9b817823d8d8d505cc382d3b334f34",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/modules/IntentResolver"
     ]
    },
    {
     "cve": "CVE-2025-22434",
     "osv_id": "ASB-A-378900798",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds isUserSetupComplete() and !keyguardOn conditions to the Meta+I keyboard shortcut in PhoneWindowManager so showSystemSettings() is no longer invoked while the keyguard is showing or before user setup completes.",
      "confidence": "high",
      "backport_notes": [
       "Single-file change to one condition. When porting, locate the KEYCODE_I / showSystemSettings() case in PhoneWindowManager and reuse the existing keyguardOn local (already used by KEYCODE_APP_SWITCH in the same switch) and isUserSetupComplete(). The commit message cites KeyGestureEventTests, but the diff contains no test changes."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2880f0ab2dc63dc6ea820afb79e9be523ecb7074",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22435",
     "osv_id": "ASB-A-273995284",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a check in avdt_msg_ind (system/stack/avdt/avdt_msg.cc) that drops an incoming AVDTP response or reject whose signal id does not match the command currently pending on the CCB (p_ccb->p_curr_cmd->event), or that arrives when no command is pending. This prevents type confusion when the response is parsed.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained hunk. The only difference between branches is the logging call: AVDT_TRACE_WARNING on 14 and log::warn on 15. The 15 variant keeps a printf-style %d in the log::warn format string, so adjust the log line to match the target branch's logging API."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "efa5f4ef386a8947f4777840c5cefff389740e86",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2025-22438",
     "osv_id": "ASB-A-343129193",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In InputDispatcher::afterKeyEventLockedInterruptable, take a std::shared_ptr<EventEntry> copy and a local copy of hasForegroundTarget() from the DispatchEntry before the lock is released for policy calls, so the KeyEntry stays valid and the DispatchEntry is no longer dereferenced even if a concurrent removeInputChannel drains the connection's waitQueue and deletes the DispatchEntry.",
      "confidence": "high",
      "backport_notes": [
       "This is a single self-contained commit touching InputDispatcher.cpp and InputDispatcher.h. On a branch where afterKeyEventLockedInterruptable still takes (connection, DispatchEntry*, KeyEntry&, handled), copy eventEntry (as a shared_ptr) and hasForegroundTarget at function entry and drop the KeyEntry& parameter. The commit message also asks for the queued command to hold the Connection by strong pointer. Check how the branch captures it.",
       "An index chunk labelled Android 14 (android-14.0.0_r75) shows a different signature (returning std::unique_ptr<const KeyEntry>, taking DispatchEntry& and const KeyEntry&). Verify the target branch's actual signature before patching."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "7ac747cb442d382c74a18d26268b7fc3751537ce",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2025-22439",
     "osv_id": "ASB-A-352294617",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DocumentsUI's file picker now runs the existing restricted-initial-path check (/Android/data, /Android/obb, /Android/sandbox) on the restored last-accessed DocumentStack. If the stack's top document is restricted, the picker falls back to the default location.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained 3-line change. It only needs shouldPreemptivelyRestrictRequestedInitialUri(Uri) and PATTERN_RESTRICTED_INITIAL_PATH to already exist in picker/ActionHandler.java. On a branch without that helper, the initial-URI restriction logic has to be ported first."
      ],
      "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_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r19",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "1f283d5b87d0c8f04a79d1d2a51ed4d9327ae864",
       "tag": "android-security-14.0.0_r19",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r19"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "382064697"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2095d130b4d7f2ba1a3284abb58ca894817f5f4a",
       "subject": "Disable \"Developer options\" by default for managed profiles.",
       "tag": "android-security-14.0.0_r19",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r19"
     ],
     "tag": "android-security-14.0.0_r19"
    },
    {
     "bug_ids": [
      "350456241"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "98b6fa464f3e4b05f935294539449250c7ff2a76",
       "subject": "Parse authority to separate userId and non-user parts of it",
       "tag": "android-security-14.0.0_r19",
       "merged": "2025-02-05",
       "markers": []
      },
      {
       "project": "platform/frameworks/base",
       "sha": "e786773b7ff04afb42e20b1a4c5f3c0c046b0028",
       "subject": "Check cross user permissions for a given UID",
       "tag": "android-security-14.0.0_r19",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r19"
     ],
     "tag": "android-security-14.0.0_r19"
    },
    {
     "bug_ids": [
      "277076451"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "de705b880c8722deabd0a49f5c645e8a4c6f59cc",
       "subject": "[DO NOT MERGE] Send dim layer in input list",
       "tag": "android-security-14.0.0_r19",
       "merged": "2025-02-05",
       "markers": [
        "restrict_automerge"
       ]
      }
     ],
     "tags": [
      "android-security-14.0.0_r19"
     ],
     "tag": "android-security-14.0.0_r19"
    },
    {
     "bug_ids": [
      "356604577"
     ],
     "commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "4519ff98a0bba3ca08c9455f6e40b95a3d55b0ed",
       "subject": "Resolve cross account user ringtone validation.",
       "tag": "android-security-14.0.0_r19",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r19"
     ],
     "tag": "android-security-14.0.0_r19"
    }
   ],
   "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-2025-22423": {
     "cve": "CVE-2025-22423",
     "summary": "dng_ifd::ParseTag in external/dng_sdk now aborts tag parsing (returns false) when CheckTagCount reports an invalid tag count for three tags, instead of ignoring the result and continuing to process the out-of-range count.",
     "component": "platform/external/dng_sdk, DNG/TIFF IFD tag parser (source/dng_ifd.cpp)",
     "weakness": "Unchecked return value of a bounds/count validation (CheckTagCount), allowing parsing to proceed with an out-of-range tag count",
     "root_cause": "In source/dng_ifd.cpp, dng_ifd::ParseTag called CheckTagCount(parentCode, tagCode, tagCount, ...) for several tags but discarded its boolean result. When a file carried a tag count outside the allowed range, parsing continued anyway. For tcBitsPerSample the following code loops `for (j = 0; j < tagCount; j++)` reading one value per iteration from the stream, so an unvalidated count directly controls the amount of work and reads done; the two other patched sites check the count against fSamplesPerPixel and likewise continued with an invalid count. The bulletin rates the issue as a Critical DoS.",
     "fix_analysis": [
      "source/dng_ifd.cpp: dng_ifd::ParseTag, case tcBitsPerSample (hunk at line 351): `CheckTagCount (parentCode, tagCode, tagCount, 1, 0x0FFFF)` is wrapped in `if (!CheckTagCount(...)) return false;` so the per-value read loop is not entered with an out-of-range count.",
      "source/dng_ifd.cpp: dng_ifd::ParseTag, second site (hunk at line 973, tag not named in the material): `CheckTagCount (parentCode, tagCode, tagCount, 1, fSamplesPerPixel)` now returns false from ParseTag on failure.",
      "source/dng_ifd.cpp: dng_ifd::ParseTag, third site (hunk at line 1025, tag not named in the material): `CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel)` now returns false from ParseTag on failure.",
      "Single commit, 6 insertions / 3 deletions in source/dng_ifd.cpp only; it wraps three existing CheckTagCount calls and does not change CheckTagCount itself."
     ],
     "completeness": [
      "Covers exactly three CheckTagCount call sites in dng_ifd::ParseTag: tcBitsPerSample and two sites bounded by fSamplesPerPixel.",
      "Other CheckTagCount calls in the same function still ignore the return value, e.g. case tcImageLength at line 365 (`CheckTagCount (parentCode, tagCode, tagCount, 1);`) in the android-security-14.0.0_r29 context; that site reads a single value via stream.TagValue_uint32, so the count does not drive a loop there.",
      "CheckTagType results remain unchecked at all three patched sites (unchanged by this fix).",
      "The fix relies on the caller of ParseTag handling a false return; that caller is not shown in the material."
     ],
     "behavior_changes": [
      "ParseTag now returns false for BitsPerSample with a count outside 1..0xFFFF, and for the two fSamplesPerPixel-checked tags with counts failing their check; previously such tags were still parsed.",
      "Whether this surfaces as a file load failure or a skipped tag depends on the ParseTag caller (not shown)."
     ],
     "preconditions": [
      "A DNG/TIFF file is parsed by dng_sdk's dng_ifd::ParseTag.",
      "The file contains a BitsPerSample tag (or one of the two other patched tags) whose count fails CheckTagCount.",
      "Which Android component feeds untrusted files into dng_sdk is not shown; per index search, the matches shown (DngCreator_setup building a TiffWriter, Dialer ExifParser in Java) do not show a call into dng_sdk parsing."
     ],
     "per_major": {
      "14": "Same fix: commit 748dbd7dfcec in android-security-14.0.0_r19 (cherry-pick). The android-security-14.0.0_r29 context shows the `if (!CheckTagCount (...)) return false;` guard present in tcBitsPerSample.",
      "15": "Same fix: commit bbed5f198692 in android-security-15.0.0_r7, same diff (identical blob indexes) as the Android 14 commit.",
      "16": "Not affected (not listed by the bulletin). The heuristic reports 5/8 lines of the patched region at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). The heuristic reports 5/8 lines of the patched region at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit in source/dng_ifd.cpp that only wraps three existing CheckTagCount calls; Android 13 is listed as affected by the bulletin but no 13 commit is in the material.",
      "Verify that CheckTagCount in the target tree returns bool (the diff relies on it) and that ParseTag's caller treats a false return as a rejected tag."
     ],
     "confidence": "high",
     "unknowns": [
      "The tag cases at lines ~973 and ~1025 are not named in the diff hunks or context.",
      "How the caller of dng_ifd::ParseTag reacts to a false return (error vs. skip) is not shown.",
      "Which Android process parses untrusted DNG files with dng_sdk, and therefore the actual attack surface, is not shown.",
      "Status of Android 13 (listed as affected) is not covered by the per-major facts."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22429": {
     "cve": "CVE-2025-22429",
     "summary": "Changes Parcel.readArrayMap to report the number of LazyValue entries through an int[] out-parameter that is updated as each value is read. BaseBundle.initializeFromParcelLocked therefore sees the correct count in its finally block even when readArrayMap throws partway, and no longer recycles a Parcel that LazyValue entries still reference.",
     "component": "platform/frameworks/base, core/java/android/os (BaseBundle / Parcel lazy unparceling)",
     "weakness": "Incorrect error-path state handling that leads to a premature Parcel recycle while LazyValue entries may still reference it (information disclosure per bulletin type ID)",
     "root_cause": "In BaseBundle.initializeFromParcelLocked, the lazy value count came from the return value of Parcel.readArrayMap: `numLazyValues = parcelledData.readArrayMap(...)`. readArrayMap adds entries to the map one by one and returns the count only at the end. If it threw after some LazyValue objects had already been added to `map`, the assignment never happened and numLazyValues stayed 0. The finally block runs regardless. With ownsParcel true and numLazyValues == 0, it called recycleParcel(parcelledData), set mLazyValues = 0 and installed mMap = map, so the map could keep LazyValue entries tied to the recycled Parcel. In the non-defuse path (sShouldDefuse false) the map is not erased before the exception propagates.",
     "fix_analysis": [
      "core/java/android/os/Parcel.java:readArrayMap: the signature changes from `int readArrayMap(map, size, sorted, lazy, loader)` to `void readArrayMap(map, size, sorted, lazy, loader, int[] lazyValueCount)`. The local counter and `return lazyValues` are removed. `lazyValueCount[0]++` now runs inside the loop each time a value is `instanceof LazyValue`, so the caller's array reflects the LazyValue entries added so far even if a later iteration throws.",
      "core/java/android/os/Parcel.java:readArrayMapInternal: updated to the new signature, passing `null` for lazyValueCount, with lazy=false.",
      "core/java/android/os/BaseBundle.java:initializeFromParcelLocked: `int numLazyValues = 0` becomes `int[] numLazyValues = new int[]{0}` and is passed as the out-parameter. The finally block tests `numLazyValues[0] == 0` to choose between recycling the Parcel and keeping `mWeakParcelledData = new WeakReference<>(parcelledData)`, and stores `mLazyValues = numLazyValues[0]`."
     ],
     "completeness": [
      "Covers the lazy call site in BaseBundle.initializeFromParcelLocked (lazy = ownsParcel). The only other caller shown in the diff is readArrayMapInternal, which uses lazy=false and passes a null counter. This is safe only if readValue never returns a LazyValue, which the material does not show.",
      "The fix does not change which exceptions are caught or how the map is left after an exception. In the non-defuse path the partially read map is still installed as mMap in finally and the exception still propagates. The fix only keeps the backing Parcel (via WeakReference) and makes the lazy count correct.",
      "Exceptions other than BadParcelableException are not caught, but the finally block still runs, and the count is now updated incrementally for those too.",
      "Passing null for lazyValueCount would cause a NullPointerException if a LazyValue were produced on that path. Any new lazy caller must pass a non-null array."
     ],
     "behavior_changes": [
      "When unparceling a Bundle throws after at least one LazyValue has been read, the owning Parcel is no longer recycled. It is kept via mWeakParcelledData, and mLazyValues reflects the number of lazy entries read.",
      "Parcel.readArrayMap (@hide) changes signature: it returns void and takes an int[] out-parameter.",
      "No change on the success path: the count and the recycle/keep decision are the same when readArrayMap completes normally."
     ],
     "preconditions": [
      "A Bundle unparceled with ownsParcel true (lazy path) in BaseBundle.initializeFromParcelLocked.",
      "readArrayMap must throw after one or more LazyValue entries have been added to the map.",
      "The retained entries matter only in the non-defuse path; with sShouldDefuse the map is erased."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base ece83fb425b1 in android-security-14.0.0_r19 (cherry-pick, Change-Id Id423936872cbb0e0265ccf2855092357cb175d47). At android-security-14.0.0_r29 the int[] out-parameter logic is present, and the recycle call reads parcelledData.recycle() instead of the diff's recycleParcel(parcelledData). Resolver reports 5/8 lines of the patched region present.",
      "15": "same fix: platform/frameworks/base 567af706fd9d in android-security-15.0.0_r7. The diff is identical to the Android 14 one; only the Parcel.java line offsets differ (5519/5529 vs 5335/5345).",
      "16": "not affected (not listed by the bulletin); at android-security-16.0.0_r8, 5/8 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin); at android-security-17.0.0_r1, 4/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single commit, two files that must move together: the Parcel.readArrayMap signature change and both callers (BaseBundle passing int[], readArrayMapInternal passing null). Any additional readArrayMap callers in a fork must be updated or the build will fail.",
      "Fork code may differ in the recycle call (recycleParcel(parcelledData) in the diff vs parcelledData.recycle() at 14.0.0_r29); the logic change is the same."
     ],
     "confidence": "medium",
     "unknowns": [
      "The Android 13 fix commit and tag are not shown in the material.",
      "The reproduction in b/373357090 and the process/IPC path are not in the material.",
      "The commit message ('considered a success case to be an unsuccessful case') does not spell out the failing scenario; the error-path reading is inferred from the diff.",
      "Whether readValue (lazy=false) can ever return a LazyValue is not shown.",
      "What the partial line matches on Android 16/17 correspond to 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-2025-26416": {
     "cve": "CVE-2025-26416",
     "summary": "In Skia's BMP decoder, SkBmpStandardCodec::initializeSwizzler now chooses the swizzler's destination format based on xformOnDecode() instead of colorXform(). The swizzler's output pixel format now matches the buffer that decodeRows actually writes into.",
     "component": "platform/external/skia, image codec (src/codec/SkBmpStandardCodec.cpp, BMP decoding)",
     "weakness": "Mismatch between the swizzler's output pixel format and the destination buffer actually written. The test comment indicates a crash. The material does not show the exact memory-safety class (for example out-of-bounds write).",
     "root_cause": "SkBmpStandardCodec::initializeSwizzler used this->colorXform() to decide whether to set up the swizzler for the intermediate color-transform buffer (kXformSrcColorType, alpha forced to unpremul, kNo_ZeroInitialized). Per the commit message, the decoder only routes rows through the intermediate xform buffer when xformOnDecode() is true. onPrepareToDecode likewise calls resetXformBuffer only when xformOnDecode() is true. So when a color transform existed but xformOnDecode() was false, the swizzler was configured for kXformSrcColorType output while decodeRows wrote directly into the caller's destination buffer in its own color type. The added test shows the problematic case: an 8bpp indexed BMP decoded to kRGB_565_SkColorType with a linear sRGB color space. The test comments call this 'a < 32bpp buffer with a color transform' that 'should not crash'.",
     "fix_analysis": [
      "src/codec/SkBmpStandardCodec.cpp:SkBmpStandardCodec::initializeSwizzler: the condition `if (this->colorXform())` becomes `if (this->xformOnDecode())`. swizzlerInfo now switches to kXformSrcColorType only when the decoder will actually use the intermediate xform buffer. In that case alpha is set to unpremul for premul destinations and fZeroInitialized to kNo_ZeroInitialized. Otherwise the swizzler is created with the caller's dstInfo.",
      "This aligns initializeSwizzler with SkBmpStandardCodec::onPrepareToDecode, which already gates resetXformBuffer(dstInfo.width()) on this->xformOnDecode().",
      "tests/CodecTest.cpp: adds DEF_TEST(Codec_bmp_indexed_colorxform) and the resource resources/images/bmp-size-32x32-8bpp.bmp. The test decodes the 8bpp BMP to kRGB_565_SkColorType with SkColorSpace::MakeSRGBLinear() via codec->getPixels(aps) and asserts kSuccess. It also adds an include of src/core/SkAutoPixmapStorage.h."
     ],
     "completeness": [
      "The fix covers the single swizzler-selection condition in SkBmpStandardCodec::initializeSwizzler. After the change, both the xform buffer allocation (onPrepareToDecode) and the swizzler output format are keyed on xformOnDecode().",
      "decodeRows itself is not changed. The fix relies on decodeRows already using xformOnDecode() to pick the destination, as stated in the commit message.",
      "Only SkBmpStandardCodec is touched. The material does not show the other BMP codec classes or other codecs that may use the same colorXform()/xformOnDecode() pattern, so their status cannot be judged.",
      "The regression test covers only one case: an 8bpp indexed BMP decoded to RGB_565 with a color space."
     ],
     "behavior_changes": [
      "For BMP decodes where a color transform exists but is not applied on decode (xformOnDecode() false), the swizzler now writes the destination color type directly and respects the caller's fZeroInitialized option instead of forcing kNo_ZeroInitialized. No new error codes are introduced.",
      "Adds a new unit test, Codec_bmp_indexed_colorxform, and a new test resource."
     ],
     "preconditions": [
      "A BMP image handled by SkBmpStandardCodec is decoded via SkCodec::getPixels or an equivalent decode entry point. The added test uses an 8bpp indexed BMP.",
      "The requested destination produces a color transform while xformOnDecode() is false. The test triggers this with a kRGB_565_SkColorType destination and a linear sRGB color space.",
      "Per index search, Android reaches Skia codecs from frameworks/base/libs/hwui/hwui/ImageDecoder.cpp (ImageDecoder wrapping SkAndroidCodec) and from the AImageDecoder NDK API, used for example in frameworks/native/libs/renderengine/benchmark/Codec.cpp. The material does not show whether those callers pick a destination that hits the vulnerable condition."
     ],
     "per_major": {
      "14": "same fix: platform/external/skia fc2ebb312c58 in android-security-14.0.0_r19 (cherry-pick of upstream skia-review 937497, Change-Id Iaf240852a9bcf0be02cda84f0e44576da8100dec). 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/skia a1c030a91f29 in android-security-15.0.0_r7. Same one-line change plus the same test and resource; only the CodecTest.cpp hunk location differs. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit with a one-line functional change. It requires that xformOnDecode() is available to SkBmpStandardCodec and that the xform buffer is already keyed on xformOnDecode(). The 14.0.0_r29 context shows this for onPrepareToDecode, and the commit message states it for decodeRows. The test and the binary resource bmp-size-32x32-8bpp.bmp are not needed for the fix itself, only for the regression test."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not include decodeRows or SkCodec::xformOnDecode(). The exact conditions under which xformOnDecode() is false while colorXform() is non-null are inferred from the commit message and the test, not shown directly.",
      "The material does not show the pixel size of kXformSrcColorType or the precise memory effect of the mismatch (for example whether it is an out-of-bounds write).",
      "The material does not show the other BMP codec classes in external/skia or other codecs, so whether they share the same colorXform()/xformOnDecode() mismatch is unknown.",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 fix commit."
     ],
     "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-40653": {
     "cve": "CVE-2024-40653",
     "summary": "Telecom's ConnectionServiceWrapper now schedules a 15 second timeout when it asks a bound ConnectionService to create a connection or conference. If the app never completes creation, the request is failed with DisconnectCause.ERROR. Per the commit message, this lets Telecom unbind the service instead of keeping it bound indefinitely.",
     "component": "platform/packages/services/Telecomm, ConnectionServiceWrapper (ConnectionService binding / call creation)",
     "weakness": "Missing timeout / unbounded resource hold (improper release of a privileged service binding)",
     "root_cause": "ConnectionServiceWrapper.createConnection() and createConference() send createConnection/createConference to the app's ConnectionService over mServiceInterface. They then wait for the app to call back handleCreateConnectionComplete/handleCreateConferenceComplete on the Adapter. That callback had no deadline, so if the app never created the connection the binding was never released. Per the commit message, this let a malicious app keep a declared permission forever, even in the background.",
     "fix_analysis": [
      "Commit b007692a8d52 (android-security-14.0.0_r12, 'DO NOT MERGE' first iteration) changes src/com/android/server/telecom/ConnectionServiceWrapper.java. It adds SERVICE_BINDING_TIMEOUT = 15000L, a single-thread ScheduledExecutorService mScheduledExecutor, and a Map<Call, ScheduledFuture<?>> mScheduledFutureMap (ConcurrentHashMap).",
      "ConnectionServiceWrapper.createConnection() and createConference(): just before calling mServiceInterface.createConnection(...)/createConference(...), the fix builds an android.telecom.Logging.Runnable('CSW.cC', mLock). Its loggedRun() checks call.isCreateConnectionComplete(). If that is false, it logs an error with the component name, adds a CREATE_CONNECTION_TIMEOUT / CREATE_CONFERENCE_TIMEOUT call event, and calls response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)) (or handleCreateConferenceFailure). The runnable is scheduled on mScheduledExecutor after SERVICE_BINDING_TIMEOUT ms, and the ScheduledFuture is stored in mScheduledFutureMap keyed by the Call.",
      "ConnectionServiceWrapper.Adapter.handleCreateConnectionComplete() and handleCreateConferenceComplete(): look up the Call via mCallIdMapper.getCall(callId). If mScheduledFutureMap contains it, cancel the pending future and remove the entry, so a timely creation disarms the timeout.",
      "src/com/android/server/telecom/LogUtils.java: adds Events.CREATE_CONNECTION_TIMEOUT and Events.CREATE_CONFERENCE_TIMEOUT. Call.getState() and ConnectionServiceWrapper.addCall() are marked @VisibleForTesting (addCall is also made public), and setScheduledExecutorService() is added for tests.",
      "Commit 12016109c473 (android-security-14.0.0_r17, second iteration, Change-Id If46cfa26...) re-applies the same logic with hardening. In both Adapter handlers, the lookup is guarded by 'call != null &&' before mScheduledFutureMap.containsKey(call). The commit message says the first iteration caused an NPE.",
      "Second iteration, ConnectionServiceWrapper.removeServiceInterface(): returns early with a warning when mServiceInterface is already null (duplicate calls from onBinderDied and onServiceDisconnected). After mServiceInterface = null, it shuts down mScheduledExecutor and sets it to null. handleConnectionServiceDeath() now clears mScheduledFutureMap alongside mCallIdMapper.clear().",
      "Second iteration also adds anomaly reporting: CREATE_CONNECTION_TIMEOUT_ERROR_UUID / CREATE_CONFERENCE_TIMEOUT_ERROR_UUID with messages, reported via mAnomalyReporter (AnomalyReporterAdapterImpl) inside the timeout runnables, plus setAnomalyReporterAdapter() for tests.",
      "Tests: CallsManagerTest.testConnectionServiceCreateConnectionTimeout registers a mock IConnectionService via ComponentContextFixture.addConnectionService and injects a TestScheduledExecutorService. It then calls service.createConnection(call, response) and asserts that a runnable is scheduled at 15000 ms. The second iteration also advances time and verifies response.handleCreateConnectionFailure(DisconnectCause.ERROR). BasicCallTests calls call.setIsCreateConnectionComplete(true) in two tests, and ComponentContextFixture gains removeConnectionService()/removeService()."
     ],
     "completeness": [
      "Covers both outbound creation paths in ConnectionServiceWrapper: createConnection() and createConference(). The timeout is disarmed only by Adapter.handleCreateConnectionComplete()/handleCreateConferenceComplete().",
      "The timeout is keyed per Call in mScheduledFutureMap. A second createConnection() for the same Call overwrites the map entry without cancelling the earlier future; the earlier runnable still only acts if call.isCreateConnectionComplete() is false.",
      "In the second iteration, removeServiceInterface() shuts down mScheduledExecutor and sets it to null, but createConnection()/createConference() call mScheduledExecutor.schedule() without a null check. The material does not show whether a wrapper instance is reused for a later bind after service death.",
      "The first iteration (b007692a8d52) lacks the null check on the Call returned by mCallIdMapper.getCall(callId), and it lacks the executor shutdown and map clearing. The second iteration (12016109c473) supersedes it. This diff does not touch other reasons the wrapper binds to a ConnectionService (outside connection/conference creation), and the material does not show those paths."
     ],
     "behavior_changes": [
      "If a ConnectionService does not complete connection/conference creation within 15 seconds of the request, the request is now failed with DisconnectCause.ERROR via CreateConnectionResponse.handleCreateConnectionFailure / handleCreateConferenceFailure. Legitimately slow services are affected the same way.",
      "New call log events CREATE_CONNECTION_TIMEOUT and CREATE_CONFERENCE_TIMEOUT, and an error log with a stack trace naming the component. The second iteration also adds anomaly reports with fixed UUIDs 54b7203d-... and caafe5ea-....",
      "Second iteration: removeServiceInterface() is idempotent (early return when mServiceInterface is null) and shuts down the wrapper's scheduled executor. handleConnectionServiceDeath() clears pending timeout futures.",
      "Each ConnectionServiceWrapper instance now creates its own single-thread ScheduledExecutorService. The timeout runnable is constructed with mLock."
     ],
     "preconditions": [
      "An app exposes a ConnectionService (protected by BIND_CONNECTION_SERVICE) that Telecom binds to and asks to create a connection or conference. That is, ConnectionServiceWrapper.createConnection() or createConference() is reached for a Call that uses that app's ConnectionService.",
      "The app never invokes handleCreateConnectionComplete/handleCreateConferenceComplete for the call id, so call.isCreateConnectionComplete() stays false."
     ],
     "per_major": {
      "14": "Fixed in two steps. First b007692a8d52 ('DO NOT MERGE' first iteration, android-security-14.0.0_r12). Then 12016109c473 (android-security-14.0.0_r17), which re-applies the change with the null check on the looked-up Call, idempotent removeServiceInterface(), executor shutdown, mScheduledFutureMap.clear() and anomaly reporting. At android-security-14.0.0_r29 the patched region is fully present (8/8 lines).",
      "15": "Same fix as the Android 14 second iteration: 4f73f974ca16 in android-security-15.0.0_r5, with the same ConnectionServiceWrapper and LogUtils changes. The test's ConnectionServiceWrapper constructor call takes an extra mFeatureFlags argument. At android-security-15.0.0_r17 the patched region is fully present (8/8 lines).",
      "16": "Not affected per the bulletin. The patched region is present at android-security-16.0.0_r8 (8/8 lines).",
      "17": "Not affected per the bulletin. The patched region is present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "Backport the second iteration (12016109c473 on 14 / 4f73f974ca16 on 15), not the DO NOT MERGE first iteration. It contains the 'call != null' guard before ConcurrentHashMap.containsKey, the removeServiceInterface() early return and executor shutdown, and mScheduledFutureMap.clear() in handleConnectionServiceDeath(). These diffs do not add the code it depends on, so the target tree must already have: Call.isCreateConnectionComplete()/setIsCreateConnectionComplete(), AnomalyReporterAdapter/AnomalyReporterAdapterImpl, android.telecom.Logging.Runnable.getRunnableToCancel(), and a tests-side TestScheduledExecutorService.",
      "Adjust the ConnectionServiceWrapper test constructor call to the branch's signature (Android 15 adds a trailing mFeatureFlags argument)."
     ],
     "confidence": "high",
     "unknowns": [
      "The commit message describes the privilege the attacker retained (the 'declared permission' / process state) only generically.",
      "The material does not show whether a ConnectionServiceWrapper instance can be reused for a new bind after removeServiceInterface() nulls mScheduledExecutor (potential NPE in createConnection).",
      "The material does not show whether other bind triggers in ConnectionServiceWrapper, besides createConnection/createConference, can keep a service bound without a deadline.",
      "The callers that drive ConnectionServiceWrapper.createConnection()/createConference() during call setup are not shown in the material.",
      "The material does not explain why the first iteration was marked DO NOT MERGE or exactly how it was superseded in the 14 security 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."
     }
    },
    "CVE-2024-49720": {
     "cve": "CVE-2024-49720",
     "summary": "Adds PackageManager.FLAG_PERMISSION_ONE_TIME to the set of flags that the role controller treats as a user choice, so role (re-)evaluation no longer overrides a permission the user chose to grant only once, and clears that flag when a role grant gives a permission that was not previously granted.",
     "component": "platform/packages/modules/Permission, PermissionController role-controller (com.android.role.controller.model.Permissions)",
     "weakness": "Incomplete check of user-choice state (missing FLAG_PERMISSION_ONE_TIME in the \"user set or fixed\" flag set), leading to improper permission grant",
     "root_cause": "In PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java, the role granting logic decides whether a permission's current state is a user choice that must be respected. When overrideUserSetAndFixed is false, it tests only FLAG_PERMISSION_USER_FIXED and FLAG_PERMISSION_USER_SET. FLAG_PERMISSION_ONE_TIME, which reflects the 'ask every time' choice, was not in that set. As a result, role re-evaluation did not treat a one-time choice as a user choice and could override it (the commit message calls this the missing ONE_TIME check). Likewise, the mask applied when a role grants a previously ungranted permission cleared USER_FIXED/USER_SET but not ONE_TIME.",
     "fix_analysis": [
      "Permissions.java, grant path (hunk at line ~249 in 14, ~258 in 15; the method that calls grantPermissionAndAppOp and then setPermissionFlags): when the permission was not previously granted (!wasPermissionOrAppOpGranted), newMask now also includes PackageManager.FLAG_PERMISSION_ONE_TIME alongside FLAG_PERMISSION_USER_FIXED and FLAG_PERMISSION_USER_SET, so a role grant clears the one-time state together with the other user-choice flags.",
      "Permissions.java, fixed-flags helper (hunk at line ~606 in 14, ~629 in 15; the method that builds fixedFlags based on overrideUserSetAndFixed and returns (flags & fixedFlags) != 0; the material does not show its name): when overrideUserSetAndFixed is false, fixedFlags now also includes FLAG_PERMISSION_ONE_TIME, so a permission with the one-time flag counts as fixed by user choice and the role controller does not override it.",
      "Single commit with two hunks in one file; no other files changed. The commit cites RolePermissionOverrideTest, which is not in the diff shown."
     ],
     "completeness": [
      "Covers both places in Permissions.java where USER_FIXED | USER_SET were used as the user-choice set: the fixed-flags check that gates overriding, and the mask applied on grant.",
      "Only the role-controller model is changed. The material does not show whether any other component evaluates user-choice flags with the same two-flag set, so coverage outside this file is unknown.",
      "When overrideUserSetAndFixed is true, the fixed-flags check does not add the user-choice flags, so ONE_TIME is still not treated as fixed in that case."
     ],
     "behavior_changes": [
      "During role evaluation without user-choice override, a permission carrying FLAG_PERMISSION_ONE_TIME is now considered fixed and the role controller does not override it.",
      "When the role controller grants a previously ungranted permission, FLAG_PERMISSION_ONE_TIME is now cleared in the same mask update as USER_FIXED and USER_SET."
     ],
     "preconditions": [
      "An app holds (or is being granted) a role whose permission grant does not override user set/fixed flags (overrideUserSetAndFixed false).",
      "The user has made a one-time ('ask every time') choice for a permission in that role's permission set, so FLAG_PERMISSION_ONE_TIME is set.",
      "Role (re-)evaluation runs and reaches the grant path in Permissions.java for that permission."
     ],
     "per_major": {
      "14": "same fix: commit e02775c62a81 in android-security-14.0.0_r19 (cherry-pick, Change-Id Ie65c2cd62ba3f24e8dd411abca49e397746bc1a9); at android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "same fix: commit 87fda0e3b03e in android-security-15.0.0_r7, with the same two hunks at shifted line numbers (258, 629); at android-security-15.0.0_r17, 7/8 lines of the patched region are present.",
      "16": "not affected (not listed by the bulletin); at android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin); at android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "The change is two one-line additions in Permissions.java. Apply both hunks (grant mask and fixed-flags check) together, as Google did on 14 and 15. Permission is a mainline module delivered via PermissionController, so a device may also receive this through a module update rather than the platform tag (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the name of the method that contains the second hunk (fixedFlags / overrideUserSetAndFixed).",
      "The material does not show which roles use overrideUserSetAndFixed false, so it does not show which permissions or role holders were affected.",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 commit.",
      "The RolePermissionOverrideTest referenced in the commit message is not included in the diff.",
      "The material does not say which line of the patched region is missing in each later tag (7/8 lines present)."
     ],
     "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-49728": {
     "cve": "CVE-2024-49728",
     "summary": "Adds a check in Bluetooth OPP's BluetoothOppSendFileInfo.generateFileInfo that rejects content URIs whose authority carries a user-id prefix for a user other than the current one. Such URIs now return SEND_FILE_INFO_ERROR and are not opened.",
     "component": "platform/packages/modules/Bluetooth, OPP (Object Push Profile) outbound share, android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java",
     "weakness": "Missing validation of caller-supplied content URI (the user id in the URI authority is not checked), information disclosure",
     "root_cause": "BluetoothOppSendFileInfo.generateFileInfo went on to call contentResolver.getType and to query and open any content URI that passed the earlier checks. The tests mock contentResolverQuery and contentResolverOpenAssetFileDescriptor for this path. A content URI authority can carry a user id as user-info (for example \"<id>@media\"). Nothing checked that this user id matched the Bluetooth app's own user (UserHandle.myUserId()) before the provider was used.",
     "fix_analysis": [
      "BluetoothOppSendFileInfo.java:generateFileInfo: after an existing early-return error check and before contentResolver.getType(uri), the fix adds `if (isContentUriForOtherUser(uri))`. On a match it logs an error with the URI and myUserId() and returns SEND_FILE_INFO_ERROR, so the URI is never queried or opened.",
      "BluetoothOppSendFileInfo.java:isContentUriForOtherUser (new private static helper): reads uri.getUserInfo() and returns true only when the user-info is non-empty and not string-equal to String.valueOf(myUserId()). URIs with no user-info, or with the current user's id, are allowed.",
      "On the Android 15 branch the same block also calls ContentProfileErrorReportUtils.report(BluetoothProfile.OPP, BluetoothProtoEnums.BLUETOOTH_OPP_SEND_FILE_INFO, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, 15) and changes the file's 'Next tag value' comment from 15 to 16. The Android 14 backport does not include this metrics call.",
      "BluetoothOppSendFileInfoTest.java: adds three tests (other user returns SEND_FILE_INFO_ERROR; implicit user returns normal info; same user returns normal info). They use a new buildContentUriWithEncodedAuthority helper that builds content URIs with an encodedAuthority such as \"<id>@media\"."
     ],
     "completeness": [
      "The check sits in generateFileInfo just before contentResolver.getType and only applies to URIs that explicitly put a user id in the authority. URIs with no user-info are treated as belonging to the current user.",
      "The comparison is a plain string equality against String.valueOf(myUserId()). Any non-empty user-info that does not match is rejected.",
      "The check runs before getType, query and openAssetFileDescriptor, so a rejected URI causes no provider interaction.",
      "This commit does not change any other OPP code path that may handle caller-supplied URIs. The material does not show whether any such path bypasses generateFileInfo."
     ],
     "behavior_changes": [
      "generateFileInfo now returns SEND_FILE_INFO_ERROR for content URIs whose authority names a different user id.",
      "A new Log.e line \"Uri: <uri> is invalid for user <id>\" is written on rejection.",
      "Android 15 only: a ContentProfileErrorReportUtils error report with tag 15 is emitted on rejection."
     ],
     "preconditions": [
      "A content URI reaches BluetoothOppSendFileInfo.generateFileInfo (the OPP outbound share path) with an authority of the form \"<otherUserId>@<authority>\"."
     ],
     "per_major": {
      "14": "adapted fix: commit 339d20053956 in android-security-14.0.0_r17 (RESTRICT AUTOMERGE backport). It has the same isContentUriForOtherUser check and tests, without the ContentProfileErrorReportUtils metrics call. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "same fix plus metrics: commit 4b65cbb339db (\"Reapply ...\") in android-security-15.0.0_r5 adds the check and a ContentProfileErrorReportUtils.report call with tag 15. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin (not listed). The patched region (8/8 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed). The patched region (8/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. The only branch difference is the Android 15 ContentProfileErrorReportUtils.report call and the 'Next tag value' comment, which the 14 backport leaves out. The 14 context shows neither that comment nor imports for that reporting scheme. When porting to a tree without ContentProfileErrorReportUtils, use the 14 form.",
      "At r29, the Android 14 test file imports TestParameterInjector and has no AndroidJUnit4 import, unlike the diff context. Check that the added tests fit the test runner used in the target tree."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which component or intent delivers the URI into generateFileInfo.",
      "The material does not show what the early-return check just before the insertion point validates.",
      "The material does not show whether the Bluetooth app could actually resolve or read another user's provider through such a URI.",
      "The Android 13 fix commit is not in the material, although the bulletin lists 13 as affected.",
      "The original Android 15 commit that 'Reapply' restores, and the reason it was reverted, 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-49730": {
     "cve": "CVE-2024-49730",
     "summary": "Changes MediaProvider FuseDaemon's MAX_READ_SIZE from a fixed 128 KiB to FUSE_MAX_MAX_PAGES (256) * getpagesize(). Per the commit message, the goal is to make the zero_addr buffer as large as the largest single request.",
     "component": "platform/packages/providers/MediaProvider, FUSE daemon (jni/FuseDaemon.cpp)",
     "weakness": "Undersized fixed buffer relative to the maximum request size (the exact memory consequence is not shown in the material)",
     "root_cause": "jni/FuseDaemon.cpp defined MAX_READ_SIZE as constexpr 128 * 1024. The commit title says zero_addr must be expanded to the maximum size of a single request, and the diff changes only MAX_READ_SIZE. This implies (an inference, not shown directly) that zero_addr was sized from MAX_READ_SIZE and could be smaller than a single request. The test name (RedactUriDeviceTest) suggests the redaction read path. The use site of zero_addr is not in the material.",
     "fix_analysis": [
      "jni/FuseDaemon.cpp (file-scope constants): adds #define FUSE_MAX_MAX_PAGES 256 and replaces `constexpr size_t MAX_READ_SIZE = 128 * 1024;` with `const size_t MAX_READ_SIZE = FUSE_MAX_MAX_PAGES * getpagesize();`. This gives 1 MiB at 4 KiB pages and 4 MiB at 16 KiB pages.",
      "No bounds check is added in the diff. The fix relies only on the constant, and so (inferred) the buffer, being large enough."
     ],
     "completeness": [
      "The fix only enlarges the constant. The diff adds no explicit check comparing the request size against MAX_READ_SIZE. Correctness depends on 256 pages being the real upper bound for a single request.",
      "Any other users of MAX_READ_SIZE in FuseDaemon.cpp also get the larger value. Those sites are not shown.",
      "At the latest tags (14.0.0_r29, 15.0.0_r17, 16.0.0_r8, 17.0.0_r1), 4/5 lines of the patched region are present. The 14.0.0_r29 code context contains both new lines, so the line that differs appears to be context rather than the fix."
     ],
     "behavior_changes": [
      "MAX_READ_SIZE changes from a compile-time constexpr to a runtime-initialized const that depends on getpagesize().",
      "MAX_READ_SIZE grows from 128 KiB to 256 pages (1 MiB on 4 KiB-page devices, 4 MiB on 16 KiB-page devices)."
     ],
     "preconditions": [
      "A FUSE read served by MediaProvider's FuseDaemon that uses zero_addr. The test name suggests the redaction path; the exact use site is not in the material.",
      "A single request larger than 128 KiB."
     ],
     "per_major": {
      "14": "same fix: commit 961c8cbd2a48 in android-security-14.0.0_r19 (cherry-pick of e45ef2e4c41c). The fix lines are present at android-security-14.0.0_r29.",
      "15": "not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-hunk change. MAX_READ_SIZE is no longer a compile-time constant, so check that it is not used in a constexpr context elsewhere in FuseDaemon.cpp, and that getpagesize is declared in the translation unit."
     ],
     "confidence": "medium",
     "unknowns": [
      "How and where zero_addr is allocated and used, and what memory operation exceeded it.",
      "Whether zero_addr is in fact sized from MAX_READ_SIZE (inferred, not shown).",
      "Other references to MAX_READ_SIZE in FuseDaemon.cpp.",
      "Which line of the 5-line patched region differs at the latest tags.",
      "Whether Android 13 (listed as affected) received a corresponding commit.",
      "The bulletin lists '15-next' among affected versions, while the per-major facts say Android 15 is not listed."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22416": {
     "cve": "CVE-2025-22416",
     "summary": "ChooserActivity now checks, through IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, that the launching caller has read access to the URI behind any URI-type Icon in the caller-supplied ChooserTarget array. If the check fails, the icon is removed from that target.",
     "component": "platform/frameworks/base, core/java/com/android/internal/app/ChooserActivity.java (share sheet / intent chooser)",
     "weakness": "Missing permission check (caller-supplied Icon URIs accepted without checking that the caller has a read grant)",
     "root_cause": "ChooserActivity parses the caller-provided Parcelable array into mCallerChooserTargets in a loop that casts pa[i] to ChooserTarget. Each target was accepted as-is, including its Icon. An Icon of type TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP references a URI, and nothing checked that the caller (getLaunchedFromUid()) had read access to that URI.",
     "fix_analysis": [
      "core/java/com/android/internal/app/ChooserActivity.java, caller-targets parsing loop (hunk at new line 689): the old line `targets[i] = (ChooserTarget) pa[i]` is replaced. Each target is now passed to the new hasValidIcon(). If that returns false, the target is replaced by removeIcon(chooserTarget) before it is stored in mCallerChooserTargets.",
      "ChooserActivity.hasValidIcon(ChooserTarget): returns true if the icon is null or is not of type Icon.TYPE_URI / Icon.TYPE_URI_ADAPTIVE_BITMAP. For URI icons it calls getUriGrantsManager().checkGrantUriPermission_ignoreNonSystem(getLaunchedFromUid(), getPackageName(), getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, getUserIdFromUri(uri)). A SecurityException or RemoteException is caught and logged with Log.e, and the method returns false.",
      "ChooserActivity.getUriGrantsManager(): thin wrapper that returns UriGrantsManager.getService() (IUriGrantsManager).",
      "ChooserActivity.removeIcon(ChooserTarget): rebuilds the ChooserTarget with the same title, score, componentName and intentExtras but a null icon. The target stays in the list without the icon.",
      "New imports added: ContentProvider.getUriWithoutUserId, IUriGrantsManager, UriGrantsManager, graphics.drawable.Icon, os.RemoteException."
     ],
     "completeness": [
      "Covers only the caller-supplied ChooserTarget array that is parsed into mCallerChooserTargets. The diff does not touch any other source of ChooserTarget objects or Icons in this file. The material does not show whether those carry caller-controlled URI icons.",
      "Only Icon types TYPE_URI and TYPE_URI_ADAPTIVE_BITMAP are checked. Other Icon types pass through unchanged.",
      "Only FLAG_GRANT_READ_URI_PERMISSION is checked.",
      "On failure the target is kept and only the icon is dropped. A RemoteException is handled the same way as a denial, so the check fails closed.",
      "The fix only changes the frameworks/base copy of ChooserActivity. The material does not show whether the separate IntentResolver module chooser (packages/modules/IntentResolver) needed or received an equivalent change."
     ],
     "behavior_changes": [
      "A caller-provided ChooserTarget whose URI icon the caller cannot read is rebuilt with a null icon, and an error line 'Failed to get URI permission for: <uri>' is logged.",
      "One extra binder call to UriGrantsManager for each caller-provided target that has a URI-type icon.",
      "Targets with non-URI icons or no icon are unaffected."
     ],
     "preconditions": [
      "An app launches ChooserActivity in frameworks/base with ChooserTarget parcelables. These are the array parsed into mCallerChooserTargets.",
      "At least one of those ChooserTargets has an Icon of type TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP pointing at a URI that the calling UID cannot read.",
      "The frameworks/base ChooserActivity is the code path in use (the diff only changes that class)."
     ],
     "per_major": {
      "14": "same fix: commit 82aa8527b7da in android-security-14.0.0_r19 (cherry-pick of internal bad47a22/729a07b8). 8/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix: commit ee65c97e3a19 in android-security-15.0.0_r7. The diff is identical; only the offset of the trailing hunk differs (4206 on 15 vs 4305 on 14). 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. It depends on IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, Icon.TYPE_URI_ADAPTIVE_BITMAP, ContentProvider.getUriWithoutUserId and getLaunchedFromUid existing on the target branch. The new helpers are appended at the end of the class."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact semantics of checkGrantUriPermission_ignoreNonSystem (what the '_ignoreNonSystem' qualifier exempts) are not shown in the material.",
      "The material does not show where in ChooserActivity the URI icon is loaded or what data could be exposed. The bulletin only rates the issue High EoP.",
      "Android 13 is listed as affected, but the material includes no Android 13 commit or tag status.",
      "The material does not show whether the packages/modules/IntentResolver chooser, including how it handles caller-supplied chooser actions, needed or received an equivalent change."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22417": {
     "cve": "CVE-2025-22417",
     "summary": "In Transition.finishTransition (services/core/java/com/android/server/wm/Transition.java), the ActivityRecordInputSink (touch-blocking) state of visible participant activities is now written into each activity's pending transaction and flushed via scheduleAnimationLocked(), instead of being applied immediately through a freshly created standalone SurfaceControl.Transaction, to fix a race that could leave the state stale.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/Transition.java, shell transitions / ActivityRecordInputSink)",
     "weakness": "Race condition (surface transaction ordering) leaving the input-sink touch-blocking state stale",
     "root_cause": "After a transition finished, Transition.java allocated a new SurfaceControl.Transaction via ar.mWmService.mTransactionFactory.get(), called ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged() on it for every visible ActivityRecord participant, and applied it immediately. Per the commit message, when finishTransition ran before the pending transaction had been applied, the result could be a stale state. Because the InputWindowHandle is only applied on changes, a later prepareSurfaces pass did not fix it, so the incorrect input-sink state persisted.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/Transition.java, finishTransition (the block commented 'Update the input-sink (touch-blocking) state now that the animation is finished'): removes the local SurfaceControl.Transaction inputSinkTransaction created from ar.mWmService.mTransactionFactory.get() and its immediate inputSinkTransaction.apply().",
      "For each participant where ar != null && ar.isVisible() && ar.getParent() != null, calls ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(ar.getPendingTransaction()) so the input-sink change is queued in that container's pending transaction.",
      "Sets a boolean scheduleAnimation and, if any participant was updated, calls mController.mAtm.mWindowManager.scheduleAnimationLocked() ('To apply pending transactions') so the pending transactions get applied, rather than relying on a separate apply."
     ],
     "completeness": [
      "Covers only the finishTransition path in Transition.java and only ActivityRecord participants; it changes where the input-sink update is queued, not the applyChangesToSurfaceIfChanged 'only on change' semantics.",
      "Flushing now depends on scheduleAnimationLocked() causing the pending transactions to be applied; the fix does not add any explicit apply of the pending transaction itself.",
      "The code at android-security-14.0.0_r29 shows the same block extended to also update task.mTaskInputSink via task.getPendingTransaction() for visible Task participants; that Task path is not part of this commit's diff.",
      "Other callers of ActivityRecordInputSink.applyChangesToSurfaceIfChanged are not shown in the material and are not touched by this commit."
     ],
     "behavior_changes": [
      "Input-sink (touch-blocking) surface updates at the end of a transition are deferred to the application of pending transactions instead of being applied synchronously in finishTransition.",
      "finishTransition now calls scheduleAnimationLocked() whenever at least one visible, attached ActivityRecord participant exists.",
      "No new SurfaceControl.Transaction is allocated via mTransactionFactory in this path."
     ],
     "preconditions": [
      "A transition with at least one visible, attached ActivityRecord participant reaches Transition.finishTransition in WindowManager.",
      "Per the commit message, finishTransition must occur before the pending transaction has been applied (timing-dependent race); the exact triggering sequence of the POC is not shown."
     ],
     "per_major": {
      "14": "same fix: commit 89e32320207c in android-security-14.0.0_r19 (DO NOT MERGE cherry-pick, Change-Id If93840ad7b2abbcb48c0c332870c48046c0f8fdc). At android-security-14.0.0_r29 the block also handles task.mTaskInputSink (3/8 patched lines present), still using getPendingTransaction() and scheduleAnimationLocked().",
      "15": "same fix: commit d0c52fbde587 in android-security-15.0.0_r7, identical hunk at a different offset (line 1398). At android-security-15.0.0_r17 3/8 lines of the patched region are present; the code there is not shown.",
      "16": "not affected per the bulletin; at android-security-16.0.0_r8 3/8 lines of the patched region are present (code not shown in the material).",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1 3/8 lines of the patched region are present (code not shown in the material)."
     },
     "backport_notes": [
      "Single self-contained hunk in Transition.finishTransition; it uses getPendingTransaction(), scheduleAnimationLocked() and ActivityRecord.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(Transaction). On trees where the block already handles task.mTaskInputSink (as at 14.0.0_r29), apply by hand: both the ActivityRecord and Task branches should pass the container's getPendingTransaction() and set scheduleAnimation."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete user-visible consequence of the stale input-sink state and the POC steps referenced in the commit message (Bug 332277530) are not in the material.",
      "Whether other callers of applyChangesToSurfaceIfChanged use a standalone transaction with the same ordering issue is not shown.",
      "The exact Transition.java content at the Android 15 r17, 16 and 17 security tags is not shown; only the 3/8 line-match count is given.",
      "When the Task input-sink branch seen at 14.0.0_r29 was introduced 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-2025-22418": {
     "cve": "CVE-2025-22418",
     "summary": "Settings' RingtonePreference now sets a target package on the ringtone picker intent by calling setPackage() with a new string resource (config_sound_picker_package_name, default com.android.soundpicker), so it no longer sends an implicit ACTION_RINGTONE_PICKER intent.",
     "component": "platform/packages/apps/Settings, RingtonePreference (ringtone/sound picker launch)",
     "weakness": "Unsafe implicit intent: the picker intent had no target package, so any app handling the action could be resolved in place of the intended sound picker",
     "root_cause": "In src/com/android/settings/RingtonePreference.java, the constructor RingtonePreference(Context, AttributeSet) called setIntent(new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)) with no target package or component. That made the intent implicit, so it could resolve to any installed app that declares a filter for ACTION_RINGTONE_PICKER rather than the intended sound picker.",
     "fix_analysis": [
      "res/values/config.xml: adds a new non-translatable string resource, config_sound_picker_package_name, with default value com.android.soundpicker. It sits between config_settingsintelligence_slice_supported and config_aosp_emergency_package_name.",
      "src/com/android/settings/RingtonePreference.java:RingtonePreference(Context, AttributeSet): the intent passed to setIntent() is now new Intent(RingtoneManager.ACTION_RINGTONE_PICKER).setPackage(context.getString(R.string.config_sound_picker_package_name)). This restricts resolution to the configured package. The rest of the constructor (ringtone type, showDefault, showSilent, setUserId) is unchanged."
     ],
     "completeness": [
      "The fix covers only the intent built in the RingtonePreference constructor. It makes the intent explicit by package (setPackage), not by component.",
      "The material does not show any other Settings code paths that build ACTION_RINGTONE_PICKER intents or replace this preference's intent, so sibling paths cannot be judged from the diff.",
      "The target package comes from a string resource in res/values/config.xml, so the protection depends on that value naming the intended sound picker package."
     ],
     "behavior_changes": [
      "The picker intent from RingtonePreference now resolves only to the package named in config_sound_picker_package_name (com.android.soundpicker by default). Other apps that handle ACTION_RINGTONE_PICKER are no longer candidates for this launch."
     ],
     "preconditions": [
      "A RingtonePreference in the Settings app is used to launch the ringtone/sound picker.",
      "Before the fix, an installed app declares an intent filter for RingtoneManager.ACTION_RINGTONE_PICKER and so is a candidate for implicit resolution."
     ],
     "per_major": {
      "14": "same fix: commit ad9fb985df47 in android-security-14.0.0_r19 (cherry-pick, Change-Id I905c0f3ddaf0f488ff2fc3bf1747b6df0d802504). The patched region is present at android-security-14.0.0_r29 (7/8 lines); both the setPackage call and the config string appear in the shown code.",
      "15": "not affected (not listed by the bulletin). The patched region (7/8 lines) is present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). The patched region (7/8 lines) is present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). The patched region (7/8 lines) is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit touching two files: add the string resource to res/values/config.xml and the setPackage() call in RingtonePreference's constructor. Forks that ship a sound picker under a different package name must make config_sound_picker_package_name match it."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which extras or result data the picker flow exposes; the material shows only the intent action.",
      "Whether other Settings code paths build ACTION_RINGTONE_PICKER intents without setPackage.",
      "What happens at runtime if the configured package is not installed.",
      "Which line of the 8-line patched region differs in the latest tags (7/8 reported), although both functional changes appear in the shown code."
     ],
     "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-2025-22419": {
     "cve": "CVE-2025-22419",
     "summary": "Adds WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to the activity window in onCreate of 18 Telephony (com.android.phone) settings, SIM PIN, FDN, SIM contacts and emergency dialer activity classes so non-system overlay windows are not shown over them.",
     "component": "platform/packages/services/Telephony, com.android.phone settings/FDN/SIM PIN/EmergencyDialer activities",
     "weakness": "Missing overlay (tapjacking) protection on sensitive UI: the activity windows did not request SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS (improper restriction of rendered UI layers)",
     "root_cause": "The Telephony activities that handle SIM PIN/PIN2 entry (ChangeIccPinScreen, EnableIccPinScreen, GetPin2Screen), FDN management (FdnSetting, FdnList, BaseFdnContactScreen, EditFdnContactScreen, DeleteFdnContactScreen), SIM contacts (ADNList, SimContacts), call feature settings (CallFeaturesSetting, GsmUmts*/Cdma* call option, forwarding and barring screens) and EmergencyDialer did not set SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS in onCreate, so overlay windows of other apps were not hidden while these screens were shown. The bulletin rates this as a High severity EoP.",
     "fix_analysis": [
      "Single commit (Change-Id I953667309753ff6fc966956e246512de97ad8638, 92f47baf4505 in android-security-14.0.0_r19). Each patched onCreate adds getWindow().addSystemFlags(android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS). In most files it directly follows super.onCreate(). In CallFeaturesSetting it follows the DBG log line. In ADNList it follows requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS). In BaseFdnContactScreen it follows resolveIntent() and requestFeature(). No other logic changes; 70 insertions, 0 deletions.",
      "src/com/android/phone/ADNList.java:onCreate, SimContacts.java:onCreate, settings/fdn/FdnList.java:onCreate: SIM/FDN contact list screens get the flag (SimContacts and FdnList extend ADNList and also set it in their own onCreate).",
      "src/com/android/phone/ChangeIccPinScreen.java:onCreate, EnableIccPinScreen.java:onCreate, settings/fdn/GetPin2Screen.java:onCreate: SIM PIN / PIN2 entry screens get the flag before setContentView.",
      "src/com/android/phone/settings/fdn/BaseFdnContactScreen.java:onCreate, EditFdnContactScreen.java:onCreate, DeleteFdnContactScreen.java:onCreate, FdnSetting.java:onCreate: FDN management screens get the flag (the two subclasses set it in addition to the base class).",
      "src/com/android/phone/CallFeaturesSetting.java:onCreate (before the admin-user check), GsmUmtsCallOptions, GsmUmtsAdditionalCallOptions, GsmUmtsCallForwardOptions, GsmUmtsCallBarringOptions, CdmaCallOptions, CdmaCallForwardOptions (all :onCreate, before addPreferencesFromResource): call settings screens get the flag.",
      "src/com/android/phone/EmergencyDialer.java:onCreate: emergency dialer window gets the flag before reading the entry type extra."
     ],
     "completeness": [
      "Covers 18 activity classes in packages/services/Telephony; the flag is applied unconditionally in onCreate.",
      "Several additions are redundant but harmless: SimContacts and FdnList inherit the flag from ADNList.onCreate, and EditFdnContactScreen/DeleteFdnContactScreen inherit it from BaseFdnContactScreen.onCreate, yet each also sets it again.",
      "The flag is set only on the activity's own window (getWindow()). Separate windows these activities may create (for example the AlertDialog shown via builder.show() in CallFeaturesSetting, displayPwChangeDialog in GsmUmtsCallBarringOptions, EditPinPreference dialogs in FdnSetting) are not touched by this diff.",
      "Only the files in the diff are changed; whether other Telephony activities outside this list lack the flag is not shown by the material.",
      "The flag hides non-system overlay windows only; system windows are not affected (general platform knowledge)."
     ],
     "behavior_changes": [
      "While any of the patched activities is visible, overlay windows belonging to non-system apps (e.g. TYPE_APPLICATION_OVERLAY from apps with SYSTEM_ALERT_WINDOW) are hidden by the window manager (general platform knowledge).",
      "No functional or API change to the screens themselves in the diff; no new error paths or permissions."
     ],
     "preconditions": [
      "One of the patched Telephony activities must be in the foreground; which of these activities are exported is not shown in the material.",
      "An app able to draw overlay windows must be present on the device.",
      "Telephony (com.android.phone) runs as a platform-signed system app, which is what allows it to set this system window flag (general platform knowledge)."
     ],
     "per_major": {
      "14": "same fix: commit 92f47baf4505 in android-security-14.0.0_r19, 18-file diff; at android-security-14.0.0_r29 the patched region is present (8/8 lines).",
      "15": "same fix: commit 81cc0fd56aa9 in android-security-15.0.0_r7 adds identical flag lines; surrounding code differs in ChangeIccPinScreen and EnableIccPinScreen (UserManager DISALLOW_CONFIG_MOBILE_NETWORKS check follows the insertion), other files differ only in line offsets. 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 lines) at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed); the patched region is present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit: each hunk inserts the same 3-line getWindow().addSystemFlags(...) call into an onCreate override (directly after super.onCreate() in most files, after other early calls in CallFeaturesSetting, ADNList and BaseFdnContactScreen). Forks that customize these activities must patch every listed onCreate. The same pattern is used in packages/apps/Settings SimDialogActivity.onCreate per index search.",
      "Android 13 is listed as affected by the bulletin but no 13 commit is included in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact attack scenario and which activity was the reported entry point; bug 335387175 details are not in the material.",
      "Whether and in which tag the Android 13 fix was applied.",
      "Whether other Telephony activities not in this diff also lack the flag.",
      "Which of the patched activities are exported.",
      "The API level at which Window.addSystemFlags became available, relevant for older-branch backports."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22421": {
     "cve": "CVE-2025-22421",
     "summary": "SystemUI stops embedding notification title/text/ticker in the accessibility content description of status bar notification icons, passing an empty string instead so only the app name is included.",
     "component": "platform/frameworks/base, SystemUI status bar (StatusBarIconView / NotificationContentDescription)",
     "weakness": "Information exposure through accessibility content description (notification content placed in a11y text of a status bar icon)",
     "root_cause": "The content-description builder for status bar notification icons (StatusBarIconView.java on Android 14; contentDescForNotification in NotificationContentDescription.kt on Android 15) read Notification.EXTRA_TITLE, Notification.EXTRA_TEXT and tickerText and formatted them, together with the app name, into R.string.accessibility_desc_notification_icon. Accessibility services (e.g. TalkBack highlighting icons, per the commit's test steps) could therefore read notification content from the small icon. The commit message states the content can be read when the full cards are visible, which is the reason given for removing it from the icon description.",
     "fix_analysis": [
      "Android 14 (platform/frameworks/base 4ec1792148a5): packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java, in the method returning R.string.accessibility_desc_notification_icon: deletes the lookup of EXTRA_TITLE, EXTRA_TEXT and tickerText, the title-vs-appName comparison and the titleOrText/ticker fallback, and returns c.getString(R.string.accessibility_desc_notification_icon, appName, \"\").",
      "Android 15 (platform/frameworks/base 3b0704fd381a): packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt, contentDescForNotification(c, n): same change in Kotlin, keeping only appName = n.loadHeaderAppName(c) ?: \"\" and passing an empty second argument.",
      "Android 15 also updates NotificationContentDescriptionTest.kt: the first test is renamed to notificationWithAllDifferentFields_descriptionIsAppName and now asserts createDescriptionText(n, \"\"); the three other tests (title matches app name: text, ticker, empty fallbacks) are removed."
     ],
     "completeness": [
      "Within the shown hunk, all three content sources (EXTRA_TITLE, EXTRA_TEXT, tickerText) are removed from the icon description; the earlier part of the Android 14 method is not shown.",
      "Only the status bar icon content description is changed. The format call still passes a second argument, now always an empty string; no other a11y surfaces are touched in the shown diffs.",
      "The Android 14 cherry-pick contains only StatusBarIconView.java; the commit message cites StatusBarIconViewTest but no test change is included in that commit."
     ],
     "behavior_changes": [
      "Accessibility content description of status bar notification icons no longer includes notification title/text/ticker; only the app name is filled in.",
      "The 'title equals app name, fall back to text, then ticker' logic is gone.",
      "On Android 15 the unit tests covering title/text/ticker in the description are removed, leaving an app-name-only assertion."
     ],
     "preconditions": [
      "An accessibility service (e.g. TalkBack) reading status bar icon content descriptions is active, per the commit message's test steps.",
      "A notification with a title, text or ticker is posted so that a status bar notification icon is shown for it.",
      "The exact disclosure scenario is not described in the material beyond the commit message."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 4ec1792148a5 in android-security-14.0.0_r19 modifies StatusBarIconView.java only; at android-security-14.0.0_r29 0/8 lines of the patched region are present.",
      "15": "adapted fix: platform/frameworks/base 3b0704fd381a in android-security-15.0.0_r7 applies the same change to contentDescForNotification in NotificationContentDescription.kt and updates NotificationContentDescriptionTest.kt; at android-security-15.0.0_r17 4/8 lines of the patched region are present.",
      "16": "not affected (not listed by the bulletin); 0/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 0/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-commit fix. Apply it to whichever file formats R.string.accessibility_desc_notification_icon: StatusBarIconView.java on Android 14, NotificationContentDescription.kt (contentDescForNotification) on Android 15.",
      "If the target branch has tests asserting title/text/ticker in the icon description, update them as the Android 15 commit does."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact name of the Android 14 method is not shown in the hunk header.",
      "The material does not describe the concrete disclosure scenario (e.g. lock screen or hidden sensitive content).",
      "Whether other SystemUI a11y surfaces expose the same notification fields is not shown.",
      "What the 4/8 patched-region line match at android-security-15.0.0_r17 means is not explained."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22422": {
     "cve": "CVE-2025-22422",
     "summary": "SystemUI's AuthController now dismisses a BiometricPrompt owned by Settings' ConfirmDeviceCredentialActivity when the top activity has a different class name. It does this by carrying the owning activity's class name in PromptInfo and comparing it with the top task's component, instead of comparing package names only.",
     "component": "platform/frameworks/base (android.hardware.biometrics BiometricPrompt/PromptInfo, SystemUI biometrics AuthController/AuthContainerView/AuthDialog) and platform/packages/apps/Settings (password/BiometricFragment)",
     "weakness": "Insufficient ownership/foreground check. The prompt-eviction logic compared only the top activity's package name with the client package, so two different activities in the same package could not be told apart. As a result, a biometric prompt could stay shown over an unrelated activity.",
     "root_cause": "In packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java, the task-stack-change handler evicted the current biometric dialog only when runningTasks.get(0).topActivity.getPackageName() differed from mCurrentDialog.getOpPackageName() and the client was not system. When an app uses createConfirmDeviceCredentialIntent(), Settings' ConfirmDeviceCredentialActivity (via BiometricFragment) requests the prompt, so the client package is Settings. If the app then switches to Settings, the top package still equals the client package and the prompt is not cancelled. Per the commit message, this leaves 'settings activity showing background bp'.",
     "fix_analysis": [
      "frameworks/base core/java/android/hardware/biometrics/PromptInfo.java: adds a new parceled field, mClassNameIfItIsConfirmDeviceCredentialActivity (default null), written and read at the end of the parcel. It also adds a package-private setter setClassNameIfItIsConfirmDeviceCredentialActivity(String) and a public getter. containsTestConfigurations() now returns true when this field is non-null, in the same chain as mAllowBackgroundAuthentication.",
      "frameworks/base core/java/android/hardware/biometrics/BiometricPrompt.java: adds the hidden method Builder.setClassNameIfItIsConfirmDeviceCredentialActivity(), annotated @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL}). It stores mContext.getClass().getName() into the PromptInfo.",
      "frameworks/base packages/SystemUI AuthDialog.java / AuthContainerView.java: adds getClassNameIfItIsConfirmDeviceCredentialActivity() to the AuthDialog interface. AuthContainerView returns mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity().",
      "frameworks/base packages/SystemUI AuthController.java: the task-stack-change handler is refactored to call the new isOwnerInBackground(). That method keeps the package rule: the owner is in background if the top package differs from the client and the client is not system. A null or empty task list returns false, and a null top package counts as a match. The method also adds a CDCA rule: if the dialog's class name is non-null and topActivity.getClassName() is non-null and different, isInBackground is true regardless of the package or system exemption. When the result is true, the dialog is dismissed without callback, callbacks receive onBiometricPromptDismissed(), and mReceiver.onDialogDismissed(DISMISSED_REASON_USER_CANCEL, null) is sent.",
      "packages/apps/Settings src/com/android/settings/password/BiometricFragment.java onCreate(): the BiometricPrompt.Builder chain gains .setClassNameIfItIsConfirmDeviceCredentialActivity(). The prompt therefore records the class name of the fragment's getContext()."
     ],
     "completeness": [
      "The class-name check applies only to prompts whose PromptInfo has the class name set. In the material, only Settings' BiometricFragment sets it. Clients without it are still checked by package name only.",
      "The system-client exemption (Utils.isSystem(mContext, clientPackage)) is preserved, except in the CDCA class-name case, which overrides it.",
      "isOwnerInBackground() is lenient when data is missing. A null or empty task list means no eviction. A null top package satisfies the package rule. A null top class name disables the CDCA class check.",
      "The recorded class name is mContext.getClass().getName() from the fragment's context. The comparison succeeds only if it equals topActivity.getClassName(). The material does not show which Context getContext() returns in all configurations.",
      "Setting the class name makes containsTestConfigurations() return true, and the Builder method is annotated with TEST_BIOMETRIC/USE_BIOMETRIC_INTERNAL. Whether and where this is enforced at runtime is not shown in the material."
     ],
     "behavior_changes": [
      "A biometric prompt started by ConfirmDeviceCredentialActivity is now dismissed when the top activity's class name differs from the recorded one. The receiver gets DISMISSED_REASON_USER_CANCEL and callbacks get onBiometricPromptDismissed.",
      "The PromptInfo parcel layout gains a trailing String field.",
      "New hidden API: BiometricPrompt.Builder.setClassNameIfItIsConfirmDeviceCredentialActivity(). PromptInfo.containsTestConfigurations() returns true when the class name is set.",
      "Log output changes ('Evicting client due to top activity is not : <pkg>', 'isInBackground <bool>')."
     ],
     "preconditions": [
      "An app uses createConfirmDeviceCredentialIntent(), and ConfirmDeviceCredentialActivity shows a BiometricPrompt through BiometricFragment, so Settings is the prompt client.",
      "While the prompt is showing, the app switches to Settings, so the top package still matches the client package.",
      "SystemUI AuthController is showing the dialog (mCurrentDialog != null) and handles a task stack change."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base 6894ba3da6da plus platform/packages/apps/Settings 1c0ab9c696d7 in android-security-14.0.0_r19 (class-name based check). 3/3 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: platform/packages/apps/Settings 32915818d780 in android-security-15.0.0_r7. It replaces setComponentNameForConfirmDeviceCredentialActivity(callingActivity) with setRealCallerForConfirmDeviceCredentialActivity(...), set from getCallingActivity() in ConfirmDeviceCredentialActivity. The matching frameworks/base change for 15 is not in the material. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Not affected per the bulletin. 8/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin. 8/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply frameworks/base 6894ba3da6da first, then Settings 1c0ab9c696d7. The base commit adds the Builder API, the PromptInfo field and the AuthDialog method, and the Settings change calls the new Builder method. Because of the PromptInfo parcel change, framework and SystemUI should ship together.",
      "The Android 15 fix uses a different mechanism (a real-caller ComponentName via setRealCallerForConfirmDeviceCredentialActivity) and replaces an existing setComponentNameForConfirmDeviceCredentialActivity Builder call. The 14 and 15 patches cannot be cherry-picked across branches."
     ],
     "confidence": "high",
     "unknowns": [
      "The frameworks/base side of the Android 15 fix and the Android 13 fix are not in the material.",
      "Whether and where a TEST_BIOMETRIC/USE_BIOMETRIC_INTERNAL gate is enforced for PromptInfo with test configurations is not shown.",
      "What Utils.isSystem(mContext, clientPackage) treats as 'system', and whether Settings qualifies, is not shown.",
      "Whether any caller other than Settings' BiometricFragment sets the class name is not shown.",
      "The name of the AuthController method that holds the eviction logic is not shown in the diff.",
      "The concrete security impact, beyond the bulletin's High/EoP rating and the commit's description, 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-2025-22427": {
     "cve": "CVE-2025-22427",
     "summary": "Removes the onResume/onPause overrides in Settings' NotificationAccessConfirmationActivity that passed the system-only flag SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to Window.addFlags/clearFlags (public flag namespace), leaving only the correct onCreate handling of that flag.",
     "component": "platform/packages/apps/Settings, notification listener access confirmation dialog (NotificationAccessConfirmationActivity)",
     "weakness": "Misuse of window flag API: a private/system LayoutParams flag value was passed to Window.addFlags/clearFlags, which operate on the public LayoutParams.flags field, so an unintended public window flag bit was set on resume and cleared on pause",
     "root_cause": "NotificationAccessConfirmationActivity overrode onResume to call getWindow().addFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) and onPause to call getWindow().clearFlags(...) with the same constant. SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS is a system flag that belongs in LayoutParams.privateFlags; Window.addFlags/clearFlags modify LayoutParams.flags. The new test makes this explicit: after onCreate the flag must be present in privateFlags and the same bit value must be absent from flags. So the onResume call set an unintended public flag bit on the activity's window (and onPause cleared it). Per the commit message, onCreate already adds the system flag correctly (the activity still statically imports SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS at android-security-14.0.0_r29), so the onResume/onPause code was both wrong and redundant. The material does not state which public flag or window behavior that bit value triggered.",
     "fix_analysis": [
      "src/com/android/settings/notification/NotificationAccessConfirmationActivity.java: delete the onResume() override that called getWindow().addFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS), so no stray public flag bit is set when the dialog resumes.",
      "src/com/android/settings/notification/NotificationAccessConfirmationActivity.java: delete the onPause() override that called getWindow().clearFlags(...) with the same constant, and drop the now-unused android.view.WindowManager import. The system flag is still applied in onCreate (per the commit message and the test).",
      "tests/robotests/.../NotificationAccessConfirmationActivityTest.java: add onCreate_setsWindowFlags, asserting that getAttributes().privateFlags has SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS set and getAttributes().flags does not contain that bit value."
     ],
     "completeness": [
      "The fix is a pure deletion confined to NotificationAccessConfirmationActivity. It removes the only two misuse sites shown in the diff (onResume addFlags, onPause clearFlags).",
      "Correct behavior relies on onCreate already applying SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS as a private flag. The onCreate body is not in the diff; only the commit message and the new test assert it.",
      "No other Settings activities are touched. The material shows no evidence either way about similar addFlags-with-system-flag patterns elsewhere."
     ],
     "behavior_changes": [
      "The activity's window no longer gets an unintended public LayoutParams.flags bit set on resume and cleared on pause. The overlay-hiding system flag set in onCreate is now the only handling of that flag.",
      "The diff shows no API, string or error-path changes. The user-visible effect of removing the stray flag bit is not shown in the material."
     ],
     "preconditions": [
      "NotificationAccessConfirmationActivity must be launched and reach onResume. It is the Settings dialog that confirms granting notification listener access to a component (EXTRA_COMPONENT_NAME / EXTRA_USER_ID from NotificationAccessConfirmationActivityContract).",
      "The device state needed for the stray public flag to have a security effect is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 7255b2eaac4b in android-security-14.0.0_r19 (cherry-pick of e0510a62eba0); at android-security-14.0.0_r29 the patched region is present (8/8 lines).",
      "15": "same fix: commit 12a8312004bb in android-security-15.0.0_r7, identical diff with shifted line numbers; at android-security-15.0.0_r17 the patched region is present (8/8 lines).",
      "16": "not affected per the bulletin; android-security-16.0.0_r8 contains the patched region (8/8 lines).",
      "17": "not affected per the bulletin; android-security-17.0.0_r1 contains the patched region (8/8 lines)."
     },
     "backport_notes": [
      "Deletion-only change with no dependencies shown. Before applying it to a fork, confirm that onCreate in NotificationAccessConfirmationActivity already applies SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS as a private flag (the static import at line 21 in the r29 context suggests it does). Otherwise, removing onResume could leave the dialog without the intended overlay protection."
     ],
     "confidence": "high",
     "unknowns": [
      "The onCreate code that correctly applies the system flag is not in the diff or the context; only the commit message and the test refer to it.",
      "The material does not state which public flag shares the bit value or what window behavior the misapplied bit caused, nor the specific EoP scenario behind the High rating.",
      "The bulletin lists Android 13 as affected, but the material gives no Android 13 commit or tag status."
     ],
     "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-2025-22428": {
     "cve": "CVE-2025-22428",
     "summary": "Settings' AppInfoBase now checks INTERACT_ACROSS_USERS_FULL against the UID that launched the hosting activity (from ActivityManager getLaunchedFromUid) instead of against the package name from SettingsActivity.getInitialCallingPackage().",
     "component": "platform/packages/apps/Settings, applications/AppInfoBase (cross-user permission gate)",
     "weakness": "Incorrect permission check: the check was keyed on a calling package name rather than the calling UID",
     "root_cause": "The cross-user gate in src/com/android/settings/applications/AppInfoBase.java got the caller via ((SettingsActivity) activity).getInitialCallingPackage() and ran mPm.checkPermission(INTERACT_ACROSS_USERS_FULL, callingPackageName). The fix title says the permission should be checked for the calling UID, not the calling package. The material does not show exactly how the package-name-based check could fail. The bulletin classifies the issue as EoP, severity High.",
     "fix_analysis": [
      "src/com/android/settings/applications/AppInfoBase.java, the INTERACT_ACROSS_USERS_FULL check helper (the method name is outside the hunk; the method begins with 'if (!(activity instanceof SettingsActivity)) return false;'): removes the getInitialCallingPackage()/TextUtils.isEmpty path and the mPm.checkPermission(permission, packageName) call.",
      "Replaces it with callerUid = ActivityManager.getService().getLaunchedFromUid(activity.getActivityToken()).",
      "Checks ActivityManager.checkUidPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callerUid). If the result is not PERMISSION_GRANTED, it logs 'Uid <uid> does not have required permission ...' and returns false; otherwise it returns true.",
      "Wraps the calls in try/catch RemoteException and returns false on failure. Adds imports for android.app.ActivityManager and android.os.RemoteException."
     ],
     "completeness": [
      "Covers one gate: the cross-user permission helper in AppInfoBase.",
      "The 'activity instanceof SettingsActivity' check is unchanged; a host that is not a SettingsActivity still gets false.",
      "On RemoteException the method returns false, so it fails closed.",
      "The material does not show whether other Settings code uses getInitialCallingPackage() for permission decisions."
     ],
     "behavior_changes": [
      "Permission is now checked for the UID that launched the hosting SettingsActivity instead of for a package name. Callers whose launching UID lacks INTERACT_ACROSS_USERS_FULL are refused.",
      "The early return for an empty calling package is gone; a RemoteException now returns false.",
      "The log message changes from 'Package <name> does not have required permission' to 'Uid <uid> does not have required permission'."
     ],
     "preconditions": [
      "The fragment derived from AppInfoBase must be hosted in a SettingsActivity (the helper returns false otherwise).",
      "Callers of the helper, exported entry points and intent extras are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 4a9cb946e978 in android-security-14.0.0_r19 (Change-Id Ib36c0a3b); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 9dba6f50302b in android-security-15.0.0_r7, identical diff apart from a hunk offset (line 176 vs 172); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin (not listed); the patched region is present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed); the patched region is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, single-commit change. It relies on IActivityManager.getLaunchedFromUid(IBinder) and ActivityManager.checkUidPermission, hidden framework APIs that Settings can use as a platform app (general platform knowledge). The diff applied with only a line offset on 14 and 15.",
      "Android 13 is listed as affected, but the material contains no 13 commit. The same hunk should port if AppInfoBase on 13 still has the getInitialCallingPackage()/mPm.checkPermission pattern."
     ],
     "confidence": "medium",
     "unknowns": [
      "The name of the patched helper method and its callers in AppInfoBase and its subclasses.",
      "Why the package-name-based check was insufficient (for example, where the initial calling package value comes from).",
      "The concrete exported entry point and extras used to reach the cross-user path.",
      "Whether the Android 13 branch received the same commit.",
      "Whether other Settings components use getInitialCallingPackage() for authorization decisions."
     ],
     "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-2025-22431": {
     "cve": "CVE-2025-22431",
     "summary": "AppOpsService stops treating a proxy-supplied attribution tag as valid when the proxied package is a non-app uid (shell, system, etc.) unless the proxy is a system app or absent, requires the next AttributionSource in a chain to also be trusted, and re-checks remaining restrictions before resuming a paused op when one restriction is lifted.",
     "component": "platform/frameworks/base, AppOps (services/core/java/com/android/server/appop/AppOpsService.java)",
     "weakness": "Missing validation of caller-supplied attribution tags leading to unbounded resource allocation (DoS per bulletin); plus a missing state re-check on restriction removal",
     "root_cause": "In AppOpsService.verifyAndGetBypass, when the target packageName resolved to a non-app uid (com.android.shell special case, or any uid returned by resolveUid), the method unconditionally returned PackageVerificationResult(RestrictionBypass.UNRESTRICTED, isAttributionTagValid=true). In the proxy note/start paths, where the attribution tag comes from the proxy app, a non-system proxy could therefore get arbitrary, unregistered attribution tags accepted whenever the proxied side was one of these exempt uids. The added code comment says the fix prevents \"abusive apps clogging the appops system with unlimited attribution tags via proxy calls\". Separately, isCallerAndAttributionTrusted returned true when the first AttributionSource was trusted, without checking attributionSource.getNext(). The restriction-change path also resumed a paused AttributedOp (attrOp.resume()) without checking whether other restrictions still applied.",
     "fix_analysis": [
      "AppOpsService.java:verifyAndGetBypass (all three overloads): a new int proxyUid parameter is threaded through. The 3-arg overload and checkPackage pass Process.INVALID_UID, and the two note/start call sites that passed proxyPackageName now also pass proxyUid.",
      "AppOpsService.java:verifyAndGetBypass (6-arg): in the non-app-uid branch (pkgUid renamed to nonAppUid), after the appId-mismatch SecurityException check, the method computes proxyIsSystemAppOrNull. It is true if proxyPackageName == null. Otherwise, if UserHandle.getAppId(proxyUid) >= Process.FIRST_APPLICATION_UID, it is set to mPackageManagerInternal.isSystemPackage(proxyPackageName). The returned PackageVerificationResult now uses this value for isAttributionTagValid instead of a hard-coded true. The call sites shown set attributionTag to null when isAttributionTagValid is false. The rest of the method keeps `int pkgUid = nonAppUid;`.",
      "AppOpsService.java:isCallerAndAttributionTrusted: the early `return true` now also requires attributionSource.getNext() == null || attributionSource.getNext().isTrusted(mContext). Otherwise the method falls through to the UPDATE_APP_OPS_STATS permission check.",
      "AppOpsService.java (restriction-change handling around attrOp.pause()/resume()): in the else-if (attrOp.isPaused()) branch, the code gets bypass via verifyAndGetBypass(uid, ops.packageName, attrOp.tag).bypass and only calls attrOp.resume() if !isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag, bypass, false).",
      "AppOpsService.java: resolveUid is renamed to resolveNonAppUid, and its call sites (checkPackage, isSpecialPackage, a packageUid resolution path that falls back to getPackageUid, verifyAndGetBypass) and the javadoc references are updated. The rename itself does not change logic."
     ],
     "completeness": [
      "The attribution-tag restriction applies only in the non-app-uid branch of verifyAndGetBypass. The regular app-package branch is not changed by this diff.",
      "Proxy-supplied tags are still accepted without registration when proxyPackageName is null, when the proxy appId is below Process.FIRST_APPLICATION_UID, or when mPackageManagerInternal.isSystemPackage(proxyPackageName) is true.",
      "The new check relies on the proxyPackageName and proxyUid that callers pass in. The diff does not show verifyAndGetBypass checking that proxyPackageName belongs to proxyUid.",
      "isCallerAndAttributionTrusted checks only one level of getNext(). Deeper chain elements are not checked in this diff.",
      "The resume re-check calls isOpRestrictedLocked with the bypass from verifyAndGetBypass. On Android 14 it passes no device id; on Android 15 it passes Context.DEVICE_ID_DEFAULT."
     ],
     "behavior_changes": [
      "For proxy note/start operations from non-system apps that target non-app uids, the proxy-supplied attribution tag is now set to null instead of being accepted.",
      "Callers passing a trusted AttributionSource whose next source is not trusted now need UPDATE_APP_OPS_STATS to pass isCallerAndAttributionTrusted.",
      "When a restriction is removed, a paused op stays paused if any other restriction still applies, instead of being resumed unconditionally.",
      "resolveUid is renamed to resolveNonAppUid (private static, internal only)."
     ],
     "preconditions": [
      "A caller reaches a proxy note/start path where the proxied package resolves through resolveNonAppUid (or equals com.android.shell) to a non-app uid, and the proxy package is not a system package.",
      "For the restriction fix: an op is paused by a restriction while another restriction also applies, and then one restriction is removed.",
      "checkPackage is reached from AppOpsManager.checkPackage (frameworks/base/core/java/android/app/AppOpsManager.java), per index search. That path only picks up the renamed resolver and the INVALID_UID proxy argument."
     ],
     "per_major": {
      "14": "same fix: commit 79211e094a73 in android-security-14.0.0_r19 (cherry-pick, Change-Id I6a7b0a24359097c0ea2f52cc69637d929a931b4f). At android-security-14.0.0_r29, 6/8 lines of the patched region are present.",
      "15": "adapted fix: commit be02cee3a50c in android-security-15.0.0_r7. It is the same change plus proxyUid threaded into startOperationDryRun and its call in the proxy start path, removal of the `boolean wasNull` line, and isOpRestrictedLocked called with Context.DEVICE_ID_DEFAULT. At android-security-15.0.0_r17, 6/8 lines of the patched region are present.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 6/8 lines of the patched region are present.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 6/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single commit in AppOpsService.java. When backporting, match local signatures: branches that have startOperationDryRun need the extra proxyUid parameter (as in the Android 15 variant), and branches with virtual-device support need the device id argument in isOpRestrictedLocked. The fix uses mPackageManagerInternal.isSystemPackage."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the full proxy entry points (noteProxyOperation/startProxyOperation) or how a non-system proxy supplies a proxied AttributionSource for a non-app uid, so the exact trigger path is not confirmed.",
      "The material does not show the names of the methods enclosing the changed note/start call sites.",
      "The commit marks tests as 'upcoming'; it includes no test change.",
      "Whether the 6/8-line match in later tags reflects the Android 15 variant or further refactoring 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-2025-22433": {
     "cve": "CVE-2025-22433",
     "summary": "IntentForwarderActivity.canForward now runs the cross-profile forwarding check (ACTION_CHOOSER rejection plus IPackageManager.canForwardTo) on both the forwarded intent and its selector, instead of checking only the selector when one is present.",
     "component": "platform/frameworks/base (core/java/com/android/internal/app/IntentForwarderActivity.java) and platform/packages/modules/IntentResolver (java/src/com/android/intentresolver/IntentForwarderActivity.java), cross-profile intent forwarding",
     "weakness": "Incorrect authorization check target (missing validation of the underlying intent when a selector is present)",
     "root_cause": "In both copies of IntentForwarderActivity.canForward(), forwardIntent was sanitized, and the ACTION_CHOOSER rejection was applied to forwardIntent. When forwardIntent.getSelector() was non-null, however, the local intentToCheck was replaced by the selector, and only the selector was type-resolved and passed to packageManager.canForwardTo(). The underlying intent, which is what is actually forwarded (the method returns forwardIntent), therefore never went through the cross-profile forwarding policy check itself. Per the commit message, an intent that would not be allowed could be given a different selector and launched across profiles.",
     "fix_analysis": [
      "platform/frameworks/base core/java/com/android/internal/app/IntentForwarderActivity.java: a new private static helper canForwardInner(Intent, sourceUserId, targetUserId, IPackageManager, ContentResolver) holds the per-intent check. It returns false if the action is Intent.ACTION_CHOOSER. Otherwise it computes resolvedType via intent.resolveTypeIfNeeded(contentResolver) and returns the result of packageManager.canForwardTo(intent, resolvedType, sourceUserId, targetUserId). A RemoteException is logged and treated as false.",
      "canForward() now first calls canForwardInner(forwardIntent, ...) and returns null on failure. Then, if forwardIntent.getSelector() != null, it calls sanitizeIntent() on the selector and canForwardInner(selector, ...), returning null on failure. forwardIntent is returned only when both checks pass. The old intentToCheck substitution is removed.",
      "platform/packages/modules/IntentResolver java/src/com/android/intentresolver/IntentForwarderActivity.java: the same logic is applied to the module copy of canForward()/canForwardInner(). In addition, canForward changes from package-private to public static.",
      "Net effect: the ACTION_CHOOSER rejection now also applies to the selector, and the canForwardTo policy check now also applies to the underlying intent, each with its own resolved MIME type."
     ],
     "completeness": [
      "On Android 14, both the framework copy (frameworks/base) and the module copy (packages/modules/IntentResolver) of IntentForwarderActivity.canForward are patched with the same logic.",
      "The check covers the forwarded intent and one level of selector. The material does not show whether a selector can carry its own selector, or whether that would be checked.",
      "In the fix commit the selector goes through sanitizeIntent() before it is checked. In the code shown at android-security-14.0.0_r29, the sanitizeIntent(forwardIntent.getSelector()) line is gone, and canForward/canForwardInner take a single precomputed resolvedType String instead of a ContentResolver. The code in the latest tag therefore differs from the fix diff (7/8 patched lines present per the per-major facts), but the dual-check structure is present.",
      "The fix does not change other callers of canForward or the surrounding helpers (isDialerIntent, isTextMessageIntent, isTargetResolverOrChooserActivity)."
     ],
     "behavior_changes": [
      "Intents with a selector are now rejected (canForward returns null) if the underlying intent fails packageManager.canForwardTo or has action ACTION_CHOOSER, even when the selector would pass.",
      "Selector intents with action ACTION_CHOOSER are now rejected. Previously only the outer intent was checked for ACTION_CHOOSER.",
      "In the fix commit the MIME type is resolved separately for the outer intent and for the selector (two resolveTypeIfNeeded calls).",
      "In the IntentResolver copy, canForward becomes a public static method."
     ],
     "preconditions": [
      "A device with more than one user/profile where IntentForwarderActivity handles the intent for cross-profile forwarding.",
      "The forwarded intent carries a selector (Intent.getSelector() != null). Without a selector, the old code already checked the intent itself.",
      "The underlying intent must be one that canForwardTo would deny while the selector is one it allows. The material does not show the exact action/type combinations."
     ],
     "per_major": {
      "14": "Same fix, two commits in android-security-14.0.0_r19: platform/frameworks/base d2216fc62a9e and platform/packages/modules/IntentResolver 20cef18d8d9b. At android-security-14.0.0_r29 the canForward/canForwardInner structure is present in both files, but it takes a resolvedType String instead of a ContentResolver and has no sanitizeIntent(selector) call (7/8 patched lines present).",
      "15": "Same fix in platform/frameworks/base 8612a293fb4a in android-security-15.0.0_r7: identical diff, hunk at line 586. At android-security-15.0.0_r17, 3/8 patched lines of the region are present; the code there is not shown. The material does not show whether an IntentResolver module commit also exists on 15.",
      "16": "Not affected: the bulletin does not list Android 16. At android-security-16.0.0_r8, 3/8 lines of the patched region are present; the code is not shown.",
      "17": "Not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 3/8 lines of the patched region are present; the code is not shown."
     },
     "backport_notes": [
      "Two copies are patched on Android 14: core/java/com/android/internal/app/IntentForwarderActivity.java in frameworks/base and java/src/com/android/intentresolver/IntentForwarderActivity.java in packages/modules/IntentResolver. The IntentResolver commit also makes canForward public.",
      "If the target branch's canForward already takes a resolvedType String rather than a ContentResolver (as at 14.0.0_r29), the code shown at r29 passes that value to both canForwardInner calls. Either way, the essential part is checking both the intent and the selector."
     ],
     "confidence": "high",
     "unknowns": [
      "Which actions/types canForwardTo denies in practice, and the concrete intent shape used in the report.",
      "Whether the IntentResolver module copy was separately fixed on Android 15, and what the regions look like on 15 (latest tag), 16 and 17.",
      "Why the later code dropped sanitizeIntent(selector) and switched to a resolvedType parameter (the follow-up change is not in the material)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22434": {
     "cve": "CVE-2025-22434",
     "summary": "Adds isUserSetupComplete() and !keyguardOn conditions to the Meta+I keyboard shortcut in PhoneWindowManager so showSystemSettings() is no longer invoked while the keyguard is showing or before user setup completes.",
     "component": "platform/frameworks/base, services/core WindowManager policy (PhoneWindowManager keyboard shortcuts)",
     "weakness": "Missing authorization/state check (keyguard lock state not checked before launching an activity from a global keyboard shortcut)",
     "root_cause": "In services/core/java/com/android/server/policy/PhoneWindowManager.java, the KEYCODE_I case of the system keyboard-shortcut switch called showSystemSettings() whenever the key was down with Meta pressed. It did not check the keyguardOn state that is already available in that switch (the KEYCODE_APP_SWITCH case uses it), and it did not check isUserSetupComplete(). As a result, a Meta+I keypress could open the Settings app on the keyguard without user authentication, which is what the commit title describes. The bulletin rates this as a High severity EoP.",
     "fix_analysis": [
      "services/core/java/com/android/server/policy/PhoneWindowManager.java, KEYCODE_I case of the keyboard shortcut switch (the material does not show the name of the enclosing method): the condition `down && event.isMetaPressed()` becomes `down && event.isMetaPressed() && isUserSetupComplete() && !keyguardOn`. If either new condition fails, the case reaches `break` instead of calling showSystemSettings() and returning key_consumed.",
      "Android 15 variant (c4183cc97a18): the same condition is added to the same KEYCODE_I case. On 15 the guard is `firstDown` instead of `down`, and the handled branch also calls logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS) and returns true."
     ],
     "completeness": [
      "Covers only the Meta+I (KEYCODE_I) shortcut path to showSystemSettings(). It is a single-line condition change and does not modify showSystemSettings() itself.",
      "Other shortcuts in the same switch shown in the context (KEYCODE_A launchAssistAction, KEYCODE_N toggleNotificationPanel / sendSystemKeyToStatusBarAsync, KEYCODE_T toggleTaskbar, KEYCODE_H/ENTER handleHomeShortcuts, KEYCODE_RECENT_APPS showRecentApps) are not modified by this commit and do not test keyguardOn at the switch level. The material does not show whether their callees enforce keyguard state internally.",
      "The fix relies on the keyguardOn value computed by the caller and adds no check inside showSystemSettings(). Any other caller of showSystemSettings() is therefore unaffected (the material shows none)."
     ],
     "behavior_changes": [
      "Meta+I no longer opens the Settings app while the keyguard is showing. This case does not consume the key event and reaches break.",
      "Meta+I is also ignored until user setup is complete (until isUserSetupComplete() returns true).",
      "The Android 14 variant adds no new error, log line or toast. On Android 15 the LAUNCH_SYSTEM_SETTINGS keyboard log event is also skipped when the shortcut is blocked."
     ],
     "preconditions": [
      "A KEYCODE_I key event with the Meta modifier pressed reaches PhoneWindowManager's shortcut handling.",
      "The device is on the lock screen (keyguardOn true), or user setup is not complete.",
      "The device runs an affected build (Android 14, 15 and 15-next per the bulletin)."
     ],
     "per_major": {
      "14": "Same fix: 2880f0ab2dc6 in android-security-14.0.0_r19 adds `&& isUserSetupComplete() && !keyguardOn` to the KEYCODE_I case. 4/4 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: c4183cc97a18 in android-security-15.0.0_r7 applies the same condition to the Android 15 form of the case (`firstDown`, plus logKeyboardSystemsEvent and `return true`). 4/4 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected: the bulletin does not list Android 16. 2/4 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. 1/4 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file change to one condition. When porting, locate the KEYCODE_I / showSystemSettings() case in PhoneWindowManager and reuse the existing keyguardOn local (already used by KEYCODE_APP_SWITCH in the same switch) and isUserSetupComplete(). The commit message cites KeyGestureEventTests, but the diff contains no test changes."
     ],
     "confidence": "high",
     "unknowns": [
      "The name of the enclosing method that contains the keycode switch is not shown in the material.",
      "Whether showSystemSettings() has other callers, and whether the callees of sibling shortcuts (launchAssistAction, toggleNotificationPanel, toggleTaskbar, handleHomeShortcuts) check keyguard state internally.",
      "How the Android 16/17 code differs from the patched region (only 2/4 and 1/4 of its lines are present there)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22435": {
     "cve": "CVE-2025-22435",
     "summary": "Adds a check in avdt_msg_ind (system/stack/avdt/avdt_msg.cc) that drops an incoming AVDTP response or reject whose signal id does not match the command currently pending on the CCB (p_ccb->p_curr_cmd->event), or that arrives when no command is pending. This prevents type confusion when the response is parsed.",
     "component": "platform/packages/modules/Bluetooth, AVDTP signaling (system/stack/avdt)",
     "weakness": "Type confusion from missing validation of a peer-supplied message field: the signal id of a response or reject is not checked against the pending command. Per the commit message this leads to an eventual out-of-bounds access.",
     "root_cause": "In avdt_msg_ind, for messages that are not a general reject, the signal id (sig) is parsed straight from the incoming packet (AVDT_MSG_PRS_SIG) and only range-checked (1..AVDT_SIG_MAX). For response messages the function then picks a parse target based on that sig. For AVDT_SIG_DISCOVER it casts p_ccb->p_proc_data to tAVDT_SEP_INFO* and uses p_ccb->proc_param as num_seps. For AVDT_SIG_GETCAP and AVDT_SIG_GET_ALLCAP it casts p_ccb->p_proc_data to AvdtpSepConfig*. Nothing checked that the sig in a received RSP or REJ matched the pending command (p_ccb->p_curr_cmd). Per the commit message, a peer could therefore reply to a pending AVDT message with the wrong type, causing type confusion and an eventual OOB access.",
     "fix_analysis": [
      "system/stack/avdt/avdt_msg.cc:avdt_msg_ind: a new block is added inside the 'not a general reject' branch, after the existing sig range check. If msg_type is AVDT_MSG_TYPE_RSP or AVDT_MSG_TYPE_REJ, and either p_ccb->p_curr_cmd is nullptr or p_ccb->p_curr_cmd->event != sig, the code logs 'Dropping msg with mismatched sig' and sets ok to false.",
      "Because ok is false, the following 'if (ok && !gen_rej)' parse block is skipped. That block is the one that chooses the parse target from sig and casts p_ccb->p_proc_data.",
      "The general-reject path already took sig from p_ccb->p_curr_cmd->event rather than from the packet. The new validation therefore targets the non-general-reject RSP/REJ path."
     ],
     "completeness": [
      "Covers all RSP and REJ messages in the non-general-reject branch of avdt_msg_ind. In the code shown, this is where sig is taken from the packet and used to choose a p_proc_data cast. The general-reject branch takes sig from p_curr_cmd and is not changed.",
      "CMD messages are not subject to the new check. Whether any CMD parse path uses p_proc_data is not shown.",
      "The check compares only the signal id with the pending command, not the transaction label. Any label matching is outside the material.",
      "The check comes after the sig range check. If sig is out of range, ok is already false and the new check is redundant but harmless."
     ],
     "behavior_changes": [
      "Unsolicited responses and rejects (no pending command), and responses and rejects whose signal id differs from the pending command, are now dropped with a warning log and are not parsed.",
      "The new block sends nothing to the peer. The message is simply discarded."
     ],
     "preconditions": [
      "The attacker is a remote Bluetooth peer with an established AVDTP signaling channel (tcid 0). Packets reach avdt_msg_ind from avdt_ad_tc_data_ind in system/stack/avdt/avdt_ad.cc (per index search).",
      "Per the commit message, the peer replies to a pending AVDT message with the wrong type. For example, the local stack has issued a discover or get-capabilities request, and the peer answers with an RSP/REJ that carries a different signal id."
     ],
     "per_major": {
      "14": "Same fix: commit efa5f4ef386a in android-security-14.0.0_r19, logging with AVDT_TRACE_WARNING. The patched region is fully present at android-security-14.0.0_r29.",
      "15": "Adapted fix: commit 0a4fd20189fa in android-security-15.0.0_r7, with the same logic but logging via log::warn. The patched region is fully present at android-security-15.0.0_r17.",
      "16": "Not affected (not listed by the bulletin). The patched region is present at android-security-16.0.0_r8.",
      "17": "Not affected (not listed by the bulletin). The patched region is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A single self-contained hunk. The only difference between branches is the logging call: AVDT_TRACE_WARNING on 14 and log::warn on 15. The 15 variant keeps a printf-style %d in the log::warn format string, so adjust the log line to match the target branch's logging API."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream OOB access site, i.e. which parse function overruns p_proc_data, is not shown.",
      "Where and for which command p_ccb->p_proc_data is set up is not shown.",
      "Whether the Android 13 branch received the same commit, and what its avdt_msg_ind looks like. The bulletin lists 13 as affected, but the material has no 13 commit.",
      "Whether transaction label validation for RSP/REJ messages exists elsewhere.",
      "Whether any CMD parse path uses p_ccb->p_proc_data."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22438": {
     "cve": "CVE-2025-22438",
     "summary": "In InputDispatcher::afterKeyEventLockedInterruptable, take a std::shared_ptr<EventEntry> copy and a local copy of hasForegroundTarget() from the DispatchEntry before the lock is released for policy calls, so the KeyEntry stays valid and the DispatchEntry is no longer dereferenced even if a concurrent removeInputChannel drains the connection's waitQueue and deletes the DispatchEntry.",
     "component": "platform/frameworks/native, inputflinger InputDispatcher (services/inputflinger/dispatcher)",
     "weakness": "Use-after-free caused by a race across a lock release (object lifetime not held while calling into policy)",
     "root_cause": "doDispatchCycleFinishedCommand looked up a DispatchEntry* in connection->waitQueue, built a KeyEntry& from dispatchEntry->eventEntry, and passed both to afterKeyEventLockedInterruptable. That function releases the lock to call into policy. While the lock is released, a removeInputChannel binder call can run. It drains the waitQueue and deletes the DispatchEntry. If the DispatchEntry held the last remaining reference to the EventEntry, the KeyEntry is freed too. The function then kept using the KeyEntry& and dispatchEntry->hasForegroundTarget(), which could point to freed memory. Per the commit message, in the observed flow the KeyEntry had been allocated by synthesizeCancelationEvents during a setFocusedWindow call.",
     "fix_analysis": [
      "services/inputflinger/dispatcher/InputDispatcher.cpp:afterKeyEventLockedInterruptable: the signature changes to (connection, DispatchEntry* dispatchEntry, bool handled), dropping the KeyEntry& parameter. At function entry, before any lock release, the function copies `std::shared_ptr<EventEntry> eventEntry = dispatchEntry->eventEntry` (a strong reference held for the whole function) and `const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget()`. It then builds `KeyEntry& keyEntry` from the local shared_ptr and sets `dispatchEntry = nullptr`.",
      "services/inputflinger/dispatcher/InputDispatcher.cpp:afterKeyEventLockedInterruptable: the later check `if (handled || !dispatchEntry->hasForegroundTarget())` now reads the local `hasForegroundTarget` copy.",
      "services/inputflinger/dispatcher/InputDispatcher.cpp:doDispatchCycleFinishedCommand: the KEY branch now calls afterKeyEventLockedInterruptable(connection, dispatchEntry, handled) and no longer builds a KeyEntry& itself.",
      "services/inputflinger/dispatcher/InputDispatcher.h: the declaration of afterKeyEventLockedInterruptable is updated to the new three-argument signature.",
      "The commit message also says the queued doDispatchCycleFinishedCommand should hold a strong pointer to the Connection rather than a reference. The Android 14 diff contains no such change, and the material does not show how the command captures the Connection on this branch."
     ],
     "completeness": [
      "Covers the KEY path only: afterKeyEventLockedInterruptable now holds a shared_ptr to the EventEntry and no longer dereferences dispatchEntry after the lock may be released.",
      "The MOTION path is untouched: afterMotionEventLockedInterruptable still receives DispatchEntry* and a MotionEntry& built from dispatchEntry->eventEntry in doDispatchCycleFinishedCommand. The material does not show whether that function releases the lock.",
      "After the policy calls, doDispatchCycleFinishedCommand already looks up the DispatchEntry again with connection->findWaitQueueEntry(seq) before erasing it, so the dequeue step was already robust to the waitQueue being drained.",
      "No test files are modified in the diff. The commit only cites `atest --host inputflinger_tests`."
     ],
     "behavior_changes": [
      "No functional change is evident: the EventEntry may live until afterKeyEventLockedInterruptable returns, and the same decisions are made from copied values."
     ],
     "preconditions": [
      "A key event (per the commit message, for example a cancellation KeyEntry synthesized during setFocusedWindow) has been dispatched to a connection and is in that connection's waitQueue.",
      "The app finishes the event, and the dispatcher runs doDispatchCycleFinishedCommand, which reaches a policy call in afterKeyEventLockedInterruptable that releases the lock.",
      "While the lock is released, removeInputChannel for that connection is processed and destroys the DispatchEntry holding the last reference to the KeyEntry.",
      "Per index search, no callers of afterKeyEventLockedInterruptable other than doDispatchCycleFinishedCommand were found."
     ],
     "per_major": {
      "14": "same fix: commit 7ac747cb442d (cherry-picked, Merged-In Ibea7117e4c85cd1e98bbd01872ce249cbb2d54bd) in android-security-14.0.0_r19. At android-security-14.0.0_r29, the fixed call `afterKeyEventLockedInterruptable(connection, dispatchEntry, handled)` and the three-argument declaration are present (resolver: 6/8 lines of the patched region present).",
      "15": "not affected per the bulletin (not listed). 0/8 lines of the patched region are present at android-security-15.0.0_r17. The material does not show the code there.",
      "16": "not affected per the bulletin (not listed). 0/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin (not listed). 0/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a single self-contained commit touching InputDispatcher.cpp and InputDispatcher.h. On a branch where afterKeyEventLockedInterruptable still takes (connection, DispatchEntry*, KeyEntry&, handled), copy eventEntry (as a shared_ptr) and hasForegroundTarget at function entry and drop the KeyEntry& parameter. The commit message also asks for the queued command to hold the Connection by strong pointer. Check how the branch captures it.",
      "An index chunk labelled Android 14 (android-14.0.0_r75) shows a different signature (returning std::unique_ptr<const KeyEntry>, taking DispatchEntry& and const KeyEntry&). Verify the target branch's actual signature before patching."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether afterMotionEventLockedInterruptable releases the lock and is exposed to the same lifetime issue.",
      "How the queued doDispatchCycleFinishedCommand captures the Connection on Android 14, given the commit message's strong-pointer change.",
      "How the Android 15/16/17 code looks, given that the bulletin lists '15-next' and the patched region is absent there.",
      "Whether the Android 13 fix (the bulletin lists 13 as affected) is identical.",
      "The exact security impact beyond the bulletin's High/EoP rating and the commit's description of a crash."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22439": {
     "cve": "CVE-2025-22439",
     "summary": "DocumentsUI's file picker now runs the existing restricted-initial-path check (/Android/data, /Android/obb, /Android/sandbox) on the restored last-accessed DocumentStack. If the stack's top document is restricted, the picker falls back to the default location.",
     "component": "platform/packages/apps/DocumentsUI, picker (Storage Access Framework) ActionHandler",
     "weakness": "Missing validation check on an alternate input path (the restricted-path check was applied to the requested initial URI but not to the restored last-accessed stack)",
     "root_cause": "In src/com/android/documentsui/picker/ActionHandler.java, shouldPreemptivelyRestrictRequestedInitialUri() treats a URI as restricted when its external-storage path matches PATTERN_RESTRICTED_INITIAL_PATH (/Android/data, /Android/obb, /Android/sandbox and their subdirectories, per the code comment). But onLastAccessedStackLoaded() did not run this check. It is reached from both initLoadLastAccessedStack() and loadLastAccessedStack() (the latter via LoadLastAccessedStackTask), and for any non-null stack from mLastAccessed it called mState.stack.reset(stack) and refreshed the UI. A last-accessed stack for the calling package that pointed into a restricted directory was therefore restored without the check. The commit message says this bypassed the scoped storage restriction placed on the initial URI.",
     "fix_analysis": [
      "src/com/android/documentsui/picker/ActionHandler.java:onLastAccessedStackLoaded(): adds an `else if (shouldPreemptivelyRestrictRequestedInitialUri(stack.peek().getDocumentUri()))` branch between the null check and the restore branch. When the top document of the loaded stack is restricted, it calls loadDefaultLocation() instead of mState.stack.reset(stack) followed by mActivity.refreshCurrentRootAndDirectory().",
      "Reuses the existing restriction predicate. The path is resolved via FileUtils.getPathFromStorageDocId and matched against PATTERN_RESTRICTED_INITIAL_PATH, and an IOException while resolving the path also counts as restricted. The same policy used for the requested initial URI therefore now applies to the last-accessed stack.",
      "Single-commit fix that adds 3 lines. Both entry points (synchronous initLoadLastAccessedStack and asynchronous loadLastAccessedStack) go through the patched method."
     ],
     "completeness": [
      "Covers both the blocking (initLoadLastAccessedStack) and asynchronous (LoadLastAccessedStackTask) load paths, since both call onLastAccessedStackLoaded.",
      "Only the top entry of the stack (stack.peek()) is checked. Lower entries are not validated one by one.",
      "The check runs only when the stack is loaded. The diff does not touch how the last-accessed stack is stored, so a restricted stack can stay stored and is simply not restored.",
      "The new branch calls stack.peek().getDocumentUri() without a null check. The material does not show what happens with a non-null but empty DocumentStack."
     ],
     "behavior_changes": [
      "When the calling package's last-accessed stack points at a restricted external-storage path (or its path cannot be resolved), the picker now opens the default location instead of restoring the previous directory.",
      "No new error, result code or user-visible message. The fallback is the same loadDefaultLocation() already used when no stack exists."
     ],
     "preconditions": [
      "The DocumentsUI picker (picker.ActionHandler) loads the last-accessed stack for the calling package.",
      "A last-accessed stack exists for that calling package, and its top document resolves to a path under /Android/data, /Android/obb or /Android/sandbox on external storage.",
      "The material does not show how such a stack could be recorded."
     ],
     "per_major": {
      "14": "same fix: commit 1f283d5b87d0 in android-security-14.0.0_r19 (cherry-pick of an internal change, Change-Id I4de8bad7174273c9390da978e186ad6a85f27be5). All 5 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix: commit a9fe0826e308 in android-security-15.0.0_r7, with the identical 3-line hunk at a slightly different offset (line 272 instead of 278). All 5 lines are present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin). The patched region is present at android-security-16.0.0_r8 (5/5 lines).",
      "17": "not affected (not listed by the bulletin). The patched region is present at android-security-17.0.0_r1 (5/5 lines)."
     },
     "backport_notes": [
      "Self-contained 3-line change. It only needs shouldPreemptivelyRestrictRequestedInitialUri(Uri) and PATTERN_RESTRICTED_INITIAL_PATH to already exist in picker/ActionHandler.java. On a branch without that helper, the initial-URI restriction logic has to be ported first."
     ],
     "confidence": "high",
     "unknowns": [
      "How a restricted-path stack comes to be stored for a calling package (the write path for the last-accessed stack is not in the material).",
      "Whether the Android 13 fix (the bulletin lists 13 as affected) is identical; no Android 13 commit is provided.",
      "How the new branch behaves for a non-null but empty DocumentStack (stack.peek() nullability not shown).",
      "Whether checking only the top entry is enough when lower entries are restricted; the full PATTERN_RESTRICTED_INITIAL_PATH regex is not shown.",
      "The full body of shouldPreemptivelyRestrictRequestedInitialUri (the lines before 248), for example how URIs not from ExternalStorageProvider are handled.",
      "How the last-accessed load path is chosen relative to a supplied initial URI."
     ],
     "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."
     }
    }
   }
  },
  "15": {
   "counts": {
    "announced": 20,
    "certain": 20,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-22423",
     "osv_id": "ASB-A-346797131",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In dng_sdk's dng_ifd::ParseTag, three tag handlers now check the boolean result of CheckTagCount and return false immediately when the tag count is out of the allowed range, instead of ignoring the result and continuing to parse with the invalid count.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit against source/dng_ifd.cpp. The same diff was applied unchanged to 14 and 15 (identical blob index 7f22065..bf3fb2c). The bulletin lists Android 13 as affected, but the material includes no 13 commit."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "bbed5f198692a56a67d7037d95b4d70b18541c23",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-22429",
     "osv_id": "ASB-A-373357090",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes Parcel.readArrayMap to report the lazy-value count through an int[] out-parameter that is incremented as each LazyValue is added. BaseBundle.initializeFromParcelLocked therefore sees the correct count, and does not recycle the backing Parcel, even when readArrayMap throws midway.",
      "confidence": "high",
      "backport_notes": [
       "The fix is a single commit, and its two files must be applied together: the Parcel.readArrayMap signature change and the BaseBundle caller update. Any downstream caller of the hidden 5-argument readArrayMap must move to the 6-argument void form. Context lines around the recycle call may differ (recycleParcel(parcelledData) vs parcelledData.recycle())."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "567af706fd9db016b0b7a7c4ffc5e2b2fd3bbb5f",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-26416",
     "osv_id": "ASB-A-388480622",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes SkBmpStandardCodec::initializeSwizzler to pick the swizzler destination format based on xformOnDecode() instead of colorXform(), so the format the swizzler assumes matches the buffer that decodeRows actually writes into.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained one-line change. It needs xformOnDecode() to exist in the target tree; SkBmpStandardCodec::onPrepareToDecode already calls it in the shown context. The test and the resource file are not needed for the security fix itself.",
       "The bulletin lists Android 13 as affected, but the material includes no Android 13 commit. Check the condition in SkBmpStandardCodec::initializeSwizzler on that branch."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/skia",
       "sha": "a1c030a91f2921d02c9deb097ba2d40f4afd079f",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/skia"
     ]
    },
    {
     "cve": "CVE-2024-40653",
     "osv_id": "ASB-A-293458004",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Telecom's ConnectionServiceWrapper now schedules a 15 second timeout when it asks a bound ConnectionService to create a connection or conference. If the app never reports completion, the creation is failed with DisconnectCause.ERROR, which the commit message says is meant to unbind the service rather than leave it bound indefinitely.",
      "confidence": "high",
      "backport_notes": [
       "The Android 15 commit uses AnomalyReporterAdapter/AnomalyReporterAdapterImpl and Call.isCreateConnectionComplete()/setIsCreateConnectionComplete(). Its test uses the FeatureFlags constructor argument of ConnectionServiceWrapper, TestScheduledExecutorService, waitUntilConditionIsTrueOrTimeout and ComponentContextFixture.removeConnectionService, none of which the commit adds. The Android 14 DO NOT MERGE variant shows a version without anomaly reporting that adds the missing test helpers.",
       "If porting the Android 14 variant, consider carrying the Android 15 additions: the null check on the resolved Call in the Adapter handlers, the removeServiceInterface() guard and executor shutdown, and the map clear on death. The commit message says a null check was added to avoid an NPE seen in the first iteration, but does not say which of these null guards that refers to."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "4f73f974ca16cc34cc3b84e9d3a8c75d7846b14b",
       "tag": "android-security-15.0.0_r5",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 57,
     "projects": [
      "platform/packages/services/Telecomm"
     ]
    },
    {
     "cve": "CVE-2024-49720",
     "osv_id": "ASB-A-355411348",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds PackageManager.FLAG_PERMISSION_ONE_TIME to the set of user-choice flags in the role-controller Permissions class so that role (re-)evaluation treats an \"ask every time\" grant as a user choice it must not override, and clears the flag when a role newly grants a permission.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained two-hunk change in a single file (Permissions.java); the material shows no other dependent commits.",
       "Backport target must have FLAG_PERMISSION_ONE_TIME in PackageManager and the overrideUserSetAndFixed parameter in the fixed-flag helper; otherwise adapt the mask by hand."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Permission",
       "sha": "87fda0e3b03e1397808fc5060176ecd12c43c023",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref e02775c62a81",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Permission"
     ]
    },
    {
     "cve": "CVE-2024-49722",
     "osv_id": "ASB-A-341688848",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes SettingsLib's EditUserPhotoController launch the avatar picker with an explicit component (package and class from new config resources) instead of an implicit AVATAR_PICKER_ACTION intent when the avatarSync flag path is taken, and then removes the guarding fixAvatarCrossUserLeak flag so the fix is unconditional.",
      "confidence": "medium",
      "backport_notes": [
       "Apply 8cba0e8bcfc2 then 2b4d662a462c, or squash them into the unflagged form seen at r17. The end state needs the two config.xml strings, the com.android.settingslib.R import, and an unconditional intent.setClassName inside the Flags.avatarSync() branch. The build/release textproto (a17efb2eadc0) matters only if the fixAvatarCrossUserLeak flag guard is kept.",
       "Devices that ship a different avatar picker component must override config_avatar_picker_package / config_avatar_picker_class, otherwise the explicit intent will not resolve."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/build/release",
       "sha": "a17efb2eadc0db53a21c8c487303d648069dd648",
       "tag": "android-security-15.0.0_r5",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "2b4d662a462c6b0269a6e6035ce443ec29fd860e",
       "tag": "android-security-15.0.0_r5",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "8cba0e8bcfc291977f33f14fba0bd2b7f7fe8f6c",
       "tag": "android-security-15.0.0_r5",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "public_before_announcement_days": 57,
     "projects": [
      "platform/build/release",
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-49728",
     "osv_id": "ASB-A-296915500",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "BluetoothOppSendFileInfo.generateFileInfo now rejects content URIs whose authority carries an explicit user id (user@authority) that differs from the Bluetooth process's own user, returning SEND_FILE_INFO_ERROR instead of resolving the URI.",
      "confidence": "high",
      "backport_notes": [
       "For branches whose OPP code lacks ContentProfileErrorReportUtils / BluetoothStatsLog, use the Android 14 variant (log and return only). Otherwise keep the report call, make sure tag 15 is not already used in the file, and bump the 'Next tag value' comment.",
       "The fix is self-contained in one source file plus tests and relies only on Uri.getUserInfo, TextUtils, Objects and UserHandle.myUserId."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r5",
      "date": "2025-02-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "4b65cbb339db4d3a7a9a6100cb2e7c9f1ece9271",
       "tag": "android-security-15.0.0_r5",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 57,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2025-22416",
     "osv_id": "ASB-A-277207798",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ChooserActivity now uses UriGrantsManager.checkGrantUriPermission_ignoreNonSystem with the launching UID to check that the caller may read the URI behind any URI-type Icon in a caller-supplied ChooserTarget, and removes the icon when the check fails.",
      "confidence": "medium",
      "backport_notes": [
       "This is a single self-contained commit that adds private helpers and five imports. It relies on IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, Icon.TYPE_URI_ADAPTIVE_BITMAP and the 5-argument ChooserTarget constructor existing on the target branch. Apart from the offset of the trailing helper methods, the Android 14 and 15 hunks are identical.",
       "The bulletin lists Android 13 as affected, but the material contains no Android 13 commit or code."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ee65c97e3a19224548376ca2b7bd93fe366e94e8",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22417",
     "osv_id": "ASB-A-332277530",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In Transition.finishTransition, the ActivityRecordInputSink (touch-blocking) state of visible participant activities is now written into each activity's pending transaction and applied via scheduleAnimationLocked, instead of a separately created and immediately applied SurfaceControl.Transaction. This removes the race with a not-yet-applied pending transaction that could leave the input-sink state stale.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, single-hunk change in Transition.finishTransition. The 14 and 15 diffs are identical apart from line offsets. If a target tree already has the Task mTaskInputSink loop (as in 15.0.0_r17), check that the ActivityRecord branch uses ar.getPendingTransaction() and that scheduleAnimationLocked() is called when scheduleAnimation is set, rather than reapplying the hunk.",
       "The fix uses ActivityRecord.getPendingTransaction() and mWindowManager.scheduleAnimationLocked(), which the 14 and 15 fix diffs call directly. The material references no other dependency commit."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d0c52fbde58768bc7fc6cf618c59d01057edd954",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22419",
     "osv_id": "ASB-A-335387175",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS in onCreate across 18 Telephony (com.android.phone) files: call settings, SIM PIN, FDN, SIM contacts and EmergencyDialer activities, plus the abstract BaseFdnContactScreen. With the flag set, non-system overlay windows are hidden while those screens are shown.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. Each hunk is an independent insertion in onCreate, and context differs between 14 and 15, so expect to adjust context when applying it. Window.addSystemFlags and SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS are hidden platform APIs that the phone process can use because it builds against the platform (general platform knowledge). Per index search, packages/apps/Settings SimDialogActivity.onCreate uses the same pattern."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "81cc0fd56aa9644aa4a31a42f898b244f3b6d6a6",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Telephony"
     ]
    },
    {
     "cve": "CVE-2025-22421",
     "osv_id": "ASB-A-338024220",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI stops putting the notification title, text and ticker into the accessibility content description of status bar notification icons. Only the app name is left.",
      "confidence": "high",
      "backport_notes": [
       "Where the description is computed inside StatusBarIconView.java (as on Android 14), apply the removal there as in commit 4ec1792148a5. Where NotificationContentDescription.kt exists, apply 3b0704fd381a together with its NotificationContentDescriptionTest.kt change."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3b0704fd381a1ea32591aba99be3a9e4e6830be2",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22422",
     "osv_id": "ASB-A-339532378",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's biometric prompt foreground-owner check is extended with a class-name comparison for prompts hosted by Settings' ConfirmDeviceCredentialActivity, so the prompt is cancelled when a different Settings activity becomes the top activity instead of staying on screen because the package name still matched.",
      "confidence": "high",
      "backport_notes": [
       "Apply the frameworks/base commit first (or atomically): the Settings commit calls the renamed Builder.setRealCallerForConfirmDeviceCredentialActivity and PromptInfo.getRealCallerForConfirmDeviceCredentialActivity, and SystemUI's BiometricPromptRequest.kt uses the renamed getter, so partial application breaks the build. SystemUI and framework must ship together because the PromptInfo Parcel format changes.",
       "On Android 14 Google used a different Settings change (no-argument Builder.setClassNameIfItIsConfirmDeviceCredentialActivity() in BiometricFragment); the corresponding framework/SystemUI change is not included in this material."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "106d3165dc7104ad389a0ca14fe768620d936e15",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "32915818d780faf7f5f81fc49b98aad91beb79d8",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22427",
     "osv_id": "ASB-A-368579654",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the onResume/onPause overrides in Settings' NotificationAccessConfirmationActivity that passed the private constant SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to the public Window.addFlags/clearFlags, relying instead on the private-flag setup that, per the commit message, is already done in onCreate.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit that only deletes two methods and an import. Before applying it to a fork, check that onCreate in that tree already sets SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS through the private-flags path, because the deletion assumes that protection is in place. The new robolectric test onCreate_setsWindowFlags checks for it."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "12a8312004bb89be890c15ab0def857a09ee2808",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22428",
     "osv_id": "ASB-A-372671447",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings AppInfoBase now checks INTERACT_ACROSS_USERS_FULL against the UID that launched the activity (ActivityManager.getLaunchedFromUid + checkUidPermission) instead of against a package name obtained from SettingsActivity.getInitialCallingPackage().",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The 14 and 15 diffs are identical apart from line offsets, so it should apply with fuzz to any branch where AppInfoBase still has the getInitialCallingPackage()/mPm.checkPermission block. ActivityManager.getService().getLaunchedFromUid and ActivityManager.checkUidPermission are hidden framework APIs that Settings can use as a platform-signed system app (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "9dba6f50302b1432ac7ca8b33c98220d822a1815",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-22430",
     "osv_id": "ASB-A-374257207",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds ACCESS_FINE_LOCATION permission enforcement to the hidden system API ITrustManager.isInSignificantPlace(), which previously had no permission check on its binder implementation.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. ITrustManager.aidl on 15 already uses @EnforcePermission (on isTrustUsuallyManaged), so the AIDL permission-annotation infrastructure is available. The three file changes must go in together, because the server calls the generated _enforcePermission helper, which exists only once the AIDL annotation is added."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "161eb6100d6f75f0a0df6da3d19da7fe842655c1",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22431",
     "osv_id": "ASB-A-375623125",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AppOpsService.verifyAndGetBypass now treats a proxy-supplied attribution tag as valid for exempt proxied packages (shell or non-app uids) only when the proxy is a system package or there is no proxy. The same commit also makes isCallerAndAttributionTrusted require a trusted next attribution source, and re-checks remaining restrictions before resuming a paused op.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, but it changes every caller of verifyAndGetBypass and resolveUid. For trees without startOperationDryRun, or where isOpRestrictedLocked has no device-id parameter, use the Android 14 variant. Make sure mPackageManagerInternal (PackageManagerInternal.isSystemPackage) is available in AppOpsService."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "be02cee3a50c6edcc40532ce6ebe709f2b71ed8b",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22433",
     "osv_id": "ASB-A-376674080",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "IntentForwarderActivity.canForward now runs the cross-profile forwarding check (ACTION_CHOOSER rejection plus IPackageManager.canForwardTo) on both the forwarded intent and its selector, instead of checking only the selector when one is present.",
      "confidence": "high",
      "backport_notes": [
       "Two independent copies must be patched in lockstep: frameworks/base core/java/com/android/internal/app/IntentForwarderActivity.java and the IntentResolver module java/src/com/android/intentresolver/IntentForwarderActivity.java. The diff is self-contained (no new dependencies) and applies to the ContentResolver-based signature used on 14 and 15 r7.",
       "If the target tree already uses the newer canForward(..., String resolvedType) signature (as at 15 r17), port the two canForwardInner calls onto that signature rather than reintroducing the ContentResolver parameter. The Android 14 commit also widens canForward to public, which does not matter for the fix."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8612a293fb4ae27225454db9463f38a925cd4d46",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/modules/IntentResolver",
       "sha": "3e7b8f353731f4dc7392307809ec9f5454ced14d",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/modules/IntentResolver"
     ]
    },
    {
     "cve": "CVE-2025-22434",
     "osv_id": "ASB-A-378900798",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds isUserSetupComplete() and !keyguardOn guards to the Meta+I keyboard shortcut in PhoneWindowManager so the system Settings app can no longer be launched from the lock screen (or before setup completes) without user authentication.",
      "confidence": "high",
      "backport_notes": [
       "The change is a one-line condition edit. On branches where the KEYCODE_I case uses `down` instead of `firstDown` or returns `key_consumed` (as on Android 14), add `&& isUserSetupComplete() && !keyguardOn` to the existing condition instead of cherry-picking verbatim. The local keyguardOn is already used by the neighbouring KEYCODE_APP_SWITCH case. Verify that isUserSetupComplete() is available in the target branch."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c4183cc97a1803d6d7865285a649cab174ee3998",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-22435",
     "osv_id": "ASB-A-273995284",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a check in avdt_msg_ind (system/stack/avdt/avdt_msg.cc) that rejects incoming AVDTP response/reject messages whose signal id does not match the locally pending command (p_ccb->p_curr_cmd). The response parser can therefore no longer be chosen by a peer-supplied signal id that does not match the pending command.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained hunk in avdt_msg_ind with no other dependencies. In trees that use AVDT_TRACE_WARNING, as Android 14 does, use the Android 14 form of the log line."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "0a4fd20189fa9e8903e13bdcdf7b138114dd03c7",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2025-22439",
     "osv_id": "ASB-A-352294617",
     "source": "osv",
     "announced": "2025-04-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DocumentsUI picker now runs the restricted-initial-URI check (shouldPreemptivelyRestrictRequestedInitialUri) on the top entry of a restored last-accessed DocumentStack and falls back to the default location if it is restricted, instead of restoring the stack unchecked.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained hunk; it relies on shouldPreemptivelyRestrictRequestedInitialUri() and loadDefaultLocation() in picker/ActionHandler.java, which the 14 and 15 diffs use. Android 13 is listed as affected but no 13 commit is in the material.",
       "DocumentsUI ships as a mainline module on recent releases, so devices may also receive this via a module update rather than the platform tag (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r7",
      "date": "2025-04-07",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "a9fe0826e3083db90b45cede7436260d864dc4b2",
       "tag": "android-security-15.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    }
   ],
   "tags": [
    "android-security-15.0.0_r7"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "350456241"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "febc0a24abdcd25585866ae196a9ab7aa6f18fee",
       "subject": "Check cross user permissions for a given UID",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-06",
       "markers": []
      },
      {
       "project": "platform/frameworks/base",
       "sha": "c67b1b413b7247eceea97729b3b4af25013faef7",
       "subject": "Parse authority to separate userId and non-user parts of it",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-06",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    },
    {
     "bug_ids": [
      "348634518"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ee06440e9a7150959f18f12f032e9580c2e3fa3e",
       "subject": "Revert \"Check IsolatedOwner permissions for known isolated_compute_apps\"",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-06",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    },
    {
     "bug_ids": [
      "382064697"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "15472b00424c42bce996d2e4cdb638cbf9c92bb2",
       "subject": "Disable \"Developer options\" by default for managed profiles.",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    },
    {
     "bug_ids": [
      "365167765"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "1d896b3404d37184a6c040641167933fa590cd2c",
       "subject": "Fix the input info missing for SurfaceView",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-26",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    },
    {
     "bug_ids": [
      "277076451"
     ],
     "commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "46266c3dc67e9e60017ebc40c6ea89280d9cfa4c",
       "subject": "Include color layers in input list to fix tapjacking vulnerability",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    },
    {
     "bug_ids": [
      "356604577"
     ],
     "commits": [
      {
       "project": "platform/packages/services/Telecomm",
       "sha": "27b78eb22c513d41aa1d7915e3a3655e2a708db9",
       "subject": "Resolve cross account user ringtone validation.",
       "tag": "android-security-15.0.0_r7",
       "merged": "2025-02-05",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r7"
     ],
     "tag": "android-security-15.0.0_r7"
    }
   ],
   "coverage": {
    "n_ingested_tags": 18,
    "first_ingested_tag": "android-security-15.0.0_r1",
    "latest_ingested_tag": "android-security-15.0.0_r18",
    "window_base_tag": "android-15.0.0_r1",
    "window_start": "2024-09-03",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2025-22423": {
     "cve": "CVE-2025-22423",
     "summary": "In dng_sdk's dng_ifd::ParseTag, three tag handlers now check the boolean result of CheckTagCount and return false immediately when the tag count is out of the allowed range, instead of ignoring the result and continuing to parse with the invalid count.",
     "component": "platform/external/dng_sdk, IFD/tag parser (source/dng_ifd.cpp)",
     "weakness": "Ignored validation result (missing bounds enforcement on tag count), leading to DoS per the bulletin",
     "root_cause": "In source/dng_ifd.cpp, dng_ifd::ParseTag called CheckTagCount(parentCode, tagCode, tagCount, ...) in three places and discarded its return value. The three places are the tcBitsPerSample case (range 1..0x0FFFF) and two further cases bounded by fSamplesPerPixel (1..fSamplesPerPixel and exactly fSamplesPerPixel). Because the result was ignored, parsing went on with the untrusted tagCount from the file. For example, the tcBitsPerSample loop `for (j = 0; j < tagCount; j++)` reads stream.TagValue_uint32 for each element, so a tag count outside the permitted range was still processed. The bulletin classifies the issue as a Critical DoS.",
     "fix_analysis": [
      "source/dng_ifd.cpp: dng_ifd::ParseTag, tcBitsPerSample case (hunk at line 351; line 387 at r17): `CheckTagCount(..., 1, 0x0FFFF)` becomes `if (!CheckTagCount(...)) return false;`, so an invalid count aborts the tag before the per-element loop runs.",
      "source/dng_ifd.cpp: dng_ifd::ParseTag, second case (hunk at line 973, range 1..fSamplesPerPixel): same change, returns false on an invalid count.",
      "source/dng_ifd.cpp: dng_ifd::ParseTag, third case (hunk at line 1025, exact count fSamplesPerPixel): same change, returns false on an invalid count.",
      "Single commit, 6 insertions and 3 deletions. CheckTagCount itself is not changed; the fix relies on its existing boolean result."
     ],
     "completeness": [
      "Covers exactly three CheckTagCount call sites in dng_ifd::ParseTag: tcBitsPerSample and two cases bounded by fSamplesPerPixel. The diff hunks do not show the tag names of the latter two.",
      "Other CheckTagCount calls in the same function still ignore the return value. One example is the tcImageLength case at line 365 in the r17 context (`CheckTagCount(parentCode, tagCode, tagCount, 1);`), which is followed by a single TagValue_uint32 read with no count-driven loop visible.",
      "The tcBitsPerSample loop already limited array indexing with `j < kMaxSamplesPerPixel`. The fix also stops the loop from running at all over an out-of-range tagCount.",
      "The material does not show whether any dng_sdk code outside dng_ifd.cpp uses a tag count without honoring CheckTagCount."
     ],
     "behavior_changes": [
      "dng_ifd::ParseTag now returns false (tag rejected) for BitsPerSample tags with a count of 0 or above 0xFFFF, and for the two fSamplesPerPixel-bounded tags when the count is outside their range. Before the fix, the tag was still parsed after the validation call.",
      "Stricter rejection of malformed DNG/TIFF files: files that used to parse despite a count violation may now fail to decode."
     ],
     "preconditions": [
      "A DNG/TIFF image whose IFD is parsed by dng_sdk's dng_ifd::ParseTag and that contains one of the three affected tags with an out-of-range count.",
      "The material does not show the specific Android entry point that feeds untrusted files into dng_sdk. The index matches (DngCreator TiffWriter in frameworks/base, Dialer ExifParser) are writers or separate parsers, not callers of dng_ifd::ParseTag (per index search). dng_sdk is used by the platform image decoding stack for RAW/DNG input (general platform knowledge)."
     ],
     "per_major": {
      "14": "same fix: commit 748dbd7dfcec in android-security-14.0.0_r19, with a diff identical to the Android 15 commit (same hunks, same index 7f22065..bf3fb2c). At android-security-14.0.0_r29, 5 of the 8 lines of the patched region are present, the same measure as the patched 15 tag.",
      "15": "same fix: commit bbed5f198692 in android-security-15.0.0_r7 (cherry-picked from 84b0c687ce84 and 445d85031ac9; Change-Id I76da86b063ce4a0a329aca2a1309997cd62ac58d). The r17 context shows the `if (!CheckTagCount(...)) return false;` form in tcBitsPerSample. 5 of the 8 lines of the patched region are present at r17.",
      "16": "not affected (not listed by the bulletin). At android-security-16.0.0_r8, 5 of the 8 lines of the patched region are present, the same measure as the patched 14/15 tags. The material gives no commit for 16.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 5 of the 8 lines of the patched region are present, the same measure as the patched 14/15 tags. The material gives no commit for 17."
     },
     "backport_notes": [
      "Single self-contained commit against source/dng_ifd.cpp. The same diff was applied unchanged to 14 and 15 (identical blob index 7f22065..bf3fb2c). The bulletin lists Android 13 as affected, but the material includes no 13 commit."
     ],
     "confidence": "high",
     "unknowns": [
      "The diff context does not show the tag names of the second and third patched cases (hunks at lines 973 and 1025).",
      "Beyond the bulletin's DoS type, the material does not state the concrete DoS mechanism.",
      "The material does not include the Android 13 fix commit.",
      "The exact state of 16 and 17 is not shown beyond the partial patched-region line count.",
      "Whether the fix applies cleanly to customized trees is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22429": {
     "cve": "CVE-2025-22429",
     "summary": "Changes Parcel.readArrayMap to report the lazy-value count through an int[] out-parameter that is incremented as each LazyValue is added. BaseBundle.initializeFromParcelLocked therefore sees the correct count, and does not recycle the backing Parcel, even when readArrayMap throws midway.",
     "component": "platform/frameworks/base, core/java/android/os (BaseBundle / Parcel lazy unparceling)",
     "weakness": "Incorrect state tracking on the error path, leading to a premature Parcel recycle while LazyValue entries may still reference it (information disclosure per bulletin)",
     "root_cause": "In BaseBundle.initializeFromParcelLocked, the number of lazy values came from the return value of Parcel.readArrayMap: `numLazyValues = parcelledData.readArrayMap(...)`. If readArrayMap threw (e.g. BadParcelableException) after one or more LazyValue objects had already been added to `map`, the assignment never ran and numLazyValues stayed 0. The finally block then ran with ownsParcel true and numLazyValues == 0, so it called recycleParcel(parcelledData), set mLazyValues = 0 and still assigned `mMap = map`. On the non-defuse path the map is not erased, so the Bundle could hold LazyValue entries tied to a recycled Parcel while recording no lazy values and no mWeakParcelledData. This is inferred from the diff and code context; the commit message only says 'This code considered a success case to be an unsuccessful case.'",
     "fix_analysis": [
      "core/java/android/os/Parcel.java:readArrayMap: the signature changes from `int readArrayMap(map, size, sorted, lazy, loader)` to `void readArrayMap(map, size, sorted, lazy, loader, int[] lazyValueCount)`. The local `lazyValues` counter and `return lazyValues` are removed. Instead, `lazyValueCount[0]++` runs each time a value `instanceof LazyValue` is read, so the count is visible to the caller even if a later iteration throws.",
      "core/java/android/os/Parcel.java:readArrayMapInternal: updated to the new signature and passes `null` for lazyValueCount. This path uses lazy=false, so readValue is used instead of readLazyValue.",
      "core/java/android/os/BaseBundle.java:initializeFromParcelLocked: `int numLazyValues = 0` becomes `int[] numLazyValues = new int[]{0}`, which is passed into readArrayMap. The finally block now checks `numLazyValues[0] == 0` before recycling the parcel and sets `mLazyValues = numLazyValues[0]`. If an exception occurs after lazy values were added, the count is non-zero, so the Parcel is kept through mWeakParcelledData instead of being recycled."
     ],
     "completeness": [
      "Covers the only lazy-reading call path shown: BaseBundle.initializeFromParcelLocked -> Parcel.readArrayMap with lazy = ownsParcel. The count is now correct both on success and when readArrayMap aborts partway.",
      "The fix does not change what happens to a partially filled `map` on the non-defuse path: the exception is rethrown, but the finally block still assigns mMap = map. It only ensures the backing Parcel is not recycled while such entries exist.",
      "readArrayMapInternal passes a null count array. This is safe only because lazy is false on that path; a caller passing lazy=true with a null array would throw NullPointerException on the first LazyValue (judged from the code).",
      "No new validation of parcel contents is added."
     ],
     "behavior_changes": [
      "When unparceling a Bundle fails after lazy values were already added, the backing Parcel is no longer recycled. It is kept through mWeakParcelledData and mLazyValues is non-zero.",
      "Parcel.readArrayMap (@hide) changes from returning int to returning void and takes an extra int[] out-parameter. Callers of this hidden method must be updated.",
      "On the success path the resulting count and recycling decision are the same as before."
     ],
     "preconditions": [
      "A Bundle is unparceled with ownsParcel true (lazy unparceling) in BaseBundle.initializeFromParcelLocked.",
      "The parcelled map contains at least one value that becomes a LazyValue, followed by content that makes Parcel.readArrayMap throw before it completes.",
      "The Bundle's lazy entries are later accessed after the Parcel has been recycled (inferred; the material does not show the access path)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base ece83fb425b1 in android-security-14.0.0_r19. The diff is identical to the Android 15 commit; only the Parcel.java line offsets differ. At android-security-14.0.0_r29, 5/8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 567af706fd9d in android-security-15.0.0_r7 (cherry-pick of 86cfb77a4664). At android-security-15.0.0_r17 the int[] numLazyValues logic is present. The recycle call there reads parcelledData.recycle() instead of recycleParcel(parcelledData) from the fix diff context.",
      "16": "not affected per the bulletin (not listed). At android-security-16.0.0_r8, 5/8 lines of the patched region are present.",
      "17": "not affected per the bulletin (not listed). At android-security-17.0.0_r1, 4/8 lines of the patched region are present."
     },
     "backport_notes": [
      "The fix is a single commit, and its two files must be applied together: the Parcel.readArrayMap signature change and the BaseBundle caller update. Any downstream caller of the hidden 5-argument readArrayMap must move to the 6-argument void form. Context lines around the recycle call may differ (recycleParcel(parcelledData) vs parcelledData.recycle())."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact exception type or parcel content used in the bug's reproducer (bug 373357090) is not shown.",
      "The material does not show which consumer of lazy values turns the recycled Parcel into an information disclosure.",
      "Whether the Android 13 fix (listed as affected by the bulletin) differs from the 14/15 diffs is not shown.",
      "How the patched code reached Android 16/17 is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-26416": {
     "cve": "CVE-2025-26416",
     "summary": "Changes SkBmpStandardCodec::initializeSwizzler to pick the swizzler destination format based on xformOnDecode() instead of colorXform(), so the format the swizzler assumes matches the buffer that decodeRows actually writes into.",
     "component": "platform/external/skia, BMP image codec (src/codec/SkBmpStandardCodec.cpp)",
     "weakness": "Inconsistent condition between swizzler setup and the decode path, leading to a mismatch between the swizzler's destination format and the real destination buffer (the added test documents a crash when decoding into a less-than-32bpp buffer with a color transform; the exact memory effect is not shown)",
     "root_cause": "In SkBmpStandardCodec::initializeSwizzler, the swizzler was configured for kXformSrcColorType output (unpremul alpha for premul destinations, no zero-init) whenever this->colorXform() was true. Per the commit message, however, decodeRows only goes through the intermediate color-xform buffer when this->xformOnDecode() is true, and onPrepareToDecode also calls resetXformBuffer only under xformOnDecode(). So when colorXform() was true but xformOnDecode() was false, the swizzler's destination format did not match the buffer actually written, which is the caller's destination (e.g. kRGB_565 in the new test). The new test documents that this case crashed.",
     "fix_analysis": [
      "src/codec/SkBmpStandardCodec.cpp:SkBmpStandardCodec::initializeSwizzler: the condition guarding the swizzlerInfo override (makeColorType(kXformSrcColorType), unpremul alpha for a premul destination, fZeroInitialized = kNo_ZeroInitialized) changes from this->colorXform() to this->xformOnDecode(). This matches the heuristic used by decodeRows and onPrepareToDecode, so SkSwizzler::Make receives the real destination format.",
      "tests/CodecTest.cpp: adds Codec_bmp_indexed_colorxform, which decodes the new resource resources/images/bmp-size-32x32-8bpp.bmp to kRGB_565_SkColorType with a linear sRGB color space and asserts kSuccess (comment: 'should not crash'). It also adds an include of src/core/SkAutoPixmapStorage.h."
     ],
     "completeness": [
      "The fix only touches SkBmpStandardCodec::initializeSwizzler. In the code shown, onPrepareToDecode already based resetXformBuffer on xformOnDecode().",
      "Other BMP codec variants (mask and RLE codecs) are not in the diff or the shown context, so the material cannot show whether they had the same mismatch.",
      "The fix is a one-line condition change with no added bounds checks. Its correctness depends on xformOnDecode() being exactly the condition decodeRows uses, which the commit message states."
     ],
     "behavior_changes": [
      "When a color transform is requested but xformOnDecode() is false, the swizzler now targets the caller's real color type and alpha type and honours the caller's fZeroInitialized option. Before, it forced kXformSrcColorType, unpremul alpha and kNo_ZeroInitialized.",
      "Decoding the 8bpp BMP test resource to kRGB_565 with a color space transform is expected to return kSuccess (new test).",
      "No new error codes or API changes in the diff."
     ],
     "preconditions": [
      "A BMP image handled by SkBmpStandardCodec (the test uses a 32x32 8bpp indexed BMP).",
      "A decode request with a color space transform (colorXform() true) into a destination for which xformOnDecode() is false, e.g. kRGB_565_SkColorType as in the test (a less-than-32bpp buffer).",
      "Platform image decoding uses Skia codecs: frameworks/base/libs/hwui/hwui/ImageDecoder.cpp wraps SkAndroidCodec (per index search)."
     ],
     "per_major": {
      "14": "same fix: platform/external/skia fc2ebb312c58 in android-security-14.0.0_r19. The code change in SkBmpStandardCodec.cpp is identical; the test hunk sits at a different offset (after Codec_kBGR_101010x_XR_SkColorType_supported). 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/skia a1c030a91f29 in android-security-15.0.0_r7 (cherry-pick of skia-review 937497). 8/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); patched region (xformOnDecode() condition) present at android-security-16.0.0_r8",
      "17": "not affected (not listed by the bulletin); patched region present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Single self-contained one-line change. It needs xformOnDecode() to exist in the target tree; SkBmpStandardCodec::onPrepareToDecode already calls it in the shown context. The test and the resource file are not needed for the security fix itself.",
      "The bulletin lists Android 13 as affected, but the material includes no Android 13 commit. Check the condition in SkBmpStandardCodec::initializeSwizzler on that branch."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact memory effect of the mismatch. The material only shows the test comment 'should not crash' and the format mismatch; the body of decodeRows, the definitions of colorXform() and xformOnDecode(), and the size of kXformSrcColorType are not shown.",
      "Whether SkBmpMaskCodec or SkBmpRLECodec have a similar swizzler initialization pattern (not in the material).",
      "The Android 13 fix commit (not provided).",
      "Which platform entry points reach this exact decode configuration."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2024-40653": {
     "cve": "CVE-2024-40653",
     "summary": "Telecom's ConnectionServiceWrapper now schedules a 15 second timeout when it asks a bound ConnectionService to create a connection or conference. If the app never reports completion, the creation is failed with DisconnectCause.ERROR, which the commit message says is meant to unbind the service rather than leave it bound indefinitely.",
     "component": "platform/packages/services/Telecomm, ConnectionServiceWrapper (binding to app ConnectionService implementations)",
     "weakness": "Missing timeout on an asynchronous remote operation: Telecom kept an untrusted app's ConnectionService bound with no upper bound while waiting for createConnection/createConference to complete (resource/privilege retention, as described in the commit message).",
     "root_cause": "In src/com/android/server/telecom/ConnectionServiceWrapper.java, createConnection() and createConference() call mServiceInterface.createConnection()/createConference() on the app's bound ConnectionService and then wait for the app to call back Adapter.handleCreateConnectionComplete()/handleCreateConferenceComplete(). Nothing put a time limit on that wait. A ConnectionService that never completed creation kept Telecom bound to it. Per the commit message, this let a malicious app keep a declared permission forever, even in the background.",
     "fix_analysis": [
      "ConnectionServiceWrapper (fields): adds SERVICE_BINDING_TIMEOUT = 15000L, a single-thread ScheduledExecutorService mScheduledExecutor, a Map<Call, ScheduledFuture<?>> mScheduledFutureMap (ConcurrentHashMap), an AnomalyReporterAdapter mAnomalyReporter, and two anomaly UUID/message pairs (CREATE_CONNECTION_TIMEOUT_ERROR_*, CREATE_CONFERENCE_TIMEOUT_ERROR_*).",
      "ConnectionServiceWrapper.createConnection(): right before mServiceInterface.createConnection(...), builds an android.telecom.Logging.Runnable(\"CSW.cC\", mLock). Its loggedRun() checks call.isCreateConnectionComplete(). If that is false, it logs an error with a stack trace, adds LogUtils.Events.CREATE_CONNECTION_TIMEOUT to the call's event log, reports the anomaly, and calls response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)). The runnable is scheduled on mScheduledExecutor to run after 15000 ms, and the ScheduledFuture is stored in mScheduledFutureMap keyed by the Call.",
      "ConnectionServiceWrapper.createConference(): same pattern, using LogUtils.Events.CREATE_CONFERENCE_TIMEOUT, the conference anomaly UUID, and response.handleCreateConferenceFailure(new DisconnectCause(DisconnectCause.ERROR)).",
      "ConnectionServiceWrapper.Adapter.handleCreateConnectionComplete() and handleCreateConferenceComplete(): under mLock, look up the Call via mCallIdMapper.getCall(callId). If the call is non-null and has a pending future in mScheduledFutureMap, cancel(false) it and remove the entry.",
      "ConnectionServiceWrapper.removeServiceInterface(): returns early with a warning if mServiceInterface is already null (the comment cites the double call from onBinderDied and onServiceDisconnected). After the existing death handling, it shuts down mScheduledExecutor and sets it to null.",
      "ConnectionServiceWrapper (service death cleanup, the block that already calls mCallIdMapper.clear()): also clears mScheduledFutureMap.",
      "ConnectionServiceWrapper: addCall() made public with @VisibleForTesting; new test hooks setScheduledExecutorService() and setAnomalyReporterAdapter().",
      "LogUtils.Events: new CREATE_CONNECTION_TIMEOUT and CREATE_CONFERENCE_TIMEOUT event names.",
      "Tests: CallsManagerTest.testConnectionServiceCreateConnectionTimeout registers a mock IConnectionService and injects a TestScheduledExecutorService. It checks that a job is scheduled at 15000 ms and that advancing time triggers handleCreateConnectionFailure(DisconnectCause.ERROR). BasicCallTests calls call.setIsCreateConnectionComplete(true) at the end of two outgoing-call tests."
     ],
     "completeness": [
      "Both creation entry points that talk to the remote service (createConnection and createConference) get the timeout, and both completion callbacks cancel it.",
      "The timer starts when mServiceInterface.createConnection()/createConference() is called, not at bind time. Despite the constant name SERVICE_BINDING_TIMEOUT, this timer does not cover the bind phase.",
      "The diff only calls response.handleCreateConnectionFailure()/handleCreateConferenceFailure(). The unbind described in the commit message happens through existing failure handling that is not part of the diff.",
      "The timeout runnable does not remove its own entry from mScheduledFutureMap. Entries for timed-out calls stay in the map until the service-death cleanup clears it.",
      "removeServiceInterface() sets mScheduledExecutor to null and never recreates it. createConnection()/createConference() call mScheduledExecutor.schedule() without a null check. The material does not show whether a wrapper instance is reused after a service disconnect.",
      "Within the diff, a pending timeout is only cancelled by the completion callbacks or cleared on service death. The runnable gates only on call.isCreateConnectionComplete()."
     ],
     "behavior_changes": [
      "Any ConnectionService, including legitimate ones, that does not report createConnection/createConference completion within 15 seconds now has the creation failed with DisconnectCause.ERROR. Per the commit message, this is intended to unbind the service.",
      "On timeout, the fix emits new call event log entries CREATE_CONNECTION_TIMEOUT / CREATE_CONFERENCE_TIMEOUT and new anomaly reports (UUIDs 54b7203d-... and caafe5ea-...).",
      "removeServiceInterface() now returns early with a warning when mServiceInterface is already null.",
      "Each ConnectionServiceWrapper now creates its own single-thread ScheduledExecutorService, which is shut down when the service interface is removed."
     ],
     "preconditions": [
      "An app exposes a ConnectionService that Telecom binds to for a call, so that ConnectionServiceWrapper.createConnection() or createConference() is invoked for it.",
      "The app's service never calls back handleCreateConnectionComplete()/handleCreateConferenceComplete(), so the creation stays pending.",
      "The bulletin rates this as a High severity EoP affecting 13, 14, 15 and 15-next. Only the commit message describes the retained privilege: a declared permission kept even in the background."
     ],
     "per_major": {
      "14": "adapted fix: DO NOT MERGE commit b007692a8d52 in android-security-14.0.0_r12. The timeout/cancel logic is the same, but it lacks the call != null check in the Adapter handlers, anomaly reporting, the removeServiceInterface() null guard and executor shutdown, and mScheduledFutureMap.clear() on death. It also adds ComponentContextFixture.removeConnectionService()/removeService(), a Condition/waitUntilConditionIsTrueOrTimeout helper in CallsManagerTest, and @VisibleForTesting on Call.getState(). Its test only checks the scheduling and does not advance time to verify the failure callback. The patched region is present 8/8 at android-security-14.0.0_r29.",
      "15": "same fix: commit 4f73f974ca16 in android-security-15.0.0_r5 (cherry-pick of 48d6b0df91fb). The patched region is present 8/8 at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); the patched region is present 8/8 at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is present 8/8 at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Android 15 commit uses AnomalyReporterAdapter/AnomalyReporterAdapterImpl and Call.isCreateConnectionComplete()/setIsCreateConnectionComplete(). Its test uses the FeatureFlags constructor argument of ConnectionServiceWrapper, TestScheduledExecutorService, waitUntilConditionIsTrueOrTimeout and ComponentContextFixture.removeConnectionService, none of which the commit adds. The Android 14 DO NOT MERGE variant shows a version without anomaly reporting that adds the missing test helpers.",
      "If porting the Android 14 variant, consider carrying the Android 15 additions: the null check on the resolved Call in the Adapter handlers, the removeServiceInterface() guard and executor shutdown, and the map clear on death. The commit message says a null check was added to avoid an NPE seen in the first iteration, but does not say which of these null guards that refers to."
     ],
     "confidence": "high",
     "unknowns": [
      "How response.handleCreateConnectionFailure()/handleCreateConferenceFailure() leads to the actual unbind of the ConnectionService (the existing failure path is not in the diff).",
      "Whether a ConnectionServiceWrapper instance can be reused for a new createConnection() after removeServiceInterface() has set mScheduledExecutor to null.",
      "Which permission or privilege state the commit message refers to as being kept in effect by the bound service.",
      "Which of the two added null guards (call != null in the Adapter handlers, or mServiceInterface == null in removeServiceInterface) addresses the NPE from the first iteration, and whether that first iteration shipped on any branch."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2024-49720": {
     "cve": "CVE-2024-49720",
     "summary": "Adds PackageManager.FLAG_PERMISSION_ONE_TIME to the set of user-choice flags in the role-controller Permissions class so that role (re-)evaluation treats an \"ask every time\" grant as a user choice it must not override, and clears the flag when a role newly grants a permission.",
     "component": "platform/packages/modules/Permission, PermissionController role-controller (com.android.role.controller.model.Permissions)",
     "weakness": "Missing flag check in authorization logic: FLAG_PERMISSION_ONE_TIME was not treated as a user-set permission state when deciding whether a role grant may override the user's choice",
     "root_cause": "In PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java the role grant logic decides whether a permission is \"fixed\" from a flag mask. When overrideUserSetAndFixed is false, that mask included only FLAG_PERMISSION_USER_FIXED and FLAG_PERMISSION_USER_SET. FLAG_PERMISSION_ONE_TIME, which per the commit message reflects the user's \"ask every time\" choice, was not in the mask, so it was not recognised as a user choice and role evaluation did not respect it. In the same way, the grant path's newMask cleared USER_FIXED/USER_SET but not ONE_TIME when a permission went from not granted to granted.",
     "fix_analysis": [
      "Permissions.java, grant path (hunk at line ~258, in the method that computes newFlags/newMask after grantPermissionAndAppOpAsUser): when !wasPermissionOrAppOpGranted, FLAG_PERMISSION_ONE_TIME is added to newMask alongside USER_FIXED and USER_SET, so a fresh role grant clears the one-time flag along with the other user flags (newFlags does not contain ONE_TIME, so it is cleared).",
      "Permissions.java, fixed-flag check (hunk at line ~629, the method taking overrideUserSetAndFixed and returning (flags & fixedFlags) != 0): when !overrideUserSetAndFixed, FLAG_PERMISSION_ONE_TIME is added to fixedFlags, so a permission with the one-time flag is reported as fixed. The caller presumably then does not override it, but the caller is not shown.",
      "Single commit, two hunks, 4 insertions / 2 deletions; no other files changed. Test referenced: RolePermissionOverrideTest (not included in the material)."
     ],
     "completeness": [
      "Covers both places in the shown Permissions.java diff where USER_FIXED|USER_SET were used as the user-choice set: the fixed-flag decision and the newMask clearing on grant.",
      "The fixed-flag hunk only matters when overrideUserSetAndFixed is false; the newMask hunk clears ONE_TIME on any new grant regardless of that setting.",
      "No other flag-handling sites are shown in the material, so whether similar masks exist elsewhere in PermissionController (e.g. revoke paths) cannot be judged from the diff."
     ],
     "behavior_changes": [
      "For roles that must respect user choices, a permission with FLAG_PERMISSION_ONE_TIME is now treated as fixed by the role fixed-flag check, so role evaluation should no longer override it.",
      "When a role newly grants a permission (not previously granted), FLAG_PERMISSION_ONE_TIME is now cleared together with USER_FIXED and USER_SET."
     ],
     "preconditions": [
      "An app holds (or is granted) a role whose permissions are applied via role-controller Permissions with overrideUserSetAndFixed false.",
      "The user has previously chosen \"ask every time\" for one of the role's permissions, leaving FLAG_PERMISSION_ONE_TIME set.",
      "A role (re-)evaluation runs for that package; the concrete trigger is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/packages/modules/Permission e02775c62a81 in android-security-14.0.0_r19, identical two-hunk change at different line offsets (249, 606); 7/8 lines of the patched region present at android-security-14.0.0_r29",
      "15": "adapted fix: 87fda0e3b03e in android-security-15.0.0_r7 (cherry-picked, Change-Id Ie65c2cd62ba3f24e8dd411abca49e397746bc1a9); at android-security-15.0.0_r17, 7/8 lines of the patched region are present, and the code context shows only the first hunk applied",
      "16": "not listed by the bulletin as affected; 7/8 lines of the patched region present at android-security-16.0.0_r8",
      "17": "not listed by the bulletin as affected; 7/8 lines of the patched region present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Self-contained two-hunk change in a single file (Permissions.java); the material shows no other dependent commits.",
      "Backport target must have FLAG_PERMISSION_ONE_TIME in PackageManager and the overrideUserSetAndFixed parameter in the fixed-flag helper; otherwise adapt the mask by hand."
     ],
     "confidence": "high",
     "unknowns": [
      "Exact names of the two patched methods (the diff hunks and code context do not show the method signatures).",
      "How the caller of the fixed-flag helper uses its result.",
      "Whether FLAG_PERMISSION_USER_SET is also present on an \"ask every time\" permission.",
      "Which line of the patched region is absent in the 7/8 match in each latest tag.",
      "Whether the fix is also delivered via Permission module updates.",
      "Contents of RolePermissionOverrideTest and which roles exercise the non-overriding path.",
      "Concrete trigger scenario and the specific role/permission involved in the reported issue."
     ],
     "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-49722": {
     "cve": "CVE-2024-49722",
     "summary": "Makes SettingsLib's EditUserPhotoController launch the avatar picker with an explicit component (package and class from new config resources) instead of an implicit AVATAR_PICKER_ACTION intent when the avatarSync flag path is taken, and then removes the guarding fixAvatarCrossUserLeak flag so the fix is unconditional.",
     "component": "platform/frameworks/base, packages/SettingsLib users (EditUserPhotoController avatar picker launch); plus platform/build/release aconfig flag value",
     "weakness": "Implicit intent launched without an explicit target component (intent interception / exposure of avatar picker flow to unintended components), per the diff; the bulletin classifies it as ID (information disclosure)",
     "root_cause": "In EditUserPhotoController.showAvatarPicker, when Flags.avatarSync() was true, the code built an Intent for AVATAR_PICKER_ACTION with CATEGORY_DEFAULT, added EXTRA_IS_USER_NEW and EXTRA_FILE_AUTHORITY, and called mActivityStarter.startActivityForResult without restricting the target. Only the non-avatarSync else branch constrained the intent (intent.setPackage to the calling app's own package). The implicit intent could therefore resolve to a component other than the intended avatar picker. The result was handled by onActivityResult (REQUEST_CODE_PICK_AVATAR), which acts on EXTRA_DEFAULT_ICON_TINT_COLOR or on data.getData() via onPhotoCropped. The flag name (fix_avatar_cross_user_leak) and the code comment tie the change to b/341688848, an avatar cross-user leak.",
     "fix_analysis": [
      "Commit 8cba0e8bcfc2 (frameworks/base): packages/SettingsLib/res/values/config.xml adds two non-translatable strings, config_avatar_picker_package = com.android.avatarpicker and config_avatar_picker_class = com.android.avatarpicker.ui.AvatarPickerActivity.",
      "Commit 8cba0e8bcfc2 (frameworks/base): EditUserPhotoController.java imports com.android.settingslib.R. In showAvatarPicker, inside the Flags.avatarSync() branch and under a new Flags.fixAvatarCrossUserLeak() guard, it reads the two config strings via mActivity.getString and calls intent.setClassName(packageName, className). This makes the intent explicit before startActivityForResult.",
      "Commit a17efb2eadc0 (build/release): adds aconfig/trunk_staging/android.multiuser/fix_avatar_cross_user_leak_flag_values.textproto, which sets android.multiuser.fix_avatar_cross_user_leak to ENABLED (READ_WRITE) in trunk staging.",
      "Commit 2b4d662a462c (frameworks/base): removes the Flags.fixAvatarCrossUserLeak() guard, so intent.setClassName(...) runs unconditionally whenever Flags.avatarSync() is true. The commit message says the flag was missing on the security branch and caused a build failure. This unflagged code is what android-security-15.0.0_r17 contains, and the textproto flag-value file is absent in that tag."
     ],
     "completeness": [
      "The fix covers only the Flags.avatarSync() branch of showAvatarPicker. The else branch already restricted the intent with setPackage to the app's own package and is unchanged.",
      "The fix pins the target to a package/class set in resources. It adds no validation of the result received in onActivityResult (EXTRA_DEFAULT_ICON_TINT_COLOR, data.getData()) and relies on the explicit component to ensure the result comes from the intended picker.",
      "The fix depends on the config strings matching the avatar picker actually shipped on the device. The material shows no runtime check that the configured component exists.",
      "After 2b4d662a462c the fix is unconditional (not gated by fixAvatarCrossUserLeak), but it still applies only when android.multiuser.Flags.avatarSync() is enabled."
     ],
     "behavior_changes": [
      "With avatarSync enabled, the avatar picker intent now resolves only to com.android.avatarpicker/com.android.avatarpicker.ui.AvatarPickerActivity (or whatever overlaid config strings specify). Other activities matching AVATAR_PICKER_ACTION can no longer handle it.",
      "If the configured component is not present on the device, startActivityForResult with an explicit component would fail with ActivityNotFoundException rather than resolving to another handler (general platform knowledge).",
      "Two new SettingsLib string resources (config_avatar_picker_package, config_avatar_picker_class) become values that OEMs can override."
     ],
     "preconditions": [
      "android.multiuser.Flags.avatarSync() must be true so that the avatarSync branch of showAvatarPicker is taken.",
      "The edit-user-photo flow of an app that uses SettingsLib's EditUserPhotoController must reach showAvatarPicker (a private method; its callers are not shown in the material).",
      "Before the fix, another installed component matching the implicit AVATAR_PICKER_ACTION / CATEGORY_DEFAULT intent would be needed for the missing explicit target to matter."
     ],
     "per_major": {
      "14": "not affected per the bulletin. The material reports the project as not present in Android 14 security tags and does not show the state of EditUserPhotoController in 14.",
      "15": "same fix: 8cba0e8bcfc2 (flagged fix + config strings) and 2b4d662a462c (flag removal) in frameworks/base at android-security-15.0.0_r5, plus a17efb2eadc0 in build/release. At android-security-15.0.0_r17 the code contains the unflagged setClassName fix and the textproto flag-value file is absent.",
      "16": "not affected (not listed by the bulletin). The state at android-security-16.0.0_r8 is not shown in the material.",
      "17": "not affected (not listed by the bulletin). The state at android-security-17.0.0_r1 is not shown in the material."
     },
     "backport_notes": [
      "Apply 8cba0e8bcfc2 then 2b4d662a462c, or squash them into the unflagged form seen at r17. The end state needs the two config.xml strings, the com.android.settingslib.R import, and an unconditional intent.setClassName inside the Flags.avatarSync() branch. The build/release textproto (a17efb2eadc0) matters only if the fixAvatarCrossUserLeak flag guard is kept.",
      "Devices that ship a different avatar picker component must override config_avatar_picker_package / config_avatar_picker_class, otherwise the explicit intent will not resolve."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether Android 14, 16 or 17 security tags contain the setClassName fix or the avatarSync branch at all (the material gives no frameworks/base state for those majors).",
      "The exact mechanism of the cross-user leak (which data crossed users) is not shown beyond the implicit intent and the activity result handling.",
      "Whether the avatarSync flag is enabled by default on affected Android 15 builds.",
      "Which code paths call showAvatarPicker.",
      "Why the trunk_staging flag-value file is absent at android-security-15.0.0_r17."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2024-49728": {
     "cve": "CVE-2024-49728",
     "summary": "BluetoothOppSendFileInfo.generateFileInfo now rejects content URIs whose authority carries an explicit user id (user@authority) that differs from the Bluetooth process's own user, returning SEND_FILE_INFO_ERROR instead of resolving the URI.",
     "component": "platform/packages/modules/Bluetooth, OPP (Object Push Profile) outbound share, BluetoothOppSendFileInfo",
     "weakness": "Missing validation of the user component of a content URI (improper authorization of a caller-supplied URI, bulletin type ID)",
     "root_cause": "generateFileInfo in android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java did not check the userInfo part of a content URI's authority before calling contentResolver.getType(uri) and querying the metadata cursor. A URI of the form content://<otherUserId>@<authority>/... was not rejected. The commit message states the fix validates that the content URI belongs to the current user.",
     "fix_analysis": [
      "BluetoothOppSendFileInfo.java:generateFileInfo: after an existing early-return SEND_FILE_INFO_ERROR block and before contentResolver.getType(uri), adds `if (isContentUriForOtherUser(uri))`. This logs an error, calls ContentProfileErrorReportUtils.report(BluetoothProfile.OPP, BLUETOOTH_OPP_SEND_FILE_INFO, LOG_ERROR, tag 15) and returns SEND_FILE_INFO_ERROR. The file's 'Next tag value' comment is bumped from 15 to 16.",
      "BluetoothOppSendFileInfo.java:isContentUriForOtherUser (new private static method): reads uri.getUserInfo() and returns true only when it is non-empty and not string-equal to String.valueOf(UserHandle.myUserId()). Implicit URIs (no userInfo) and URIs naming the current user pass.",
      "BluetoothOppSendFileInfoTest.java: adds three tests plus the helper buildContentUriWithEncodedAuthority. A URI for another user returns SEND_FILE_INFO_ERROR. An implicit-user URI and a same-user URI return normal info with the correct length, name and stream.",
      "The commit is labelled 'Reapply' and reverts the revert db25b12501095bc3f371a7271e372834cde5cee5 (Change-Id I76b25fcd446d5e0530308e21deafa68d0b768edc)."
     ],
     "completeness": [
      "The check covers the generateFileInfo entry point. It is placed before contentResolver.getType(uri) and the metadata query in that method. The diff does not show code earlier in the method or where openAssetFileDescriptor is called relative to the check.",
      "Only the explicit userInfo component of the authority is validated. URIs without a user prefix are left to the ContentResolver's normal handling, which the fix does not change.",
      "The comparison is exact string equality with String.valueOf(myUserId()). Any non-canonical or non-numeric userInfo (for example, leading zeros) is rejected rather than normalised.",
      "Other OPP code that may open caller-supplied URIs outside generateFileInfo is not part of the diff and cannot be judged from the material."
     ],
     "behavior_changes": [
      "Outbound OPP shares whose content URI names a different user than the Bluetooth process now fail with SEND_FILE_INFO_ERROR, and a Log.e line 'Uri: ... is invalid for user N' is emitted.",
      "On Android 15, the rejection also files a new ContentProfileErrorReportUtils report (OPP, BLUETOOTH_OPP_SEND_FILE_INFO, LOG_ERROR, tag 15). The Android 14 backport only logs.",
      "URIs with no user prefix or with the current user's id are unaffected."
     ],
     "preconditions": [
      "A content:// URI reaches BluetoothOppSendFileInfo.generateFileInfo (the tests call generateFileInfo(context, uri, type, true)). The URI authority must contain a userInfo component that differs from the Bluetooth process's user id.",
      "The URI must already pass the existing checks that come before the new one in generateFileInfo. The diff shows an earlier return SEND_FILE_INFO_ERROR block immediately before the inserted check.",
      "How a third party supplies the URI to OPP is not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix: RESTRICT AUTOMERGE backport 339d20053956 in android-security-14.0.0_r17. It has the same isContentUriForOtherUser check and tests, but no ContentProfileErrorReportUtils.report call and none of the BluetoothProfile/BluetoothProtoEnums/BluetoothStatsLog imports that call needs. It also drops an unused android.util.Log import in the test. Present 8/8 at android-security-14.0.0_r29.",
      "15": "same fix: 4b65cbb339db (Reapply) in android-security-15.0.0_r5. Patched region 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": [
      "For branches whose OPP code lacks ContentProfileErrorReportUtils / BluetoothStatsLog, use the Android 14 variant (log and return only). Otherwise keep the report call, make sure tag 15 is not already used in the file, and bump the 'Next tag value' comment.",
      "The fix is self-contained in one source file plus tests and relies only on Uri.getUserInfo, TextUtils, Objects and UserHandle.myUserId."
     ],
     "confidence": "high",
     "unknowns": [
      "Why the original change was reverted (db25b12501095bc3f371a7271e372834cde5cee5) before being reapplied is not shown, and neither is whether the reapplied content differs from the original.",
      "The concrete caller path that lets an external party hand a content URI to generateFileInfo is not in the material.",
      "The code in generateFileInfo before the inserted check, and where openAssetFileDescriptor is called, are not shown.",
      "Whether other OPP URI-handling code paths perform an equivalent user check is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22416": {
     "cve": "CVE-2025-22416",
     "summary": "ChooserActivity now uses UriGrantsManager.checkGrantUriPermission_ignoreNonSystem with the launching UID to check that the caller may read the URI behind any URI-type Icon in a caller-supplied ChooserTarget, and removes the icon when the check fails.",
     "component": "platform/frameworks/base, core/java/com/android/internal/app/ChooserActivity (share sheet / chooser)",
     "weakness": "Missing permission check on a caller-supplied URI (URI-type Icon in a ChooserTarget)",
     "root_cause": "ChooserActivity copied the ChooserTarget array supplied by the caller into mCallerChooserTargets unchanged (targets[i] = (ChooserTarget) pa[i]). Such a ChooserTarget can carry an Icon of type TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP. Nothing checked that the launching app itself had read permission on that URI.",
     "fix_analysis": [
      "core/java/com/android/internal/app/ChooserActivity.java, in the loop that builds mCallerChooserTargets: each element cast to ChooserTarget now goes through hasValidIcon(). If that returns false, removeIcon(chooserTarget) replaces the element before it is stored in targets[i].",
      "New ChooserActivity.hasValidIcon(ChooserTarget): returns true for a null icon and for any icon type other than TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP. For URI icons it calls getUriGrantsManager().checkGrantUriPermission_ignoreNonSystem(getLaunchedFromUid(), getPackageName(), getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, getUserIdFromUri(uri)). On a SecurityException or RemoteException it logs with Log.e and returns false.",
      "New ChooserActivity.getUriGrantsManager(): returns UriGrantsManager.getService().",
      "New static ChooserActivity.removeIcon(ChooserTarget): builds a new ChooserTarget with the same title, score, componentName and intentExtras and a null icon.",
      "New imports: ContentProvider.getUriWithoutUserId, IUriGrantsManager, UriGrantsManager, graphics.drawable.Icon, os.RemoteException."
     ],
     "completeness": [
      "The fix covers only ChooserTargets that the caller supplies in the chooser intent (the loop that fills mCallerChooserTargets). This diff does not touch targets from other sources, and the material does not show whether those paths accept caller-controlled URI icons.",
      "Only TYPE_URI and TYPE_URI_ADAPTIVE_BITMAP icons are checked. Other icon types pass through unchanged.",
      "The check asks only for FLAG_GRANT_READ_URI_PERMISSION and uses checkGrantUriPermission_ignoreNonSystem. The material does not show the exact semantics of the _ignoreNonSystem variant.",
      "A failed check does not reject the ChooserTarget or the chooser intent: the target is kept with its icon removed. getLaunchedFromUid() serves as the caller identity.",
      "The change is only in the frameworks/base ChooserActivity. Per index search, packages/modules/IntentResolver has a separate ChooserActivity, but the material does not show how it handles icons on caller-provided targets."
     ],
     "behavior_changes": [
      "Caller-provided ChooserTargets whose URI-type Icon fails the URI grant check are kept with a null icon.",
      "Each rejected icon logs an error line (Log.e, 'Failed to get URI permission for: <uri>').",
      "A binder call to UriGrantsManager is made for each caller-supplied ChooserTarget that has a URI-type icon, while mCallerChooserTargets is being built. A RemoteException counts as a failed check, so the icon is removed."
     ],
     "preconditions": [
      "An app launches the chooser with a ChooserTarget array in the intent extras that ChooserActivity parses into mCallerChooserTargets.",
      "At least one of those ChooserTargets has an Icon of type TYPE_URI or TYPE_URI_ADAPTIVE_BITMAP pointing at a URI the caller has no read permission for.",
      "The frameworks/base com.android.internal.app.ChooserActivity handles the request."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base 82aa8527b7da in android-security-14.0.0_r19. The hunks are identical to Android 15; only the offset of the trailing helper methods differs (@@ -4305 vs -4206). All 8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/frameworks/base ee65c97e3a19 in android-security-15.0.0_r7 (Change-Id I08992550507572a4878c501184360a58adef53ad). All 8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected: the bulletin does not list Android 16. All 8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected: the bulletin does not list Android 17. All 8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a single self-contained commit that adds private helpers and five imports. It relies on IUriGrantsManager.checkGrantUriPermission_ignoreNonSystem, Icon.TYPE_URI_ADAPTIVE_BITMAP and the 5-argument ChooserTarget constructor existing on the target branch. Apart from the offset of the trailing helper methods, the Android 14 and 15 hunks are identical.",
      "The bulletin lists Android 13 as affected, but the material contains no Android 13 commit or code."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show where, or with which identity, ChooserActivity loads or renders the URI icon.",
      "The semantics of checkGrantUriPermission_ignoreNonSystem (which providers or URIs it skips) are not shown.",
      "The material does not show whether the IntentResolver module ChooserActivity (packages/modules/IntentResolver) had or needed an equivalent change, or which chooser implementation handles the request on each major.",
      "The Android 13 fix commit and code are not included in the material.",
      "The method that encloses the loop building mCallerChooserTargets is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22417": {
     "cve": "CVE-2025-22417",
     "summary": "In Transition.finishTransition, the ActivityRecordInputSink (touch-blocking) state of visible participant activities is now written into each activity's pending transaction and applied via scheduleAnimationLocked, instead of a separately created and immediately applied SurfaceControl.Transaction. This removes the race with a not-yet-applied pending transaction that could leave the input-sink state stale.",
     "component": "platform/frameworks/base, WindowManager transitions (services/core/java/com/android/server/wm/Transition.java)",
     "weakness": "Race condition / transaction ordering bug leaving stale input (touch-blocking) state",
     "root_cause": "At the end of Transition.finishTransition, the code looped over mParticipants. For each visible ActivityRecord with a parent, it called ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged() on a fresh transaction from ar.mWmService.mTransactionFactory.get(), then applied that transaction immediately. Per the commit message, when finishTransition ran before the pending transaction was applied, the input-sink state could end up stale. A later prepareSurfaces pass did not repair it because the InputWindowHandle is only applied on changes.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/Transition.java:finishTransition: removes the locally created SurfaceControl.Transaction inputSinkTransaction (mTransactionFactory.get() + apply()).",
      "For each participant ActivityRecord that is non-null, isVisible() and has a parent, calls ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(ar.getPendingTransaction()), so the input-sink change goes into the activity's pending transaction.",
      "Sets a boolean scheduleAnimation when at least one such participant was processed, then calls mController.mAtm.mWindowManager.scheduleAnimationLocked() so the pending transactions get applied."
     ],
     "completeness": [
      "The fix changes only the finishTransition input-sink update path in Transition.java. ActivityRecordInputSink itself and the prepareSurfaces path are not modified.",
      "This commit handles only ActivityRecord participants. The code at android-security-15.0.0_r17 also applies the same pending-transaction pattern to task.mTaskInputSink for visible Task participants. That Task handling is not part of this commit and does not appear in the 14/15 fix diffs.",
      "Applying the input-sink state now depends on scheduleAnimationLocked eventually applying the pending transaction. The diff shows no other path that applies it if that does not happen."
     ],
     "behavior_changes": [
      "Input-sink (touch-blocking) surface changes at transition finish are no longer applied synchronously in finishTransition. They go into each activity's pending transaction and are applied on the next scheduled animation pass.",
      "finishTransition now calls scheduleAnimationLocked whenever at least one visible ActivityRecord participant with a parent was processed."
     ],
     "preconditions": [
      "A transition reaches Transition.finishTransition with at least one participant ActivityRecord that is visible and has a parent (ar.getParent() != null).",
      "finishTransition runs before the pending transaction is applied (from the commit message).",
      "The exact trigger sequence (PoC from bug 332277530) is not included in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 89e32320207c in android-security-14.0.0_r19, identical diff apart from the line offset (hunk at ~1240). At android-security-14.0.0_r29 the resolver reports 3/8 patched lines present. The r29 code is not shown.",
      "15": "same fix: platform/frameworks/base d0c52fbde587 in android-security-15.0.0_r7 (DO NOT MERGE cherry-pick, Change-Id If93840ad7b2abbcb48c0c332870c48046c0f8fdc). At android-security-15.0.0_r17 the code shows the fixed form (getPendingTransaction + scheduleAnimationLocked), restructured and extended to cover task.mTaskInputSink. This likely accounts for the 3/8 line match.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8 the resolver reports 3/8 patched lines present, the same as the fixed and restructured 15.0.0_r17 code. The code is not shown in the material.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1 the resolver reports 3/8 patched lines present, the same as the fixed and restructured 15.0.0_r17 code. The code is not shown in the material."
     },
     "backport_notes": [
      "Single-file, single-hunk change in Transition.finishTransition. The 14 and 15 diffs are identical apart from line offsets. If a target tree already has the Task mTaskInputSink loop (as in 15.0.0_r17), check that the ActivityRecord branch uses ar.getPendingTransaction() and that scheduleAnimationLocked() is called when scheduleAnimation is set, rather than reapplying the hunk.",
      "The fix uses ActivityRecord.getPendingTransaction() and mWindowManager.scheduleAnimationLocked(), which the 14 and 15 fix diffs call directly. The material references no other dependency commit."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete PoC / trigger sequence from bug 332277530 and the exact user-visible effect of the stale input sink are not in the material.",
      "The internals of ActivityRecordInputSink.applyChangesToSurfaceIfChanged (the change-only behavior the commit message refers to) are not shown.",
      "Code at android-security-14.0.0_r29 and at the 16 and 17 security tags is not shown. Their status rests only on the resolver's line-match count and the bulletin's affected list."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22419": {
     "cve": "CVE-2025-22419",
     "summary": "Adds WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS in onCreate across 18 Telephony (com.android.phone) files: call settings, SIM PIN, FDN, SIM contacts and EmergencyDialer activities, plus the abstract BaseFdnContactScreen. With the flag set, non-system overlay windows are hidden while those screens are shown.",
     "component": "platform/packages/services/Telephony, TeleService phone app UI (call settings, ICC PIN screens, FDN screens, ADN/SIM contacts, EmergencyDialer)",
     "weakness": "Missing overlay protection on sensitive UI (tapjacking / UI overlay attack surface)",
     "root_cause": "The listed Telephony activities created their windows without SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS. These are call feature settings, call forwarding/barring/additional options for GSM and CDMA, ChangeIccPinScreen, EnableIccPinScreen, the FDN list/edit/delete/PIN2 entry screens, ADNList/SimContacts and EmergencyDialer. Nothing in their onCreate paths asked for non-system overlay windows to be hidden, so windows drawn by other apps could stay on top of these screens. The commit message states the intent: ensure only system overlays are allowed on telephony settings screens.",
     "fix_analysis": [
      "Single commit: 81cc0fd56aa9 on Android 15, 92f47baf4505 on Android 14. In each touched onCreate it adds getWindow().addSystemFlags(android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS). The call usually comes right after super.onCreate. Exceptions: in CallFeaturesSetting it comes after the DBG log line, in ADNList after requestFeature(FEATURE_INDETERMINATE_PROGRESS), and in BaseFdnContactScreen after resolveIntent() and requestFeature().",
      "Files/functions: ADNList.onCreate, CallFeaturesSetting.onCreate, CdmaCallForwardOptions.onCreate, CdmaCallOptions.onCreate, ChangeIccPinScreen.onCreate, EmergencyDialer.onCreate, EnableIccPinScreen.onCreate, GsmUmtsAdditionalCallOptions.onCreate, GsmUmtsCallBarringOptions.onCreate, GsmUmtsCallForwardOptions.onCreate, GsmUmtsCallOptions.onCreate, SimContacts.onCreate, settings/fdn/BaseFdnContactScreen.onCreate, settings/fdn/DeleteFdnContactScreen.onCreate, settings/fdn/EditFdnContactScreen.onCreate, settings/fdn/FdnList.onCreate, settings/fdn/FdnSetting.onCreate, settings/fdn/GetPin2Screen.onCreate.",
      "In CallFeaturesSetting.onCreate the flag is set before the isAdminUser() check that can call finish(). In ChangeIccPinScreen and EnableIccPinScreen it is set before the DISALLOW_CONFIG_MOBILE_NETWORKS restriction check, which only sets mDisallowedConfig.",
      "No code paths, permissions or intent handling change. The fix only adds a window flag in each onCreate."
     ],
     "completeness": [
      "Covers 18 files in com.android.phone and com.android.phone.settings.fdn; BaseFdnContactScreen is abstract. Some additions are redundant through inheritance. SimContacts and FdnList extend ADNList, and DeleteFdnContactScreen and EditFdnContactScreen extend BaseFdnContactScreen. Each of those base classes already sets the flag in its own onCreate, which the subclasses call first, so the subclass additions are harmless duplicates.",
      "The flag is set on the activity's main window only. Dialogs and popups these activities create are separate windows, and the diff does not touch them. Examples: the AlertDialog in CallFeaturesSetting, the password/PIN change dialogs in GsmUmtsCallBarringOptions and FdnSetting, and SelectionPopupMenu in FdnList. Whether this matters depends on how WindowManager handles child windows, which the material does not show.",
      "Telephony activities outside the diff are not covered. The material does not list the package's full set of activities, so coverage beyond these files cannot be judged."
     ],
     "behavior_changes": [
      "While any patched activity is visible, WindowManager hides non-system overlay windows, such as windows drawn by apps holding SYSTEM_ALERT_WINDOW; system overlays stay visible (general platform knowledge).",
      "No functional change to settings logic, intents, or permission checks."
     ],
     "preconditions": [
      "One of the patched Telephony activities must be on screen. The bulletin rates the issue High EoP, and the commit describes overlays on telephony settings screens.",
      "An attacker would need to be able to draw windows over other apps, which for third-party apps normally requires SYSTEM_ALERT_WINDOW (general platform knowledge). The material does not give specific trigger details.",
      "The material does not show which of these activities are exported or can be launched by third-party apps."
     ],
     "per_major": {
      "13": "The bulletin lists Android 13 as affected, but the material has no commit or tag information for 13, so its status is unknown.",
      "14": "Same fix: commit 92f47baf4505 in android-security-14.0.0_r19, with identical added lines in the same 18 files. Context differs slightly; for example, ChangeIccPinScreen and EnableIccPinScreen lack the UserManager restriction lines on 14. All 8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: commit 81cc0fd56aa9 in android-security-15.0.0_r7 (cherry-pick, Change-Id I953667309753ff6fc966956e246512de97ad8638). All 8 lines are present at android-security-15.0.0_r17.",
      "16": "Not affected per the bulletin, which does not list it. All 8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin, which does not list it. All 8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. Each hunk is an independent insertion in onCreate, and context differs between 14 and 15, so expect to adjust context when applying it. Window.addSystemFlags and SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS are hidden platform APIs that the phone process can use because it builds against the platform (general platform knowledge). Per index search, packages/apps/Settings SimDialogActivity.onCreate uses the same pattern."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe the overlay scenario behind the High EoP rating (which screen, what action).",
      "Whether the Android 13 branch received the same change.",
      "Export status and launch restrictions of the patched activities, and whether the dialogs they open are protected."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22421": {
     "cve": "CVE-2025-22421",
     "summary": "SystemUI stops putting the notification title, text and ticker into the accessibility content description of status bar notification icons. Only the app name is left.",
     "component": "platform/frameworks/base, SystemUI status bar notification icons (accessibility content description)",
     "weakness": "Information disclosure: notification content exposed through the accessibility content description of the status bar notification icon.",
     "root_cause": "contentDescForNotification in packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt built the icon content description from Notification.EXTRA_TITLE, Notification.EXTRA_TEXT and tickerText. It used the title, or the text if the title equalled the app name, and fell back to the ticker. It then formatted that into R.string.accessibility_desc_notification_icon together with the app name. So notification content was exposed through the icon's accessibility description. The commit message gives the reason for removing it: the content can be read when the full cards are visible.",
     "fix_analysis": [
      "NotificationContentDescription.kt:contentDescForNotification: removes the reads of EXTRA_TITLE, EXTRA_TEXT and tickerText and the title/text/ticker selection logic. The function now returns c.getString(R.string.accessibility_desc_notification_icon, appName, \"\"), so only the app name (from loadHeaderAppName) is included.",
      "NotificationContentDescriptionTest.kt: the tests asserting title/text/ticker selection are collapsed into one test, notificationWithAllDifferentFields_descriptionIsAppName. It asserts that the description equals the formatted string with an empty content argument.",
      "Android 14 variant (StatusBarIconView.java, commit 4ec1792148a5): the same removal is applied to the equivalent logic, which lived in StatusBarIconView.java on that version. It returns the formatted string with appName and \"\"."
     ],
     "completeness": [
      "The fix removes all notification-content fields (title, text, ticker) from the icon description, leaving only the app name. Per index search, the function is used by StatusBarIconView.setNotification and IconBuilder.getIconContentDescription, so both now get the reduced string.",
      "The string resource accessibility_desc_notification_icon is unchanged. It is still formatted with an empty second argument.",
      "The now-unused android.text.TextUtils import remains in NotificationContentDescription.kt.",
      "The fix adds no conditional logic. It drops the content unconditionally."
     ],
     "behavior_changes": [
      "Accessibility services (e.g. TalkBack) get only the app name in the content description of status bar notification icons. Notification title, text and ticker are no longer included in any state.",
      "Unit tests that asserted title/text/ticker in the icon description were removed."
     ],
     "preconditions": [
      "A notification is posted and SystemUI creates a status bar icon for it. Per index search, StatusBarIconView.setNotification and IconBuilder.getIconContentDescription call contentDescForNotification.",
      "An accessibility service that reads view content descriptions is enabled and focuses the notification icon. The commit's Test: lines mention enabling TalkBack and highlighting icons."
     ],
     "per_major": {
      "14": "adapted fix: commit 4ec1792148a5 in android-security-14.0.0_r19 applies the same removal in StatusBarIconView.java. At android-security-14.0.0_r29, 0/8 lines of the patched region are present, which is consistent with the code being in a different file on 14.",
      "15": "same fix: commit 3b0704fd381a in android-security-15.0.0_r7 modifies NotificationContentDescription.kt and its test. The fixed function body (app name only) is present at android-security-15.0.0_r17 (4/8 lines of the patched region).",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 4/8 lines of the patched region are present, the same count as the patched 15 tag, which is consistent with the fixed code.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, 4/8 lines of the patched region are present, the same count as the patched 15 tag."
     },
     "backport_notes": [
      "Where the description is computed inside StatusBarIconView.java (as on Android 14), apply the removal there as in commit 4ec1792148a5. Where NotificationContentDescription.kt exists, apply 3b0704fd381a together with its NotificationContentDescriptionTest.kt change."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact UI states (e.g. lockscreen with hidden sensitive content) in which the icon description was readable while the card content was not shown are not given in the material.",
      "Whether other callers beyond those found by index search compute or reuse a similar description is not shown.",
      "Whether OEM or downstream views reuse the removed title/text/ticker logic is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22422": {
     "cve": "CVE-2025-22422",
     "summary": "SystemUI's biometric prompt foreground-owner check is extended with a class-name comparison for prompts hosted by Settings' ConfirmDeviceCredentialActivity, so the prompt is cancelled when a different Settings activity becomes the top activity instead of staying on screen because the package name still matched.",
     "component": "platform/frameworks/base (android.hardware.biometrics BiometricPrompt/PromptInfo, SystemUI biometrics AuthController) and platform/packages/apps/Settings (password/ConfirmDeviceCredentialActivity, BiometricFragment)",
     "weakness": "Insufficient identity check of the prompt owner: the foreground check compared only the top task's package name with the client package, which is ambiguous when the client is ConfirmDeviceCredentialActivity inside the Settings package.",
     "root_cause": "AuthController.isOwnerInForeground() (packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java) fetched the top running task (getTasks(1)) and treated the prompt owner as foreground whenever topActivity.getPackageName() equalled mCurrentDialog.getOpPackageName() or the client was a system app. When an app uses createConfirmDeviceCredentialIntent(), the BiometricPrompt client is ConfirmDeviceCredentialActivity, which belongs to the Settings package. If the app then switches to Settings, the package-name comparison still succeeds, so neither the task-stack listener (onTaskStackChanged) nor showDialog() cancelled the prompt, and the biometric prompt remained displayed. PromptInfo carried the real caller component (used for the logo) but no class name of the hosting activity, and the check did not distinguish activities within the package.",
     "fix_analysis": [
      "frameworks/base core/java/android/hardware/biometrics/PromptInfo.java: renames mComponentNameForConfirmDeviceCredentialActivity to mRealCallerForConfirmDeviceCredentialActivity (setter/getter renamed accordingly, the else-if field check updated to the new field) and adds a new String field mClassNameIfItIsConfirmDeviceCredentialActivity with a package-private setter setClassNameIfItIsConfirmDeviceCredentialActivity(String) and public getter getClassNameIfItIsConfirmDeviceCredentialActivity(); the field is written/read in the Parcel after the real-caller ComponentName.",
      "frameworks/base core/java/android/hardware/biometrics/BiometricPrompt.java: Builder.setComponentNameForConfirmDeviceCredentialActivity is renamed to setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) (still @hide, @RequiresPermission TEST_BIOMETRIC or USE_BIOMETRIC_INTERNAL); besides storing the real caller it now records mContext.getClass().getName() as the class name in the PromptInfo.",
      "frameworks/base packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt: adds ActivityTaskManager.isSystemAppOrInBackground(context, clientPackage, clientClassNameIfItIsConfirmDeviceCredentialActivity). It takes tasks[0].topActivity and returns true (treat as background) when neither the client is a system app nor the top package equals the client package, OR when the class name is non-null and topActivity.className differs from it. If there are no tasks it returns false.",
      "frameworks/base packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java and AuthContainerView.java: new interface method getClassNameIfItIsConfirmDeviceCredentialActivity(), implemented in AuthContainerView by returning mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity().",
      "frameworks/base packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java: isOwnerInForeground() is replaced by isOwnerInBackground(), which delegates to Utils.isSystemAppOrInBackground with the dialog's op package and class name; mTaskStackListener.onTaskStackChanged() and showDialog() (the !promptInfo.isAllowBackgroundAuthentication() branch) now call cancelIfOwnerIsNotInForeground() when isOwnerInBackground() is true. Returns false when there is no current dialog.",
      "frameworks/base packages/SystemUI biometrics/domain/model/BiometricPromptRequest.kt, ui/viewmodel/PromptViewModel.kt and tests PromptSelectorInteractorImplTest.kt: follow the getter rename (realCallerForConfirmDeviceCredentialActivity) and TODO bug number change; logic unchanged.",
      "frameworks/base services/core/java/com/android/server/biometrics/Utils.java: only LINT.IfChange/ThenChange markers added around isBackground(); its logic is unchanged.",
      "Settings src/com/android/settings/password/ConfirmDeviceCredentialActivity.java: where the PromptInfo is built, it now calls promptInfo.setRealCallerForConfirmDeviceCredentialActivity(getCallingActivity()); BiometricFragment.newInstance no longer receives the calling activity.",
      "Settings src/com/android/settings/password/BiometricFragment.java: the KEY_CALLING_ACTIVITY bundle plumbing is removed and the BiometricPrompt.Builder (constructed with getContext()) calls setRealCallerForConfirmDeviceCredentialActivity(promptInfo.getRealCallerForConfirmDeviceCredentialActivity()), which also makes the Builder record the context's class name."
     ],
     "completeness": [
      "The class-name comparison is applied only when the PromptInfo carries a class name, i.e. only for prompts created through Builder.setRealCallerForConfirmDeviceCredentialActivity (Settings' BiometricFragment on behalf of ConfirmDeviceCredentialActivity). For all other clients the check remains package-name based (any top activity of the client package, or any system client, keeps the prompt alive).",
      "Both call sites of the foreground check in AuthController (showDialog and onTaskStackChanged) use the new helper. showDialog still skips the check when promptInfo.isAllowBackgroundAuthentication() is true (TODO b/353597496 left in place).",
      "The helper only inspects tasks[0].topActivity even though it requests getTasks(Int.MAX_VALUE); the server-side services/core Utils.isBackground() (which iterates all tasks and checks isVisible()) gets LINT markers only and keeps a package-only comparison.",
      "The class name is taken from mContext.getClass().getName() of the Builder's context; correctness relies on the Builder being created with the hosting activity's context (BiometricFragment uses getContext()).",
      "The else-if field check in PromptInfo was not extended to the new class-name field; the setter is package-private and only called from Builder.setRealCallerForConfirmDeviceCredentialActivity, and a non-null class name only adds a cancellation condition in the helper."
     ],
     "behavior_changes": [
      "For a biometric prompt hosted by ConfirmDeviceCredentialActivity, SystemUI now cancels the prompt (cancelIfOwnerIsNotInForeground) when the top activity's class differs from the recorded class name, including when the new top activity is another Settings activity.",
      "PromptInfo Parcel layout gains a trailing String (class name); framework and SystemUI must be built together.",
      "Hidden API rename: BiometricPrompt.Builder.setComponentNameForConfirmDeviceCredentialActivity -> setRealCallerForConfirmDeviceCredentialActivity; PromptInfo getter/setter renamed to *RealCallerForConfirmDeviceCredentialActivity*. Settings and SystemUI callers updated.",
      "AuthController log text changes (\"Evicting client due to top activity is not : <pkg>\"); Utils.kt logs under tag SysUIBiometricUtils.",
      "Settings: the real caller ComponentName is now stored in the PromptInfo by ConfirmDeviceCredentialActivity instead of being passed to BiometricFragment via a bundle key."
     ],
     "preconditions": [
      "An application uses createConfirmDeviceCredentialIntent() and launches Settings' ConfirmDeviceCredentialActivity, which shows a BiometricPrompt through BiometricFragment (per the commit message and Settings diff).",
      "While that prompt is showing, the app switches to Settings so a Settings activity is on top, and the package-only comparison in AuthController still matched.",
      "The SystemUI AuthController check is reached via its TaskStackListener.onTaskStackChanged() or at showDialog() time (per the diff)."
     ],
     "per_major": {
      "14": "adapted fix: platform/packages/apps/Settings 1c0ab9c696d7 in android-security-14.0.0_r19 adds a no-argument Builder call .setClassNameIfItIsConfirmDeviceCredentialActivity() in BiometricFragment (next to setAllowBackgroundAuthentication(true)). The matching frameworks/base change for 14 is not in the material. Region 3/3 lines present at android-security-14.0.0_r29.",
      "15": "same fix: frameworks/base 106d3165dc71 plus Settings 32915818d780 in android-security-15.0.0_r7; 8/8 lines of the patched region present at android-security-15.0.0_r17 and the frameworks/base changes are visible in the r17 code context.",
      "16": "not affected per the bulletin; at android-security-16.0.0_r8 only 4/8 lines of the patched region are present. Exact state unknown from the material.",
      "17": "not affected per the bulletin; at android-security-17.0.0_r1 only 4/8 lines of the patched region are present. Exact state unknown from the material."
     },
     "backport_notes": [
      "Apply the frameworks/base commit first (or atomically): the Settings commit calls the renamed Builder.setRealCallerForConfirmDeviceCredentialActivity and PromptInfo.getRealCallerForConfirmDeviceCredentialActivity, and SystemUI's BiometricPromptRequest.kt uses the renamed getter, so partial application breaks the build. SystemUI and framework must ship together because the PromptInfo Parcel format changes.",
      "On Android 14 Google used a different Settings change (no-argument Builder.setClassNameIfItIsConfirmDeviceCredentialActivity() in BiometricFragment); the corresponding framework/SystemUI change is not included in this material."
     ],
     "confidence": "high",
     "unknowns": [
      "The frameworks/base (SystemUI/PromptInfo) side of the Android 14 fix and any Android 13 fix are not in the material.",
      "Whether the server-side services/core/java/com/android/server/biometrics/Utils.isBackground() path is relevant to this issue; the commit only adds LINT markers there.",
      "Why only 4/8 lines of the patched region are present on Android 16 and 17.",
      "No automated test is included (Test: manual test with sample app); the precise user-visible consequence beyond the prompt staying displayed is not described."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22427": {
     "cve": "CVE-2025-22427",
     "summary": "Removes the onResume/onPause overrides in Settings' NotificationAccessConfirmationActivity that passed the private constant SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS to the public Window.addFlags/clearFlags, relying instead on the private-flag setup that, per the commit message, is already done in onCreate.",
     "component": "platform/packages/apps/Settings, notification listener access confirmation dialog (NotificationAccessConfirmationActivity)",
     "weakness": "Incorrect API use / flag namespace confusion: a WindowManager.LayoutParams privateFlags constant was passed to the public Window.addFlags/clearFlags API",
     "root_cause": "NotificationAccessConfirmationActivity.onResume called getWindow().addFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS), and onPause called getWindow().clearFlags(...) with the same constant. SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS is a privateFlags bit, not a public flags bit. The new test confirms this: it asserts the bit is set in getAttributes().privateFlags and clear in getAttributes().flags. The commit message says the flag is already correctly added in onCreate, so the onResume/onPause code was both wrong and unnecessary. The material does not show what effect the stray bit had in the public flags field.",
     "fix_analysis": [
      "src/com/android/settings/notification/NotificationAccessConfirmationActivity.java: deletes the onResume override (super.onResume() plus getWindow().addFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)) and the onPause override (getWindow().clearFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) plus super.onPause()). It also drops the now unused import android.view.WindowManager. The static import of SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS remains (line 21 at android-security-15.0.0_r17), consistent with the onCreate path the commit message describes.",
      "tests/robotests/.../NotificationAccessConfirmationActivityTest.java: adds onCreate_setsWindowFlags. The test starts the activity and asserts that SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS is present in getAttributes().privateFlags and absent from getAttributes().flags."
     ],
     "completeness": [
      "Covers the only misuse visible in the diff. Both the addFlags (onResume) and clearFlags (onPause) calls with the private constant are removed, so this activity no longer touches the public flags field with that value.",
      "The overlay-hiding protection itself is not weakened: per the commit message and the new test, onCreate already sets the flag through the private-flags path, and the fix does not touch that code.",
      "The fix is confined to NotificationAccessConfirmationActivity. The material does not show whether other Settings activities have the same addFlags/private-constant pattern."
     ],
     "behavior_changes": [
      "While resumed, the activity window no longer has the public LayoutParams.flags bit whose numeric value equals SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS. The new test asserts that bit is 0 in flags.",
      "The privateFlags bit set in onCreate is expected to stay set (asserted by the new test).",
      "No API, error-code or string changes appear in the diff."
     ],
     "preconditions": [
      "NotificationAccessConfirmationActivity must be started and reach onResume. The activity reads EXTRA_COMPONENT_NAME and EXTRA_USER_ID from NotificationAccessConfirmationActivityContract. The launching flow is not shown in the material.",
      "The material does not show which public flag the misapplied bit maps to or how it could be abused. The bulletin rates this as High EoP."
     ],
     "per_major": {
      "14": "same fix: commit 7255b2eaac4b in android-security-14.0.0_r19. The diff is identical apart from line offsets. Patched region fully present at android-security-14.0.0_r29.",
      "15": "same fix: commit 12a8312004bb in android-security-15.0.0_r7 (cherry-pick of e0510a62eba0, Change-Id I539fca77a1d136bdd0e4892993020884430b8a4e). Patched region fully 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 that only deletes two methods and an import. Before applying it to a fork, check that onCreate in that tree already sets SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS through the private-flags path, because the deletion assumes that protection is in place. The new robolectric test onCreate_setsWindowFlags checks for it."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact onCreate code that sets the private flag is not shown in the provided context. Only the static import at line 21, the commit message and the test point to it.",
      "Which public window flag the stray bit value set, and the concrete security consequence, are not shown in the material. The bulletin classifies it only as High EoP.",
      "How and by whom NotificationAccessConfirmationActivity is launched is not shown.",
      "Whether Android 13 (listed as affected) received the same commit is not shown in the per-major facts."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22428": {
     "cve": "CVE-2025-22428",
     "summary": "Settings AppInfoBase now checks INTERACT_ACROSS_USERS_FULL against the UID that launched the activity (ActivityManager.getLaunchedFromUid + checkUidPermission) instead of against a package name obtained from SettingsActivity.getInitialCallingPackage().",
     "component": "platform/packages/apps/Settings, applications/AppInfoBase (app info fragments, cross-user caller permission check)",
     "weakness": "Improper authorization: the permission check used a caller package name string instead of the launching UID",
     "root_cause": "In src/com/android/settings/applications/AppInfoBase.java, the helper that checks INTERACT_ACROSS_USERS_FULL for the caller got a package name from ((SettingsActivity) activity).getInitialCallingPackage() and then called mPm.checkPermission(INTERACT_ACROSS_USERS_FULL, callingPackageName). The decision therefore depended on a package name string rather than on the system-recorded UID of the launcher. The material does not show how getInitialCallingPackage() derives that name, so it does not show exactly how the old check could be met by a launcher without the permission. The bulletin rates this as a High severity EoP.",
     "fix_analysis": [
      "AppInfoBase.java (the method after the `activity instanceof SettingsActivity` guard): removes the getInitialCallingPackage() lookup, the TextUtils.isEmpty() early return and the package-name based mPm.checkPermission() call.",
      "Replaces them with ActivityManager.getService().getLaunchedFromUid(activity.getActivityToken()) to get the launching UID, then calls ActivityManager.checkUidPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callerUid). It returns true only on PERMISSION_GRANTED.",
      "Wraps the calls in try/catch RemoteException and returns false (deny) on failure. Adds imports for android.app.ActivityManager and android.os.RemoteException."
     ],
     "completeness": [
      "Covers the single permission-check helper in AppInfoBase. The material does not show which subclasses call it.",
      "The existing guard `if (!(activity instanceof SettingsActivity)) return false;` is kept, so hosts that are not a SettingsActivity are still denied.",
      "The diff does not touch SettingsActivity.getInitialCallingPackage() itself. The material shows no other Settings code that relies on that package name for authorization, and this commit changes none.",
      "The fix does not show or change the callers of this helper or the user-id handling that depends on its result."
     ],
     "behavior_changes": [
      "The permission decision is now made for the launching UID. A launcher without INTERACT_ACROSS_USERS_FULL is denied even if getInitialCallingPackage() would have returned a package name that holds the permission.",
      "If ActivityManager throws a RemoteException, the helper now returns false (deny).",
      "The 'Not able to get calling package name' warning path is removed. The warning log now reports the UID instead of the package name.",
      "ActivityManager.getService().getLaunchedFromUid and ActivityManager.checkUidPermission replace PackageManager.checkPermission on this path."
     ],
     "preconditions": [
      "An app-info fragment derived from AppInfoBase is hosted in a SettingsActivity and calls this helper to check whether the caller holds INTERACT_ACROSS_USERS_FULL.",
      "The activity is launched by a caller that does not hold INTERACT_ACROSS_USERS_FULL. The material does not show exactly how the old package-name path could be influenced."
     ],
     "per_major": {
      "13": "Listed as affected by the bulletin, but the material includes no Android 13 commit; status unknown.",
      "14": "Same fix: platform/packages/apps/Settings 4a9cb946e978 in android-security-14.0.0_r19, an identical diff with a 4-line offset. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Same fix: 9dba6f50302b in android-security-15.0.0_r7 (cherry-pick of e719575e92c1 / 5bd45baa9365). 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "Not affected (not listed by the bulletin). The patched UID-based code is present at android-security-16.0.0_r8 (8/8 lines).",
      "17": "Not affected (not listed by the bulletin). The patched UID-based code is present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "Single self-contained commit. The 14 and 15 diffs are identical apart from line offsets, so it should apply with fuzz to any branch where AppInfoBase still has the getInitialCallingPackage()/mPm.checkPermission block. ActivityManager.getService().getLaunchedFromUid and ActivityManager.checkUidPermission are hidden framework APIs that Settings can use as a platform-signed system app (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The name of the patched method is not shown in the hunk header.",
      "How SettingsActivity.getInitialCallingPackage() derives the package name, and therefore exactly how the old check could be met by an unprivileged launcher.",
      "Which AppInfoBase subclasses call this helper and what cross-user action its result controls.",
      "Whether an Android 13 backport exists (the bulletin lists 13 but the material provides no commit).",
      "How the old code behaved if the PackageManager call failed."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22430": {
     "cve": "CVE-2025-22430",
     "summary": "Adds ACCESS_FINE_LOCATION permission enforcement to the hidden system API ITrustManager.isInSignificantPlace(), which previously had no permission check on its binder implementation.",
     "component": "platform/frameworks/base, TrustManagerService (services/core, com.android.server.trust) and ITrustManager AIDL / TrustManager client",
     "weakness": "missing permission check",
     "root_cause": "The binder implementation of isInSignificantPlace() in TrustManagerService.java (the ITrustManager.Stub override) did no caller permission check before querying the significant place provider through mSignificantPlaceServiceWatcher.runOnBinder(...). The method in ITrustManager.aidl also had no @EnforcePermission annotation, unlike the sibling isTrustUsuallyManaged, which is annotated @EnforcePermission(\"TRUST_LISTENER\"). As a result, callers able to reach the trust service binder could learn whether the device is in a significant place without holding a location permission. The bulletin classifies this as High severity information disclosure.",
     "fix_analysis": [
      "core/java/android/app/trust/ITrustManager.aidl: adds @EnforcePermission(\"ACCESS_FINE_LOCATION\") to isInSignificantPlace(). This makes the AIDL compiler generate a Stub.isInSignificantPlace_enforcePermission() helper (general platform knowledge).",
      "services/core/java/com/android/server/trust/TrustManagerService.java: imports android.annotation.EnforcePermission, annotates the Stub override isInSignificantPlace() with @EnforcePermission(Manifest.permission.ACCESS_FINE_LOCATION), and calls super.isInSignificantPlace_enforcePermission() as the first statement, before the android.security.Flags.significantPlaces() check and the provider query. The generated helper throws SecurityException when the caller lacks the permission (general platform knowledge).",
      "core/java/android/app/trust/TrustManager.java: adds @RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION) to the client wrapper isInSignificantPlace(). This annotation is for documentation and lint and does not enforce anything at runtime (general platform knowledge)."
     ],
     "completeness": [
      "The server-side check covers the binder method isInSignificantPlace() in TrustManagerService, the only entry point to this data that the material shows. The check runs regardless of the significantPlaces() feature flag.",
      "This commit changes no other ITrustManager methods (isActiveUnlockRunning, isDeviceLocked and so on are untouched). The material shows no evidence that those need a check."
     ],
     "behavior_changes": [
      "Binder callers of ITrustManager.isInSignificantPlace() that lack ACCESS_FINE_LOCATION now fail the permission check instead of getting a boolean. The generated helper throws SecurityException (general platform knowledge).",
      "The permission check runs before the android.security.Flags.significantPlaces() check, so it applies even when the flag is off.",
      "TrustManager.isInSignificantPlace() now carries @RequiresPermission(ACCESS_FINE_LOCATION)."
     ],
     "preconditions": [
      "The caller can obtain the trust service binder (ITrustManager) and invoke isInSignificantPlace(). The API is @hide.",
      "The provider query is reached only when android.security.Flags.significantPlaces() is enabled."
     ],
     "per_major": {
      "14": "not affected per the bulletin; 5/8 patched-region lines present at android-security-14.0.0_r29. The material does not show whether isInSignificantPlace() exists there.",
      "15": "same fix; resolved via bug_id in android-security-15.0.0_r7 as cherry-pick 161eb6100d6f (Change-Id I1c5ef857c750dbbd0577f5aadc43e9b6122de790); 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. ITrustManager.aidl on 15 already uses @EnforcePermission (on isTrustUsuallyManaged), so the AIDL permission-annotation infrastructure is available. The three file changes must go in together, because the server calls the generated _enforcePermission helper, which exists only once the AIDL annotation is added."
     ],
     "confidence": "high",
     "unknowns": [
      "Which platform callers (for example SystemUI or keyguard) invoke TrustManager.isInSignificantPlace(), and whether they hold ACCESS_FINE_LOCATION.",
      "Whether a test accompanied the fix; the commit says Test: manual.",
      "The original commits 389e4cf737571bf0c438239400295fd229acc3e8 and f085fccf0bb7963962ae56618c5b3a1762d3aeab are referenced but not shown.",
      "The state of isInSignificantPlace() on Android 14 (only 5/8 patched-region lines present).",
      "What isInSignificantPlace() returns when the flag is off or no provider is bound; the diff is truncated."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22431": {
     "cve": "CVE-2025-22431",
     "summary": "AppOpsService.verifyAndGetBypass now treats a proxy-supplied attribution tag as valid for exempt proxied packages (shell or non-app uids) only when the proxy is a system package or there is no proxy. The same commit also makes isCallerAndAttributionTrusted require a trusted next attribution source, and re-checks remaining restrictions before resuming a paused op.",
     "component": "platform/frameworks/base, system_server AppOps (services/core/java/com/android/server/appop/AppOpsService.java)",
     "weakness": "Missing validation of proxy-supplied attribution tags, leading to uncontrolled resource consumption in AppOps state (DoS per bulletin)",
     "root_cause": "In AppOpsService.verifyAndGetBypass, if the proxied packageName was \"com.android.shell\" or resolved to a non-app uid via resolveUid (now resolveNonAppUid), the method always returned PackageVerificationResult(RestrictionBypass.UNRESTRICTED, isAttributionTagValid=true). These packages do not have to register attribution tags, so the tag was accepted as given. On the proxy note/start paths the tag comes from the proxy app. As the commit message puts it, the proxy's tag was trusted whenever the proxied app was one of these exemptions. A non-system proxy could therefore get arbitrary, unverified attribution tags recorded. The comment added by the fix describes the risk as abusive apps \"clogging the appops system with unlimited attribution tags via proxy calls\". verifyAndGetBypass was not passed the proxy uid. Separately, isCallerAndAttributionTrusted trusted an AttributionSource without checking its next element, and the restriction-removal path resumed a paused AttributedOp without checking whether other restrictions still applied.",
     "fix_analysis": [
      "AppOpsService.java:verifyAndGetBypass (6-argument version) gets a new int proxyUid parameter. In the exempt branch (nonAppUid != Process.INVALID_UID), after the uid/appId mismatch check, it computes proxyIsSystemAppOrNull. This is true if proxyPackageName == null. Otherwise, if UserHandle.getAppId(proxyUid) >= Process.FIRST_APPLICATION_UID, it is set to mPackageManagerInternal.isSystemPackage(proxyPackageName). The result is returned as isAttributionTagValid instead of the constant true. Callers shown in the diff already set attributionTag to null when isAttributionTagValid is false.",
      "AppOpsService.java: the 4-argument verifyAndGetBypass overload becomes 5 arguments (proxyUid added). The 3-argument version now passes Process.INVALID_UID and null. checkPackage passes Process.INVALID_UID. The note-path and start-path call sites (around lines 3126 and 3768) and startOperationDryRun now pass proxyUid. startOperationDryRun gets an int proxyUid parameter, and its caller on the proxy-start path (the dry run before starting the proxy operation) passes proxyUid.",
      "AppOpsService.java:isCallerAndAttributionTrusted: the early 'return true' for a trusted AttributionSource that is not the caller now also requires attributionSource.getNext() == null or getNext().isTrusted(mContext). Otherwise it falls through to the UPDATE_APP_OPS_STATS permission check.",
      "AppOpsService.java, restriction-change handler (around line 6515, the block that pauses and resumes AttributedOp): a paused op is resumed only if isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag, Context.DEVICE_ID_DEFAULT, bypass, false) returns false. bypass comes from verifyAndGetBypass(uid, ops.packageName, attrOp.tag).",
      "AppOpsService.java: resolveUid is renamed resolveNonAppUid, and its call sites in checkPackage, isSpecialPackage, verifyAndGetBypass and one more around line 5415 are updated. The local pkgUid in verifyAndGetBypass is renamed nonAppUid, and 'int pkgUid = nonAppUid;' is added before the regular-package path. The Android 15 commit also removes the 'boolean wasNull' line on the note path."
     ],
     "completeness": [
      "The three call sites of the proxy-aware verifyAndGetBypass overload shown in the diff (note path, start path, startOperationDryRun) are updated, so both the note and the start proxy paths get the system-proxy check.",
      "The system-package check runs only when proxyPackageName != null and the proxy app id is >= Process.FIRST_APPLICATION_UID. Proxies with lower app ids are trusted without asking PackageManager. With no proxy, exempt packages keep the old behaviour (tag accepted).",
      "verifyAndGetBypass does not itself check that proxyPackageName belongs to proxyUid. Whether the callers verify this is not shown in the material.",
      "The regular (non-exempt) package path is unchanged.",
      "The isCallerAndAttributionTrusted and resume-path changes are extra hardening in the same commit. The material does not say whether they are needed for this CVE."
     ],
     "behavior_changes": [
      "When a non-system proxy app supplies an attribution tag for a proxied operation on an exempt package (shell or non-app uid), the tag is discarded (attributionTag set to null) instead of recorded. No new exception is thrown in this case.",
      "The trusted-AttributionSource shortcut in isCallerAndAttributionTrusted returns true only if the next AttributionSource is also trusted or absent. Otherwise the UPDATE_APP_OPS_STATS permission check decides.",
      "When a restriction is removed, a paused started op is resumed only if no other restriction still applies to it.",
      "resolveUid is renamed resolveNonAppUid, and the startOperationDryRun signature changes (internal only)."
     ],
     "preconditions": [
      "A proxy note or start op call whose proxied package is exempt: \"com.android.shell\", or a package that resolveNonAppUid maps to a non-app uid.",
      "The proxy app is a non-system app (app id >= FIRST_APPLICATION_UID and not a system package).",
      "The material does not show the permission checks the entry points apply to the proxy caller."
     ],
     "per_major": {
      "14": "Adapted fix: platform/frameworks/base 79211e094a73 in android-security-14.0.0_r19, same logic. Differences: the diff does not touch a startOperationDryRun method (the start-path hunk it updates takes a boolean dryRun parameter), the resume-path isOpRestrictedLocked call has no Context.DEVICE_ID_DEFAULT argument, and the 'wasNull' line is kept. 6/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/frameworks/base be02cee3a50c in android-security-15.0.0_r7 (cherry-pick of 9dd915f74a88, Change-Id I6a7b0a24359097c0ea2f52cc69637d929a931b4f). 6/8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected per the bulletin (not listed). 6/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin (not listed). 6/8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, but it changes every caller of verifyAndGetBypass and resolveUid. For trees without startOperationDryRun, or where isOpRestrictedLocked has no device-id parameter, use the Android 14 variant. Make sure mPackageManagerInternal (PackageManagerInternal.isSystemPackage) is available in AppOpsService."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not name the methods around the changed call sites (note path near line 3126, start path near line 3768, proxy-start caller near line 3728, call site near line 5415) or the method with the pause/resume block near line 6515.",
      "Whether the isCallerAndAttributionTrusted and resume-path changes address this CVE or are separate hardening is not stated beyond the commit message.",
      "The material does not show the permission and trust requirements the proxy entry points apply to the proxy caller, or whether callers check that proxyPackageName belongs to proxyUid.",
      "The commit lists its test as 'upcoming'; no test is included."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22433": {
     "cve": "CVE-2025-22433",
     "summary": "IntentForwarderActivity.canForward now runs the cross-profile forwarding check (ACTION_CHOOSER rejection plus IPackageManager.canForwardTo) on both the forwarded intent and its selector, instead of checking only the selector when one is present.",
     "component": "platform/frameworks/base (core/java/com/android/internal/app/IntentForwarderActivity.java) and platform/packages/modules/IntentResolver (java/src/com/android/intentresolver/IntentForwarderActivity.java), cross-profile intent forwarding",
     "weakness": "Authorization check applied to the wrong object: the forwardability check was performed on the intent selector rather than on the intent actually forwarded (improper authorization / incorrect check target)",
     "root_cause": "In both copies of IntentForwarderActivity.canForward, when the incoming intent carried a selector, the code set intentToCheck = forwardIntent.getSelector() and called packageManager.canForwardTo on that selector only, while the ACTION_CHOOSER rejection was applied only to the outer intent. The object returned and launched in the target profile was forwardIntent (the outer intent), which therefore never had canForwardTo applied to it. As the commit message states, an intent that would not be allowed across profiles could be paired with a different selector that passes the check and then be launched across profiles.",
     "fix_analysis": [
      "frameworks/base core/java/com/android/internal/app/IntentForwarderActivity.java:canForward - the body is split into canForward plus a new private static canForwardInner(intent, sourceUserId, targetUserId, packageManager, contentResolver). canForwardInner rejects ACTION_CHOOSER, resolves the type via intent.resolveTypeIfNeeded(contentResolver), and returns the result of packageManager.canForwardTo (false on RemoteException).",
      "canForward first calls canForwardInner on forwardIntent (the sanitized copy of the incoming intent); if it fails, null is returned. Then, if forwardIntent.getSelector() != null, the selector is sanitized with sanitizeIntent(forwardIntent.getSelector()) and canForwardInner is run on the selector too; only if both pass is forwardIntent returned.",
      "packages/modules/IntentResolver java/src/com/android/intentresolver/IntentForwarderActivity.java:canForward - identical restructuring into canForward + canForwardInner (commit 3e7b8f353731), so the module copy and the internal framework copy behave the same.",
      "Net effect: the previously missing canForwardTo check on the outer intent is added, and the ACTION_CHOOSER rejection is now also applied to the selector."
     ],
     "completeness": [
      "Both the outer intent and its selector are now checked with the same canForwardInner logic, and the selector is sanitized before being checked (in the r7 version).",
      "Both known copies of the code are patched: the frameworks/base internal IntentForwarderActivity and the IntentResolver module IntentForwarderActivity.",
      "Only one level of selector is examined (forwardIntent.getSelector()); the material does not show whether a selector can itself carry a nested selector or how canForwardTo treats that.",
      "At android-security-15.0.0_r17 the code has been further changed: canForward/canForwardInner take a String resolvedType supplied by the caller instead of a ContentResolver, and the explicit sanitizeIntent(forwardIntent.getSelector()) call in canForward is no longer present. The two-check structure of the fix remains; whether selector sanitization moved elsewhere is not shown in the material."
     ],
     "behavior_changes": [
      "Intents whose outer intent fails IPackageManager.canForwardTo are now rejected (canForward returns null) even if their selector would pass.",
      "Intents whose selector has action ACTION_CHOOSER are now rejected; previously only the outer action was tested for ACTION_CHOOSER.",
      "The selector is now passed through sanitizeIntent before checking (r7 version).",
      "canForwardTo may be invoked twice per forwarded intent (outer intent and selector), with resolvedType computed per intent in the r7 version."
     ],
     "preconditions": [
      "A device with more than one user/profile where IntentForwarderActivity is used to forward intents across profiles (canForward is called with sourceUserId and targetUserId).",
      "The incoming intent carries a selector (forwardIntent.getSelector() != null), since without a selector the outer intent was already checked.",
      "Per index search, canForward's check is exercised through IntentForwarderActivity launches (see frameworks/base coretests IntentForwarderActivityTest, which verify canForwardTo is called); no other production callers were found by the index search, but that does not prove there are none."
     ],
     "per_major": {
      "14": "same fix in platform/packages/modules/IntentResolver 20cef18d8d9b (android-security-14.0.0_r19); same canForward/canForwardInner restructuring, and it also changes canForward from package-private static to public static. 7/8 lines of the patched region present at android-security-14.0.0_r29. The material does not show a frameworks/base commit for 14.",
      "15": "same fix: platform/frameworks/base 8612a293fb4a and platform/packages/modules/IntentResolver 3e7b8f353731 in android-security-15.0.0_r7. At android-security-15.0.0_r17 the fixed two-check structure is present, but the code has since been refactored to take a String resolvedType instead of a ContentResolver (3/8 lines of the original patched region match).",
      "16": "not affected (not listed by the bulletin); 3/8 lines of the patched region present at android-security-16.0.0_r8. The code itself is not shown in the material.",
      "17": "not affected (not listed by the bulletin); 3/8 lines of the patched region present at android-security-17.0.0_r1. The code itself is not shown in the material."
     },
     "backport_notes": [
      "Two independent copies must be patched in lockstep: frameworks/base core/java/com/android/internal/app/IntentForwarderActivity.java and the IntentResolver module java/src/com/android/intentresolver/IntentForwarderActivity.java. The diff is self-contained (no new dependencies) and applies to the ContentResolver-based signature used on 14 and 15 r7.",
      "If the target tree already uses the newer canForward(..., String resolvedType) signature (as at 15 r17), port the two canForwardInner calls onto that signature rather than reintroducing the ContentResolver parameter. The Android 14 commit also widens canForward to public, which does not matter for the fix."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether a frameworks/base commit was also applied on Android 14 and on Android 13 (the bulletin lists 13 but no 13 commit is in the material).",
      "Whether the explicit sanitizeIntent(selector) call removed by the later refactor (seen at 15 r17) was relocated into sanitizeIntent or elsewhere.",
      "What sanitizeIntent and IPackageManager.canForwardTo do internally; the material does not include them.",
      "The actual code state at android-security-16.0.0_r8 and 17.0.0_r1 beyond the 3/8 line match."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22434": {
     "cve": "CVE-2025-22434",
     "summary": "Adds isUserSetupComplete() and !keyguardOn guards to the Meta+I keyboard shortcut in PhoneWindowManager so the system Settings app can no longer be launched from the lock screen (or before setup completes) without user authentication.",
     "component": "platform/frameworks/base, services/core WindowManager policy (PhoneWindowManager keyboard shortcut handling)",
     "weakness": "Missing authorization check (keyguard state not checked before launching a privileged UI from a hardware keyboard shortcut)",
     "root_cause": "In services/core/java/com/android/server/policy/PhoneWindowManager.java, the KeyEvent.KEYCODE_I case of the system keyboard shortcut dispatcher called showSystemSettings() whenever the key was first pressed with Meta held (firstDown && event.isMetaPressed()). Some sibling cases, such as KEYCODE_APP_SWITCH, are gated on !keyguardOn. This path did not check the keyguard state or the user-setup state, so a Meta+I keypress while the device was locked could open Settings without user authentication (per the commit title).",
     "fix_analysis": [
      "services/core/java/com/android/server/policy/PhoneWindowManager.java, KEYCODE_I case in the system key handler: the condition changes from `firstDown && event.isMetaPressed()` to `firstDown && event.isMetaPressed() && isUserSetupComplete() && !keyguardOn`. When the keyguard is showing or setup is not complete, the shortcut falls through to `break` instead of calling showSystemSettings() and logging LAUNCH_SYSTEM_SETTINGS, so this case does not consume the event.",
      "Single one-line commit. The test reference is atest KeyGestureEventTests. The commit is marked Flag: EXEMPT bugfix, so no feature flag gates the change."
     ],
     "completeness": [
      "Covers only the KEYCODE_I (Meta+I) shortcut path to showSystemSettings() in PhoneWindowManager.",
      "The fix also adds an isUserSetupComplete() gate, which goes beyond the keyguard case described in the commit title.",
      "This commit does not change other Meta shortcuts in the same switch, for example KEYCODE_A (launchAssistAction) and KEYCODE_N (toggleNotificationPanel), which are visible in the surrounding context. The material does not show whether they need a keyguard gate.",
      "No change to showSystemSettings() itself or to any other callers of it. Only this shortcut entry point is guarded."
     ],
     "behavior_changes": [
      "Meta+I no longer opens Settings while the keyguard is showing. This case does not consume the key event (it falls to `break`).",
      "Meta+I no longer opens Settings before user setup is complete.",
      "The KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS log entry is no longer emitted in those two states."
     ],
     "preconditions": [
      "A physical or external keyboard (or another source of KeyEvents with the Meta modifier) delivers Meta+I to the device.",
      "The device is locked (keyguardOn true) when the key is pressed. The handler is reached in PhoneWindowManager's system-key interception path on the first key-down event."
     ],
     "per_major": {
      "14": "adapted fix: platform/frameworks/base 2880f0ab2dc6 in android-security-14.0.0_r19 adds the same two conditions to the KEYCODE_I case. The Android 14 code uses `down && event.isMetaPressed()` and `return key_consumed;` and has no logKeyboardSystemsEvent call. At android-security-14.0.0_r29 the patched region is fully present (4/4 lines).",
      "15": "same fix: platform/frameworks/base c4183cc97a18 in android-security-15.0.0_r7 (cherry-pick of d615298466085c4a88c6733804160e0c1ee7e31e). At android-security-15.0.0_r17 the patched region is fully present (4/4 lines).",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8, 3/4 lines of the patched region match. The material does not show the Android 16 form of this code.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1, only 1/4 lines of the patched region match. The material does not show where or whether an equivalent check exists."
     },
     "backport_notes": [
      "The change is a one-line condition edit. On branches where the KEYCODE_I case uses `down` instead of `firstDown` or returns `key_consumed` (as on Android 14), add `&& isUserSetupComplete() && !keyguardOn` to the existing condition instead of cherry-picking verbatim. The local keyguardOn is already used by the neighbouring KEYCODE_APP_SWITCH case. Verify that isUserSetupComplete() is available in the target branch."
     ],
     "confidence": "high",
     "unknowns": [
      "How Android 16 and 17 handle this shortcut. The patched region only partially matches there, and the bulletin lists them as not affected.",
      "Whether showSystemSettings() has other callers that lack a keyguard check.",
      "The material does not include the contents of KeyGestureEventTests, which the commit references."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22435": {
     "cve": "CVE-2025-22435",
     "summary": "Adds a check in avdt_msg_ind (system/stack/avdt/avdt_msg.cc) that rejects incoming AVDTP response/reject messages whose signal id does not match the locally pending command (p_ccb->p_curr_cmd). The response parser can therefore no longer be chosen by a peer-supplied signal id that does not match the pending command.",
     "component": "platform/packages/modules/Bluetooth, AVDTP signaling (system/stack/avdt)",
     "weakness": "Type confusion caused by missing validation of a remote response against local state (the commit message says this leads to out-of-bounds access)",
     "root_cause": "In avdt_msg_ind, for a message that is not a general reject, the signal id (sig) is parsed from the remote packet and only range-checked (sig != 0 and sig <= AVDT_SIG_MAX). For AVDT_MSG_TYPE_RSP messages the code then uses that peer-supplied sig to choose how to parse the body. This includes casting the cached p_ccb->p_proc_data to a signal-specific type (tAVDT_SEP_INFO* for AVDT_SIG_DISCOVER, AvdtpSepConfig* for AVDT_SIG_GETCAP/GET_ALLCAP) and using p_ccb->proc_param as num_seps. Nothing checked that the response's sig matched the pending command (p_ccb->p_curr_cmd->event). Per the commit message, a peer could therefore reply to a pending AVDT message with the wrong type, causing type confusion and eventually an out-of-bounds access.",
     "fix_analysis": [
      "system/stack/avdt/avdt_msg.cc:avdt_msg_ind: a new block is added inside the 'not a general reject' branch, after the existing sig range check. If msg_type is AVDT_MSG_TYPE_RSP or AVDT_MSG_TYPE_REJ, and either p_ccb->p_curr_cmd is nullptr or p_ccb->p_curr_cmd->event != sig, the function logs a warning and sets ok = false.",
      "Because ok is false, the later 'if (ok && !gen_rej)' block is skipped. That block selects the parser and casts p_ccb->p_proc_data based on sig, so type-specific parsing never happens for mismatched messages.",
      "Single commit, 7 lines added, no other files changed."
     ],
     "completeness": [
      "The check covers all RSP and REJ messages that take the non-general-reject path, which is where the shown code uses a sig read from the packet to choose a response parser.",
      "The general reject branch (AVDT_MSG_TYPE_REJ with p_buf->len == AVDT_LEN_GEN_REJ) is unchanged. It already takes sig from p_ccb->p_curr_cmd->event rather than from the packet.",
      "AVDT_MSG_TYPE_CMD messages are not subject to the new check. The parser selection shown uses p_proc_data only for RSP messages.",
      "The check compares only the signal id with the pending command, not the transaction label. The material does not show whether a label mismatch has any consequence.",
      "The new check also runs when sig is already out of range (ok is already false). This is redundant but harmless."
     ],
     "behavior_changes": [
      "Response or reject messages whose signal id differs from the pending command, or that arrive when no command is pending, are now rejected (ok = false) with a warning log ('Dropping msg with mismatched sig') and are not parsed into type-specific structures.",
      "Unsolicited or mismatched AVDTP responses from peers are no longer processed.",
      "Minor: the Android 15 log line passes a printf-style '%d' to log::warn, while the surrounding log::warn calls use '{}' placeholders, so the sig value may not appear in the log (cosmetic only)."
     ],
     "preconditions": [
      "A remote Bluetooth device with an established AVDTP signaling channel (tcid 0). Per index search, avdt_msg_ind is reached from avdt_ad_tc_data_ind in system/stack/avdt/avdt_ad.cc for data on the signaling channel.",
      "The remote device sends an RSP or REJ (non-general-reject) message whose signal id differs from the command held in p_ccb->p_curr_cmd, or sends one when no command is outstanding.",
      "The bulletin rates this as High severity EoP."
     ],
     "per_major": {
      "14": "adapted fix: the same validation block was applied in android-security-14.0.0_r19 (commit efa5f4ef386a), using the AVDT_TRACE_WARNING logging macro instead of log::warn. Present at android-security-14.0.0_r29 (2/2 lines).",
      "15": "same fix: commit 0a4fd20189fa in android-security-15.0.0_r7, using log::warn. Present at android-security-15.0.0_r17 (2/2 lines).",
      "16": "not affected (not listed by the bulletin). The patched region is present at android-security-16.0.0_r8 (2/2 lines).",
      "17": "not affected (not listed by the bulletin). The patched region is present at android-security-17.0.0_r1 (2/2 lines)."
     },
     "backport_notes": [
      "Self-contained hunk in avdt_msg_ind with no other dependencies. In trees that use AVDT_TRACE_WARNING, as Android 14 does, use the Android 14 form of the log line."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream out-of-bounds access site (which parser, which cast) is not shown. Only the parser selection code with p_proc_data casts is visible.",
      "Where p_proc_data and proc_param are set, and how their size relates to the pending command, is not shown.",
      "Whether Android 13 received a corresponding fix, and in which tag, is not shown, although the bulletin lists 13 as affected.",
      "Whether the response transaction label is validated elsewhere against the pending command is not shown.",
      "Whether the fix is also delivered through Bluetooth mainline module updates is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-22439": {
     "cve": "CVE-2025-22439",
     "summary": "DocumentsUI picker now runs the restricted-initial-URI check (shouldPreemptivelyRestrictRequestedInitialUri) on the top entry of a restored last-accessed DocumentStack and falls back to the default location if it is restricted, instead of restoring the stack unchecked.",
     "component": "platform/packages/apps/DocumentsUI, picker ActionHandler (last accessed stack restore)",
     "weakness": "Missing authorization/validation check on a restored persisted location (scoped storage restriction bypass)",
     "root_cause": "In src/com/android/documentsui/picker/ActionHandler.java, the picker restricts a caller-supplied initial URI via shouldPreemptivelyRestrictRequestedInitialUri(), which resolves the storage docId to a file path and matches it against PATTERN_RESTRICTED_INITIAL_PATH (/Android/data, /Android/obb, /Android/sandbox and subdirectories on external storage; on IOException it returns true). However onLastAccessedStackLoaded() took the last accessed DocumentStack for the calling package (from mLastAccessed.getLastAccessed or LoadLastAccessedStackTask) and, when non-null, applied it directly with mState.stack.reset(stack) and refreshCurrentRootAndDirectory() without any such check. A persisted stack whose current directory was inside a restricted path was therefore restored, bypassing the restriction applied to the initial URI.",
     "fix_analysis": [
      "src/com/android/documentsui/picker/ActionHandler.java:onLastAccessedStackLoaded adds an else-if branch between the null check and the restore: if shouldPreemptivelyRestrictRequestedInitialUri(stack.peek().getDocumentUri()) returns true, loadDefaultLocation() is called instead of mState.stack.reset(stack) / refreshCurrentRootAndDirectory().",
      "Both entry points that produce the stack, initLoadLastAccessedStack() (synchronous) and loadLastAccessedStack() (LoadLastAccessedStackTask callback), funnel into onLastAccessedStackLoaded, so both are covered by the single change.",
      "The check reuses the existing path-pattern logic, so its failure mode (IOException while resolving the path) is also restrictive, resulting in the default location."
     ],
     "completeness": [
      "Covers restore of a last-accessed stack in the picker ActionHandler for both the synchronous and async load paths, since both call onLastAccessedStackLoaded.",
      "Only the top entry of the stack (stack.peek()) is checked; parent entries in the restored DocumentStack are not individually validated. From the code alone it is not shown whether a non-restricted top with restricted ancestors is possible or meaningful.",
      "The code does not guard against stack.peek() returning null for an empty non-null stack; the material does not show whether getLastAccessed can return such a stack.",
      "The restriction is the same path-pattern check used for the initial URI (PATTERN_RESTRICTED_INITIAL_PATH).",
      "The fix does not change how the last accessed stack is persisted (mLastAccessed); it only adds a check at restore time."
     ],
     "behavior_changes": [
      "When the picker loads the calling package's last accessed location and it resolves to /Android/data, /Android/obb, /Android/sandbox or a subdirectory on external storage (or its path cannot be resolved), the picker now opens the default location instead of that directory.",
      "No new error, API or persisted-format change; purely stricter validation at restore time."
     ],
     "preconditions": [
      "The DocumentsUI picker is launched (picker ActionHandler) and initLoadLastAccessedStack() or loadLastAccessedStack() runs.",
      "A last accessed DocumentStack exists for the calling package and its top document resolves to a path matched by PATTERN_RESTRICTED_INITIAL_PATH.",
      "Per index search, no callers of onLastAccessedStackLoaded outside the picker ActionHandler were found; the function is private to that class."
     ],
     "per_major": {
      "14": "same fix: commit 1f283d5b87d0 in android-security-14.0.0_r19, identical 3-line hunk in ActionHandler.onLastAccessedStackLoaded (line offset differs); 5/5 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit a9fe0826e308 in android-security-15.0.0_r7 (cherry-pick of 0f051c7b87a5); 5/5 lines present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); the patched region is present (5/5 lines) at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); the patched region is present (5/5 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk; it relies on shouldPreemptivelyRestrictRequestedInitialUri() and loadDefaultLocation() in picker/ActionHandler.java, which the 14 and 15 diffs use. Android 13 is listed as affected but no 13 commit is in the material.",
      "DocumentsUI ships as a mainline module on recent releases, so devices may also receive this via a module update rather than the platform tag (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "How the last accessed stack comes to point into a restricted directory (persistence path / LastAccessedProvider behaviour) is not shown in the material.",
      "Whether the Android 13 branch received the fix and in which tag.",
      "Whether an empty non-null DocumentStack can reach onLastAccessedStackLoaded (stack.peek() null handling).",
      "How shouldPreemptivelyRestrictRequestedInitialUri treats URIs from roots or providers other than external storage (the start of the function is not shown).",
      "Under which conditions the picker calls initLoadLastAccessedStack or loadLastAccessedStack."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "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/2025-04-01",
  "latest_version": "1.3",
  "latest_revision": {
   "version": "1.3",
   "date": "February 25, 2026",
   "notes": "Updated CVE table"
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "April 7, 2025",
    "notes": "Bulletin published"
   },
   {
    "version": "1.1",
    "date": "April 8, 2025",
    "notes": "AOSP links added"
   },
   {
    "version": "1.2",
    "date": "April 22, 2025",
    "notes": "Updated CVE table"
   },
   {
    "version": "1.3",
    "date": "February 25, 2026",
    "notes": "Updated CVE table"
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [],
  "out_of_scope_rows": {
   "Arm components": 1,
   "Imagination Technologies": 9,
   "Kernel": 4,
   "MediaTek components": 4,
   "Qualcomm closed-source components": 6,
   "Qualcomm components": 7
  },
  "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."
 }
}