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

Commit 9c323d97 authored by Iván Budnik's avatar Iván Budnik Committed by Android (Google) Code Review
Browse files

Merge "Use PlaybackInfo to provide routing information for media apps" into main

parents 4c1803f8 8874c5c6
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -31,3 +31,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "use_playback_info_for_routing_controls"
    namespace: "media_solutions"
    description: "Use app-provided playback info when providing media routing information."
    bug: "333564788"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+77 −6
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import static android.media.session.MediaController.PlaybackInfo;

import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;

@@ -51,6 +52,8 @@ import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Build;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -135,19 +138,28 @@ public abstract class InfoMediaManager {
    @NonNull protected final UserHandle mUserHandle;
    private final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
    private MediaDevice mCurrentConnectedDevice;
    private MediaController mMediaController;
    private PlaybackInfo mLastKnownPlaybackInfo;
    private final LocalBluetoothManager mBluetoothManager;
    private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
            new ConcurrentHashMap<>();

    private final MediaController.Callback mMediaControllerCallback = new MediaControllerCallback();

    /* package */ InfoMediaManager(
            @NonNull Context context,
            @NonNull String packageName,
            @NonNull UserHandle userHandle,
            @NonNull LocalBluetoothManager localBluetoothManager) {
            @NonNull LocalBluetoothManager localBluetoothManager,
            @Nullable MediaController mediaController) {
        mContext = context;
        mBluetoothManager = localBluetoothManager;
        mPackageName = packageName;
        mUserHandle = userHandle;
        mMediaController = mediaController;
        if (mediaController != null) {
            mLastKnownPlaybackInfo = mediaController.getPlaybackInfo();
        }
    }

    /**
@@ -159,12 +171,19 @@ public abstract class InfoMediaManager {
     *     speakers, as opposed to app-specific routing (for example, casting to another device).
     * @param userHandle The {@link UserHandle} of the user on which the app to control is running,
     *     or null if the caller does not need app-specific routing (see {@code packageName}).
     * @param token The token of the associated {@link MediaSession} for which to do media routing.
     */
    public static InfoMediaManager createInstance(
            Context context,
            @Nullable String packageName,
            @Nullable UserHandle userHandle,
            LocalBluetoothManager localBluetoothManager) {
            LocalBluetoothManager localBluetoothManager,
            @Nullable MediaSession.Token token) {
        MediaController mediaController = null;

        if (Flags.usePlaybackInfoForRoutingControls() && token != null) {
            mediaController = new MediaController(context, token);
        }

        // The caller is only interested in system routes (headsets, built-in speakers, etc), and is
        // not interested in a specific app's routing. The media routing APIs still require a
@@ -180,16 +199,16 @@ public abstract class InfoMediaManager {
        if (Flags.useMediaRouter2ForInfoMediaManager()) {
            try {
                return new RouterInfoMediaManager(
                        context, packageName, userHandle, localBluetoothManager);
                        context, packageName, userHandle, localBluetoothManager, mediaController);
            } catch (PackageNotAvailableException ex) {
                // TODO: b/293578081 - Propagate this exception to callers for proper handling.
                Log.w(TAG, "Returning a no-op InfoMediaManager for package " + packageName);
                return new NoOpInfoMediaManager(
                        context, packageName, userHandle, localBluetoothManager);
                        context, packageName, userHandle, localBluetoothManager, mediaController);
            }
        } else {
            return new ManagerInfoMediaManager(
                    context, packageName, userHandle, localBluetoothManager);
                    context, packageName, userHandle, localBluetoothManager, mediaController);
        }
    }

@@ -310,6 +329,9 @@ public abstract class InfoMediaManager {
            if (wasEmpty) {
                mMediaDevices.clear();
                registerRouter();
                if (mMediaController != null) {
                    mMediaController.registerCallback(mMediaControllerCallback);
                }
                updateRouteListingPreference();
                refreshDevices();
            }
@@ -323,6 +345,9 @@ public abstract class InfoMediaManager {
     */
    public final void unregisterCallback(@NonNull MediaDeviceCallback callback) {
        if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
            if (mMediaController != null) {
                mMediaController.unregisterCallback(mMediaControllerCallback);
            }
            unregisterRouter();
        }
    }
@@ -389,7 +414,34 @@ public abstract class InfoMediaManager {
    private RoutingSessionInfo getActiveRoutingSession() {
        // List is never empty.
        final List<RoutingSessionInfo> sessions = getRoutingSessionsForPackage();
        return sessions.get(sessions.size() - 1);
        RoutingSessionInfo activeSession = sessions.get(sessions.size() - 1);

        // Logic from MediaRouter2Manager#getRoutingSessionForMediaController
        if (!Flags.usePlaybackInfoForRoutingControls() || mMediaController == null) {
            return activeSession;
        }

        PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
        if (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
            // Return system session.
            return sessions.get(0);
        }

        // For PLAYBACK_TYPE_REMOTE.
        String volumeControlId = playbackInfo.getVolumeControlId();
        for (RoutingSessionInfo session : sessions) {
            if (TextUtils.equals(volumeControlId, session.getId())) {
                return session;
            }
            // Workaround for provider not being able to know the unique session ID.
            if (TextUtils.equals(volumeControlId, session.getOriginalId())
                    && TextUtils.equals(
                            mMediaController.getPackageName(), session.getOwnerPackageName())) {
                return session;
            }
        }

        return activeSession;
    }

    boolean isRoutingSessionAvailableForVolumeControl() {
@@ -808,4 +860,23 @@ public abstract class InfoMediaManager {
            }
        }
    }

    private final class MediaControllerCallback extends MediaController.Callback {
        @Override
        public void onSessionDestroyed() {
            mMediaController = null;
            refreshDevices();
        }

        @Override
        public void onAudioInfoChanged(@NonNull PlaybackInfo info) {
            if (info.getPlaybackType() != mLastKnownPlaybackInfo.getPlaybackType()
                    || !TextUtils.equals(
                            info.getVolumeControlId(),
                            mLastKnownPlaybackInfo.getVolumeControlId())) {
                refreshDevices();
            }
            mLastKnownPlaybackInfo = info;
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -149,7 +149,11 @@ public class LocalMediaManager implements BluetoothCallback {
                // TODO: b/321969740 - Take the userHandle as a parameter and pass it through. The
                // package name is not sufficient to unambiguously identify an app.
                InfoMediaManager.createInstance(
                        context, packageName, /* userHandle */ null, mLocalBluetoothManager);
                        context,
                        packageName,
                        /* userHandle */ null,
                        mLocalBluetoothManager,
                        /* token */ null);
    }

    /**
+4 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -55,8 +56,9 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
            Context context,
            @NonNull String packageName,
            @NonNull UserHandle userHandle,
            LocalBluetoothManager localBluetoothManager) {
        super(context, packageName, userHandle, localBluetoothManager);
            LocalBluetoothManager localBluetoothManager,
            @Nullable MediaController mediaController) {
        super(context, packageName, userHandle, localBluetoothManager, mediaController);

        mRouterManager = MediaRouter2Manager.getInstance(context);
    }
+4 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.os.UserHandle;

import androidx.annotation.NonNull;
@@ -60,8 +61,9 @@ import java.util.List;
            Context context,
            @NonNull String packageName,
            @NonNull UserHandle userHandle,
            LocalBluetoothManager localBluetoothManager) {
        super(context, packageName, userHandle, localBluetoothManager);
            LocalBluetoothManager localBluetoothManager,
            @Nullable MediaController mediaController) {
        super(context, packageName, userHandle, localBluetoothManager, mediaController);
    }

    @Override
Loading