CVE-2026-55270 AI-generated analysis
platform/frameworks/opt/telephony, Phone dial / USSD handling (GsmCdmaPhone, imsphone/ImsPhone)
Adds a `wrappedCallback == null` guard before `handleInCallMmiCommands(newDialString)` in `dialInternal` of both `GsmCdmaPhone` and `ImsPhone`, so that USSD requests (which carry a non-null ResultReceiver) are no longer interpreted as in-call MMI supplementary-service commands.
Root cause
`dialInternal(String dialString, DialArgs dialArgs, ResultReceiver wrappedCallback)` serves both ordinary dialing (`ImsPhone.dial` passes `wrappedCallback = null`, per the code context) and USSD requests, which according to the commit message and tests carry a non-null ResultReceiver (the tests call `handleUssdRequest("2", mockCallback)`). Before the fix, `dialInternal` called `handleInCallMmiCommands(newDialString)` with no condition: on every call in ImsPhone, and inside the `isPhoneTypeGsm()` branch in GsmCdmaPhone. It returned whenever the string was handled. A USSD request whose string matched an in-call MMI code (the tests use "2") therefore ran the matching in-call supplementary service instead of being treated as USSD. The tests check `acceptCall` on the call trackers, and the commit message also names hold, swap and conference. The bulletin rates this as a High severity EoP.
How the fix works
- src/java/com/android/internal/telephony/GsmCdmaPhone.java:dialInternal (inside the `isPhoneTypeGsm()` branch): `if (handleInCallMmiCommands(newDialString))` becomes `if (wrappedCallback == null && handleInCallMmiCommands(newDialString))`. Because of short-circuit evaluation, `handleInCallMmiCommands` is never called when a ResultReceiver is present.
- src/java/com/android/internal/telephony/imsphone/ImsPhone.java:dialInternal: the same change. The code context at android-security-14.0.0_r30 (line 981) shows the guard in place. The string then reaches `ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback)` as before.
- tests/telephonytests/.../GsmCdmaPhoneTest.java: the new `testHandleUssdRequestWithInCallMmiCode` sets the call state to INCOMING, calls `mPhoneUT.handleUssdRequest("2", mockCallback)`, asserts it returns true, and verifies with `verify(mCT, never()).acceptCall()`.
- tests/telephonytests/.../imsphone/ImsPhoneTest.java: the new `testHandleUssdRequestWithInCallMmiCode` expects `handleUssdRequest("2", mockCallback)` to throw `CallStateException` with message `Phone.CS_FALLBACK`, and verifies that `mImsCT.acceptCall(anyInt())` is never called.
Completeness
- Covers the in-call MMI pre-check in `dialInternal` of both `GsmCdmaPhone` (GSM phone-type branch) and `ImsPhone`.
- The guard relies only on `wrappedCallback` being non-null to mark a USSD request. It is correct only if every USSD entry passes a non-null ResultReceiver and every ordinary dial passes null; the material shows `ImsPhone.dial` passing null. No separate check of where the request came from is added.
- `handleInCallMmiCommands` itself is unchanged, so ordinary dials still run in-call MMI codes as before.
- After the guard, USSD strings still go through regular MMI parsing (shown for ImsPhone via `ImsPhoneMmiCode.newFromDialString` with `wrappedCallback`). The material does not show how the GSM path handles a string such as "2" afterwards, beyond `handleUssdRequest` returning true.
Behavior changes
- USSD requests whose string matches an in-call MMI code no longer trigger call-control actions (the tests check accept; the commit message names hold, swap and conference).
- On ImsPhone, per the new test, such a USSD request results in a `CallStateException` with message `Phone.CS_FALLBACK`.
- On GsmCdmaPhone, per the new test, `handleUssdRequest` returns true for such input and no `acceptCall` happens on the call tracker.
- Normal dialing (`wrappedCallback == null`) is unchanged.
Per major
- Android 14: same fix: commit a702397eed29 in android-security-14.0.0_r29. A one-line condition change in GsmCdmaPhone.dialInternal (GSM branch) and in ImsPhone.dialInternal, plus the two tests. The guard is confirmed present in ImsPhone at android-security-14.0.0_r30.
- Android 15: same fix: commit 92836c48ba6a in android-security-15.0.0_r17. The same one-line changes at shifted locations (hunks at GsmCdmaPhone line 1714 and ImsPhone line 1001) and the same tests.
- Android 16: adapted fix: commit ee04a37583c0 in android-security-16.0.0_r8. In both files the in-call MMI check has two branches, selected by `mFeatureFlags.skipMmiCodeCheckForEmergencyCall()`. `wrappedCallback == null` is added to both: the flagged branch becomes `!dialArgs.isEmergency && wrappedCallback == null && handleInCallMmiCommands(...)`, and the unflagged condition gets the same guard. Same tests.
- Android 17: adapted fix: commit 802f8f5c9920 in android-security-17.0.0_r1. The in-call MMI handling sits under an outer `if (!dialArgs.isEmergency)` block, with `mFeatureFlags.ignoreIncallMmiForEmergency()` logic inside. The outer condition becomes `!dialArgs.isEmergency && wrappedCallback == null`. Same tests.
Unknowns
- The material does not show `handleUssdRequest`, the public entry point(s) that reach it, or the permission (if any) that gates them, so the attacker position implied by the EoP rating is not visible.
- The full GsmCdmaPhone.dialInternal (including any CDMA branch, and processing after the guard) is not shown.
- The full list of in-call MMI codes handled by `handleInCallMmiCommands`, beyond the "2" used in the tests, is not shown.
- The per-major note '6/8 lines of the patched region present' at the latest security tags is not explained, although the ImsPhone context shown at 14.0.0_r30 does contain the guard.