mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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
This commit is contained in:
parent
e2bae0c50d
commit
8c262d6c07
@ -66,6 +66,9 @@
|
|||||||
`handlePlayPauseButtonAction` to write custom UI elements with a
|
`handlePlayPauseButtonAction` to write custom UI elements with a
|
||||||
play/pause button.
|
play/pause button.
|
||||||
|
|
||||||
|
* Fix memory leak of `MediaSessionService` or `MediaLibraryService`
|
||||||
|
([#346](https://github.com/androidx/media/issues/346)).
|
||||||
|
|
||||||
* Audio:
|
* Audio:
|
||||||
|
|
||||||
* Fix bug where some playbacks fail when tunneling is enabled and
|
* Fix bug where some playbacks fail when tunneling is enabled and
|
||||||
|
@ -26,6 +26,7 @@ import androidx.media3.session.MediaSession.ControllerInfo;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -62,14 +63,14 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
private final ArrayMap<ControllerInfo, ConnectedControllerRecord<T>> controllerRecords =
|
private final ArrayMap<ControllerInfo, ConnectedControllerRecord<T>> controllerRecords =
|
||||||
new ArrayMap<>();
|
new ArrayMap<>();
|
||||||
|
|
||||||
private final MediaSessionImpl sessionImpl;
|
private final WeakReference<MediaSessionImpl> sessionImpl;
|
||||||
|
|
||||||
public ConnectedControllersManager(MediaSessionImpl session) {
|
public ConnectedControllersManager(MediaSessionImpl session) {
|
||||||
// Initialize default values.
|
// Initialize default values.
|
||||||
lock = new Object();
|
lock = new Object();
|
||||||
|
|
||||||
// Initialize members with params.
|
// Initialize members with params.
|
||||||
sessionImpl = session;
|
sessionImpl = new WeakReference<>(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addController(
|
public void addController(
|
||||||
@ -136,6 +137,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
record.sequencedFutureManager.release();
|
record.sequencedFutureManager.release();
|
||||||
|
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
|
||||||
|
if (sessionImpl == null || sessionImpl.isReleased()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
postOrRun(
|
postOrRun(
|
||||||
sessionImpl.getApplicationHandler(),
|
sessionImpl.getApplicationHandler(),
|
||||||
() -> {
|
() -> {
|
||||||
@ -214,8 +219,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
info = controllerRecords.get(controllerInfo);
|
info = controllerRecords.get(controllerInfo);
|
||||||
}
|
}
|
||||||
|
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
|
||||||
return info != null
|
return info != null
|
||||||
&& info.playerCommands.contains(commandCode)
|
&& info.playerCommands.contains(commandCode)
|
||||||
|
&& sessionImpl != null
|
||||||
&& sessionImpl.getPlayerWrapper().getAvailableCommands().contains(commandCode);
|
&& sessionImpl.getPlayerWrapper().getAvailableCommands().contains(commandCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +255,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private void flushCommandQueue(ConnectedControllerRecord<T> info) {
|
private void flushCommandQueue(ConnectedControllerRecord<T> info) {
|
||||||
|
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
|
||||||
|
if (sessionImpl == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
AtomicBoolean continueRunning = new AtomicBoolean(true);
|
AtomicBoolean continueRunning = new AtomicBoolean(true);
|
||||||
while (continueRunning.get()) {
|
while (continueRunning.get()) {
|
||||||
continueRunning.set(false);
|
continueRunning.set(false);
|
||||||
|
@ -145,12 +145,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
appPackageName = context.getPackageName();
|
appPackageName = context.getPackageName();
|
||||||
sessionManager = MediaSessionManager.getSessionManager(context);
|
sessionManager = MediaSessionManager.getSessionManager(context);
|
||||||
controllerLegacyCbForBroadcast = new ControllerLegacyCbForBroadcast();
|
controllerLegacyCbForBroadcast = new ControllerLegacyCbForBroadcast();
|
||||||
connectionTimeoutHandler =
|
|
||||||
new ConnectionTimeoutHandler(session.getApplicationHandler().getLooper());
|
|
||||||
mediaPlayPauseKeyHandler =
|
mediaPlayPauseKeyHandler =
|
||||||
new MediaPlayPauseKeyHandler(session.getApplicationHandler().getLooper());
|
new MediaPlayPauseKeyHandler(session.getApplicationHandler().getLooper());
|
||||||
connectedControllersManager = new ConnectedControllersManager<>(session);
|
connectedControllersManager = new ConnectedControllersManager<>(session);
|
||||||
connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
|
connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
|
||||||
|
connectionTimeoutHandler =
|
||||||
|
new ConnectionTimeoutHandler(
|
||||||
|
session.getApplicationHandler().getLooper(), connectedControllersManager);
|
||||||
|
|
||||||
// Select a media button receiver component.
|
// Select a media button receiver component.
|
||||||
ComponentName receiverComponentName = queryPackageManagerForMediaButtonReceiver(context);
|
ComponentName receiverComponentName = queryPackageManagerForMediaButtonReceiver(context);
|
||||||
@ -1359,12 +1360,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConnectionTimeoutHandler extends Handler {
|
private static class ConnectionTimeoutHandler extends Handler {
|
||||||
|
|
||||||
private static final int MSG_CONNECTION_TIMED_OUT = 1001;
|
private static final int MSG_CONNECTION_TIMED_OUT = 1001;
|
||||||
|
|
||||||
public ConnectionTimeoutHandler(Looper looper) {
|
private final ConnectedControllersManager<RemoteUserInfo> connectedControllersManager;
|
||||||
|
|
||||||
|
public ConnectionTimeoutHandler(
|
||||||
|
Looper looper, ConnectedControllersManager<RemoteUserInfo> connectedControllersManager) {
|
||||||
super(looper);
|
super(looper);
|
||||||
|
this.connectedControllersManager = connectedControllersManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user