Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediaSessionService leak #346

Closed
1 task
bubenheimer opened this issue Apr 20, 2023 · 10 comments
Closed
1 task

MediaSessionService leak #346

bubenheimer opened this issue Apr 20, 2023 · 10 comments
Assignees
Labels

Comments

@bubenheimer
Copy link

Media3 Version

Media3 1.0.1

Devices that reproduce the issue

Android emulator API 33

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Not tested

Reproduction steps

  1. Connect MediaController in one process to MediaSession (via MediaSessionService) in another process.
  2. Do some minimal interaction.
  3. Release MediaController.
  4. Wait for MediaSessionService to be destroyed.
  5. Wait a long time to really make sure that Android will give up the Service references. Mash the Android Studio Profiler GC button.
  6. Observe MediaSessionService leaking, via LeakCanary, Android Studio Profiler, and StrictMode setClassInstanceLimit.

Expected result

MediaSessionService garbage collected

Actual result

MediaSessionService leaks. Is this a known leak or expected? If so, is there a workaround? If it's not clear what's going on here I can work on a repro.

It looks like something (Binder-related I assume) is keeping a reference to MediaSessionStub.

====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS

References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.

5665 bytes retained by leaking objects
Signature: ee46fed2ca31f02081399eeb1f2a47c36a7e8daf
┬───
│ GC Root: Global variable in native code
│
├─ androidx.media3.session.MediaSessionStub instance
│    Leaking: UNKNOWN
│    Retaining 16.5 kB in 252 objects
│    ↓ MediaSessionStub.connectedControllersManager
│                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~
├─ androidx.media3.session.ConnectedControllersManager instance
│    Leaking: UNKNOWN
│    Retaining 15.7 kB in 245 objects
│    ↓ ConnectedControllersManager.sessionImpl
│                                  ~~~~~~~~~~~
├─ androidx.media3.session.MediaSessionImpl instance
│    Leaking: UNKNOWN
│    Retaining 14.6 kB in 221 objects
│    context instance of com.bubenheimer.rucksack.d.CW
│    ↓ MediaSessionImpl.context
│                       ~~~~~~~
╰→ com.bubenheimer.rucksack.d.CW instance
​     Leaking: YES (ObjectWatcher was watching this because com.bubenheimer.rucksack.d.CW received Service#onDestroy()
​     callback and Service not held by ActivityThread)
​     Retaining 5.7 kB in 95 objects
​     key = 4ec1a0de-07d9-487f-b346-18fa0fe5c9f5
​     watchDurationMillis = 1233
​     retainedDurationMillis = 229
​     mApplication instance of com.bubenheimer.rucksack.d.D
​     mBase instance of android.app.ContextImpl
====================================
0 LIBRARY LEAKS

A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
====================================
0 UNREACHABLE OBJECTS

An unreachable object is still in memory but LeakCanary could not find a strong reference path
from GC roots.
====================================
METADATA

Please include this in bug reports and Stack Overflow questions.

Build.VERSION.SDK_INT: 33
Build.MANUFACTURER: Google
LeakCanary version: 2.10
App process name: com.bubenheimer.rucksack
Class count: 28631
Instance count: 179207
Primitive array count: 132734
Object array count: 25331
Thread count: 36
Heap total bytes: 27161738
Bitmap count: 0
Bitmap total bytes: 0
Large bitmap count: 0
Large bitmap total bytes: 0
Db 1: open /data/user/0/com.bubenheimer.rucksack/databases/com.google.android.datatransport.events
Db 2: open /data/user/0/com.bubenheimer.rucksack/databases/rucksack-16885692033713938602.db
Stats: LruCache[maxSize=3000,hits=108537,misses=178040,hitRate=37%]
RandomAccess[bytes=9035219,reads=178040,travel=58762959483,range=32300280,size=40283652]
Heap dump reason: 1 retained objects, app is visible
Analysis duration: 25266 ms
Heap dump file path: /storage/emulated/0/Download/leakcanary-com.bubenheimer.rucksack/2023-04-20_12-46-30_323.hprof
Heap dump timestamp: 1682009225088
Heap dump duration: 4144 ms

Media

N/A

Bug Report

  • You will email the zip file produced by adb bugreport to [email protected] after filing this issue.
@bubenheimer
Copy link
Author

To be clear: I use a pure MediaSessionService approach, my Manifest does not declare MediaBrowserService, so I'd think that media3 should be expected to handle this cleanly, without baggage from legacy code & requirements.

I have some bound services of my own in the same process as MediaSessionService. Those are garbage collected cleanly.

I have verified these behaviors on APIs 33 & 32. UpsideDownCake 34 Beta1 appears to leak all services whatsoever.

