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

Commit 0a36da2f authored by Vidhi Shah's avatar Vidhi Shah
Browse files

Stop route scanning after succesfully connecting to route.

Currently, we stop route scan at the time of device discovery and issuing a connection request. This causes the route to disappear during an ongoing connection attempt leading to failure to cast.

The solution is to continue scanning for devices until the connection is established with the suggested device once it is clicked in the UMO.
Additionally, added a check for connection status when reporting connection attempt completing due to connection success vs timeout.

Bug: 422720290
Flag: com.android.media.flags.enable_suggested_device_api
Test: Manual testing and ran InfoMediaManagerTest, LocalMediaManagerTest
Change-Id: I6e7b6933d2aaee5813e050bc596767fe12d1db77
parent 7f40afe3
Loading
Loading
Loading
Loading
+8 −10
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import static android.media.session.MediaController.PlaybackInfo;

import static com.android.media.flags.Flags.avoidBinderCallsDuringRender;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTED;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTING;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED;
@@ -140,7 +141,7 @@ public abstract class InfoMediaManager {
    }

    /**
     * Wrapper class around SuggestedDeviceInfo and the corresponsing connection state of the
     * Wrapper class around SuggestedDeviceInfo and the corresponding connection state of the
     * suggestion.
     */
    public static class SuggestedDeviceState {
@@ -833,19 +834,16 @@ public abstract class InfoMediaManager {
        }
    }

    final void onConnectionAttemptCompletedForSuggestion(@NonNull SuggestedDeviceState suggestion) {
    final void onConnectionAttemptCompletedForSuggestion(
            @NonNull SuggestedDeviceState suggestion, boolean success) {
        if (!Objects.equals(suggestion, mSuggestedDeviceState)) {
            return;
        }
        if (mSuggestedDeviceState.getConnectionState() == STATE_CONNECTING
                || mSuggestedDeviceState.getConnectionState() == STATE_GROUPING) {
        int state = success ? STATE_CONNECTED : STATE_CONNECTING_FAILED;
        mSuggestedDeviceState =
                    new SuggestedDeviceState(
                            mSuggestedDeviceState.getSuggestedDeviceInfo(),
                            STATE_CONNECTING_FAILED);
                new SuggestedDeviceState(mSuggestedDeviceState.getSuggestedDeviceInfo(), state);
        dispatchOnSuggestedDeviceUpdated();
    }
    }

    private void dispatchOnSuggestedDeviceUpdated() {
        for (MediaDeviceCallback callback : getCallbacks()) {
+54 −36
Original line number Diff line number Diff line
@@ -240,9 +240,7 @@ public class LocalMediaManager implements BluetoothCallback {
                    return;
                }
            }
            mConnectingSuggestedDeviceState =
                    new ConnectingSuggestedDeviceState(
                            currentSuggestion, mConnectSuggestedDeviceHandler);
            mConnectingSuggestedDeviceState = new ConnectingSuggestedDeviceState(currentSuggestion);
            mConnectingSuggestedDeviceState.tryConnect();
        }
    }
