Skip to content

Commit 8c262d6

Browse files
toniheimarcbaechinger
authored andcommitted
Fix leaks of media session service.
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: androidx/media#346 PiperOrigin-RevId: 527543396
1 parent e2bae0c commit 8c262d6

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

RELEASENOTES.md

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
`handlePlayPauseButtonAction` to write custom UI elements with a
6767
play/pause button.
6868

69+
* Fix memory leak of `MediaSessionService` or `MediaLibraryService`
70+
([#346](https://github.com/androidx/media/issues/346)).
71+
6972
* Audio:
7073

7174
* Fix bug where some playbacks fail when tunneling is enabled and

libraries/session/src/main/java/androidx/media3/session/ConnectedControllersManager.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.common.collect.ImmutableList;
2727
import com.google.common.util.concurrent.ListenableFuture;
2828
import com.google.common.util.concurrent.MoreExecutors;
29+
import java.lang.ref.WeakReference;
2930
import java.util.ArrayDeque;
3031
import java.util.Deque;
3132
import java.util.concurrent.atomic.AtomicBoolean;
@@ -62,14 +63,14 @@ public interface AsyncCommand {
6263
private final ArrayMap<ControllerInfo, ConnectedControllerRecord<T>> controllerRecords =
6364
new ArrayMap<>();
6465

65-
private final MediaSessionImpl sessionImpl;
66+
private final WeakReference<MediaSessionImpl> sessionImpl;
6667

6768
public ConnectedControllersManager(MediaSessionImpl session) {
6869
// Initialize default values.
6970
lock = new Object();
7071

7172
// Initialize members with params.
72-
sessionImpl = session;
73+
sessionImpl = new WeakReference<>(session);
7374
}
7475

7576
public void addController(
@@ -136,6 +137,10 @@ record = controllerRecords.remove(controllerInfo);
136137
}
137138

138139
record.sequencedFutureManager.release();
140+
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
141+
if (sessionImpl == null || sessionImpl.isReleased()) {
142+
return;
143+
}
139144
postOrRun(
140145
sessionImpl.getApplicationHandler(),
141146
() -> {
@@ -214,8 +219,10 @@ public boolean isPlayerCommandAvailable(
214219
synchronized (lock) {
215220
info = controllerRecords.get(controllerInfo);
216221
}
222+
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
217223
return info != null
218224
&& info.playerCommands.contains(commandCode)
225+
&& sessionImpl != null
219226
&& sessionImpl.getPlayerWrapper().getAvailableCommands().contains(commandCode);
220227
}
221228

@@ -248,6 +255,10 @@ public void flushCommandQueue(ControllerInfo controllerInfo) {
248255

249256
@GuardedBy("lock")
250257
private void flushCommandQueue(ConnectedControllerRecord<T> info) {
258+
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
259+
if (sessionImpl == null) {
260+
return;
261+
}
251262
AtomicBoolean continueRunning = new AtomicBoolean(true);
252263
while (continueRunning.get()) {
253264
continueRunning.set(false);

libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,13 @@ public MediaSessionLegacyStub(
145145
appPackageName = context.getPackageName();
146146
sessionManager = MediaSessionManager.getSessionManager(context);
147147
controllerLegacyCbForBroadcast = new ControllerLegacyCbForBroadcast();
148-
connectionTimeoutHandler =
149-
new ConnectionTimeoutHandler(session.getApplicationHandler().getLooper());
150148
mediaPlayPauseKeyHandler =
151149
new MediaPlayPauseKeyHandler(session.getApplicationHandler().getLooper());
152150
connectedControllersManager = new ConnectedControllersManager<>(session);
153151
connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
152+
connectionTimeoutHandler =
153+
new ConnectionTimeoutHandler(
154+
session.getApplicationHandler().getLooper(), connectedControllersManager);
154155

155156
// Select a media button receiver component.
156157
ComponentName receiverComponentName = queryPackageManagerForMediaButtonReceiver(context);
@@ -1359,12 +1360,16 @@ public void onFailure(Throwable t) {
13591360
}
13601361
}
13611362

1362-
private class ConnectionTimeoutHandler extends Handler {
1363+
private static class ConnectionTimeoutHandler extends Handler {
13631364

13641365
private static final int MSG_CONNECTION_TIMED_OUT = 1001;
13651366

1366-
public ConnectionTimeoutHandler(Looper looper) {
1367+
private final ConnectedControllersManager<RemoteUserInfo> connectedControllersManager;
1368+
1369+
public ConnectionTimeoutHandler(
1370+
Looper looper, ConnectedControllersManager<RemoteUserInfo> connectedControllersManager) {
13671371
super(looper);
1372+
this.connectedControllersManager = connectedControllersManager;
13681373
}
13691374

13701375
@Override

0 commit comments

Comments
 (0)