Add basic functionality of MR2Manager in MR2

As a first step of adding MR2Manager functionality in MR2,
this CL makes MR2 able to:
 - get routes for specific package
 - make transfer / stop

For now, the future system APIs are not marked as @SystemApi yet.
They will be opened with adding CTS tests,
once the implementation is complete.

Bug: 181122201
Test: Manually tested transfer/stop using Support7Demos.
      Test code will be added when MR2Manager's callback functionality
      is implemented.
Change-Id: I71bea98b0ff3e42d193cf04c1b1735fe50c7d673
This commit is contained in:
Hyundo Moon
2021-03-08 13:10:43 +09:00
parent 7e3f7d1061
commit dd4780eba1
2 changed files with 98 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -66,6 +67,10 @@ public final class MediaRouter2 {
// The manager request ID representing that no manager is involved.
private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE;
@GuardedBy("sRouterLock")
private static Map<String, MediaRouter2> sMediaRouter2Map = new ArrayMap<>();
private static MediaRouter2Manager sManager;
@GuardedBy("sRouterLock")
private static MediaRouter2 sInstance;
@@ -82,7 +87,9 @@ public final class MediaRouter2 {
private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests =
new CopyOnWriteArrayList<>();
private final String mClientPackageName;
private final String mPackageName;
@GuardedBy("sRouterLock")
final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>();
@@ -120,6 +127,42 @@ public final class MediaRouter2 {
}
}
/**
* Gets an instance of the media router which controls the app's media routing.
* Returns {@code null} if the given package name is invalid.
*
* @param clientPackageName the package name of the app to control
* @hide
*/
//@SystemApi
@Nullable
public static MediaRouter2 getInstance(@NonNull Context context,
@NonNull String clientPackageName) {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(clientPackageName, 0);
} catch (PackageManager.NameNotFoundException ex) {
Log.e(TAG, "Package " + clientPackageName + " not found. Ignoring.");
return null;
}
synchronized (sRouterLock) {
MediaRouter2 instance = sMediaRouter2Map.get(clientPackageName);
if (instance == null) {
// TODO: Add permission check here using MODIFY_AUDIO_ROUTING.
if (sManager == null) {
sManager = MediaRouter2Manager.getInstance(context.getApplicationContext());
}
instance = new MediaRouter2(context, clientPackageName);
sMediaRouter2Map.put(clientPackageName, instance);
}
return instance;
}
}
private MediaRouter2(Context appContext) {
mContext = appContext;
mMediaRouterService = IMediaRouterService.Stub.asInterface(
@@ -148,6 +191,17 @@ public final class MediaRouter2 {
mRoutes.put(route.getId(), route);
}
mSystemController = new SystemRoutingController(currentSystemSessionInfo);
mClientPackageName = null;
}
private MediaRouter2(Context context, String clientPackageName) {
mClientPackageName = clientPackageName;
mContext = context;
mMediaRouterService = null;
mPackageName = null;
mHandler = new Handler(Looper.getMainLooper());
mSystemController = null;
}
/**
@@ -165,6 +219,19 @@ public final class MediaRouter2 {
return false;
}
/**
* Gets the target package name of the app which this media router controls.
* This is only non-null when the router instance is created with the target package name.
*
* @see #getInstance(Context, String)
* @hide
*/
//@SystemApi
@Nullable
public String getClientPackageName() {
return mClientPackageName;
}
/**
* Registers a callback to discover routes and to receive events when they change.
* <p>
@@ -270,6 +337,10 @@ public final class MediaRouter2 {
*/
@NonNull
public List<MediaRoute2Info> getRoutes() {
if (mClientPackageName != null) {
return sManager.getAvailableRoutes(mClientPackageName);
}
synchronized (sRouterLock) {
if (mShouldUpdateRoutes) {
mShouldUpdateRoutes = false;
@@ -378,6 +449,11 @@ public final class MediaRouter2 {
* @see TransferCallback#onTransferFailure
*/
public void transferTo(@NonNull MediaRoute2Info route) {
if (mClientPackageName != null) {
sManager.selectRoute(mClientPackageName, route);
return;
}
Objects.requireNonNull(route, "route must not be null");
Log.v(TAG, "Transferring to route: " + route);
transfer(getCurrentController(), route);
@@ -388,6 +464,12 @@ public final class MediaRouter2 {
* controls the media routing, this method is a no-op.
*/
public void stop() {
if (mClientPackageName != null) {
List<RoutingSessionInfo> sessionInfos = sManager.getRoutingSessions(mClientPackageName);
RoutingSessionInfo sessionToRelease = sessionInfos.get(sessionInfos.size() - 1);
sManager.releaseSession(sessionToRelease);
return;
}
getCurrentController().release();
}
@@ -397,7 +479,13 @@ public final class MediaRouter2 {
* @param route the route you want to transfer the media to.
* @hide
*/
void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
//@SystemApi
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
if (mClientPackageName != null) {
sManager.transfer(controller.getRoutingSessionInfo(), route);
return;
}
Objects.requireNonNull(controller, "controller must not be null");
Objects.requireNonNull(route, "route must not be null");
@@ -486,6 +574,14 @@ public final class MediaRouter2 {
*/
@NonNull
public List<RoutingController> getControllers() {
// TODO: Do not create the controller instances every time,
// Instead, update the list using the sessions' ID and session related callbacks.
if (mClientPackageName != null) {
return sManager.getRoutingSessions(mClientPackageName).stream()
.map(info -> new RoutingController(info))
.collect(Collectors.toList());
}
List<RoutingController> result = new ArrayList<>();
result.add(0, mSystemController);
synchronized (sRouterLock) {

View File

@@ -326,6 +326,7 @@ public final class RoutingSessionInfo implements Parcelable {
.append("RoutingSessionInfo{ ")
.append("sessionId=").append(getId())
.append(", name=").append(getName())
.append(", clientPackageName=").append(getClientPackageName())
.append(", selectedRoutes={")
.append(String.join(",", getSelectedRoutes()))
.append("}")