CVE-2026-28577 AI-generated analysis
platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm)
WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is true and the target WindowToken already contains a window, enforcing one toast window per toast token.
Root cause
In WindowManagerService.addWindow, the only toast-specific guard in the `type == TYPE_TOAST` block was displayContent.canAddToastWindowForUid(callingUid), which limits toasts per calling UID. Nothing in that block checked whether the supplied WindowToken already held a window, even though a TYPE_TOAST WindowToken is meant to hold only one toast window (the comment added by the fix says so). A caller from a different UID that presented the same toast token passed the per-UID check and could attach a second toast window to that token. The added test testAddToastWindow_singleWindowPerToken shows this ("Add second window with same token but different UID to bypass the canAddToastWindowForUid check").
How the fix works
- services/core/java/com/android/server/wm/WindowManagerService.java:addWindow: inside the `type == TYPE_TOAST` block, right after the canAddToastWindowForUid check, the fix adds `if (addToastWindowRequiresToken && !token.isEmpty())`. When it matches, the code logs a WM_ERROR ProtoLog warning and returns WindowManagerGlobal.ADD_BAD_APP_TOKEN.
- data/etc/services.core.protolog.json: registers the new ProtoLog message hash -610155150 ("Adding toast window with non-empty token.", WARN, WM_ERROR) on the Android 14 branch.
- services/core/java/com/android/server/wm/Session.java: refactor for testing. The public Session(service, callback) constructor now delegates to a new @VisibleForTesting Session(service, callback, callingPid, callingUid) constructor, so tests can create sessions for any pid/uid. Production callers still get Binder.getCallingPid() and Binder.getCallingUid(), as before.
- services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java adds createTestSession(atms, pid, uid), described as a partial backport that omits process creation. services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java adds testAddToastWindow_singleWindowPerToken. The test adds a toast window from uid 1234 to a TYPE_TOAST token, then asserts that a second toast from uid 1235 on the same token returns ADD_BAD_APP_TOKEN.
Completeness
- The new check only applies when addToastWindowRequiresToken is true. How that value is computed is not shown in the material. When it is false, nothing new prevents a second toast window on one token.
- The check is inside the `type == TYPE_TOAST` branch, so it only blocks additional TYPE_TOAST windows on a non-empty token. The material does not show whether non-toast window types can be attached to a TYPE_TOAST token, or whether that is checked elsewhere.
- The new early return comes after `win.openInputChannel(outInputChannel)` (line 1668 in the context), like the existing ADD_DUPLICATE_ADD return path. The fix does not change that ordering.
Behavior changes
- addWindow returns WindowManagerGlobal.ADD_BAD_APP_TOKEN for a TYPE_TOAST window when addToastWindowRequiresToken is true and the WindowToken already has a child. Before the fix, the test scenario (second UID, same token) was not rejected.
- New WM_ERROR ProtoLog warning "Adding toast window with non-empty token." on Android 14 and 17. On Android 15 and 16 the ProtoLog line is commented out.
- Session gains a package-private @VisibleForTesting constructor in the 14 commit. The production constructor behaves as before.
Per major
- Android 14: Same fix plus extra test scaffolding: commit 2f08e7d895e3 in android-security-14.0.0_r28 adds the WMS check, the protolog.json entry, the @VisibleForTesting Session constructor and WindowTestsBase.createTestSession. 8/8 patched lines are present at android-security-14.0.0_r29.
- Android 15: Adapted fix: commit 9af61140db79 in android-security-15.0.0_r16 adds the same WMS check with the ProtoLog line commented out, plus the test. No protolog.json or Session.java change. 8/8 lines are present at android-security-15.0.0_r17.
- Android 16: Adapted fix: commit d9ad94e69f4e in android-security-16.0.0_r7, same shape as 15 (ProtoLog commented out, WMS check plus test only). 8/8 lines are present at android-security-16.0.0_r8.
- Android 17: Same fix: commit a00072f03623 in android-security-17.0.0_r1 adds the WMS check with an active ProtoLog call, plus the test (the test uses the WindowRelayoutResult signature of addWindow). No protolog.json change in the diff. 8/8 lines are present at android-security-17.0.0_r1.
Unknowns
- How addToastWindowRequiresToken is computed (target SDK and/or permission conditions) is not in the provided diff or context.
- The material does not describe the concrete security impact of a second toast window sharing a token, or why it is rated High EoP.
- Why the 17 branch keeps an active ProtoLog call without a services.core.protolog.json change, while 15 and 16 commented it out.
- The material does not show whether a same-UID second toast on the same token can ever get past canAddToastWindowForUid and reach the new check.