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

Commit 5ef5012f authored by wenyu zhang's avatar wenyu zhang
Browse files

Hook InputRouteManager into AudioSwitchController

AudioSwitchController listens to input devices changes, maintains a list
of input devices and displays them when the input routing flag is on.

Change-Id: Iebf9c50cbcb60fb9361d3f9a45016e12032326dc
Bug: b/355684672, b/357122624
Test atest AudioSwitchControllerTest
Flag: com.android.media.flags.enable_audio_input_device_routing_and_volume_control
parent 5a7e18f0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public final class InputRouteManager {
                }
            };

    /* package */ InputRouteManager(@NonNull Context context, @NonNull AudioManager audioManager) {
    public InputRouteManager(@NonNull Context context, @NonNull AudioManager audioManager) {
        mContext = context;
        mAudioManager = audioManager;
        Handler handler = new Handler(context.getMainLooper());
+4 −0
Original line number Diff line number Diff line
@@ -3114,6 +3114,10 @@
    <string name="media_output_group_title_speakers_and_displays">Speakers &amp; Displays</string>
    <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] -->
    <string name="media_output_group_title_suggested_device">Suggested Devices</string>
    <!-- Title for input device group. [CHAR LIMIT=NONE] -->
    <string name="media_input_group_title">Input</string>
    <!-- Title for output device group. [CHAR LIMIT=NONE] -->
    <string name="media_output_group_title">Output</string>
    <!-- Summary for end session dialog. [CHAR LIMIT=NONE] -->
    <string name="media_output_end_session_dialog_summary">Stop your shared session to move media to another device</string>
    <!-- Button text for stopping session [CHAR LIMIT=60] -->
+80 −13
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.InputRouteManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.flags.Flags;
@@ -138,10 +139,12 @@ public class MediaSwitchingController
    private final DialogTransitionAnimator mDialogTransitionAnimator;
    private final CommonNotifCollection mNotifCollection;
    protected final Object mMediaDevicesLock = new Object();
    protected final Object mInputMediaDevicesLock = new Object();
    @VisibleForTesting
    final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
    final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
    private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
    private final List<MediaItem> mOutputMediaItemList = new CopyOnWriteArrayList<>();
    private final List<MediaItem> mInputMediaItemList = new CopyOnWriteArrayList<>();
    private final AudioManager mAudioManager;
    private final PowerExemptionManager mPowerExemptionManager;
    private final KeyguardManager mKeyGuardManager;
@@ -154,6 +157,7 @@ public class MediaSwitchingController
    @VisibleForTesting
    boolean mNeedRefresh = false;
    private MediaController mMediaController;
    private InputRouteManager mInputRouteManager;
    @VisibleForTesting
    Callback mCallback;
    @VisibleForTesting
@@ -182,6 +186,18 @@ public class MediaSwitchingController
        ACTION_BROADCAST_INFO_ICON
    }

    @VisibleForTesting
    final InputRouteManager.InputDeviceCallback mInputDeviceCallback =
            new InputRouteManager.InputDeviceCallback() {
                @Override
                public void onInputDeviceListUpdated(@NonNull List<MediaDevice> devices) {
                    synchronized (mInputMediaDevicesLock) {
                        buildInputMediaItems(devices);
                        mCallback.onDeviceListChanged();
                    }
                }
            };

    @AssistedInject
    public MediaSwitchingController(
            Context context,
@@ -242,6 +258,10 @@ public class MediaSwitchingController
                R.dimen.media_output_dialog_default_margin_end);
        mItemMarginEndSelectable = (int) mContext.getResources().getDimension(
                R.dimen.media_output_dialog_selectable_margin_end);

        if (enableInputRouting()) {
            mInputRouteManager = new InputRouteManager(mContext, audioManager);
        }
    }

    @AssistedFactory
