Repository navigation
ForegroundServiceDidNotStartInTimeException due to dropped KeyCode. KEYCODE_HEADSETHOOK #2768
Description
Version
Media3 main branch
More version details
No response
Devices that reproduce the issue
Samsung devices are the vast majority of the crashes.
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
No
Reproduction steps
I can reproduce the issue via unit tests
Expected result
The app doesn't crash.
Actual result
Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{f5d3566 u0 xxxx/xxxxService}
at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2118)
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2089)
at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2354)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:211)
at android.os.Looper.loop(Looper.java:300)
at android.app.ActivityThread.main(ActivityThread.java:8227)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1028)
Media
I've had an ongoing issue with ForegroundServiceDidNotStartInTimeExceptions. I implement onPlaybackResumption, and I have a MediaButtonReceiver that implements shouldStartForegroundService to verify I have something to play. My service implements MediaLibraryService and I have a custom player. Over the past few months, I've been adding Google Analytics events at various points. When the crashes occur, onPlaybackResumption isn't called. I just overrode onMediaButtonEvent, it just logs an event and returns super. onMediaButtonEvent is called. It's called from onMediaButtonEvent in MediaSessionImpl. I'm also logging the intent. The intent is always something like this:
Intent { act=android.intent.action.MEDIA_BUTTON flg=0x10000010 cmp=xxxx/xxxxrService (has extras) } Bundle[android.intent.extra.PACKAGE_NAME=android, android.intent.extra.KEY_EVENT=KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_HEADSETHOOK, scanCode=0, metaState=0, flags=0x8, repeatCount=0, eventTime=617362637000000, downTime=617362634000000, deviceId=-1, source=0x101, displayId=-1 }]
Its always KEYCODE_HEADSETHOOK, always repeatCount=0. I've traced through the code in MediaSessionImpl, I see right after callback.onMediaButtonEvent, it does a swicth with a case for KEYCODE_HEADSETHOOK. With the info I have, it's hard to know which branch its hits there. One branch tries to play/pause without calling onPlaybackResumption (so I haven't loaded anything to play). I see doubleTapCompleted is true, then the KEYCODE_HEADSETHOOK will be changed to KEYCODE_MEDIA_NEXT in applyMediaButtonKeyEvent (called at the bottom MediaSessionImpl.onMediaButtonEvent). Otherwise KEYCODE_HEADSETHOOK doesn't appear to get handled. I may well have missed something in my walk through on the code, but regardless, the bottom line is onPlaybackResumption is never called, so we don't even attempt to play anything. Thus the timeout.
Bug Report
- You will email the zip file produced by
adb bugreportto android-media-github@google.com after filing this issue.
Activity
johngray1965 commentedon Sep 14, 2025
Now I have tests that confirm this. They aren't very well structured tests, the tests just start the service with an KeyCode. I need to read to logs to get the results, but here's. the gist. KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE results on onPlaybackRsumption being called. KeyEvent.KEYCODE_HEADSETHOOK does not. All the crashes I see are KeyEvent.KEYCODE_HEADSETHOOK.
[-]ForegroundServiceDidNotStartInTimeException due dropped KeyCode[/-][+]ForegroundServiceDidNotStartInTimeException due to dropped KeyCode. KEYCODE_HEADSETHOOK[/+]johngray1965 commentedon Sep 14, 2025
Now I have tests that don't require reading the logs. They are dependent on the fact that my MediaLibrarySession.Callback impl is injected into my service, allowing me to replace it for the tests (really wrap it with a spy, so I can flag whether onPlaybackResumption was called or not). I'm toying with the idea of creating a minimal project that will demo the issue. I don't think the demo app will work without reworking it to inject at least the callback.
johngray1965 commentedon Sep 14, 2025
https://github.com/johngray1965/Media3KeycodeTest. Note there two tests, one sets the expectation to should see a call onPlaybackResumption, the other sets it to should not. I believe they should both set it. If I am wrong about that, please explain.
johngray1965 commentedon Sep 14, 2025
Better yet, now they both are set to expect a call to onPlaybackResumption, one test fails.
johngray1965 commentedon Sep 15, 2025
I see that according to: https://developer.android.com/reference/androidx/media3/session/MediaSession#media-key-events-mapping. KEYCODE_HEADSETHOOK is expected to act like KEYCODE_MEDIA_PLAY_PAUSE. It does not. in MediaSessionImpl::applyMediaButtonKeyEvent there's a switch statement that has a case for KEYCODE_MEDIA_PLAY_PAUSE, but not for KEYCODE_HEADSETHOOK.
johngray1965 commentedon Sep 15, 2025
Since its documented that KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK should do the same thing I searched for KEYCODE_MEDIA_PLAY_PAUSE, to see if there were other places that KEYCODE_HEADSETHOOK wasn't being handled. Besides the place I mentioned above, I see MediaSessionKeyEventTest has tests for KEYCODE_HEADSETHOOK double click, but not a single. MediaSessionTest tests KEYCODE_MEDIA_PLAY_PAUSE, but doesn't test KEYCODE_HEADSETHOOK at all. I'd bet extending those tests to cover KEYCODE_HEADSETHOOK will bear out the issue I've been seeing. I had been leery of suggesting a fix, not being sure I fully understood the issue, I think I'm past that now.
johngray1965 commentedon Sep 16, 2025
@marcbaechinger I hope you'll find time to take a look at the PR I submitted for this. I believe you'll find it both simple and important.
marcbaechinger commentedon Sep 17, 2025
Yeah, totally. Thanks for all the analysis! Really appreciated. I try to get to this asap.
johngray1965 commentedon Sep 26, 2025
FYI I'm using a local build with these changes in the latest release of my app. Happily the crash is gone.
johngray1965 commentedon Oct 4, 2025
#2816 has been merged, so this can be closed.