summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kyunglyul Hyun <klhyun@google.com> 2020-01-21 16:30:28 +0900
committer Kyunglyul Hyun <klhyun@google.com> 2020-01-22 20:46:22 +0900
commit581fc98d65bc8426d08359c5f03c29df424572d1 (patch)
tree1fcdf80759b28911058cec44f24ff6548fbdfc19
parent0e4d731bbcb9b6b6a4310c83aea9a4fab0e1c35f (diff)
MediaRouter: enable transferring from/to BT devices.
The main objective of this CL is to enable selecting BT device. For that it updates MRM and SystemMediaRoute2Provider such that - MRM.getRoutingControllers() returns a list including the system session - SystemMediaRoute2Provider marks routes as "transferable" - SystemMediaRoute2Provider sets provider id correctly - SystemMediaRoute2Provider handles transferToRoute() Bug: 147979868 Bug: 147122575 Test: atest mediaroutertest && manually selecting phone / bt devices from output switcher Change-Id: I2e2032fd6677f79b9f864c313c40846daa87f113
-rw-r--r--media/java/android/media/MediaRouter2Manager.java15
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java12
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java57
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java46
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java47
5 files changed, 119 insertions, 58 deletions
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5cb32d6ab550..61e2f77b7d2e 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -178,8 +178,9 @@ public class MediaRouter2Manager {
/**
* Gets routing controllers of an application with the given package name.
- * If the application isn't running or it doesn't use {@link MediaRouter2}, an empty list
- * will be returned.
+ * The first element of the returned list is the system routing controller.
+ *
+ * @see MediaRouter2#getSystemController()
*/
@NonNull
public List<RoutingController> getRoutingControllers(@NonNull String packageName) {
@@ -188,7 +189,8 @@ public class MediaRouter2Manager {
List<RoutingController> controllers = new ArrayList<>();
for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
- if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
+ if (sessionInfo.isSystemSession()
+ || TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
controllers.add(new RoutingController(sessionInfo));
}
}
@@ -196,8 +198,11 @@ public class MediaRouter2Manager {
}
/**
- * Gets the list of all active routing sessions. It doesn't include default routing sessions
- * of applications.
+ * Gets the list of all active routing sessions.
+ * The first element of the list is the system routing session containing
+ * phone speakers, wired headset, Bluetooth devices.
+ * The system routing session is shared by apps such that controlling it will affect
+ * all apps.
*/
@NonNull
public List<RoutingSessionInfo> getActiveSessions() {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 7726e90674f2..4a2044af0431 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -246,7 +246,7 @@ public class MediaRouterManagerTest {
}
});
- assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
+ assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -254,14 +254,14 @@ public class MediaRouterManagerTest {
List<MediaRouter2Manager.RoutingController> controllers =
mManager.getRoutingControllers(mPackageName);
- assertEquals(1, controllers.size());
+ assertEquals(2, controllers.size());
- MediaRouter2Manager.RoutingController routingController = controllers.get(0);
+ MediaRouter2Manager.RoutingController routingController = controllers.get(1);
awaitOnRouteChangedManager(
() -> routingController.release(),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), null));
- assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
+ assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
}
/**
@@ -290,8 +290,8 @@ public class MediaRouterManagerTest {
List<MediaRouter2Manager.RoutingController> controllers =
mManager.getRoutingControllers(mPackageName);
- assertEquals(1, controllers.size());
- MediaRouter2Manager.RoutingController routingController = controllers.get(0);
+ assertEquals(2, controllers.size());
+ MediaRouter2Manager.RoutingController routingController = controllers.get(1);
awaitOnRouteChangedManager(
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 837c489c0cac..8179c32ae2df 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -29,12 +29,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaRoute2Info;
import android.text.TextUtils;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.R;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -59,7 +60,6 @@ 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,
@@ -104,6 +104,43 @@ class BluetoothRouteProvider {
mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
}
+ /**
+ * Clears the active device for all known profiles.
+ */
+ public void clearActiveDevices() {
+ BluetoothA2dp a2dpProfile = mA2dpProfile;
+ BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
+ if (a2dpProfile != null) {
+ a2dpProfile.setActiveDevice(null);
+ }
+ if (hearingAidProfile != null) {
+ hearingAidProfile.setActiveDevice(null);
+ }
+ }
+
+ /**
+ * Sets the active device.
+ * @param deviceId the id of the Bluetooth device
+ */
+ public void setActiveDevice(@NonNull String deviceId) {
+ BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(deviceId);
+ if (btRouteInfo == null) {
+ Slog.w(TAG, "setActiveDevice: unknown device id=" + deviceId);
+ return;
+ }
+ BluetoothA2dp a2dpProfile = mA2dpProfile;
+ BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
+
+ if (a2dpProfile != null
+ && btRouteInfo.connectedProfiles.get(BluetoothProfile.A2DP, false)) {
+ a2dpProfile.setActiveDevice(btRouteInfo.btDevice);
+ }
+ if (hearingAidProfile != null
+ && btRouteInfo.connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
+ hearingAidProfile.setActiveDevice(btRouteInfo.btDevice);
+ }
+ }
+
private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
mEventReceiverMap.put(action, eventReceiver);
mIntentFilter.addAction(action);
@@ -157,12 +194,12 @@ class BluetoothRouteProvider {
private void setRouteConnectionStateForDevice(BluetoothDevice device,
@MediaRoute2Info.ConnectionState int state) {
if (device == null) {
- Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
+ Slog.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
return;
}
BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
if (btRoute == null) {
- Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+ Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
return;
}
if (btRoute.route.getConnectionState() != state) {
@@ -184,24 +221,36 @@ class BluetoothRouteProvider {
// These callbacks run on the main thread.
private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ List<BluetoothDevice> activeDevices;
switch (profile) {
case BluetoothProfile.A2DP:
mA2dpProfile = (BluetoothA2dp) proxy;
+ // It may contain null.
+ activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice());
break;
case BluetoothProfile.HEARING_AID:
mHearingAidProfile = (BluetoothHearingAid) proxy;
+ activeDevices = mHearingAidProfile.getActiveDevices();
break;
default:
return;
}
+ //TODO: Check a pair of HAP devices whether there exist two or more active devices.
for (BluetoothDevice device : proxy.getConnectedDevices()) {
BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
if (btRoute == null) {
btRoute = createBluetoothRoute(device);
mBluetoothRoutes.put(device.getAddress(), btRoute);
}
+ if (activeDevices.contains(device)) {
+ mActiveDevice = device;
+ setRouteConnectionStateForDevice(device,
+ MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+ }
+
btRoute.connectedProfiles.put(profile, true);
}
+ notifyBluetoothRoutesUpdated();
}
public void onServiceDisconnected(int profile) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index f9169ee69d41..9594659f23d0 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -760,17 +760,12 @@ class MediaRouter2ServiceImpl {
Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown manager.");
return;
}
- //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
Client2Record clientRecord = managerRecord.mUserRecord.mHandler
.findClientforSessionLocked(sessionId);
- if (clientRecord == null) {
- Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown session.");
- return;
- }
- clientRecord.mUserRecord.mHandler.sendMessage(
+ managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::selectRouteOnHandler,
- clientRecord.mUserRecord.mHandler,
+ managerRecord.mUserRecord.mHandler,
clientRecord, sessionId, route));
}
@@ -783,17 +778,12 @@ class MediaRouter2ServiceImpl {
Slog.w(TAG, "deselectClientRouteLocked: Ignoring unknown manager.");
return;
}
- //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
Client2Record clientRecord = managerRecord.mUserRecord.mHandler
.findClientforSessionLocked(sessionId);
- if (clientRecord == null) {
- Slog.w(TAG, "deslectClientRouteLocked: Ignoring unknown session.");
- return;
- }
- clientRecord.mUserRecord.mHandler.sendMessage(
+ managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::deselectRouteOnHandler,
- clientRecord.mUserRecord.mHandler,
+ managerRecord.mUserRecord.mHandler,
clientRecord, sessionId, route));
}
@@ -806,17 +796,12 @@ class MediaRouter2ServiceImpl {
Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown manager.");
return;
}
- //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
Client2Record clientRecord = managerRecord.mUserRecord.mHandler
.findClientforSessionLocked(sessionId);
- if (clientRecord == null) {
- Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown session.");
- return;
- }
- clientRecord.mUserRecord.mHandler.sendMessage(
+ managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::transferToRouteOnHandler,
- clientRecord.mUserRecord.mHandler,
+ managerRecord.mUserRecord.mHandler,
clientRecord, sessionId, route));
}
@@ -1166,7 +1151,7 @@ class MediaRouter2ServiceImpl {
requestId, sessionHints);
}
- private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
+ private void selectRouteOnHandler(@Nullable Client2Record clientRecord,
String uniqueSessionId, MediaRoute2Info route) {
if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route,
"selecting")) {
@@ -1182,7 +1167,7 @@ class MediaRouter2ServiceImpl {
provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
}
- private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
+ private void deselectRouteOnHandler(@Nullable Client2Record clientRecord,
String uniqueSessionId, MediaRoute2Info route) {
if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route,
"deselecting")) {
@@ -1198,7 +1183,7 @@ class MediaRouter2ServiceImpl {
provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
}
- private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
+ private void transferToRouteOnHandler(Client2Record clientRecord,
String uniqueSessionId, MediaRoute2Info route) {
if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route,
"transferring to")) {
@@ -1215,7 +1200,7 @@ class MediaRouter2ServiceImpl {
route.getOriginalId());
}
- private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord,
+ private boolean checkArgumentsForSessionControl(@Nullable Client2Record clientRecord,
String uniqueSessionId, MediaRoute2Info route, @NonNull String description) {
if (route == null) {
Slog.w(TAG, "Ignoring " + description + " null route");
@@ -1236,6 +1221,17 @@ class MediaRouter2ServiceImpl {
return false;
}
+ // Bypass checking client if it's the system session (clientRecord should be null)
+ if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) {
+ return true;
+ }
+
+ //TODO: Handle RCN case.
+ if (clientRecord == null) {
+ Slog.w(TAG, "Ignoring " + description + " route from unknown client.");
+ return false;
+ }
+
Client2Record matchingRecord = mSessionToClientMap.get(uniqueSessionId);
if (matchingRecord != clientRecord) {
Slog.w(TAG, "Ignoring " + description + " route from non-matching client. "
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6f6d8a1e8df4..558eb8d05783 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -64,7 +64,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
SystemMediaRoute2Provider.class.getPackageName$(),
SystemMediaRoute2Provider.class.getName());
- //TODO: Clean up these when audio manager support multiple bt devices
MediaRoute2Info mDefaultRoute;
@NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
@@ -91,6 +90,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
+ initializeDefaultRoute();
mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
mBluetoothRoutes = routes;
publishRoutes();
@@ -103,7 +103,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
notifySessionInfoUpdated();
}
});
- initializeRoutes();
+ initializeSessionInfo();
}
@Override
@@ -119,17 +119,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@Override
public void selectRoute(String sessionId, String routeId) {
- //TODO: implement method
+ // Do nothing since we don't support multiple BT yet.
}
@Override
public void deselectRoute(String sessionId, String routeId) {
- //TODO: implement method
+ // Do nothing since we don't support multiple BT yet.
}
@Override
public void transferToRoute(String sessionId, String routeId) {
- //TODO: implement method
+ if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
+ mBtRouteProvider.clearActiveDevices();
+ } else {
+ mBtRouteProvider.setActiveDevice(routeId);
+ }
}
//TODO: implement method
@@ -147,8 +151,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
public void requestUpdateVolume(String routeId, int delta) {
}
- void initializeRoutes() {
- //TODO: adds necessary info
+ private void initializeDefaultRoute() {
mDefaultRoute = new MediaRoute2Info.Builder(
DEFAULT_ROUTE_ID,
mContext.getResources().getText(R.string.default_audio_route_name).toString())
@@ -172,7 +175,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
// route yet.
updateAudioRoutes(newAudioRoutes);
}
+ }
+ private void initializeSessionInfo() {
mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
@@ -183,11 +188,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
setProviderState(builder.build());
mHandler.post(() -> notifyProviderState());
- // Note: No lock needed when initializing.
- updateSessionInfosIfNeededLocked();
+ //TODO: clean up this
+ // This is required because it is not instantiated in the main thread and
+ // BluetoothRoutesUpdatedListener can be called before this function
+ synchronized (mLock) {
+ updateSessionInfosIfNeededLocked();
+ }
}
- void updateAudioRoutes(AudioRoutesInfo newRoutes) {
+ private void updateAudioRoutes(AudioRoutesInfo newRoutes) {
int name = R.string.default_audio_route_name;
mCurAudioRoutesInfo.mainType = newRoutes.mainType;
if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
@@ -226,15 +235,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
.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();
+ builder.addSelectedRoute(activeBtDeviceAddress);
+ builder.addTransferrableRoute(mDefaultRoute.getId());
} else {
// Default device
- newSessionInfo = builder.addSelectedRoute(mDefaultRoute.getId()).build();
+ builder.addSelectedRoute(mDefaultRoute.getId());
}
+ for (MediaRoute2Info route : mBluetoothRoutes) {
+ if (!TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+ builder.addTransferrableRoute(route.getId());
+ }
+ }
+
+ RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
if (Objects.equals(oldSessionInfo, newSessionInfo)) {
return false;
} else {
@@ -244,11 +260,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
}
- /**
- * 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.
- */
void publishRoutes() {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
builder.addRoute(mDefaultRoute);