Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b52b29b authored by Antony Sargent's avatar Antony Sargent
Browse files

Add a new API to let media routes have visibility restricted by permissions

Bug: 367799834
Flag: com.android.media.flags.enable_route_visibility_control_api
Test: MediaRoute2InfoTest and MediaRouter2HostSideTest
Change-Id: I61b89b11b7eb6cb05d9d237134af566ed289bb8c
parent 28c7df24
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -24852,6 +24852,7 @@ package android.media {
    method @Nullable public android.net.Uri getIconUri();
    method @NonNull public String getId();
    method @NonNull public CharSequence getName();
    method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.Set<java.lang.String> getRequiredPermissions();
    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
    method public int getType();
    method public int getVolume();
@@ -24917,6 +24918,7 @@ package android.media {
    method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
    method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
    method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
    method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.Set<java.lang.String>);
    method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
    method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
    method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
+36 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AN
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
import static com.android.media.flags.Flags.FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -569,6 +570,7 @@ public final class MediaRoute2Info implements Parcelable {
    private final String mProviderId;
    private final boolean mIsVisibilityRestricted;
    private final Set<String> mAllowedPackages;
    private final Set<String> mRequiredPermissions;
    @SuitabilityStatus private final int mSuitabilityStatus;

    MediaRoute2Info(@NonNull Builder builder) {
@@ -592,6 +594,7 @@ public final class MediaRoute2Info implements Parcelable {
        mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
        mAllowedPackages = builder.mAllowedPackages;
        mSuitabilityStatus = builder.mSuitabilityStatus;
        mRequiredPermissions = Set.copyOf(builder.mRequiredPermissions);
    }

    MediaRoute2Info(@NonNull Parcel in) {
@@ -615,6 +618,7 @@ public final class MediaRoute2Info implements Parcelable {
        mProviderId = in.readString();
        mIsVisibilityRestricted = in.readBoolean();
        mAllowedPackages = Set.of(in.createString8Array());
        mRequiredPermissions = Set.of(in.createString8Array());
        mSuitabilityStatus = in.readInt();
    }

@@ -850,6 +854,15 @@ public final class MediaRoute2Info implements Parcelable {
                || mAllowedPackages.contains(packageName);
    }

    /**
     * @return the set of permissions which must be held to see this route
     */
    @NonNull
    @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
    public Set<String> getRequiredPermissions() {
        return mRequiredPermissions;
    }

    /**
     * Returns whether this route's type can only be published by the system route provider.
     *
@@ -920,6 +933,7 @@ public final class MediaRoute2Info implements Parcelable {
        pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
        pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
        pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
        pw.println(indent + "mRequiredPermissions=" + mRequiredPermissions);
    }

    private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -955,6 +969,7 @@ public final class MediaRoute2Info implements Parcelable {
                && Objects.equals(mProviderId, other.mProviderId)
                && (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
                && Objects.equals(mAllowedPackages, other.mAllowedPackages)
                && Objects.equals(mRequiredPermissions, other.mRequiredPermissions)
                && mSuitabilityStatus == other.mSuitabilityStatus;
    }

@@ -980,6 +995,7 @@ public final class MediaRoute2Info implements Parcelable {
                mProviderId,
                mIsVisibilityRestricted,
                mAllowedPackages,
                mRequiredPermissions,
                mSuitabilityStatus);
    }

@@ -1018,6 +1034,8 @@ public final class MediaRoute2Info implements Parcelable {
                .append(mIsVisibilityRestricted)
                .append(", allowedPackages=")
                .append(String.join(",", mAllowedPackages))
                .append(", mRequiredPermissions=")
                .append(String.join(",", mRequiredPermissions))
                .append(", suitabilityStatus=")
                .append(mSuitabilityStatus)
                .append(" }")
@@ -1050,6 +1068,7 @@ public final class MediaRoute2Info implements Parcelable {
        dest.writeString(mProviderId);
        dest.writeBoolean(mIsVisibilityRestricted);
        dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
        dest.writeString8Array(mRequiredPermissions.toArray(new String[0]));
        dest.writeInt(mSuitabilityStatus);
    }

@@ -1169,6 +1188,7 @@ public final class MediaRoute2Info implements Parcelable {
        private String mProviderId;
        private boolean mIsVisibilityRestricted;
        private Set<String> mAllowedPackages;
        private Set<String> mRequiredPermissions;
        @SuitabilityStatus private int mSuitabilityStatus;

        /**
@@ -1194,6 +1214,7 @@ public final class MediaRoute2Info implements Parcelable {
            mDeduplicationIds = Set.of();
            mAllowedPackages = Set.of();
            mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
            mRequiredPermissions = Set.of();
        }

        /**
@@ -1242,6 +1263,7 @@ public final class MediaRoute2Info implements Parcelable {
            mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
            mAllowedPackages = routeInfo.mAllowedPackages;
            mSuitabilityStatus = routeInfo.mSuitabilityStatus;
            mRequiredPermissions = routeInfo.mRequiredPermissions;
        }

        /**
@@ -1461,6 +1483,7 @@ public final class MediaRoute2Info implements Parcelable {
        public Builder setVisibilityPublic() {
            mIsVisibilityRestricted = false;
            mAllowedPackages = Set.of();
            mRequiredPermissions = Set.of();
            return this;
        }

@@ -1484,6 +1507,19 @@ public final class MediaRoute2Info implements Parcelable {
            return this;
        }

        /**
         * Limits the visibility of this route to holders of a set of permissions.
         *
         * @param requiredPermissions the list of all permissions which must be held in order to
         *                            see this route.
         */
        @NonNull
        @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
        public Builder setRequiredPermissions(@NonNull Set<String> requiredPermissions) {
            mRequiredPermissions = Set.copyOf(requiredPermissions);
            return this;
        }

        /**
         * Sets route suitability status.
         *
+23 −3
Original line number Diff line number Diff line
@@ -1136,6 +1136,7 @@ class MediaRouter2ServiceImpl {
        UserRecord userRecord = getOrCreateUserRecordLocked(userId);
        RouterRecord routerRecord =
                new RouterRecord(
                        mContext,
                        userRecord,
                        router,
                        uid,
@@ -2055,6 +2056,7 @@ class MediaRouter2ServiceImpl {
    }

    final class RouterRecord implements IBinder.DeathRecipient {
        public final Context mContext;
        public final UserRecord mUserRecord;
        public final String mPackageName;
        public final List<Integer> mSelectRouteSequenceNumbers;
@@ -2073,6 +2075,7 @@ class MediaRouter2ServiceImpl {
        @Nullable public RouteListingPreference mRouteListingPreference;

        RouterRecord(
                Context context,
                UserRecord userRecord,
                IMediaRouter2 router,
                int uid,
@@ -2082,6 +2085,7 @@ class MediaRouter2ServiceImpl {
                boolean hasModifyAudioRoutingPermission,
                boolean hasMediaContentControlPermission,
                boolean hasMediaRoutingControl) {
            mContext = context;
            mUserRecord = userRecord;
            mPackageName = packageName;
            mSelectRouteSequenceNumbers = new ArrayList<>();
@@ -2322,18 +2326,34 @@ class MediaRouter2ServiceImpl {
        }

        /**
         * Returns a filtered copy of {@code routes} that contains only the routes that are {@link
         * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
         * Returns a filtered copy of {@code routes} that contains only the routes that are visible
         * to this RouterRecord.
         */
        private List<MediaRoute2Info> getVisibleRoutes(@NonNull List<MediaRoute2Info> routes) {
            List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
            for (MediaRoute2Info route : routes) {
                if (route.isVisibleTo(mPackageName)) {
                if (route.isVisibleTo(mPackageName) && hasPermissionsToSeeRoute(route)) {
                    filteredRoutes.add(route);
                }
            }
            return filteredRoutes;
        }

        /**
         * @return whether this RouterRecord has the required permissions to see the given route.
         */
        private boolean hasPermissionsToSeeRoute(MediaRoute2Info route) {
            if (!Flags.enableRouteVisibilityControlApi()) {
                return true;
            }
            for (String permission : route.getRequiredPermissions()) {
                if (mContext.checkPermission(permission, mPid, mUid)
                        != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
    }

    final class ManagerRecord implements IBinder.DeathRecipient {