@bubenheimer
Copy link
Author

Also, I see that the leaking behavior is more significant if there is no significant interaction between MediaController & MediaSession. In this case the MediaSession is torn down when the MediaController calls release(), the MediaSessionService is destroyed and leaks.

In the case where there is more interaction, the MediaSession does not seem to be released upon MediaController.release(), and is reused when a MediaController connects again. May be another bug, or I'm doing something wrong. I thought it would release right away in the case of a pure MediaSessionService, as opposed to when it also supports MediaBrowserService compatibility.

@tonihei tonihei assigned tonihei and unassigned marcbaechinger Apr 25, 2023
@tonihei
Copy link
Collaborator

tonihei commented Apr 25, 2023

I can reproduce a similar leak that looks slightly different, which I assume is due to differences in the type of controller and/or the interactions caused with it. In any case, fixing the leak I can reproduce affects the same code location your leak above is complaining about, so I'd assume it fixes this as well.

@bubenheimer
Copy link
Author

I found a bug in AndroidX code that caused the leak in the "little interaction" case. However, now there is still a somewhat lengthy delay of about 10 seconds from onDestroy() to being finalizable on my MediaSessionService. If I trigger a GC at 11 seconds, it will be finalized, with 10 seconds it won't. Can this be reduced? It tends to trigger spurious leak reports.

I also still don't understand the second case: Why will the MediaSessionService not be destroyed if is there is meaningful interaction prior to releasing the MediaSession?

@tonihei
Copy link
Collaborator

tonihei commented Apr 26, 2023

Thanks for detailed investigations - feels like you found a few interesting edge case bugs in our libraries!

I'd suggest you retry your tests once the Media3 fix is uploaded to GitHub. If there is another (different) leak you can reproduce in Media3 code after including the fix, then please open a new issue. If you see any leaks reported in other libraries (Android system code or androidx.media. packages, then please continue the discussion on the already existing Android bugs. Just pointing this out to make sure we have individual tracking bugs for distinct libraries and we can make sure they are all fixed individually.

marcbaechinger pushed a commit that referenced this issue May 3, 2023
References to the service are kept from MediaSessionStub
and from a long-delayed Handler messages in ConnectionTimeoutHandler.

Remove strong references from these places by making the timeout
handler static and ensuring ConnectedControllersManager only keeps
a weak reference to the service (as it's part of MediaSessionStub).

Issue: #346
PiperOrigin-RevId: 527543396
@bubenheimer
Copy link
Author

bubenheimer commented May 5, 2023

Thanks for the fix! I'm not sure how to publish the project artifacts, in particular publishToMavenLocal is not doing anything for me on main branch.

Edit: just saw the brand new commits on main suggesting an imminent alpha release, so will wait for that to drop. Still would be nice to know how to get this going myself without manually copying artifacts around. Or is there a repo for nightlies?

Also tried using via project(...) dependency from README, but this project looks incompatible with AGP 8.0+. What a waste of time.

@tonihei
Copy link
Collaborator

tonihei commented May 9, 2023

To depend on the latest dev branch, you'd need to checkout the sources and depend on them locally. See https://github.com/androidx/media#locally for details. We are also planning to publish a 1.0.2 release with this fix as the stable 1.1.0 will still be while off.

@tonihei
Copy link
Collaborator

tonihei commented May 9, 2023

I'll close this issue under the assumption that it is fixed. If you still see something similar (in media3 code), please reopen or file a new issue.

@tonihei tonihei closed this as completed May 9, 2023
@bubenheimer
Copy link
Author

Thanks. Like I mentioned earlier, depending on media3 project locally fails due to my project's build setup being incompatible with media3. In particular, I use the current AGP 8.0+, and media3 does not. I'd expect this recommendation to not work smoothly in general.

This being a public Github project, it would be desirable to have a way to test out artifacts that's less dependent on requiring me to have a build setup that's compatible with whatever you guys have.

@bubenheimer
Copy link
Author

I've validated that the 10 second finalization delay leak is fixed in 1.1.0-alpha01. Yay and thanks!

The problem with the MediaSessionService not reaching onDestroy() after some amount of player interaction is still present. I will create a new issue.

icbaker pushed a commit that referenced this issue May 17, 2023
References to the service are kept from MediaSessionStub
and from a long-delayed Handler messages in ConnectionTimeoutHandler.

Remove strong references from these places by making the timeout
handler static and ensuring ConnectedControllersManager only keeps
a weak reference to the service (as it's part of MediaSessionStub).

Issue: #346
PiperOrigin-RevId: 527543396
(cherry picked from commit 8c262d6)
@androidx androidx locked and limited conversation to collaborators Jul 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants