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

Commit caec868d authored by Antony Sargent's avatar Antony Sargent Committed by Android (Google) Code Review
Browse files

Merge "Allowlist and advertise hidden routes when they are selected" into main

parents 97e781b3 318425e7
Loading
Loading
Loading
Loading
+42 −3
Original line number Diff line number Diff line
@@ -648,6 +648,7 @@ public final class MediaRoute2Info implements Parcelable {
    private final boolean mAlsoAllowPrivilegedPackages;
    private final List<Set<String>> mRequiredPermissions;
    @SuitabilityStatus private final int mSuitabilityStatus;
    private Set<String> mTemporaryVisibilityPackages;

    MediaRoute2Info(@NonNull Builder builder) {
        mId = builder.mId;
@@ -673,6 +674,7 @@ public final class MediaRoute2Info implements Parcelable {
        mAlsoAllowPrivilegedPackages = builder.mAlsoAllowPrivilegedPackages;
        mSuitabilityStatus = builder.mSuitabilityStatus;
        mRequiredPermissions = List.copyOf(builder.mRequiredPermissions);
        mTemporaryVisibilityPackages = builder.mTemporaryVisibilityPackages;
    }

    MediaRoute2Info(@NonNull Parcel in) {
@@ -705,6 +707,7 @@ public final class MediaRoute2Info implements Parcelable {
        }
        mRequiredPermissions = List.copyOf(requiredPermissions); // Use copyOf to make it immutable.
        mSuitabilityStatus = in.readInt();
        mTemporaryVisibilityPackages = Set.of();
    }

    /**
@@ -962,6 +965,15 @@ public final class MediaRoute2Info implements Parcelable {
        return true;
    }

    /**
     * Returns whether this route can be seen by any router (i.e. has no visibility or permissions
     * restrictions).
     * @hide
     */
    public boolean isPublic() {
        return !mIsVisibilityRestricted && mRequiredPermissions.isEmpty();
    }

    /**
     * Returns whether this route is visible to the package with the given name.
     *
@@ -970,7 +982,8 @@ public final class MediaRoute2Info implements Parcelable {
    public boolean isVisibleTo(@NonNull String packageName) {
        return !mIsVisibilityRestricted
                || TextUtils.equals(getProviderPackageName(), packageName)
                || mAllowedPackages.contains(packageName);
                || mAllowedPackages.contains(packageName)
                || mTemporaryVisibilityPackages.contains(packageName);
    }

    /**
@@ -980,7 +993,8 @@ public final class MediaRoute2Info implements Parcelable {
        // TODO(b/426044649) - see the comment above on the mAlsoAllowPrivilegedPackages member
        //  variable when removing the enableRouteVisibilityControlApi flag.
        return isVisibleTo(packageName) || (Flags.enableRouteVisibilityControlApi()
                && callerIsPrivileged && mAlsoAllowPrivilegedPackages);
                && callerIsPrivileged && mAlsoAllowPrivilegedPackages)
                || mTemporaryVisibilityPackages.contains(packageName);
    }

    /**
@@ -993,6 +1007,16 @@ public final class MediaRoute2Info implements Parcelable {
        return mRequiredPermissions;
    }

    /**
     * Returns a set of packages that should be allowed to see this route regardless of other
     * visibility or permissions-based restrictions.
     *
     * @hide
     */
    public Set<String> getTemporaryVisibilityPackages() {
        return mTemporaryVisibilityPackages;
    }

    /**
     * Returns whether this route's type can only be published by the system route provider.
     *
@@ -1104,7 +1128,8 @@ public final class MediaRoute2Info implements Parcelable {
                && Objects.equals(mAllowedPackages, other.mAllowedPackages)
                && mAlsoAllowPrivilegedPackages == other.mAlsoAllowPrivilegedPackages
                && Objects.equals(mRequiredPermissions, other.mRequiredPermissions)
                && mSuitabilityStatus == other.mSuitabilityStatus;
                && mSuitabilityStatus == other.mSuitabilityStatus
                && Objects.equals(mTemporaryVisibilityPackages, other.mTemporaryVisibilityPackages);
    }

    @Override
@@ -1370,6 +1395,7 @@ public final class MediaRoute2Info implements Parcelable {
        //    VISIBILITY_RESTRICTED_TO_ALLOWLIST_AND_PRIVILEGED
        private boolean mAlsoAllowPrivilegedPackages;
        private List<Set<String>> mRequiredPermissions;
        private Set<String> mTemporaryVisibilityPackages;
        @SuitabilityStatus private int mSuitabilityStatus;

        /**
@@ -1396,6 +1422,7 @@ public final class MediaRoute2Info implements Parcelable {
            mAllowedPackages = Set.of();
            mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
            mRequiredPermissions = List.of();
            mTemporaryVisibilityPackages = Set.of();
        }

        /**
@@ -1447,6 +1474,7 @@ public final class MediaRoute2Info implements Parcelable {
            mAlsoAllowPrivilegedPackages = routeInfo.mAlsoAllowPrivilegedPackages;
            mSuitabilityStatus = routeInfo.mSuitabilityStatus;
            mRequiredPermissions = routeInfo.mRequiredPermissions;
            mTemporaryVisibilityPackages = routeInfo.mTemporaryVisibilityPackages;
        }

        /**
@@ -1759,6 +1787,17 @@ public final class MediaRoute2Info implements Parcelable {
            return this;
        }

        /**
         * Sets a list of package names that will be temporarily given access to this route, even
         * if those apps otherwise would not have access due to visibility or permissions.
         *
         * @hide
         */
        public Builder setTemporaryAllowedPackages(@NonNull Set<String> packageNames) {
            mTemporaryVisibilityPackages = Set.copyOf(packageNames);
            return this;
        }

        /**
         * Sets route suitability status.
         *
+6 −1
Original line number Diff line number Diff line
@@ -144,10 +144,15 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
        }

        public Builder(@NonNull MediaRoute2ProviderInfo descriptor) {
            this(descriptor, descriptor.mRoutes);
        }

        public Builder(@NonNull MediaRoute2ProviderInfo descriptor,
                ArrayMap<String, MediaRoute2Info> routes) {
            Objects.requireNonNull(descriptor, "descriptor must not be null");

            mUniqueId = descriptor.mUniqueId;
            mRoutes = new ArrayMap<>(descriptor.mRoutes);
            mRoutes = new ArrayMap<>(routes);
        }

        /**
+32 −10
Original line number Diff line number Diff line
@@ -980,6 +980,18 @@ public final class MediaRouter2 {
        }
        mDiscoveryPreference = newDiscoveryPreference;
        updateFilteredRoutesLocked();
        if (Flags.enableRouteVisibilityControlCompatFixes()) {
            mHandler.sendMessage(
                    obtainMessage(
                            MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler,
                            this,
                            mFilteredRoutes));
            mHandler.sendMessage(
                    obtainMessage(
                            MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler,
                            this,
                            new HashMap<>(mRoutes)));
        }
        return true;
    }

@@ -1426,6 +1438,10 @@ public final class MediaRouter2 {
                mRoutes.put(route.getId(), route);
            }
            updateFilteredRoutesLocked();
            if (Flags.enableRouteVisibilityControlCompatFixes()) {
                dispatchFilteredRoutesUpdatedOnHandler(mFilteredRoutes);
                dispatchControllerUpdatedIfNeededOnHandler(mRoutes);
            }
        }
    }

@@ -1435,6 +1451,7 @@ public final class MediaRouter2 {
        mFilteredRoutes =
                Collections.unmodifiableList(
                        filterRoutesWithCompositePreferenceLocked(List.copyOf(mRoutes.values())));
        if (!Flags.enableRouteVisibilityControlCompatFixes()) {
            mHandler.sendMessage(
                    obtainMessage(
                            MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler,
@@ -1446,6 +1463,7 @@ public final class MediaRouter2 {
                            this,
                            new HashMap<>(mRoutes)));
        }
    }

    /**
     * Creates a controller and calls the {@link TransferCallback#onTransfer}. If the controller
@@ -3886,6 +3904,10 @@ public final class MediaRouter2 {
                updateFilteredRoutesLocked();
            }
            notifyPreferredFeaturesChanged(preference.getPreferredFeatures());
            if (Flags.enableRouteVisibilityControlCompatFixes()) {
                dispatchFilteredRoutesUpdatedOnHandler(mFilteredRoutes);
                dispatchControllerUpdatedIfNeededOnHandler(mRoutes);
            }
        }

        private void onRouteListingPreferenceChangedOnHandler(
+82 −0
Original line number Diff line number Diff line
@@ -28,11 +28,15 @@ import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -112,6 +116,17 @@ abstract class MediaRoute2Provider {
    void setProviderState(MediaRoute2ProviderInfo providerInfo) {
        if (providerInfo == null) {
            mProviderInfo = null;
            return;
        }

        List<MediaRoute2Info> possiblyUpdatedRoutes = null;
        if (Flags.enableRouteVisibilityControlCompatFixes()) {
            possiblyUpdatedRoutes =
                    getVisibilityUpdatedRoutesIfNeeded(providerInfo.getRoutes(), getSessionInfos());
        }

        if (possiblyUpdatedRoutes != null) {
            setProviderStateWithUpdatedRoutes(providerInfo, possiblyUpdatedRoutes);
        } else {
            mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
                    .setUniqueId(mComponentName.getPackageName(), mUniqueId)
@@ -120,6 +135,15 @@ abstract class MediaRoute2Provider {
        }
    }

    private void setProviderStateWithUpdatedRoutes(@NonNull MediaRoute2ProviderInfo providerInfo,
            @NonNull List<MediaRoute2Info> updatedRoutes) {
        mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo, new ArrayMap<>())
                .addRoutes(updatedRoutes)
                .setUniqueId(mComponentName.getPackageName(), mUniqueId)
                .setSystemRouteProvider(mIsSystemRouteProvider)
                .build();
    }

    protected boolean haveCallback() {
        return mCallback != null;
    }
@@ -132,6 +156,7 @@ abstract class MediaRoute2Provider {

    protected void notifySessionCreated(long requestId, @Nullable RoutingSessionInfo sessionInfo) {
        if (mCallback != null) {
            maybeUpdateProviderStateForRouteVisibility();
            mCallback.onSessionCreated(this, requestId, sessionInfo);
        }
    }
@@ -142,6 +167,7 @@ abstract class MediaRoute2Provider {
            Set<String> packageNamesWithRoutingSessionOverrides,
            boolean shouldShowVolumeSystemUi) {
        if (mCallback != null) {
            maybeUpdateProviderStateForRouteVisibility();
            mCallback.onSessionUpdated(this, sessionInfo,
                    packageNamesWithRoutingSessionOverrides, shouldShowVolumeSystemUi);
        }
@@ -150,6 +176,7 @@ abstract class MediaRoute2Provider {
    protected void notifySessionReleased(@NonNull RoutingSessionInfo sessionInfo) {
        if (mCallback != null) {
            mCallback.onSessionReleased(this, sessionInfo);
            maybeUpdateProviderStateForRouteVisibility();
        }
    }

@@ -312,4 +339,59 @@ abstract class MediaRoute2Provider {
                    .anyMatch(mTargetOriginalRouteId::equals);
        }
    }

    private void maybeUpdateProviderStateForRouteVisibility() {
        if (!Flags.enableRouteVisibilityControlCompatFixes()) {
            return;
        }
        List<MediaRoute2Info> possiblyUpdatedRoutes =
                getVisibilityUpdatedRoutesIfNeeded(mProviderInfo.getRoutes(), mSessionInfos);
        if (possiblyUpdatedRoutes != null) {
            setProviderStateWithUpdatedRoutes(mProviderInfo, possiblyUpdatedRoutes);
            notifyProviderStateChanged();
        }
    }

    /**
     * Returns a copy of routes with any missing visibility added, or null if the existing
     * visibility is sufficient.
     *
     * <p>We consider visibility to be missing when a route is not visible to a given app, but a
     * routing session exists where that app is the {@link #getClientPackageName client} and that
     * route is selected.
     *
     * <p>In summary, this method ensures that all routes which are selected by an app are visible
     * to that app.
     */
    @Nullable
    private List<MediaRoute2Info> getVisibilityUpdatedRoutesIfNeeded(
            Collection<MediaRoute2Info> routes, List<RoutingSessionInfo> sessions) {
        ArrayMap<String, Set<String>> selectedRouteToClient = new ArrayMap<>();
        for (RoutingSessionInfo session : sessions) {
            session.getSelectedRoutes().forEach(routeId -> {
                Set<String> clients =
                        selectedRouteToClient.computeIfAbsent(routeId, k -> new ArraySet<>());
                clients.add(session.getClientPackageName());
            });
        }

        boolean updatedSomeRoute = false;
        ArrayList<MediaRoute2Info> updatedRoutes = new ArrayList<>();
        for (MediaRoute2Info route : routes) {
            String fullId = MediaRouter2Utils.toUniqueId(mUniqueId, route.getOriginalId());
            MediaRoute2Info routeToAdd = route;
            if (!route.isPublic()) {
                Set<String> clients = selectedRouteToClient.getOrDefault(fullId, Set.of());
                if (!clients.equals(route.getTemporaryVisibilityPackages())) {
                    routeToAdd = new MediaRoute2Info.Builder(route)
                            .setTemporaryAllowedPackages(clients)
                            .build();
                    updatedSomeRoute = true;
                }
            }
            updatedRoutes.add(routeToAdd);
        }

        return updatedSomeRoute ? updatedRoutes : null;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -2852,6 +2852,10 @@ class MediaRouter2ServiceImpl {
            if (!Flags.enableRouteVisibilityControlApi()) {
                return true;
            }
            if (Flags.enableRouteVisibilityControlCompatFixes()
                    && route.getTemporaryVisibilityPackages().contains(mPackageName)) {
                return true;
            }
            List<Set<String>> permissionSets = route.getRequiredPermissions();
            if (permissionSets.isEmpty()) {
                return true;