summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hyundo Moon <hdmoon@google.com> 2020-01-20 00:26:52 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-01-20 00:26:52 +0000
commitece12a89bcac2d7daf98cbdc7c11518c63d7d658 (patch)
tree896bf0de5e949e7c402d9c4df46e4a5bebf316c9
parentf1cbec5f5ded6af059bd91b2ff7f0aa42a7f1a40 (diff)
parent67c41fddd8926f70774878e5af70a9cb81393641 (diff)
Merge "MediaRouter2: Add getDefaultController()"
-rw-r--r--media/java/android/media/IMediaRouterService.aidl1
-rw-r--r--media/java/android/media/MediaRouter2.java56
-rw-r--r--media/java/android/media/MediaRouter2Utils.java7
-rw-r--r--media/java/android/media/RoutingSessionInfo.java26
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java8
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java2
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java12
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java45
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java6
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java53
10 files changed, 203 insertions, 13 deletions
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 281e7c6b6f68..dac0fba876be 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -46,6 +46,7 @@ interface IMediaRouterService {
// Methods for media router 2
List<MediaRoute2Info> getSystemRoutes();
+ RoutingSessionInfo getSystemSessionInfo();
void registerClient2(IMediaRouter2Client client, String packageName);
void unregisterClient2(IMediaRouter2Client client);
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 51d08ec96e6f..a6198842abe8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -76,6 +76,8 @@ public class MediaRouter2 {
@GuardedBy("sRouterLock")
final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
+ final RoutingController mDefaultController;
+
@GuardedBy("sRouterLock")
private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
@@ -116,19 +118,26 @@ public class MediaRouter2 {
mHandler = new Handler(Looper.getMainLooper());
List<MediaRoute2Info> currentSystemRoutes = null;
+ RoutingSessionInfo currentSystemSessionInfo = null;
try {
currentSystemRoutes = mMediaRouterService.getSystemRoutes();
+ currentSystemSessionInfo = mMediaRouterService.getSystemSessionInfo();
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to get current currentSystemRoutes", ex);
+ Log.e(TAG, "Unable to get current system's routes / session info", ex);
}
if (currentSystemRoutes == null || currentSystemRoutes.isEmpty()) {
throw new RuntimeException("Null or empty currentSystemRoutes. Something is wrong.");
}
+ if (currentSystemSessionInfo == null) {
+ throw new RuntimeException("Null currentSystemSessionInfo. Something is wrong.");
+ }
+
for (MediaRoute2Info route : currentSystemRoutes) {
mRoutes.put(route.getId(), route);
}
+ mDefaultController = new DefaultRoutingController(currentSystemSessionInfo);
}
/**
@@ -341,6 +350,22 @@ public class MediaRouter2 {
}
/**
+ * Gets a {@link RoutingController} which can control the routes provided by system.
+ * e.g. Phone speaker, wired headset, Bluetooth, etc.
+ * <p>
+ * Note: The default controller can't be released. Calling {@link RoutingController#release()}
+ * will be no-op.
+ * <p>
+ * This method will always return the same instance.
+ *
+ * @hide
+ */
+ @NonNull
+ public RoutingController getDefaultController() {
+ return mDefaultController;
+ }
+
+ /**
* Sends a media control request to be performed asynchronously by the route's destination.
*
* @param route the route that will receive the control request
@@ -526,6 +551,15 @@ public class MediaRouter2 {
return;
}
+ if (sessionInfo.isSystemSession()) {
+ // The session info is sent from SystemMediaRoute2Provider.
+ RoutingController defaultController = getDefaultController();
+ RoutingSessionInfo oldInfo = defaultController.getRoutingSessionInfo();
+ defaultController.setRoutingSessionInfo(sessionInfo);
+ notifySessionInfoChanged(defaultController, oldInfo, sessionInfo);
+ return;
+ }
+
RoutingController matchingController;
synchronized (sRouterLock) {
matchingController = mRoutingControllers.get(sessionInfo.getId());
@@ -766,7 +800,7 @@ public class MediaRouter2 {
*
* @hide
*/
- public final class RoutingController {
+ public class RoutingController {
private final Object mControllerLock = new Object();
@GuardedBy("mControllerLock")
@@ -1074,7 +1108,6 @@ public class MediaRouter2 {
// TODO: This method uses two locks (mLock outside, sLock inside).
// Check if there is any possiblity of deadlock.
private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) {
-
List<MediaRoute2Info> routes = new ArrayList<>();
synchronized (sRouterLock) {
// TODO: Maybe able to change using Collection.stream()?
@@ -1089,6 +1122,23 @@ public class MediaRouter2 {
}
}
+ class DefaultRoutingController extends RoutingController {
+ DefaultRoutingController(@NonNull RoutingSessionInfo sessionInfo) {
+ super(sessionInfo);
+ }
+
+ @Override
+ public void release() {
+ // Do nothing. DefaultRoutingController will never be released
+ }
+
+ @Override
+ public boolean isReleased() {
+ // DefaultRoutingController will never be released
+ return false;
+ }
+ }
+
final class RouteCallbackRecord {
public final Executor mExecutor;
public final RouteCallback mRouteCallback;
diff --git a/media/java/android/media/MediaRouter2Utils.java b/media/java/android/media/MediaRouter2Utils.java
index 49045828dbe8..c15972dcff2e 100644
--- a/media/java/android/media/MediaRouter2Utils.java
+++ b/media/java/android/media/MediaRouter2Utils.java
@@ -29,9 +29,6 @@ public class MediaRouter2Utils {
static final String TAG = "MR2Utils";
static final String SEPARATOR = ":";
- /**
- * @hide
- */
@NonNull
public static String toUniqueId(@NonNull String providerId, @NonNull String id) {
if (TextUtils.isEmpty(providerId)) {
@@ -49,8 +46,6 @@ public class MediaRouter2Utils {
/**
* Gets provider ID from unique ID.
* If the corresponding provider ID could not be generated, it will return null.
- *
- * @hide
*/
@Nullable
public static String getProviderId(@NonNull String uniqueId) {
@@ -75,8 +70,6 @@ public class MediaRouter2Utils {
/**
* Gets the original ID (i.e. non-unique route/session ID) from unique ID.
* If the corresponding ID could not be generated, it will return null.
- *
- * @hide
*/
@Nullable
public static String getOriginalId(@NonNull String uniqueId) {
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 228addebe029..cb996f493eb4 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -59,6 +59,7 @@ public final class RoutingSessionInfo implements Parcelable {
final List<String> mTransferrableRoutes;
@Nullable
final Bundle mControlHints;
+ final boolean mIsSystemSession;
RoutingSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -78,6 +79,7 @@ public final class RoutingSessionInfo implements Parcelable {
convertToUniqueRouteIds(builder.mTransferrableRoutes));
mControlHints = builder.mControlHints;
+ mIsSystemSession = builder.mIsSystemSession;
}
RoutingSessionInfo(@NonNull Parcel src) {
@@ -93,6 +95,7 @@ public final class RoutingSessionInfo implements Parcelable {
mTransferrableRoutes = ensureList(src.createStringArrayList());
mControlHints = src.readBundle();
+ mIsSystemSession = src.readBoolean();
}
private static String ensureString(String str) {
@@ -193,6 +196,15 @@ public final class RoutingSessionInfo implements Parcelable {
return mControlHints;
}
+ /**
+ * Gets whether this session is in system media route provider.
+ * @hide
+ */
+ @Nullable
+ public boolean isSystemSession() {
+ return mIsSystemSession;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -208,6 +220,7 @@ public final class RoutingSessionInfo implements Parcelable {
dest.writeStringList(mDeselectableRoutes);
dest.writeStringList(mTransferrableRoutes);
dest.writeBundle(mControlHints);
+ dest.writeBoolean(mIsSystemSession);
}
@Override
@@ -278,6 +291,7 @@ public final class RoutingSessionInfo implements Parcelable {
* Builder class for {@link RoutingSessionInfo}.
*/
public static final class Builder {
+ // TODO: Reorder these (important ones first)
final String mId;
final String mClientPackageName;
String mProviderId;
@@ -286,6 +300,7 @@ public final class RoutingSessionInfo implements Parcelable {
final List<String> mDeselectableRoutes;
final List<String> mTransferrableRoutes;
Bundle mControlHints;
+ boolean mIsSystemSession;
/**
* Constructor for builder to create {@link RoutingSessionInfo}.
@@ -333,6 +348,7 @@ public final class RoutingSessionInfo implements Parcelable {
mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
mControlHints = sessionInfo.mControlHints;
+ mIsSystemSession = sessionInfo.mIsSystemSession;
}
/**
@@ -491,6 +507,16 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
+ * Sets whether this session is in system media route provider.
+ * @hide
+ */
+ @NonNull
+ public Builder setSystemSession(boolean isSystemSession) {
+ mIsSystemSession = isSystemSession;
+ return this;
+ }
+
+ /**
* Builds a routing session info.
*
* @throws IllegalArgumentException if no selected routes are added.
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 32d03db6f7ee..a9fcf7703cfc 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -692,6 +692,14 @@ public class MediaRouter2Test {
}
}
+ // TODO: Consider adding tests with bluetooth connection/disconnection.
+ @Test
+ public void testGetDefaultController() {
+ final RoutingController defaultController = mRouter2.getDefaultController();
+ assertNotNull(defaultController);
+ assertFalse(defaultController.isReleased());
+ }
+
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index cba8452fe9c2..39313eec6bf8 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -227,7 +227,7 @@ public class MediaRouterManagerTest {
mManager.selectRoute(mPackageName, routeToSelect);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(1, mManager.getActiveSessions().size());
+ assertEquals(2, mManager.getActiveSessions().size());
}
@Test
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index b67335ab82bc..9f42c3625a51 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -17,6 +17,7 @@
package com.android.server.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -58,6 +59,7 @@ class BluetoothRouteProvider {
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
+ // TODO: The mActiveDevice should be set when BluetoothRouteProvider is created.
private BluetoothDevice mActiveDevice = null;
static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
@@ -125,6 +127,14 @@ class BluetoothRouteProvider {
return routes;
}
+ @Nullable String getActiveDeviceAddress() {
+ BluetoothDevice device = mActiveDevice;
+ if (device == null) {
+ return null;
+ }
+ return device.getAddress();
+ }
+
private void notifyBluetoothRoutesUpdated() {
if (mListener != null) {
mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
@@ -281,8 +291,8 @@ class BluetoothRouteProvider {
setRouteConnectionStateForDevice(device,
MediaRoute2Info.CONNECTION_STATE_CONNECTED);
}
- notifyBluetoothRoutesUpdated();
mActiveDevice = device;
+ notifyBluetoothRoutesUpdated();
}
break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 216753017010..f9169ee69d41 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -112,6 +112,30 @@ class MediaRouter2ServiceImpl {
}
}
+ @NonNull
+ public RoutingSessionInfo getSystemSessionInfo() {
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ RoutingSessionInfo systemSessionInfo = null;
+ synchronized (mLock) {
+ UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+ List<RoutingSessionInfo> sessionInfos =
+ userRecord.mHandler.mSystemProvider.getSessionInfos();
+ if (sessionInfos != null && !sessionInfos.isEmpty()) {
+ systemSessionInfo = sessionInfos.get(0);
+ } else {
+ Slog.w(TAG, "System provider does not have any session info.");
+ }
+ }
+ return systemSessionInfo;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void registerClient(@NonNull IMediaRouter2Client client,
@NonNull String packageName) {
Objects.requireNonNull(client, "client must not be null");
@@ -1351,6 +1375,16 @@ class MediaRouter2ServiceImpl {
List<IMediaRouter2Manager> managers = getManagers();
notifySessionInfosChangedToManagers(managers);
+ // For system provider, notify all clients.
+ if (provider == mSystemProvider) {
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ if (service == null) {
+ return;
+ }
+ notifySessionInfoChangedToClients(getClients(), sessionInfo);
+ return;
+ }
+
Client2Record client2Record = mSessionToClientMap.get(
sessionInfo.getId());
if (client2Record == null) {
@@ -1509,6 +1543,17 @@ class MediaRouter2ServiceImpl {
}
}
+ private void notifySessionInfoChangedToClients(List<IMediaRouter2Client> clients,
+ RoutingSessionInfo sessionInfo) {
+ for (IMediaRouter2Client client : clients) {
+ try {
+ client.notifySessionInfoChanged(sessionInfo);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify session info changed. Client probably died.", ex);
+ }
+ }
+ }
+
private void notifyRoutesToManager(IMediaRouter2Manager manager) {
List<MediaRoute2Info> routes = new ArrayList<>();
for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 5437fadf2d74..e1d38039b955 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -444,6 +444,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
+ public RoutingSessionInfo getSystemSessionInfo() {
+ return mService2.getSystemSessionInfo();
+ }
+
+ // Binder call
+ @Override
public void registerClient2(IMediaRouter2Client client, String packageName) {
final int uid = Binder.getCallingUid();
if (!validatePackageName(uid, packageName)) {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 56c33fe339ea..ebea6c3dc558 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -26,17 +26,20 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
@@ -46,7 +49,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
- static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE";
+ static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
// TODO: Move these to a proper place
public static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
@@ -92,6 +95,14 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
mBluetoothRoutes = routes;
publishRoutes();
+
+ boolean sessionInfoChanged;
+ synchronized (mLock) {
+ sessionInfoChanged = updateSessionInfosIfNeededLocked();
+ }
+ if (sessionInfoChanged) {
+ notifySessionInfoUpdated();
+ }
});
initializeRoutes();
}
@@ -172,6 +183,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
setProviderState(builder.build());
mHandler.post(() -> notifyProviderState());
+
+ // Note: No lock needed when initializing.
+ updateSessionInfosIfNeededLocked();
}
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
@@ -203,6 +217,35 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
/**
+ * Updates the mSessionInfo. Returns true if the session info is changed.
+ */
+ boolean updateSessionInfosIfNeededLocked() {
+ RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
+
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, "" /* clientPackageName */)
+ .setSystemSession(true);
+ String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+
+ RoutingSessionInfo newSessionInfo;
+ if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
+ // Bluetooth route. Set the route ID with the device's address.
+ newSessionInfo = builder.addSelectedRoute(activeBtDeviceAddress).build();
+ } else {
+ // Default device
+ newSessionInfo = builder.addSelectedRoute(mDefaultRoute.getId()).build();
+ }
+
+ if (Objects.equals(oldSessionInfo, newSessionInfo)) {
+ return false;
+ } else {
+ mSessionInfos.clear();
+ mSessionInfos.add(newSessionInfo);
+ return true;
+ }
+ }
+
+ /**
* The first route should be the currently selected system route.
* For example, if there are two system routes (BT and device speaker),
* BT will be the first route in the list.
@@ -215,4 +258,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
setAndNotifyProviderState(builder.build());
}
+
+ void notifySessionInfoUpdated() {
+ RoutingSessionInfo sessionInfo;
+ synchronized (mLock) {
+ sessionInfo = mSessionInfos.get(0);
+ }
+ mCallback.onSessionUpdated(this, sessionInfo);
+ }
}