@@ -269,20 +267,6 @@ public class LocalMediaManager implements BluetoothCallback {
        return mInfoMediaManager.getSuggestedDevice();
    }

    private boolean connectToDeviceIfConnectionPending(MediaDevice device) {
        synchronized (mMediaDevicesLock) {
            if (mConnectingSuggestedDeviceState != null
                    && mConnectingSuggestedDeviceState
                            .mSuggestedDeviceState
                            .getSuggestedDeviceInfo()
                            .getRouteId()
                            .equals(device.getId())) {
                return connectDevice(device);
            }
            return false;
        }
    }

    void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
        for (DeviceCallback callback : getCallbacks()) {
            callback.onSelectedDeviceStateChanged(device, state);
@@ -905,43 +889,77 @@ public class LocalMediaManager implements BluetoothCallback {
        private static final int SCAN_DURATION_MS = 10000;

        @NonNull final SuggestedDeviceState mSuggestedDeviceState;
        @NonNull final Handler mConnectSuggestedDeviceHandler;
        @NonNull final DeviceCallback mDeviceCallback;
        @NonNull final Runnable mConnectionAttemptFinishedRunnable;

        ConnectingSuggestedDeviceState(SuggestedDeviceState suggestedDeviceState, Handler handler) {
        boolean mIsConnectionAttemptActive = false;
        boolean mDidAttemptCompleteSuccessfully = false;

        ConnectingSuggestedDeviceState(SuggestedDeviceState suggestedDeviceState) {
            mSuggestedDeviceState = suggestedDeviceState;
            mConnectSuggestedDeviceHandler = handler;
            mDeviceCallback =
                    new DeviceCallback() {
                        @Override
                        public void onDeviceListUpdate(List<MediaDevice> mediaDevices) {
                            synchronized (mMediaDevicesLock) {
                                for (MediaDevice mediaDevice : mediaDevices) {
                                if (connectToDeviceIfConnectionPending(mediaDevice)) {
                                    if (isSuggestedDevice(mediaDevice)) {
                                        connectDevice(mediaDevice);
                                        mIsConnectionAttemptActive = true;
                                        break;
                                    }
                                }
                            }
                        }

                        @Override
                        public void onSelectedDeviceStateChanged(
                                @NonNull MediaDevice device, @MediaDeviceState int state) {
                            if (isSuggestedDevice(device)
                                    && state == MediaDeviceState.STATE_CONNECTED) {
                                if (!mConnectSuggestedDeviceHandler.hasCallbacks(
                                        mConnectionAttemptFinishedRunnable)) {
                                    return;
                                }
                                mDidAttemptCompleteSuccessfully = true;
                                // Remove the postDelayed runnable previously set and post a new one
                                // to be executed right away.
                                mConnectSuggestedDeviceHandler.removeCallbacks(
                                        mConnectionAttemptFinishedRunnable);
                                    mConnectionAttemptFinishedRunnable.run();
                                    break;
                                mConnectSuggestedDeviceHandler.post(
                                        mConnectionAttemptFinishedRunnable);
                            }
                        }

                        private boolean isSuggestedDevice(MediaDevice device) {
                            return mConnectingSuggestedDeviceState != null
                                    && mConnectingSuggestedDeviceState
                                            .mSuggestedDeviceState
                                            .getSuggestedDeviceInfo()
                                            .getRouteId()
                                            .equals(device.getId());
                        }
                    };
            mConnectionAttemptFinishedRunnable =
                    new Runnable() {
                        @Override
                        public void run() {
                    () -> {
                        synchronized (mMediaDevicesLock) {
                            mConnectingSuggestedDeviceState = null;
                            mIsConnectionAttemptActive = false;
                        }
                        unregisterCallback(mDeviceCallback);
                        stopScan();
                        mInfoMediaManager.onConnectionAttemptCompletedForSuggestion(
                                    mSuggestedDeviceState);
                        }
                                mSuggestedDeviceState, mDidAttemptCompleteSuccessfully);
                    };
        }

        void tryConnect() {
            // Attempt connection only if there isn't one already in progress.
            if (mIsConnectionAttemptActive) {
                return;
            }
            // Reset mDidAttemptCompleteSuccessfully at the start of each connection attempt.
            mDidAttemptCompleteSuccessfully = false;
            registerCallback(mDeviceCallback);
            startScan();
            mConnectSuggestedDeviceHandler.postDelayed(
+35 −5
Original line number Diff line number Diff line
@@ -1305,7 +1305,7 @@ public class InfoMediaManagerTest {
                new SuggestedDeviceState(suggestedDeviceInfo1));
        clearInvocations(mCallback);
        mediaManager.onConnectionAttemptCompletedForSuggestion(
                new SuggestedDeviceState(suggestedDeviceInfo2));
                new SuggestedDeviceState(suggestedDeviceInfo2), true);

        verify(mCallback, never()).onSuggestedDeviceUpdated(any());
    }
@@ -1329,17 +1329,18 @@ public class InfoMediaManagerTest {
                new SuggestedDeviceState(suggestedDeviceInfo));
        mediaManager.onConnectionAttemptCompletedForSuggestion(
                new SuggestedDeviceState(
                        suggestedDeviceInfo, LocalMediaManager.MediaDeviceState.STATE_CONNECTING));
                        suggestedDeviceInfo, LocalMediaManager.MediaDeviceState.STATE_CONNECTING),
                false);
        clearInvocations(mCallback);
        mediaManager.onConnectionAttemptCompletedForSuggestion(
                new SuggestedDeviceState(suggestedDeviceInfo));
                new SuggestedDeviceState(suggestedDeviceInfo), false);

        verify(mCallback, never()).onSuggestedDeviceUpdated(any());
    }

    @EnableFlags(Flags.FLAG_ENABLE_SUGGESTED_DEVICE_API)
    @Test
    public void onConnectionAttemptCompletedForSuggestion_connecting_callbackNotified() {
    public void onConnectionAttemptCompletedForSuggestion_unsuccessful_callbackNotified() {
        SuggestedDeviceInfo suggestedDeviceInfo =
                new SuggestedDeviceInfo.Builder("device_name", TEST_ID_3, 0).build();
        RouterInfoMediaManager mediaManager = createRouterInfoMediaManager();
@@ -1357,13 +1358,42 @@ public class InfoMediaManagerTest {
        clearInvocations(mCallback);
        mediaManager.onConnectionAttemptCompletedForSuggestion(
                new SuggestedDeviceState(
                        suggestedDeviceInfo, LocalMediaManager.MediaDeviceState.STATE_CONNECTING));
                        suggestedDeviceInfo, LocalMediaManager.MediaDeviceState.STATE_CONNECTING),
                false);

        verify(mCallback).onSuggestedDeviceUpdated(mSuggestedDeviceStateCaptor.capture());
        assertThat(mSuggestedDeviceStateCaptor.getValue().getConnectionState())
                .isEqualTo(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
    }

    @EnableFlags(Flags.FLAG_ENABLE_SUGGESTED_DEVICE_API)
    @Test
    public void onConnectionAttemptCompletedForSuggestion_successful_callbackNotified() {
        SuggestedDeviceInfo suggestedDeviceInfo =
                new SuggestedDeviceInfo.Builder("device_name", TEST_ID_3, 0).build();
        RouterInfoMediaManager mediaManager = createRouterInfoMediaManager();
        setAvailableRoutesList(TEST_PACKAGE_NAME);
        mediaManager.registerCallback(mCallback);
        verify(mRouter2)
                .registerDeviceSuggestionsUpdatesCallback(
                        any(), mDeviceSuggestionsUpdatesCallback.capture());
        mDeviceSuggestionsUpdatesCallback
                .getValue()
                .onSuggestionsUpdated("random_package_name", List.of(suggestedDeviceInfo));

        mediaManager.onConnectionAttemptedForSuggestion(
                new SuggestedDeviceState(suggestedDeviceInfo));
        clearInvocations(mCallback);
        mediaManager.onConnectionAttemptCompletedForSuggestion(
                new SuggestedDeviceState(
                        suggestedDeviceInfo, LocalMediaManager.MediaDeviceState.STATE_CONNECTING),
                true);

        verify(mCallback).onSuggestedDeviceUpdated(mSuggestedDeviceStateCaptor.capture());
        assertThat(mSuggestedDeviceStateCaptor.getValue().getConnectionState())
                .isEqualTo(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
    }

    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
    @Test
    public void composePreferenceRouteListing_useSystemOrderingIsFalse() {
+37 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadow.api.Shadow;

import java.util.ArrayList;
@@ -662,6 +663,42 @@ public class LocalMediaManagerTest {
        verify(mInfoMediaManager).startScan();
    }

    @Test
    public void connectSuggestedDevice_handlerTimesOut_completesConnectionAttempt() {
        when(mInfoMediaManager.getSuggestedDevice()).thenReturn(mSuggestedDeviceState);
        when(mSuggestedDeviceInfo.getRouteId()).thenReturn(TEST_DEVICE_ID_1);
        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);

        mLocalMediaManager.connectSuggestedDevice(mSuggestedDeviceState);
        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
        mLocalMediaManager.dispatchDeviceListUpdate();

        verify(mInfoMediaManager).connectToDevice(mInfoMediaDevice1);

        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

        verify(mInfoMediaManager)
                .onConnectionAttemptCompletedForSuggestion(mSuggestedDeviceState, false);
    }

    @Test
    public void connectSuggestedDevice_connectionSuccess_completesConnectionAttempt() {
        when(mInfoMediaManager.getSuggestedDevice()).thenReturn(mSuggestedDeviceState);
        when(mSuggestedDeviceInfo.getRouteId()).thenReturn(TEST_DEVICE_ID_1);
        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);

        mLocalMediaManager.connectSuggestedDevice(mSuggestedDeviceState);
        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
        mLocalMediaManager.dispatchDeviceListUpdate();

        verify(mInfoMediaManager).connectToDevice(mInfoMediaDevice1);

        mLocalMediaManager.dispatchSelectedDeviceStateChanged(mInfoMediaDevice1,
            LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
        verify(mInfoMediaManager)
                .onConnectionAttemptCompletedForSuggestion(mSuggestedDeviceState, true);
    }

    @Test
    public void getSessionReleaseType_returnCorrectType() {
        when(mInfoMediaManager.getSessionReleaseType())