MediaRouter: save sessions in media router service
Each MediaRoute2Provider instance saves session infos published by MediaRoute2ProviderService. For that, MediaRouter2ProviderService notifies provider info and session infos simultaneously and to minimize the number of binder calls, schedulePublishState is added into MediaRoute2ProviderService. The saved sessions can be obtained by calling MRM.getActiveSessions(). Test: atest mediaroutertest && manually call MRM.getActiveSessions() Change-Id: I6938105f8d154ad9542f7e7c6210a451ee4ae90d
This commit is contained in:
@@ -25,6 +25,7 @@ import android.os.Bundle;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IMediaRoute2ProviderClient {
|
||||
void updateProviderInfo(in MediaRoute2ProviderInfo info);
|
||||
void updateState(in MediaRoute2ProviderInfo providerInfo,
|
||||
in List<RouteSessionInfo> sessionInfos);
|
||||
void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, long requestId);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.media.IMediaRouter2Manager;
|
||||
import android.media.IMediaRouterClient;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouterClientState;
|
||||
import android.media.RouteSessionInfo;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
@@ -63,4 +64,6 @@ interface IMediaRouterService {
|
||||
in MediaRoute2Info route, int volume);
|
||||
void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
|
||||
in MediaRoute2Info route, int direction);
|
||||
|
||||
List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -48,6 +49,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
|
||||
private final Handler mHandler;
|
||||
private final Object mSessionLock = new Object();
|
||||
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
|
||||
private ProviderStub mStub;
|
||||
private IMediaRoute2ProviderClient mClient;
|
||||
private MediaRoute2ProviderInfo mProviderInfo;
|
||||
@@ -132,20 +134,22 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
*/
|
||||
public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
|
||||
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
|
||||
int sessionId = sessionInfo.getSessionId();
|
||||
|
||||
//TODO: notify updated session info
|
||||
int sessionId = sessionInfo.getSessionId();
|
||||
if (sessionInfo.getSelectedRoutes().isEmpty()) {
|
||||
releaseSession(sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mSessionLock) {
|
||||
if (mSessionInfo.containsKey(sessionId)) {
|
||||
mSessionInfo.put(sessionId, sessionInfo);
|
||||
schedulePublishState();
|
||||
} else {
|
||||
Log.w(TAG, "Ignoring unknown session info.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (sessionInfo.getSelectedRoutes().isEmpty()) {
|
||||
releaseSession(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,6 +173,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
}
|
||||
mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
|
||||
}
|
||||
schedulePublishState();
|
||||
}
|
||||
|
||||
if (mClient == null) {
|
||||
@@ -192,11 +197,12 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
//TODO: notify media router service of release.
|
||||
RouteSessionInfo sessionInfo;
|
||||
synchronized (mSessionLock) {
|
||||
sessionInfo = mSessionInfo.put(sessionId, null);
|
||||
sessionInfo = mSessionInfo.remove(sessionId);
|
||||
}
|
||||
if (sessionInfo != null) {
|
||||
mHandler.sendMessage(obtainMessage(
|
||||
MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo));
|
||||
schedulePublishState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,23 +274,37 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
/**
|
||||
* Updates provider info and publishes routes and session info.
|
||||
*/
|
||||
public final void updateProviderInfo(MediaRoute2ProviderInfo info) {
|
||||
mProviderInfo = info;
|
||||
publishState();
|
||||
public final void updateProviderInfo(@NonNull MediaRoute2ProviderInfo providerInfo) {
|
||||
mProviderInfo = Objects.requireNonNull(providerInfo, "providerInfo must not be null");
|
||||
schedulePublishState();
|
||||
}
|
||||
|
||||
void setClient(IMediaRoute2ProviderClient client) {
|
||||
mClient = client;
|
||||
publishState();
|
||||
schedulePublishState();
|
||||
}
|
||||
|
||||
void publishState() {
|
||||
//TODO: sends session info
|
||||
void schedulePublishState() {
|
||||
if (mStatePublishScheduled.compareAndSet(false, true)) {
|
||||
mHandler.post(this::publishState);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishState() {
|
||||
if (!mStatePublishScheduled.compareAndSet(true, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mClient == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RouteSessionInfo> sessionInfos;
|
||||
synchronized (mSessionLock) {
|
||||
sessionInfos = new ArrayList<>(mSessionInfo.values());
|
||||
}
|
||||
try {
|
||||
mClient.updateProviderInfo(mProviderInfo);
|
||||
mClient.updateState(mProviderInfo, sessionInfos);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to send onProviderInfoUpdated");
|
||||
}
|
||||
|
||||
@@ -176,7 +176,18 @@ public class MediaRouter2Manager {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<RouteSessionInfo> getRouteSessions() {
|
||||
public List<RouteSessionInfo> getActiveSessions() {
|
||||
Client client;
|
||||
synchronized (sLock) {
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
return mMediaRouterService.getActiveSessions(client);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to get sessions. Service probably died.", ex);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@@ -280,12 +280,12 @@ public class MediaRouterManagerTest {
|
||||
});
|
||||
|
||||
//TODO: it fails due to not releasing session
|
||||
assertEquals(0, mManager.getActiveRoutes().size());
|
||||
assertEquals(0, mManager.getActiveSessions().size());
|
||||
|
||||
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
|
||||
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertEquals(1, mManager.getActiveRoutes().size());
|
||||
assertEquals(1, mManager.getActiveSessions().size());
|
||||
|
||||
//TODO: release the session
|
||||
/*
|
||||
|
||||
@@ -24,6 +24,9 @@ import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.RouteSessionInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
abstract class MediaRoute2Provider {
|
||||
@@ -31,7 +34,8 @@ abstract class MediaRoute2Provider {
|
||||
final String mUniqueId;
|
||||
|
||||
Callback mCallback;
|
||||
private MediaRoute2ProviderInfo mProviderInfo;
|
||||
private volatile MediaRoute2ProviderInfo mProviderInfo;
|
||||
private volatile List<RouteSessionInfo> mSessionInfos = Collections.emptyList();
|
||||
|
||||
MediaRoute2Provider(@NonNull ComponentName componentName) {
|
||||
mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
|
||||
@@ -64,15 +68,29 @@ abstract class MediaRoute2Provider {
|
||||
return mProviderInfo;
|
||||
}
|
||||
|
||||
void setAndNotifyProviderInfo(MediaRoute2ProviderInfo info) {
|
||||
//TODO: check if info is not updated
|
||||
if (info == null) {
|
||||
@NonNull
|
||||
public List<RouteSessionInfo> getSessionInfos() {
|
||||
return mSessionInfos;
|
||||
}
|
||||
|
||||
void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo,
|
||||
List<RouteSessionInfo> sessionInfos) {
|
||||
if (providerInfo == null) {
|
||||
mProviderInfo = null;
|
||||
} else {
|
||||
mProviderInfo = new MediaRoute2ProviderInfo.Builder(info)
|
||||
mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
|
||||
.setUniqueId(mUniqueId)
|
||||
.build();
|
||||
}
|
||||
List<RouteSessionInfo> sessionInfoWithProviderId = new ArrayList<RouteSessionInfo>();
|
||||
for (RouteSessionInfo sessionInfo : sessionInfos) {
|
||||
sessionInfoWithProviderId.add(
|
||||
new RouteSessionInfo.Builder(sessionInfo)
|
||||
.setProviderId(mUniqueId)
|
||||
.build());
|
||||
}
|
||||
mSessionInfos = sessionInfoWithProviderId;
|
||||
|
||||
if (mCallback != null) {
|
||||
mCallback.onProviderStateChanged(this);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ import android.util.Slog;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -268,14 +270,15 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
}
|
||||
}
|
||||
|
||||
private void onProviderInfoUpdated(Connection connection, MediaRoute2ProviderInfo info) {
|
||||
private void onProviderStateUpdated(Connection connection,
|
||||
MediaRoute2ProviderInfo providerInfo, List<RouteSessionInfo> sessionInfos) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, this + ": State changed ");
|
||||
}
|
||||
setAndNotifyProviderInfo(info);
|
||||
setAndNotifyProviderState(providerInfo, sessionInfos);
|
||||
}
|
||||
|
||||
private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
|
||||
@@ -291,7 +294,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
mConnectionReady = false;
|
||||
mActiveConnection.dispose();
|
||||
mActiveConnection = null;
|
||||
setAndNotifyProviderInfo(null);
|
||||
setAndNotifyProviderState(null, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,8 +400,10 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
mHandler.post(() -> onConnectionDied(Connection.this));
|
||||
}
|
||||
|
||||
void postProviderInfoUpdated(MediaRoute2ProviderInfo info) {
|
||||
mHandler.post(() -> onProviderInfoUpdated(Connection.this, info));
|
||||
void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo,
|
||||
List<RouteSessionInfo> sessionInfos) {
|
||||
mHandler.post(() -> onProviderStateUpdated(Connection.this,
|
||||
providerInfo, sessionInfos));
|
||||
}
|
||||
|
||||
void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
|
||||
@@ -419,10 +424,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProviderInfo(MediaRoute2ProviderInfo info) {
|
||||
public void updateState(MediaRoute2ProviderInfo providerInfo,
|
||||
List<RouteSessionInfo> sessionInfos) {
|
||||
Connection connection = mConnectionRef.get();
|
||||
if (connection != null) {
|
||||
connection.postProviderInfoUpdated(info);
|
||||
connection.postProviderStateUpdated(providerInfo, sessionInfos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -288,6 +288,18 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return getActiveSessionsLocked(manager);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Review this is handling multi-user properly.
|
||||
void switchUser() {
|
||||
synchronized (mLock) {
|
||||
@@ -523,6 +535,21 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private List<RouteSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
|
||||
final IBinder binder = manager.asBinder();
|
||||
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
|
||||
|
||||
if (managerRecord == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<RouteSessionInfo> sessionInfos = new ArrayList<>();
|
||||
for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) {
|
||||
sessionInfos.addAll(provider.getSessionInfos());
|
||||
}
|
||||
return sessionInfos;
|
||||
}
|
||||
|
||||
private void initializeUserLocked(UserRecord userRecord) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, userRecord + ": Initialized");
|
||||
@@ -731,6 +758,7 @@ class MediaRouter2ServiceImpl {
|
||||
this, provider, sessionInfo, requestId));
|
||||
}
|
||||
|
||||
//TODO: notify session info updates
|
||||
private void updateProvider(MediaRoute2Provider provider) {
|
||||
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
|
||||
MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.media.MediaRouter;
|
||||
import android.media.MediaRouterClientState;
|
||||
import android.media.RemoteDisplayState;
|
||||
import android.media.RemoteDisplayState.RemoteDisplayInfo;
|
||||
import android.media.RouteSessionInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@@ -523,6 +524,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
mService2.requestUpdateVolume2Manager(manager, route, delta);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
|
||||
return mService2.getActiveSessions(manager);
|
||||
}
|
||||
|
||||
void restoreBluetoothA2dp() {
|
||||
try {
|
||||
boolean a2dpOn;
|
||||
|
||||
@@ -35,6 +35,8 @@ import android.util.Log;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
|
||||
*/
|
||||
@@ -214,6 +216,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
builder.addRoute(mBluetoothA2dpRoute);
|
||||
}
|
||||
builder.addRoute(mDefaultRoute);
|
||||
setAndNotifyProviderInfo(builder.build());
|
||||
setAndNotifyProviderState(builder.build(), Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user