{
 "month": "2024-01",
 "generated": "2026-10-06T12:05+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 8,
    "certain": 8,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2023-21245",
     "osv_id": "ASB-A-222446076",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes the SIM PIN/PUK shortcut in SystemUI's KeyguardSecurityContainerController so the bouncer is finished only when the target user's security mode is None AND (the lockscreen is disabled for the current user or the target user's setup is incomplete), instead of when the security mode is None OR the lockscreen is disabled.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, but it changes the @Inject constructor signature of KeyguardSecurityContainerController; DeviceProvisionedController must be injectable in the SystemUI Dagger graph and test/manual instantiations must pass it. Bulletin lists 11, 12, 12L and 13 as affected but only the 14 commit is 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."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "710dfb62f479c75624f3d7524d1b32dab54c3200",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-0016",
     "osv_id": "ASB-A-279169188",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds length validation to four HCI BLE event parsers in the Bluetooth stack (BIGInfo advertising report, LE advertising report, LE extended advertising report, LE data length change) so that fixed-size fields and per-report variable data are only read when the received packet actually contains them.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit with four independent hunks that use LOG_ERROR / LOG(ERROR) in btm_ble_gap.cc and btu_hcif.cc. The bulletin also lists Android 13 as affected, but the material includes no Android 13 commit."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "b32ccd826436db5a72c473821c77c42ece580075",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2024-0017",
     "osv_id": "ASB-A-285142084",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Camera2's CameraActivity.shouldUseNoOpLocation() now returns true (use a no-op location) for a capture intent with no calling package (launched via startActivity rather than startActivityForResult). Otherwise it evaluates Camera2's own package permissions, instead of returning false whenever getCallingPackage() was null.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, self-contained change. It needs isCaptureIntent() and mAppContext in CameraActivity, which the diff uses without adding them."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Camera2",
       "sha": "6dff83d2279fdb4e64b5fbdd4f9b123b2758f217",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Camera2"
     ]
    },
    {
     "cve": "CVE-2024-0018",
     "osv_id": "ASB-A-300476626",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rewrites the main loop condition in ColorConverter::convertYUV420Planar16ToY410 from `x < src.cropWidth() - 3` to `x + 3 < src.cropWidth()`, so a crop width smaller than 3 no longer wraps the unsigned loop bound and lets the loop run past the source and destination buffers.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit change (2 insertions, 1 deletion) with no visible dependencies. It applies to any branch whose non-NEON convertYUV420Planar16ToY410 still has `x < src.cropWidth() - 3`. If the surrounding code differs, apply the same transformation (move the constant to the index side) instead of the literal hunk."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/av",
       "sha": "a424120a625cb5ad08b01c43cac75d65957b1889",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/av"
     ]
    },
    {
     "cve": "CVE-2024-0019",
     "osv_id": "ASB-A-294104969",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "System UI's AppOpsControllerImpl now queries AppOpsManager.getPackagesForOps for already-running ops when it starts listening, and both SystemStatusAnimationScheduler implementations no longer drop force-visible (privacy) status events during the early-uptime window, so privacy indicators reappear after a System UI restart.",
      "confidence": "high",
      "backport_notes": [
       "For 12/12L/13 (listed as affected, but no commit shown here): apply the scheduler change to whichever SystemStatusAnimationScheduler file(s) exist on that branch (14 has both Impl and LegacyImpl), and verify that the branch's onOpActiveChanged signature and the OpEntry/AttributedOpEntry APIs used by fetchCurrentActiveOps match before porting."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f747b1e48830db25aa3deb32b9d4e4f14f9a8177",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2024-0020",
     "osv_id": "ASB-A-299614635",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a shared RingtonePreference.isValidRingtoneUri() check that rejects picked ringtone content URIs whose embedded userId (content://{userId}@...) belongs to a user that is not a permitted profile of the current user, and applies it in DefaultRingtonePreference.onSaveRingtone and NotificationSoundPreference.onActivityResult.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. It relies on UserManager.getUserProperties/UserProperties.isMediaSharedWithParent, UserManager.isProfile, UserManager.getProfileParent and ContentProvider.getUserIdFromUri being available in the target framework.",
       "The unit test switches the ContentResolver mock to ContentResolver.wrap(mock(ContentInterface.class)) and stubs isDefaultRingtone and UserManager. Keep those test changes together with the source change."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "460d0b5a63d123abb48adf97d25875bd1da7acbd",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2024-0021",
     "osv_id": "ASB-A-282934003",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds an early exit to Settings' NotificationAccessConfirmationActivity.onCreate so that, when the activity is running in a managed (work) profile, it shows a toast and finishes instead of presenting the dialog that can enable a notification listener service.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. It depends on DevicePolicyManager.getResources() and DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS. It also depends on the Settings string R.string.notification_settings_work_profile, which the diff does not add, so the string must already exist in the target branch. The DevicePolicyResources string override mechanism was introduced in Android 13 (general platform knowledge).",
       "On branches without DevicePolicyResources, the toast text can be replaced with a plain getString(R.string.notification_settings_work_profile). Keep the isManagedProfile() early exit, which is the part that blocks the flow."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "0fb3408e879230cea2f3c06b6b376e35506170d3",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 53ea491d276f",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2024-0023",
     "osv_id": "ASB-A-283099444",
     "source": "osv",
     "announced": "2024-01-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ConvertRGBToPlanarYUV in Codec2BufferUtils.cpp now bounds its conversion loops by the source view's crop rectangle (src.crop().width/height) instead of the view's full dimensions (src.width()/height()).",
      "confidence": "medium",
      "backport_notes": [
       "This is a single two-line change with no dependencies shown. Apply it to the two for-loops in ConvertRGBToPlanarYUV in media/codec2/sfplugin/utils/Codec2BufferUtils.cpp, and check that callers size the destination from the crop dimensions."
      ],
      "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_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r4",
      "date": "2024-01-03",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/av",
       "sha": "4f9fa2df202d2731451f8f76bb3b47ed6697fc7f",
       "tag": "android-security-14.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/av"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r4"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "295335110"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "74d17f63ec61216187f11d78d00c3f2359f5e5eb",
       "subject": "[CDM] Validate component name length before requesting notification access.",
       "tag": "android-security-14.0.0_r4",
       "merged": "2023-11-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r4"
     ],
     "tag": "android-security-14.0.0_r4"
    }
   ],
   "coverage": {
    "n_ingested_tags": 30,
    "first_ingested_tag": "android-security-14.0.0_r1",
    "latest_ingested_tag": "android-security-14.0.0_r30",
    "window_base_tag": "android-14.0.0_r1",
    "window_start": "2023-10-04",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2023-21245": {
     "cve": "CVE-2023-21245",
     "summary": "Changes the SIM PIN/PUK shortcut in SystemUI's KeyguardSecurityContainerController so the bouncer is finished only when the target user's security mode is None AND (the lockscreen is disabled for the current user or the target user's setup is incomplete), instead of when the security mode is None OR the lockscreen is disabled.",
     "component": "platform/frameworks/base, SystemUI keyguard (packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java)",
     "weakness": "unknown",
     "root_cause": "In KeyguardSecurityContainerController, the SimPin/SimPuk case (\"Shortcut for SIM PIN/PUK to go to directly to user's security screen or home\") set finish = true (BOUNCER_DISMISS_SIM) when securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser()). The condition did not consider whether the target user's setup was complete. Per the commit message, the intent is that when user setup is not complete the device should not go to the lockscreen even if the lockscreen is not disabled. The diff also changes the OR to an AND; the material does not state which of these changes addresses the vulnerability.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java, SimPin/SimPuk case: computes boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser()) || !mDeviceProvisionedController.isUserSetup(targetUserId), and changes the finish condition to (securityMode == SecurityMode.None && isLockscreenDisabled).",
      "Same file: adds a DeviceProvisionedController dependency (new import, final field mDeviceProvisionedController, new @Inject constructor parameter placed after UserSwitcherController and assigned in the constructor) so the controller can query isUserSetup(targetUserId).",
      "packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java: updates the constructor call to pass mock(DeviceProvisionedController.class); no new test method appears in the shown diff despite the commit message saying 'add Unit test'."
     ],
     "completeness": [
      "Only the SimPin/SimPuk shortcut in the security-mode switch is changed; no other keyguard dismissal path is touched in this commit.",
      "isLockScreenDisabled() is still evaluated for KeyguardUpdateMonitor.getCurrentUser() while getSecurityMode() and isUserSetup() use targetUserId; the fix does not unify these user ids, but the AND now requires the target user's security mode to be None.",
      "The new isUserSetup(targetUserId) term only affects the result when securityMode is None; it cannot by itself cause the shortcut to finish when the target user's security mode is not None.",
      "The shown test diff only adapts the constructor call; coverage of the new condition is not visible in the material."
     ],
     "behavior_changes": [
      "After SIM PIN/PUK handling, the shortcut no longer finishes the bouncer for a target user whose security mode is not None, even if isLockScreenDisabled(currentUser) returns true.",
      "When the target user's security mode is None and user setup is incomplete, the shortcut finishes the bouncer even if the lockscreen is not flagged disabled (commit test: wipe device, auth SIM PIN in setup, observe no lockscreen).",
      "When the target user's security mode is None, user setup is complete and the lockscreen is not disabled, the shortcut no longer sets finish; the code path taken instead is not shown in the material.",
      "KeyguardSecurityContainerController's @Inject constructor gains a DeviceProvisionedController parameter; manual construction (e.g. tests) must be updated."
     ],
     "preconditions": [
      "Device has a SIM requiring PIN or PUK so the bouncer is in SecurityMode.SimPin or SimPuk, reaching the SimPin/SimPuk case in KeyguardSecurityContainerController.",
      "The exact state needed to reach the flaw before the fix is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 710dfb62f479 (DO NOT MERGE, cherry-pick of 448f1b18a783) in android-security-14.0.0_r4; at android-security-14.0.0_r29 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin; patched region (8/8 lines) present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; patched region (8/8 lines) present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; patched region (8/8 lines) present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, but it changes the @Inject constructor signature of KeyguardSecurityContainerController; DeviceProvisionedController must be injectable in the SystemUI Dagger graph and test/manual instantiations must pass it. Bulletin lists 11, 12, 12L and 13 as affected but only the 14 commit is in the material."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which part of the condition change (OR to AND, or the added isUserSetup term) addresses the vulnerability, and the exact trigger behind the High EoP rating.",
      "The enclosing method name is not visible in the diff hunk header.",
      "The code path taken when the shortcut does not finish.",
      "The fix commits for Android 11, 12, 12L and 13 and any differences in those branches.",
      "Whether a dedicated unit test for the new condition exists beyond the constructor change 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-0016": {
     "cve": "CVE-2024-0016",
     "summary": "Adds length validation to four HCI BLE event parsers in the Bluetooth stack (BIGInfo advertising report, LE advertising report, LE extended advertising report, LE data length change) so that fixed-size fields and per-report variable data are only read when the received packet actually contains them.",
     "component": "platform/packages/modules/Bluetooth, system/stack BTM / BTU HCI event parsing (btm_ble_gap.cc, btu_hcif.cc)",
     "weakness": "Missing / insufficient bounds check on HCI event payload parsing (out-of-bounds read)",
     "root_cause": "Several HCI LE event handlers read fields with STREAM_TO_* macros without first checking the payload length. btm_ble_biginfo_adv_report_rcvd read 19 bytes of fixed fields with no check on param_len. btu_ble_data_length_change_evt read 8 bytes with no evt_len >= 8 check. btm_ble_process_adv_pkt and btm_ble_process_ext_adv_pkt read num_reports without checking data_len >= 1, and their per-report checks were pointer comparisons against data + data_len. The commit describes the result as missing bounds checks that could lead to out-of-bounds access.",
     "fix_analysis": [
      "system/stack/btm/btm_ble_gap.cc:btm_ble_biginfo_adv_report_rcvd: adds `if (param_len < 19) { LOG_ERROR(\"Insufficient data\"); return; }` before the STREAM_TO_* sequence. 19 is the sum of the fixed fields (sync_handle 2, num_bises 1, nse 1, iso_interval 2, bn/pto/irc 3, max_pdu 2, sdu_interval 3, max_sdu 2, phy/framing/encryption 3).",
      "system/stack/btm/btm_ble_gap.cc:btm_ble_process_ext_adv_pkt: adds a running `size_t bytes_to_process` counter. It checks data_len >= 1 before reading num_reports. For each report it adds 24 (the fixed report header) and checks data_len < bytes_to_process. After reading pkt_data_len it adds that length and checks again. These integer checks replace the old pointer comparisons (`p + extended_report_header_size > data + data_len`, `p > data + data_len`).",
      "system/stack/btm/btm_ble_gap.cc:btm_ble_process_adv_pkt: same scheme. It checks data_len >= 1 for num_reports. Per report it adds 9 header bytes (the old code used a 10-byte constant in a pointer check). After reading pkt_data_len it adds pkt_data_len + 1 so the trailing RSSI byte is covered. This replaces `p > data + data_len - sizeof(rssi)`.",
      "system/stack/btu/btu_hcif.cc:btu_ble_data_length_change_evt: adds `if (evt_len < 8) { LOG_ERROR(\"Event packet too short\"); return; }` before reading handle, tx_data_len, TxTimer (skipped) and rx_data_len (2 bytes each)."
     ],
     "completeness": [
      "Covers the four parsers in the diff. All fixed fields of the BIGInfo report and the data length change event are length-checked. Both advertising report parsers now validate the num_reports byte, each report header, the variable advertising data and, for legacy reports, the RSSI byte, using integer length counters.",
      "When a report is too short, the loop returns. Reports handled in earlier iterations of the same event have already been processed.",
      "This commit does not change the other HCI event handlers visible in the btu_hcif.cc context (btu_hcif_inquiry_comp_evt, btu_hcif_authentication_comp_evt, btu_hcif_rmt_name_request_comp_evt). btu_hcif_rmt_name_request_comp_evt subtracts 1 + BD_ADDR_LEN from evt_len with no length check visible in the function. The material does not show whether its callers check the length."
     ],
     "behavior_changes": [
      "BIGInfo advertising reports shorter than 19 bytes and LE data length change events shorter than 8 bytes are now dropped with LOG_ERROR instead of being parsed.",
      "LE advertising and extended advertising report events with an empty payload, a short report header, or advertising data (plus RSSI for legacy reports) running past data_len are dropped with LOG(ERROR). The old BTM_TRACE_ERROR messages are replaced by LOG(ERROR) messages with new text."
     ],
     "preconditions": [
      "The host receives HCI LE events (LE Advertising Report, LE Extended Advertising Report, LE BIGInfo Advertising Report, LE Data Length Change) whose payload is shorter than the fields the host parses.",
      "For btm_ble_process_adv_pkt and btm_ble_process_ext_adv_pkt, a BLE scan must be active. Both functions return early if !btm_cb.ble_ctr_cb.is_ble_scan_active().",
      "The material does not show which dispatch path calls these parsers on Android 14. Per index search, the only related result for btm_ble_process_adv_pkt was the GD shim (system/main/shim/btm.cc, Btm::ScanningCallbacks::OnScanResult), which calls btm_ble_process_adv_pkt_cont with already parsed data, not the patched parser."
     ],
     "per_major": {
      "14": "same fix: commit b32ccd826436 in android-security-14.0.0_r4 (Change-Id If7752f6edd749d6d5a4bb957b4824c22b5602737, cherry-picked from internal commits 66e2be05 and 5d1a3feb). At android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin. At android-security-15.0.0_r17 only 2/8 lines of the patched region are present. The material does not show how the equivalent parsing is handled there.",
      "16": "not affected per the bulletin. At android-security-16.0.0_r8 only 2/8 lines of the patched region are present. The material does not show how the equivalent parsing is handled there.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1 only 2/8 lines of the patched region are present. The material does not show how the equivalent parsing is handled there."
     },
     "backport_notes": [
      "Single self-contained commit with four independent hunks that use LOG_ERROR / LOG(ERROR) in btm_ble_gap.cc and btu_hcif.cc. The bulletin also lists Android 13 as affected, but the material includes no Android 13 commit."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the HCI dispatch path on Android 14 that calls btm_ble_process_adv_pkt, btm_ble_process_ext_adv_pkt, btm_ble_biginfo_adv_report_rcvd or btu_ble_data_length_change_evt.",
      "The material does not show whether length validation happens upstream of these handlers (for example in the HCI layer).",
      "The material does not show what the existing early return in btu_ble_data_length_change_evt (before the new check) tests.",
      "The Android 13 fix commit is not included in the material.",
      "The material does not show how Android 15 and later handle the equivalent events. Most of the patched region is absent there."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2024-0017": {
     "cve": "CVE-2024-0017",
     "summary": "Camera2's CameraActivity.shouldUseNoOpLocation() now returns true (use a no-op location) for a capture intent with no calling package (launched via startActivity rather than startActivityForResult). Otherwise it evaluates Camera2's own package permissions, instead of returning false whenever getCallingPackage() was null.",
     "component": "platform/packages/apps/Camera2, CameraActivity location/EXIF handling",
     "weakness": "Missing permission check (information disclosure of location data)",
     "root_cause": "shouldUseNoOpLocation() decides whether captured images get valid location in EXIF by checking the calling package's ACCESS_FINE_LOCATION grant. It relied on getCallingPackage(), which per the code comment is only set when the activity is started through startActivityForResult. When it returned null, the function returned false (do not use no-op location). A capture intent sent with plain startActivity also gives a null calling package. Per the commit message, Camera2 therefore unconditionally granted location to such starters, whatever location permission they held.",
     "fix_analysis": [
      "src/com/android/camera/CameraActivity.java:shouldUseNoOpLocation(): when getCallingPackage() is null and isCaptureIntent() is true, return true (use no-op location).",
      "src/com/android/camera/CameraActivity.java:shouldUseNoOpLocation(): when getCallingPackage() is null and it is not a capture intent, set callingPackage to mAppContext.getPackageName(). The existing requestedPermissions / REQUESTED_PERMISSION_GRANTED loop then evaluates Camera2's own ACCESS_FINE_LOCATION grant instead of short-circuiting to false."
     ],
     "completeness": [
      "Covers the null-calling-package branch for both the capture-intent and the non-capture-intent cases. The non-null calling package path (startActivityForResult) is unchanged and still checks the caller's ACCESS_FINE_LOCATION grant.",
      "The new protection depends entirely on isCaptureIntent(). Its implementation and the intent actions it covers are not in the material, so the code shown does not tell whether every externally triggerable capture action is treated as a capture intent.",
      "The code after the permission loop (including the behaviour when packageInfo is null because getPackageInfo throws) is not shown, so the fallback for an unresolvable package is unknown."
     ],
     "behavior_changes": [
      "When Camera2 is started with a capture intent via startActivity (no calling package), shouldUseNoOpLocation() now returns true, per the commit message so that location is not passed for this case.",
      "When Camera2 is started without a capture intent and with no calling package, the result now depends on Camera2's own ACCESS_FINE_LOCATION grant (the function returns false if it is granted) instead of always returning false."
     ],
     "preconditions": [
      "Camera2 (packages/apps/Camera2) handles the capture intent on the device.",
      "The activity is started with a capture intent through startActivity, so getCallingPackage() returns null.",
      "shouldUseNoOpLocation() is private to CameraActivity. Per index search, no other chunk mentioning it was found."
     ],
     "per_major": {
      "14": "same fix; commit 6dff83d2279f in android-security-14.0.0_r4, and all 5 patched lines are present at android-security-14.0.0_r29.",
      "15": "not affected per bulletin; the patched region is fully present (5/5 lines) at android-security-15.0.0_r17.",
      "16": "not affected per bulletin; 3/5 patched lines present at android-security-16.0.0_r8. The material does not show why the region differs.",
      "17": "not affected per bulletin; 3/5 patched lines present at android-security-17.0.0_r1. The material does not show why the region differs."
     },
     "backport_notes": [
      "Single-file, self-contained change. It needs isCaptureIntent() and mAppContext in CameraActivity, which the diff uses without adding them."
     ],
     "confidence": "high",
     "unknowns": [
      "Implementation of isCaptureIntent() and the set of intent actions it recognises.",
      "How the result of shouldUseNoOpLocation() is consumed, and whether location tagging settings or Camera2's permissions also gate it.",
      "The return value after the permission loop and the fallback when getPackageInfo fails.",
      "What changed in the patched region in Android 16/17 (only 3/5 lines match)."
     ],
     "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-0018": {
     "cve": "CVE-2024-0018",
     "summary": "Rewrites the main loop condition in ColorConverter::convertYUV420Planar16ToY410 from `x < src.cropWidth() - 3` to `x + 3 < src.cropWidth()`, so a crop width smaller than 3 no longer wraps the unsigned loop bound and lets the loop run past the source and destination buffers.",
     "component": "platform/frameworks/av, libstagefright color conversion (media/libstagefright/colorconversion/ColorConverter.cpp)",
     "weakness": "Integer underflow in an unsigned loop bound leading to out-of-bounds read/write (missing bounds check)",
     "root_cause": "The non-NEON implementation of ColorConverter::convertYUV420Planar16ToY410 has a per-row loop that processes 4 pixels at a time. That loop was bounded by `x < src.cropWidth() - 3`, with x a size_t. If the crop width is below 3, the subtraction gives a huge unsigned bound, so the loop condition stays true. The loop then keeps reading 32-bit words from ptr_u, ptr_v, ptr_ytop and ptr_ybot and writing 32-bit words to dst_top and dst_bot past the bounds of the source and destination bitmaps. The commit message calls this an overflow for an \"unsupported cropwidth\".",
     "fix_analysis": [
      "media/libstagefright/colorconversion/ColorConverter.cpp:ColorConverter::convertYUV420Planar16ToY410 (the `#if !USE_NEON_Y410` variant): the loop condition changes from `x < src.cropWidth() - 3` to `x + 3 < src.cropWidth()`. The subtraction that could wrap is replaced by an addition on the loop index. x starts at 0 and is always a multiple of 4, so `x + 3` cannot overflow. For cropWidth() < 4 the loop body is never entered.",
      "A comment is added explaining why `x + 3` is safe. The trailing code that handles the remaining pixels (at most 2, per the code comment) through `if (x < src.cropWidth())` is unchanged. For tiny widths it now receives x == 0."
     ],
     "completeness": [
      "The fix covers only the 4-pixels-per-iteration loop in the non-NEON ColorConverter::convertYUV420Planar16ToY410. It adds no higher-level validation of cropWidth(). Widths of 1 or 2 go to the existing tail path, and the code comment says that path assumes even alignment.",
      "The pointer arithmetic for src_u/src_v and the row loop (`y += 2` over cropHeight()) are untouched. The diff does not show whether odd heights or other small-dimension cases are validated elsewhere.",
      "The material does not show the USE_NEON_Y410 variant of this function, so we cannot see whether it has an equivalent bound.",
      "A structurally identical loop (`for (; x < width - 3; x += 4)`) exists in frameworks/av/media/codec2/components/base/SimpleC2Component.cpp convertYUV420Planar16ToY410 (per index search). This commit does not touch it (see potential_variants)."
     ],
     "behavior_changes": [
      "For crop widths below 4, the 4-pixel loop no longer runs and only the tail path does. For widths of 3 or more, the two conditions are equivalent, so iteration is unchanged. No new error codes or API changes."
     ],
     "preconditions": [
      "A 16-bit YUV420 planar to Y410 conversion is requested through ColorConverter on a build where USE_NEON_Y410 is not set (the patched function is inside `#if !USE_NEON_Y410`).",
      "The source BitmapParams has a crop width smaller than 3, which the commit message calls an unsupported crop width. The material does not show which callers can supply such dimensions.",
      "Per index search, the only convertYUV420Planar16ToY410 users found in Android 14 are separate helper functions in frameworks/av/media/codec2/components/base/SimpleC2Component.cpp. They implement their own copy instead of calling the ColorConverter method. The index returned no direct caller of the ColorConverter method, so its concrete entry points are not established."
     ],
     "per_major": {
      "14": "Same fix: commit a424120a625c in android-security-14.0.0_r4 (cherry-pick of the internal change, Merged-In I8631426188af3c5f9b6c1ff6a0039254c252f733). At android-security-14.0.0_r29, 3 of 4 lines of the patched region are present, including the new loop condition and comment.",
      "15": "Not affected per the bulletin. At android-security-15.0.0_r17, 3 of 4 lines of the patched region are present.",
      "16": "Not affected per the bulletin. At android-security-16.0.0_r8, 3 of 4 lines of the patched region are present.",
      "17": "Not affected per the bulletin. At android-security-17.0.0_r1, 3 of 4 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-commit change (2 insertions, 1 deletion) with no visible dependencies. It applies to any branch whose non-NEON convertYUV420Planar16ToY410 still has `x < src.cropWidth() - 3`. If the surrounding code differs, apply the same transformation (move the constant to the index side) instead of the literal hunk."
     ],
     "confidence": "high",
     "unknowns": [
      "Which caller or input path can produce a crop width smaller than 3 that reaches this function (the material shows no direct caller of the ColorConverter method).",
      "Whether the USE_NEON_Y410 build variant of the function has the same loop bound.",
      "Which line of the patched region is missing in the latest tags (3 of 4 lines present).",
      "Whether the SimpleC2Component.cpp copy of the loop was fixed in a separate change not included in this material."
     ],
     "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-0019": {
     "cve": "CVE-2024-0019",
     "summary": "System UI's AppOpsControllerImpl now queries AppOpsManager.getPackagesForOps for already-running ops when it starts listening, and both SystemStatusAnimationScheduler implementations no longer drop force-visible (privacy) status events during the early-uptime window, so privacy indicators reappear after a System UI restart.",
     "component": "platform/frameworks/base, SystemUI (appops privacy indicators / status bar event scheduler)",
     "weakness": "Missing state synchronization on startup: active app ops were tracked only via change callbacks, so pre-existing active ops were never surfaced (privacy indicator not shown); plus an early-uptime filter that discarded privacy events.",
     "root_cause": "AppOpsControllerImpl.setListening(true) only registered mAppOps.startWatchingActive/startWatchingNoted and audio-recording callbacks. It never fetched the ops that were already active at that moment, so if System UI was (re)started while an app was using camera, microphone or location, the controller's active-op list stayed empty and the privacy chip/dot was not shown until the op state changed again. Independently, SystemStatusAnimationSchedulerImpl.onStatusEvent and SystemStatusAnimationSchedulerLegacyImpl.onStatusEvent returned early whenever isTooEarly() was true, including for privacy events (event.forceVisible), so a privacy event delivered shortly after process start was silently dropped.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java:setListening: when listening becomes true, calls new fetchCurrentActiveOps() before registering startWatchingActive/startWatchingNoted.",
      "AppOpsControllerImpl.java:fetchCurrentActiveOps (new): calls mAppOps.getPackagesForOps(OPS); for every PackageOps -> OpEntry -> AttributedOpEntry whose isRunning() is true, invokes onOpActiveChanged(entry.getOpStr(), op.getUid(), op.getPackageName(), attributionTag, active=true, AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE), reusing the existing live-callback path to populate the active list and notify callbacks.",
      "packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt:onStatusEvent: early-return condition changed from `isTooEarly() || !isImmersiveIndicatorEnabled()` to `(isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()`, so force-visible events are processed even before MIN_UPTIME.",
      "packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt:onStatusEvent: identical condition change for the legacy scheduler.",
      "Tests: AppOpsControllerTest adds startListening_fetchesCurrentActive_* and addCallback_existingCallbacksNotifiedOfCurrentActive (regression tests for b/294104969); SystemStatusAnimationSchedulerImplTest adds testPrivacyStatusEvent_beforeSystemUptime_stillDisplayed and an advancePastMinUptime parameter."
     ],
     "completeness": [
      "Covers active ops in AppOpsControllerImpl.OPS only (whatever that list contains); ops not in OPS are not fetched, matching the existing watch scope.",
      "Only active (isRunning) ops are reconstructed; previously noted ops (startWatchingNoted path) are not back-filled on startup.",
      "Attribution flags and chain ID are defaulted to ATTRIBUTION_FLAGS_NONE / ATTRIBUTION_CHAIN_ID_NONE for fetched ops (the diff comment notes AppOpsManager offers no way to retrieve them from an OpEntry); whether onOpActiveChanged handles fetched ops differently as a result is not shown.",
      "Both scheduler implementations present in Android 14 (SystemStatusAnimationSchedulerImpl and SystemStatusAnimationSchedulerLegacyImpl) are patched; the isImmersiveIndicatorEnabled() gate is unchanged and still suppresses events, including privacy ones, when the device config disables the indicator.",
      "The fetch runs synchronously inside setListening(true); the material does not show which thread calls setListening."
     ],
     "behavior_changes": [
      "setListening(true) now calls getPackagesForOps and may synchronously fire onOpActiveChanged for each running attributed op, so callbacks see pre-existing active ops.",
      "Privacy (forceVisible) status events are no longer discarded during the first MIN_UPTIME after System UI process start; non-forceVisible events are still dropped in that window.",
      "Privacy chip/dot now reappears after a System UI restart while recording or the camera is in use (per commit test steps)."
     ],
     "preconditions": [
      "An app holds an active op in AppOpsControllerImpl.OPS (e.g. camera, microphone, location per the commit tests) at the moment System UI starts or restarts listening.",
      "For the scheduler part: the privacy StatusEvent reaches onStatusEvent before isTooEarly() becomes false (uptime below MIN_UPTIME since process start) and isImmersiveIndicatorEnabled() is true.",
      "Index search for setListening callers returned only unrelated QS/Doze setListening methods; the actual caller of AppOpsControllerImpl.setListening is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit f747b1e48830 in android-security-14.0.0_r4 (cherry-pick of 084a7afb4bb4); all 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "not affected per bulletin; patched code (8/8 lines) present at android-security-15.0.0_r17.",
      "16": "not affected per bulletin; patched code (8/8 lines) present at android-security-16.0.0_r8.",
      "17": "not affected per bulletin; patched code (8/8 lines) present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "For 12/12L/13 (listed as affected, but no commit shown here): apply the scheduler change to whichever SystemStatusAnimationScheduler file(s) exist on that branch (14 has both Impl and LegacyImpl), and verify that the branch's onOpActiveChanged signature and the OpEntry/AttributedOpEntry APIs used by fetchCurrentActiveOps match before porting."
     ],
     "confidence": "high",
     "unknowns": [
      "Fix commits for Android 12, 12L and 13 are not included in the material.",
      "Whether the onOpActiveChanged signature and OpEntry.getAttributedOpEntries()/AttributedOpEntry.isRunning() are available on 12/12L/13.",
      "Which component calls AppOpsControllerImpl.setListening and on which thread; the index results shown are unrelated setListening methods.",
      "Contents of AppOpsControllerImpl.OPS and whether onOpActiveChanged treats ATTRIBUTION_FLAGS_NONE/CHAIN_ID_NONE differently from real flags (not shown)."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2024-0020": {
     "cve": "CVE-2024-0020",
     "summary": "Adds a shared RingtonePreference.isValidRingtoneUri() check that rejects picked ringtone content URIs whose embedded userId (content://{userId}@...) belongs to a user that is not a permitted profile of the current user, and applies it in DefaultRingtonePreference.onSaveRingtone and NotificationSoundPreference.onActivityResult.",
     "component": "platform/packages/apps/Settings, ringtone/notification sound preferences (RingtonePreference, DefaultRingtonePreference, NotificationSoundPreference)",
     "weakness": "Missing authorization check on cross-user content URIs (Settings persisted a URI referencing another user's content without validating the user relationship)",
     "root_cause": "Ringtone URIs returned from the ringtone picker were accepted with too little validation. DefaultRingtonePreference.onSaveRingtone only resolved the mime type through mUserContext.getContentResolver().getType() and checked that it was audio/* or application/ogg. It never looked at the userId encoded in the URI (content://{userId}@...), so a URI pointing at content of an unrelated user could be stored through RingtoneManager.setActualDefaultRingtoneUri. NotificationSoundPreference.onActivityResult did no validation at all before setRingtone(uri) and callChangeListener(uri). The bulletin classifies the result as information disclosure (ID), High.",
     "fix_analysis": [
      "src/com/android/settings/RingtonePreference.java: adds a public isDefaultRingtone(Uri) (null or RingtoneManager.isDefault) and a protected isValidRingtoneUri(Uri). isValidRingtoneUri accepts default URIs and android.resource scheme URIs early. Otherwise it requires a non-null mime type from mUserContext.getContentResolver().getType() that is audio/*, application/ogg or application/x-flac.",
      "RingtonePreference.isValidRingtoneUri then extracts the user via ContentProvider.getUserIdFromUri(ringtoneUri, mUserId). If that differs from mUserId, it requires, via UserManager: isSameProfileGroup(mUserId, userIdFromUri); that getProfileParent of the profile side (chosen by userManager.isProfile()) equals the parent side; that the parent has no DISALLOW_SHARE_INTO_MANAGED_PROFILE restriction; and that the profile is a managed profile or has UserProperties.isMediaSharedWithParent(). Any failure logs and returns false.",
      "src/com/android/settings/DefaultRingtonePreference.java:onSaveRingtone: the inline mime-type checks are replaced by a single isValidRingtoneUri(ringtoneUri) call. On failure the URI is ignored and setActualDefaultRingtoneUri is not called. The null-URI early path (silence) is unchanged.",
      "src/com/android/settings/notification/app/NotificationSoundPreference.java:onActivityResult: the picked EXTRA_RINGTONE_PICKED_URI must now pass isValidRingtoneUri(uri) before setRingtone/callChangeListener. Invalid URIs are logged and dropped.",
      "tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java: adds cases for an other-user URI not in the same profile group, a non-managed profile, a managed profile (allowed) and a default URI (allowed)."
     ],
     "completeness": [
      "Covers both RingtonePreference subclasses shown in the material: DefaultRingtonePreference and NotificationSoundPreference.",
      "The userId check runs only after the mime-type check succeeds. android.resource URIs and default URIs return true before either check, so they are never compared on userId.",
      "Cross-user URIs are still accepted for managed profiles and for profiles with isMediaSharedWithParent, unless DISALLOW_SHARE_INTO_MANAGED_PROFILE is set on the parent. This is a deliberate allow-list rather than a blanket rejection.",
      "The null-URI path in DefaultRingtonePreference.onSaveRingtone skips validation by design (silence).",
      "The check lives only in the Settings app. It does not change RingtoneManager or the ringtone picker. The material does not show whether other callers of RingtoneManager.setActualDefaultRingtoneUri exist outside these two preferences."
     ],
     "behavior_changes": [
      "Picked ringtone URIs that reference a different user which is not a managed or media-sharing profile in the same profile group are now ignored with only a Log.e. This applies to default ringtones and to notification sounds.",
      "application/x-flac is now accepted as a ringtone mime type (previously only audio/* and application/ogg).",
      "android.resource URIs are now accepted without a getType() lookup. Previously, a null mime type for such a URI caused rejection in DefaultRingtonePreference.",
      "NotificationSoundPreference.onActivityResult now validates mime type and user. Previously it accepted any URI, so non-audio or unresolvable URIs are now dropped there too.",
      "Any cross-user URI between a parent and its profile is refused when DISALLOW_SHARE_INTO_MANAGED_PROFILE is set on the parent user."
     ],
     "preconditions": [
      "A ringtone picker activity result delivered to Settings carrying RingtoneManager.EXTRA_RINGTONE_PICKED_URI, reaching DefaultRingtonePreference.onSaveRingtone or NotificationSoundPreference.onActivityResult.",
      "The picked URI is a content URI with a userId prefix for a user other than mUserId (content://{userId}@...), and mUserContext can resolve its type.",
      "A multi-user device with at least one other user. The material does not show the disclosure path beyond persisting the URI."
     ],
     "per_major": {
      "14": "same fix: commit 460d0b5a63d1 in android-security-14.0.0_r4 (cherry-pick, Change-Id Ib266b285a3a1c6c5265ae2321159e61e08e349f6). At android-security-14.0.0_r29 the code context shows isValidRingtoneUri in use in onSaveRingtone, with 5/8 lines of the patched region present.",
      "15": "not affected per the bulletin. android-security-15.0.0_r17 has 5/8 lines of the patched region present.",
      "16": "not affected per the bulletin. android-security-16.0.0_r8 has 5/8 lines of the patched region present.",
      "17": "not affected per the bulletin. android-security-17.0.0_r1 has 5/8 lines of the patched region present."
     },
     "backport_notes": [
      "Single commit. It relies on UserManager.getUserProperties/UserProperties.isMediaSharedWithParent, UserManager.isProfile, UserManager.getProfileParent and ContentProvider.getUserIdFromUri being available in the target framework.",
      "The unit test switches the ContentResolver mock to ContentResolver.wrap(mock(ContentInterface.class)) and stubs isDefaultRingtone and UserManager. Keep those test changes together with the source change."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete information-disclosure consequence of persisting a cross-user ringtone URI (which component later reads the other user's content) is not shown in the material.",
      "Whether any other RingtonePreference subclass or other Settings code path persists picked ringtone URIs without calling isValidRingtoneUri.",
      "How the fix was adapted for Android 11, 12, 12L and 13, which the bulletin lists as affected but for which no diffs are provided, including whether UserProperties is available there.",
      "Why only 5/8 lines of the patched region match at the latest tags of 14 through 17."
     ],
     "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-0021": {
     "cve": "CVE-2024-0021",
     "summary": "Adds an early exit to Settings' NotificationAccessConfirmationActivity.onCreate so that, when the activity is running in a managed (work) profile, it shows a toast and finishes instead of presenting the dialog that can enable a notification listener service.",
     "component": "platform/packages/apps/Settings, notification access confirmation (NotificationAccessConfirmationActivity)",
     "weakness": "Missing authorization/state check (no managed-profile check before offering notification listener enablement)",
     "root_cause": "NotificationAccessConfirmationActivity is the Settings confirmation activity for granting notification listener access to a component passed via EXTRA_COMPONENT_NAME/EXTRA_USER_ID. Before the fix it did not check whether it was running inside a managed (work) profile. The fix's log message says apps in the work profile do not support notification listeners, and the commit title says a work profile app must not be able to enable notification listener services. The confirmation flow could still be reached from a work profile.",
     "fix_analysis": [
      "src/com/android/settings/notification/NotificationAccessConfirmationActivity.java:onCreate: after SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS is applied, the fix gets UserManager and DevicePolicyManager via getSystemService. If mUm.isManagedProfile() is true, it logs a warning and shows a Toast built from DevicePolicyManager.getResources().getString(WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS), with R.string.notification_settings_work_profile as the fallback. It then calls finish() and returns before mNm and mComponentName (from EXTRA_COMPONENT_NAME) are initialized, so the rest of onCreate never runs.",
      "The fix adds new fields mDpm and mUm, plus imports for DevicePolicyManager, UserManager, Toast and the WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS string constant. No other logic in the activity is changed."
     ],
     "completeness": [
      "The guard covers only the entry point of NotificationAccessConfirmationActivity. It decides based on the user the activity instance runs in (mUm.isManagedProfile()), not on the EXTRA_USER_ID extra in the intent, so it relies on the activity being launched in the requesting profile.",
      "This commit does not touch other Settings surfaces or framework APIs that can enable notification listeners. The material does not show whether they reject work-profile components on their own.",
      "The check runs before mNm and mComponentName are initialized, so no partial state is set up before finish()."
     ],
     "behavior_changes": [
      "When launched in a managed profile, the activity no longer shows the confirmation dialog. It shows a short toast and finishes immediately. The toast text is the device-policy overridable string WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS, defaulting to R.string.notification_settings_work_profile.",
      "In that case a new Slog warning is emitted: 'Apps in the work profile do not support notification listeners'."
     ],
     "preconditions": [
      "The device has a managed (work) profile, and the attacker-controlled app is installed in that profile.",
      "NotificationAccessConfirmationActivity is started in the work profile with EXTRA_COMPONENT_NAME pointing at the app's notification listener component. Per the code context, the activity imports EXTRA_COMPONENT_NAME and EXTRA_USER_ID.",
      "The bulletin rates this as a High severity EoP. The material does not detail the exact trigger."
     ],
     "per_major": {
      "14": "Same fix: commit 0fb3408e8792 in android-security-14.0.0_r4 (cherry-pick of 81893c2b6f07, Change-Id I7fed6d1baa1a40e9126493d37e33d63236bb4b3c). 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Not affected per the bulletin. The patched region is fully present (8/8) at android-security-15.0.0_r17.",
      "16": "Not affected per the bulletin. The patched region is fully present (8/8) at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin. The patched region is fully present (8/8) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. It depends on DevicePolicyManager.getResources() and DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS. It also depends on the Settings string R.string.notification_settings_work_profile, which the diff does not add, so the string must already exist in the target branch. The DevicePolicyResources string override mechanism was introduced in Android 13 (general platform knowledge).",
      "On branches without DevicePolicyResources, the toast text can be replaced with a plain getString(R.string.notification_settings_work_profile). Keep the isManagedProfile() early exit, which is the part that blocks the flow."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the rest of onCreate, including how EXTRA_USER_ID is validated and how the listener is actually enabled. The exact pre-fix flow that granted access is inferred from the activity's purpose.",
      "The material does not show whether R.string.notification_settings_work_profile exists in Settings on every target branch.",
      "The bulletin lists Android 13 as affected, but only the Android 14 commit is provided, so the Android 13 fix is not shown."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2024-0023": {
     "cve": "CVE-2024-0023",
     "summary": "ConvertRGBToPlanarYUV in Codec2BufferUtils.cpp now bounds its conversion loops by the source view's crop rectangle (src.crop().width/height) instead of the view's full dimensions (src.width()/height()).",
     "component": "platform/frameworks/av, media/codec2 sfplugin utils (Codec2BufferUtils, RGB to YUV conversion)",
     "weakness": "Likely out-of-bounds memory access caused by incorrect loop bounds (full view dimensions used instead of cropped dimensions)",
     "root_cause": "Before the fix, ConvertRGBToPlanarYUV in media/codec2/sfplugin/utils/Codec2BufferUtils.cpp looped y over src.height() and x over src.width(), the full dimensions of the source C2GraphicView. Inside the loops it wrote dstY[x], dstU[x >> 1] and dstV[x >> 1] and advanced the source plane pointers by colInc. The only size check, dstStride * dstVStride * 3 / 2 > bufferSize, compares the destination against its own stride parameters, not against the loop extents. If the view's full dimensions exceed the crop region that the destination layout covers, the loops can process more rows and pixels than intended.",
     "fix_analysis": [
      "media/codec2/sfplugin/utils/Codec2BufferUtils.cpp:ConvertRGBToPlanarYUV: the outer loop bound changes from src.height() to src.crop().height and the inner loop bound from src.width() to src.crop().width. The number of rows and pixels processed now follows the crop rectangle.",
      "Nothing else in the visible code changes: the bufferSize check, the plane pointer setup (pRed/pGreen/pBlue from src.data()), the colInc stepping and the color math are the same. The fix is a single commit that changes 2 lines."
     ],
     "completeness": [
      "The fix covers the conversion loop in ConvertRGBToPlanarYUV: the loop extents now come from the crop rectangle.",
      "The function still has no explicit check that src.crop().width <= dstStride or src.crop().height <= dstVStride. Correctness depends on how callers size the destination, and the material does not show that.",
      "The even-dimension CHECKs (lines 596-597) still test src.width() and src.height(), not the crop dimensions. An odd crop width or height is therefore not rejected, even though the chroma handling (x >> 1, y & 1) works in pairs of pixels and rows.",
      "The source plane pointers start at src.data()[PLANE_*] with no offset for crop().left/top. The material does not show whether data() already accounts for the crop origin."
     ],
     "behavior_changes": [
      "RGB to YUV conversion now processes only the cropped region. When a view's full dimensions are larger than its crop, fewer rows and pixels are converted than before. The fix adds no new error codes or return paths."
     ],
     "preconditions": [
      "A Codec2 (CCodec/sfplugin) path calls ConvertRGBToPlanarYUV with a C2GraphicView whose full width or height exceeds its crop rectangle. The index search did not surface the callers, so the exact path is unknown."
     ],
     "per_major": {
      "14": "Same fix. Resolved via bug_id in android-security-14.0.0_r4 (cherry-pick chain, Change-Id I42c71616c9d50f61c92f461f6a91f5addb1d724a). At android-security-14.0.0_r29 the loops use src.crop().height/width, as shown in the code context (5/7 lines of the patched region present).",
      "15": "Not affected per the bulletin. At android-security-15.0.0_r17, 5/7 lines of the patched region are present. The material does not say which lines match.",
      "16": "Not affected per the bulletin. At android-security-16.0.0_r8, 5/7 lines of the patched region are present.",
      "17": "Not affected per the bulletin. At android-security-17.0.0_r1, 5/7 lines of the patched region are present."
     },
     "backport_notes": [
      "This is a single two-line change with no dependencies shown. Apply it to the two for-loops in ConvertRGBToPlanarYUV in media/codec2/sfplugin/utils/Codec2BufferUtils.cpp, and check that callers size the destination from the crop dimensions."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the callers of ConvertRGBToPlanarYUV or how they size the destination buffer relative to the crop.",
      "It does not show whether C2GraphicView::data() already includes the crop origin (left/top).",
      "It does not show the rest of the loop (the row pointer advancement after line 650).",
      "It does not show which process runs this code for a given trigger path.",
      "The PoC mentioned in the bug is not in the material, so the concrete triggering input is unknown.",
      "For 15/16/17, it is unknown which 5 of the 7 patched-region lines match."
     ],
     "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": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "coverage": {
    "n_ingested_tags": 18,
    "first_ingested_tag": "android-security-15.0.0_r1",
    "latest_ingested_tag": "android-security-15.0.0_r18",
    "window_base_tag": "android-15.0.0_r1",
    "window_start": "2024-09-03",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {}
  },
  "16": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "coverage": {
    "n_ingested_tags": 9,
    "first_ingested_tag": "android-security-16.0.0_r1",
    "latest_ingested_tag": "android-security-16.0.0_r9",
    "window_base_tag": "android-16.0.0_r2",
    "window_start": "2025-06-10",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {}
  },
  "17": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "coverage": {
    "n_ingested_tags": 2,
    "first_ingested_tag": "android-security-17.0.0_r1",
    "latest_ingested_tag": "android-security-17.0.0_r2",
    "window_base_tag": "android-17.0.0_r1",
    "window_start": "2026-06-16",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {}
  }
 },
 "bulletin": {
  "url": "https://source.android.com/docs/security/bulletin/2024-01-01",
  "latest_version": "1.1",
  "latest_revision": {
   "version": "1.1",
   "date": "January 16, 2024",
   "notes": "Bulletin revised to include AOSP links."
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "January 3, 2024",
    "notes": "Bulletin published."
   },
   {
    "version": "1.1",
    "date": "January 16, 2024",
    "notes": "Bulletin revised to include AOSP links."
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [],
  "out_of_scope_rows": {
   "Arm components": 2,
   "Imagination Technologies": 1,
   "MediaTek components": 2,
   "Qualcomm closed-source components": 25,
   "Qualcomm components": 8,
   "Unisoc components": 10
  },
  "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."
 }
}