@@ -254,7 +274,7 @@ public class MediaSwitchingController
    protected void start(@NonNull Callback cb) {
        synchronized (mMediaDevicesLock) {
            mCachedMediaDevices.clear();
            mMediaItemList.clear();
            mOutputMediaItemList.clear();
        }
        mNearbyDeviceInfoMap.clear();
        if (mNearbyMediaDevicesManager != null) {
@@ -278,6 +298,10 @@ public class MediaSwitchingController
        mCallback = cb;
        mLocalMediaManager.registerCallback(this);
        mLocalMediaManager.startScan();

        if (enableInputRouting()) {
            mInputRouteManager.registerCallback(mInputDeviceCallback);
        }
    }

    boolean shouldShowLaunchSection() {
@@ -301,12 +325,19 @@ public class MediaSwitchingController
        mLocalMediaManager.stopScan();
        synchronized (mMediaDevicesLock) {
            mCachedMediaDevices.clear();
            mMediaItemList.clear();
            mOutputMediaItemList.clear();
        }
        if (mNearbyMediaDevicesManager != null) {
            mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
        }
        mNearbyDeviceInfoMap.clear();

        if (enableInputRouting()) {
            mInputRouteManager.unregisterCallback(mInputDeviceCallback);
            synchronized (mInputMediaDevicesLock) {
                mInputMediaItemList.clear();
            }
        }
    }

    private MediaController getMediaController() {
@@ -336,7 +367,7 @@ public class MediaSwitchingController

    @Override
    public void onDeviceListUpdate(List<MediaDevice> devices) {
        boolean isListEmpty = mMediaItemList.isEmpty();
        boolean isListEmpty = mOutputMediaItemList.isEmpty();
        if (isListEmpty || !mIsRefreshing) {
            buildMediaItems(devices);
            mCallback.onDeviceListChanged();
@@ -353,7 +384,8 @@ public class MediaSwitchingController
    public void onSelectedDeviceStateChanged(MediaDevice device,
            @LocalMediaManager.MediaDeviceState int state) {
        mCallback.onRouteChanged();
        mMetricLogger.logOutputItemSuccess(device.toString(), new ArrayList<>(mMediaItemList));
        mMetricLogger.logOutputItemSuccess(
                device.toString(), new ArrayList<>(mOutputMediaItemList));
    }

    @Override
@@ -364,7 +396,7 @@ public class MediaSwitchingController
    @Override
    public void onRequestFailed(int reason) {
        mCallback.onRouteChanged();
        mMetricLogger.logOutputItemFailure(new ArrayList<>(mMediaItemList), reason);
        mMetricLogger.logOutputItemFailure(new ArrayList<>(mOutputMediaItemList), reason);
    }

    /**
@@ -383,7 +415,7 @@ public class MediaSwitchingController
        }
        try {
            synchronized (mMediaDevicesLock) {
                mMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
                mOutputMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
            }
            mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
        } catch (Exception e) {
@@ -639,9 +671,9 @@ public class MediaSwitchingController

    private void buildMediaItems(List<MediaDevice> devices) {
        synchronized (mMediaDevicesLock) {
            List<MediaItem> updatedMediaItems = buildMediaItems(mMediaItemList, devices);
            mMediaItemList.clear();
            mMediaItemList.addAll(updatedMediaItems);
            List<MediaItem> updatedMediaItems = buildMediaItems(mOutputMediaItemList, devices);
            mOutputMediaItemList.clear();
            mOutputMediaItemList.addAll(updatedMediaItems);
        }
    }

@@ -715,6 +747,19 @@ public class MediaSwitchingController
        }
    }

    private boolean enableInputRouting() {
        return com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl();
    }

    private void buildInputMediaItems(List<MediaDevice> devices) {
        synchronized (mInputMediaDevicesLock) {
            List<MediaItem> updatedInputMediaItems =
                    devices.stream().map(MediaItem::createDeviceMediaItem).toList();
            mInputMediaItemList.clear();
            mInputMediaItemList.addAll(updatedInputMediaItems);
        }
    }

    /**
     * Initial categorization of current devices, will not be called for updates to the devices
     * list.
@@ -779,7 +824,6 @@ public class MediaSwitchingController
                mediaDevice.setRangeZone(mNearbyDeviceInfoMap.get(mediaDevice.getId()));
            }
        }

    }

    boolean isCurrentConnectedDeviceRemote() {
@@ -838,8 +882,31 @@ public class MediaSwitchingController
        });
    }

    private void addInputDevices(List<MediaItem> mediaItems) {
        mediaItems.add(
                MediaItem.createGroupDividerMediaItem(
                        mContext.getString(R.string.media_input_group_title)));
        mediaItems.addAll(mInputMediaItemList);
    }

    private void addOutputDevices(List<MediaItem> mediaItems) {
        mediaItems.add(
                MediaItem.createGroupDividerMediaItem(
                        mContext.getString(R.string.media_output_group_title)));
        mediaItems.addAll(mOutputMediaItemList);
    }

    public List<MediaItem> getMediaItemList() {
        return mMediaItemList;
        // If input routing is not enabled, only return output media items.
        if (!enableInputRouting()) {
            return mOutputMediaItemList;
        }

        // If input routing is enabled, return both output and input media items.
        List<MediaItem> mediaItems = new ArrayList<>();
        addOutputDevices(mediaItems);
        addInputDevices(mediaItems);
        return mediaItems;
    }

    public MediaDevice getCurrentConnectedMediaDevice() {
@@ -922,7 +989,7 @@ public class MediaSwitchingController

    public boolean isAnyDeviceTransferring() {
        synchronized (mMediaDevicesLock) {
            for (MediaItem mediaItem : mMediaItemList) {
            for (MediaItem mediaItem : mOutputMediaItemList) {
                if (mediaItem.getMediaDevice().isPresent()
                        && mediaItem.getMediaDevice().get().getState()
                        == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
+53 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.MediaDescription;
import android.media.MediaMetadata;
@@ -58,6 +59,7 @@ import android.os.Bundle;
import android.os.PowerExemptionManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
import android.text.TextUtils;
@@ -67,8 +69,10 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.media.flags.Flags;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.InputMediaDevice;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
@@ -530,6 +534,55 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
        verify(mCb).onDeviceListChanged();
    }

    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
    @Test
    public void onInputDeviceListUpdate_verifyDeviceListCallback() {
        AudioDeviceInfo[] audioDeviceInfos = {};
        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
                .thenReturn(audioDeviceInfos);
        mMediaSwitchingController.start(mCb);

        // Output devices have changed.
        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);

        final int MAX_VOLUME = 1;
        final int CURRENT_VOLUME = 0;
        final boolean IS_VOLUME_FIXED = true;
        final MediaDevice mediaDevice3 =
                InputMediaDevice.create(
                        mContext,
                        TEST_DEVICE_3_ID,
                        AudioDeviceInfo.TYPE_BUILTIN_MIC,
                        MAX_VOLUME,
                        CURRENT_VOLUME,
                        IS_VOLUME_FIXED);
        final MediaDevice mediaDevice4 =
                InputMediaDevice.create(
                        mContext,
                        TEST_DEVICE_4_ID,
                        AudioDeviceInfo.TYPE_WIRED_HEADSET,
                        MAX_VOLUME,
                        CURRENT_VOLUME,
                        IS_VOLUME_FIXED);
        final List<MediaDevice> inputDevices = new ArrayList<>();
        inputDevices.add(mediaDevice3);
        inputDevices.add(mediaDevice4);

        // Input devices have changed.
        mMediaSwitchingController.mInputDeviceCallback.onInputDeviceListUpdated(inputDevices);

        final List<MediaDevice> devices = new ArrayList<>();
        for (MediaItem item : mMediaSwitchingController.getMediaItemList()) {
            if (item.getMediaDevice().isPresent()) {
                devices.add(item.getMediaDevice().get());
            }
        }

        assertThat(devices).containsAtLeastElementsIn(mMediaDevices);
        assertThat(devices).hasSize(mMediaDevices.size() + inputDevices.size());
        verify(mCb, atLeastOnce()).onDeviceListChanged();
    }

    @Test
    public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() {
        when(mMediaDevice1.isSuggestedDevice()).thenReturn(true);