Merge "Add DISABLE_REASON_APP_ONLY and parameter to navigate to app"
This commit is contained in:
@@ -24326,15 +24326,19 @@ package android.media {
|
||||
|
||||
public final class RouteListingPreference implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @Nullable public android.content.ComponentName getInAppOnlyItemRoutingReceiver();
|
||||
method @NonNull public java.util.List<android.media.RouteListingPreference.Item> getItems();
|
||||
method public boolean getUseSystemOrdering();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference> CREATOR;
|
||||
field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
|
||||
}
|
||||
|
||||
public static final class RouteListingPreference.Builder {
|
||||
ctor public RouteListingPreference.Builder();
|
||||
method @NonNull public android.media.RouteListingPreference build();
|
||||
method @NonNull public android.media.RouteListingPreference.Builder setInAppOnlyItemRoutingReceiver(@Nullable android.content.ComponentName);
|
||||
method @NonNull public android.media.RouteListingPreference.Builder setItems(@NonNull java.util.List<android.media.RouteListingPreference.Item>);
|
||||
method @NonNull public android.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
|
||||
}
|
||||
@@ -24349,6 +24353,7 @@ package android.media {
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
|
||||
field public static final int DISABLE_REASON_AD = 3; // 0x3
|
||||
field public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2; // 0x2
|
||||
field public static final int DISABLE_REASON_IN_APP_ONLY = 4; // 0x4
|
||||
field public static final int DISABLE_REASON_NONE = 0; // 0x0
|
||||
field public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1; // 0x1
|
||||
field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
|
||||
|
||||
@@ -19,6 +19,9 @@ package android.media;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
@@ -40,6 +43,18 @@ import java.util.Objects;
|
||||
*/
|
||||
public final class RouteListingPreference implements Parcelable {
|
||||
|
||||
/**
|
||||
* {@link Intent} action for apps to take the user to a screen for transferring media playback
|
||||
* to the route with the id provided by the extra with key {@link #EXTRA_ROUTE_ID}.
|
||||
*/
|
||||
public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
|
||||
|
||||
/**
|
||||
* {@link Intent} string extra key that contains the {@link Item#getRouteId() id} of the route
|
||||
* to transfer to, as part of an {@link #ACTION_TRANSFER_MEDIA} intent.
|
||||
*/
|
||||
public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
|
||||
|
||||
@NonNull
|
||||
public static final Creator<RouteListingPreference> CREATOR =
|
||||
new Creator<>() {
|
||||
@@ -56,10 +71,12 @@ public final class RouteListingPreference implements Parcelable {
|
||||
|
||||
@NonNull private final List<Item> mItems;
|
||||
private final boolean mUseSystemOrdering;
|
||||
@Nullable private final ComponentName mInAppOnlyItemRoutingReceiver;
|
||||
|
||||
private RouteListingPreference(Builder builder) {
|
||||
mItems = builder.mItems;
|
||||
mUseSystemOrdering = builder.mUseSystemOrdering;
|
||||
mInAppOnlyItemRoutingReceiver = builder.mInAppOnlyItemRoutingReceiver;
|
||||
}
|
||||
|
||||
private RouteListingPreference(Parcel in) {
|
||||
@@ -67,6 +84,7 @@ public final class RouteListingPreference implements Parcelable {
|
||||
in.readParcelableList(new ArrayList<>(), Item.class.getClassLoader(), Item.class);
|
||||
mItems = List.copyOf(items);
|
||||
mUseSystemOrdering = in.readBoolean();
|
||||
mInAppOnlyItemRoutingReceiver = ComponentName.readFromParcel(in);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +108,21 @@ public final class RouteListingPreference implements Parcelable {
|
||||
return mUseSystemOrdering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ComponentName} for handling routes disabled via {@link
|
||||
* Item#DISABLE_REASON_IN_APP_ONLY}, or null if the user needs to manually navigate to the app
|
||||
* in order to route to select the corresponding routes.
|
||||
*
|
||||
* <p>If the user selects an {@link Item} disabled via {@link Item#DISABLE_REASON_IN_APP_ONLY},
|
||||
* and this method returns a non-null {@link ComponentName}, the system takes the user back to
|
||||
* the app by launching an intent to the returned {@link ComponentName}, using action {@link
|
||||
* #ACTION_TRANSFER_MEDIA}, with the extra {@link #EXTRA_ROUTE_ID}.
|
||||
*/
|
||||
@Nullable
|
||||
public ComponentName getInAppOnlyItemRoutingReceiver() {
|
||||
return mInAppOnlyItemRoutingReceiver;
|
||||
}
|
||||
|
||||
// RouteListingPreference Parcelable implementation.
|
||||
|
||||
@Override
|
||||
@@ -101,6 +134,7 @@ public final class RouteListingPreference implements Parcelable {
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelableList(mItems, flags);
|
||||
dest.writeBoolean(mUseSystemOrdering);
|
||||
ComponentName.writeToParcel(mInAppOnlyItemRoutingReceiver, dest);
|
||||
}
|
||||
|
||||
// Equals and hashCode.
|
||||
@@ -114,12 +148,15 @@ public final class RouteListingPreference implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
RouteListingPreference that = (RouteListingPreference) other;
|
||||
return mItems.equals(that.mItems) && mUseSystemOrdering == that.mUseSystemOrdering;
|
||||
return mItems.equals(that.mItems)
|
||||
&& mUseSystemOrdering == that.mUseSystemOrdering
|
||||
&& Objects.equals(
|
||||
mInAppOnlyItemRoutingReceiver, that.mInAppOnlyItemRoutingReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mItems, mUseSystemOrdering);
|
||||
return Objects.hash(mItems, mUseSystemOrdering, mInAppOnlyItemRoutingReceiver);
|
||||
}
|
||||
|
||||
/** Builder for {@link RouteListingPreference}. */
|
||||
@@ -127,6 +164,7 @@ public final class RouteListingPreference implements Parcelable {
|
||||
|
||||
private List<Item> mItems;
|
||||
private boolean mUseSystemOrdering;
|
||||
private ComponentName mInAppOnlyItemRoutingReceiver;
|
||||
|
||||
/** Creates a new instance with default values (documented in the setters). */
|
||||
public Builder() {
|
||||
@@ -158,6 +196,18 @@ public final class RouteListingPreference implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #getInAppOnlyItemRoutingReceiver()}.
|
||||
*
|
||||
* <p>The default value is {@code null}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setInAppOnlyItemRoutingReceiver(
|
||||
@Nullable ComponentName inAppOnlyItemRoutingReceiver) {
|
||||
mInAppOnlyItemRoutingReceiver = inAppOnlyItemRoutingReceiver;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link RouteListingPreference} instance with the given
|
||||
* parameters.
|
||||
@@ -203,7 +253,8 @@ public final class RouteListingPreference implements Parcelable {
|
||||
DISABLE_REASON_NONE,
|
||||
DISABLE_REASON_SUBSCRIPTION_REQUIRED,
|
||||
DISABLE_REASON_DOWNLOADED_CONTENT,
|
||||
DISABLE_REASON_AD
|
||||
DISABLE_REASON_AD,
|
||||
DISABLE_REASON_IN_APP_ONLY
|
||||
})
|
||||
public @interface DisableReason {}
|
||||
|
||||
@@ -221,6 +272,14 @@ public final class RouteListingPreference implements Parcelable {
|
||||
public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2;
|
||||
/** The corresponding route is not available because an ad is in progress. */
|
||||
public static final int DISABLE_REASON_AD = 3;
|
||||
/**
|
||||
* The corresponding route is only available for routing from within the app.
|
||||
*
|
||||
* <p>The user may still select the corresponding route if the app provides an {@link
|
||||
* #getInAppOnlyItemRoutingReceiver() in-app routing receiver}, in which case the system
|
||||
* will take the user to the app.
|
||||
*/
|
||||
public static final int DISABLE_REASON_IN_APP_ONLY = 4;
|
||||
|
||||
@NonNull
|
||||
public static final Creator<Item> CREATOR =
|
||||
@@ -257,7 +316,11 @@ public final class RouteListingPreference implements Parcelable {
|
||||
Preconditions.checkArgument(mSessionParticipantCount >= 0);
|
||||
}
|
||||
|
||||
/** Returns the id of the route that corresponds to this route listing preference item. */
|
||||
/**
|
||||
* Returns the id of the route that corresponds to this route listing preference item.
|
||||
*
|
||||
* @see MediaRoute2Info#getId()
|
||||
*/
|
||||
@NonNull
|
||||
public String getRouteId() {
|
||||
return mRouteId;
|
||||
@@ -282,6 +345,7 @@ public final class RouteListingPreference implements Parcelable {
|
||||
* @see #DISABLE_REASON_SUBSCRIPTION_REQUIRED
|
||||
* @see #DISABLE_REASON_DOWNLOADED_CONTENT
|
||||
* @see #DISABLE_REASON_AD
|
||||
* @see #DISABLE_REASON_IN_APP_ONLY
|
||||
*/
|
||||
@DisableReason
|
||||
public int getDisableReason() {
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
@@ -285,6 +286,15 @@ class MediaRouter2ServiceImpl {
|
||||
public void setRouteListingPreference(
|
||||
@NonNull IMediaRouter2 router,
|
||||
@Nullable RouteListingPreference routeListingPreference) {
|
||||
ComponentName inAppOnlyItemRoutingReceiver =
|
||||
routeListingPreference != null
|
||||
? routeListingPreference.getInAppOnlyItemRoutingReceiver()
|
||||
: null;
|
||||
if (inAppOnlyItemRoutingReceiver != null) {
|
||||
MediaServerUtils.enforcePackageName(
|
||||
inAppOnlyItemRoutingReceiver.getPackageName(), Binder.getCallingUid());
|
||||
}
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
@@ -787,6 +797,12 @@ class MediaRouter2ServiceImpl {
|
||||
obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
|
||||
routerRecord.mUserRecord.mHandler,
|
||||
routerRecord.mPackageName, null));
|
||||
routerRecord.mUserRecord.mHandler.sendMessage(
|
||||
obtainMessage(
|
||||
UserHandler::notifyRouteListingPreferenceChangeToManagers,
|
||||
routerRecord.mUserRecord.mHandler,
|
||||
routerRecord.mPackageName,
|
||||
/* routeListingPreference= */ null));
|
||||
userRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
|
||||
userRecord.mHandler));
|
||||
|
||||
@@ -18,7 +18,13 @@ package com.android.server.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -26,6 +32,39 @@ import java.io.PrintWriter;
|
||||
* Util class for media server.
|
||||
*/
|
||||
class MediaServerUtils {
|
||||
|
||||
/**
|
||||
* Throws if the given {@code packageName} does not correspond to the given {@code uid}.
|
||||
*
|
||||
* <p>This method trusts calls from {@link Process#ROOT_UID} and {@link Process#SHELL_UID}.
|
||||
*
|
||||
* @param packageName A package name to verify (usually sent over binder by an app).
|
||||
* @param uid The calling uid, obtained via {@link Binder#getCallingUid()}.
|
||||
* @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
|
||||
* given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
|
||||
*/
|
||||
public static void enforcePackageName(String packageName, int uid) {
|
||||
if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
throw new IllegalArgumentException("packageName may not be empty");
|
||||
}
|
||||
final PackageManagerInternal packageManagerInternal =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
final int actualUid =
|
||||
packageManagerInternal.getPackageUid(
|
||||
packageName, 0 /* flags */, UserHandle.getUserId(uid));
|
||||
if (!UserHandle.isSameApp(uid, actualUid)) {
|
||||
throw new IllegalArgumentException(
|
||||
"packageName does not belong to the calling uid; "
|
||||
+ "pkg="
|
||||
+ packageName
|
||||
+ ", uid="
|
||||
+ uid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that caller holds {@link android.Manifest.permission#DUMP}.
|
||||
*/
|
||||
|
||||
@@ -538,30 +538,11 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
mHandler.postSessionsChanged(session);
|
||||
}
|
||||
|
||||
private void enforcePackageName(String packageName, int uid) {
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
throw new IllegalArgumentException("packageName may not be empty");
|
||||
}
|
||||
if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
|
||||
// If the caller is shell, then trust the packageName given and allow it
|
||||
// to proceed.
|
||||
return;
|
||||
}
|
||||
final PackageManagerInternal packageManagerInternal =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
final int actualUid = packageManagerInternal.getPackageUid(
|
||||
packageName, 0 /* flags */, UserHandle.getUserId(uid));
|
||||
if (!UserHandle.isSameApp(uid, actualUid)) {
|
||||
throw new IllegalArgumentException("packageName does not belong to the calling uid; "
|
||||
+ "pkg=" + packageName + ", uid=" + uid);
|
||||
}
|
||||
}
|
||||
|
||||
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
|
||||
int callingPid, int callingUid, String callingPackage, String reason) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(callingPackage, callingUid);
|
||||
MediaServerUtils.enforcePackageName(callingPackage, callingUid);
|
||||
if (targetUid != callingUid) {
|
||||
boolean canAllowWhileInUse = mActivityManagerLocal
|
||||
.canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
|
||||
@@ -1206,7 +1187,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
final int uid = Binder.getCallingUid();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(packageName, uid);
|
||||
MediaServerUtils.enforcePackageName(packageName, uid);
|
||||
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
|
||||
if (cb == null) {
|
||||
throw new IllegalArgumentException("Controller callback cannot be null");
|
||||
@@ -1258,7 +1239,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
final int userId = userHandle.getIdentifier();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(packageName, uid);
|
||||
MediaServerUtils.enforcePackageName(packageName, uid);
|
||||
enforceMediaPermissions(packageName, pid, uid, userId);
|
||||
|
||||
MediaSessionRecordImpl record;
|
||||
@@ -1289,7 +1270,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
final int userId = userHandle.getIdentifier();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(packageName, uid);
|
||||
MediaServerUtils.enforcePackageName(packageName, uid);
|
||||
enforceMediaPermissions(packageName, pid, uid, userId);
|
||||
|
||||
MediaSessionRecordImpl record;
|
||||
@@ -1615,7 +1596,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
final int userId = userHandle.getIdentifier();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
enforcePackageName(packageName, uid);
|
||||
MediaServerUtils.enforcePackageName(packageName, uid);
|
||||
enforceMediaPermissions(packageName, pid, uid, userId);
|
||||
|
||||
synchronized (mLock) {
|
||||
@@ -2129,7 +2110,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
// If they gave us a component name verify they own the
|
||||
// package
|
||||
packageName = componentName.getPackageName();
|
||||
enforcePackageName(packageName, uid);
|
||||
MediaServerUtils.enforcePackageName(packageName, uid);
|
||||
}
|
||||
// Check that they can make calls on behalf of the user and get the final user id
|
||||
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
|
||||
|
||||
Reference in New Issue
Block a user