CVE-2026-0099 AI-generated analysis
platform/packages/apps/Nfc (platform/packages/modules/Nfc on 16+), card emulation / HostEmulationManager
Adds onBindingDied and onNullBinding handlers to the ServiceConnection objects in HostEmulationManager so the NFC service unbinds from a HostApduService whose binding died or whose onBind returned null, instead of leaving the binding in place.
Root cause
HostEmulationManager binds to app-provided HostApduService implementations through mPaymentConnection and mConnection. bindPaymentServiceLocked passes Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS. Before the fix, neither connection overrode onBindingDied or onNullBinding. So if the target service returned a null binder or its binding died, nothing tore the binding down: mPaymentServiceBound / mServiceBound stayed true and mContext.unbindService was not called. Per the commit message, the NFC service was left "hanging around waiting for a service that has crashed". The commit title calls this several BAL (background activity launch) bypasses in HostApduService.
How the fix works
- src/com/android/nfc/cardemulation/HostEmulationManager.java, mPaymentConnection (anonymous ServiceConnection): adds onBindingDied(ComponentName) and onNullBinding(ComponentName). Each takes mLock and calls unbindPaymentServiceLocked(). If mPaymentServiceBound is set, that helper calls mContext.unbindService(mPaymentConnection) and clears mPaymentServiceBound, mPaymentService, mPaymentServiceName and mPaymentServiceUserId.
- src/com/android/nfc/cardemulation/HostEmulationManager.java, mConnection (anonymous ServiceConnection): adds onBindingDied and onNullBinding. Each takes mLock and calls unbindServiceIfNeededLocked(). If mServiceBound is set, that helper calls mContext.unbindService(mConnection) and clears mServiceBound. Any further state reset is outside the shown context.
- Net effect: a binding to a HostApduService that never delivered a binder, or whose binding died, is released instead of staying in place. For the payment binding, that binding was made with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS.
Completeness
- On Android 14 the fix covers the two ServiceConnection objects it patches (mPaymentConnection and mConnection) and both lifecycle callbacks (onBindingDied, onNullBinding).
- The Android 14 variant goes through the existing Locked helpers, so the bound-state flags are reset. In the 15/16/17 variants, onNullBinding calls mContext.unbindService(this) directly and does not clear mPaymentServiceBound / mServiceBound in the shown diff. The onBindingDied added to mConnection (15) or HostEmulationServiceConnection (16/17) calls unbindServiceIfNeededLocked() without taking mLock. On 15/16/17 the payment connection gets no new onBindingDied.
- The fix does not change the bind flags: bindPaymentServiceLocked still passes BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS. It only shortens the life of a binding that does not work. The material does not show whether a service that binds successfully but never answers APDUs is limited in any other way.
- On 16/17 the diff shows another 'private ServiceConnection mConnection =' declaration right after the patched HostEmulationServiceConnection region. The material does not show whether that connection has these callbacks.
Behavior changes
- When onBindingDied or onNullBinding fires on the payment or active HostApduService connection, the NFC service now calls unbindService. On Android 14 the bound-state fields are also cleared.
- A HostApduService that returns null from onBind is unbound instead of staying bound.
- On 16/17 the payment connection's onNullBinding logs a new NfcEventProto BindState value, SERVICE_NULL_BINDING. The 15+ variants add Log.i lines in the new callbacks.
Per major
- Android 14: Fixed in platform/packages/apps/Nfc 34a7821a1bfe (android-security-14.0.0_r28): onBindingDied and onNullBinding added to both mPaymentConnection and mConnection, each calling the existing Locked unbind helper under mLock. 4/4 patched lines present at android-security-14.0.0_r29.
- Android 15: Adapted fix in platform/packages/apps/Nfc 7fb6795b51c3 (android-security-15.0.0_r16). mPaymentConnection gets only onNullBinding (mContext.unbindService(this) under mLock). mConnection gets onBindingDied (unbindServiceIfNeededLocked, no lock) and onNullBinding (unbindService(this) under mLock). 4/4 lines present at android-security-15.0.0_r17.
- Android 16: Adapted fix in platform/packages/modules/Nfc 127162a9e764 (android-security-16.0.0_r7), under NfcNci/ paths. Same callback additions as 15: onNullBinding on the payment connection, onBindingDied and onNullBinding on HostEmulationServiceConnection. Also adds NfcEventProto logging and a new BindState.SERVICE_NULL_BINDING in NfcNci/proto/event.proto. 6/6 lines present at android-security-16.0.0_r8.
- Android 17: Same as 16: platform/packages/modules/Nfc cfbd02ac1630 in the android-17.0.0_r1 base (NfcNci/ paths, proto change included). 6/6 lines present at android-security-17.0.0_r1.
Unknowns
- Which exact BAL bypass scenarios the commit title ('Several BAL bypasses') refers to beyond the lifecycle handling shown, and whether other commits under bug 385917501 cover more paths.
- The bind flags used for mConnection, which are not shown.
- The full body of unbindServiceIfNeededLocked() beyond clearing mServiceBound (context truncated).
- Which existing callback in the 15+ payment connection calls bindPaymentServiceLocked again (hunk context only).
- Whether the extra mConnection declared after the patched region on 16/17 also handles these callbacks.