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

Commit c1291efb authored by Iván Budnik's avatar Iván Budnik
Browse files

Prevent unneeded scanning in SystemUI routing components

This change allows SettingsLib clients to receive routing state updates
without the need to actively scan, which should be only used by
components like the Output Switcher dialog.

As a follow up change, we should inspect Settings usages to remove any
remaining unjustified scanning.

Flag: ACONFIG remove_unnecessary_route_scanning DEVELOPMENT
Bug: 332515672
Test: atest com.android.settingslib.media
Change-Id: I9ad7aa0b52e4f36ca14f0f7ced35a33c0a7c5cad
parent 7dca5acb
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -21,3 +21,13 @@ flag {
    description: "Enable Output Switcher when no media is playing."
    bug: "284227163"
}

flag {
    name: "remove_unnecessary_route_scanning"
    namespace: "media_solutions"
    description: "Avoid active scan requests on UI components that only display route status information."
    bug: "332515672"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+26 −8
Original line number Diff line number Diff line
@@ -173,11 +173,7 @@ public abstract class InfoMediaManager {
    }

    public void startScan() {
        mMediaDevices.clear();
        registerRouter();
        startScanOnRouter();
        updateRouteListingPreference();
        refreshDevices();
    }

    private void updateRouteListingPreference() {
@@ -191,7 +187,6 @@ public abstract class InfoMediaManager {

    public final void stopScan() {
        stopScanOnRouter();
        unregisterRouter();
    }

    protected abstract void stopScanOnRouter();
@@ -278,14 +273,37 @@ public abstract class InfoMediaManager {
        return null;
    }

    protected final void registerCallback(MediaDeviceCallback callback) {
    /**
     * Registers the specified {@code callback} to receive state updates about routing information.
     *
     * <p>As long as there is a registered {@link MediaDeviceCallback}, {@link InfoMediaManager}
     * will receive state updates from the platform.
     *
     * <p>Call {@link #unregisterCallback(MediaDeviceCallback)} once you no longer need platform
     * updates.
     */
    public final void registerCallback(@NonNull MediaDeviceCallback callback) {
        boolean wasEmpty = mCallbacks.isEmpty();
        if (!mCallbacks.contains(callback)) {
            mCallbacks.add(callback);
            if (wasEmpty) {
                mMediaDevices.clear();
                registerRouter();
                updateRouteListingPreference();
                refreshDevices();
            }
        }
    }

    protected final void unregisterCallback(MediaDeviceCallback callback) {
        mCallbacks.remove(callback);
    /**
     * Unregisters the specified {@code callback}.
     *
     * @see #registerCallback(MediaDeviceCallback)
     */
    public final void unregisterCallback(@NonNull MediaDeviceCallback callback) {
        if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
            unregisterRouter();
        }
    }

    private void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) {
+11 −8
Original line number Diff line number Diff line
@@ -106,14 +106,23 @@ public class LocalMediaManager implements BluetoothCallback {
     * Register to start receiving callbacks for MediaDevice events.
     */
    public void registerCallback(DeviceCallback callback) {
        boolean wasEmpty = mCallbacks.isEmpty();
        if (!mCallbacks.contains(callback)) {
            mCallbacks.add(callback);
            if (wasEmpty) {
                mInfoMediaManager.registerCallback(mMediaDeviceCallback);
            }
        }
    }

    /**
     * Unregister to stop receiving callbacks for MediaDevice events
     */
    public void unregisterCallback(DeviceCallback callback) {
        mCallbacks.remove(callback);
        if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
            mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
            unRegisterDeviceAttributeChangeCallback();
        }
    }

    /**
@@ -225,10 +234,6 @@ public class LocalMediaManager implements BluetoothCallback {
     * Start scan connected MediaDevice
     */
    public void startScan() {
        synchronized (mMediaDevicesLock) {
            mMediaDevices.clear();
        }
        mInfoMediaManager.registerCallback(mMediaDeviceCallback);
        mInfoMediaManager.startScan();
    }

@@ -278,9 +283,7 @@ public class LocalMediaManager implements BluetoothCallback {
     * Stop scan MediaDevice
     */
    public void stopScan() {
        mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
        mInfoMediaManager.stopScan();
        unRegisterDeviceAttributeChangeCallback();
    }

    /**
+7 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.settingslib.volume.data.repository

import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.flags.Flags
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.CoroutineScope
@@ -69,10 +70,14 @@ class LocalMediaRepositoryImpl(
                        }
                    }
                localMediaManager.registerCallback(callback)
                if (!Flags.removeUnnecessaryRouteScanning()) {
                    localMediaManager.startScan()
                }

                awaitClose {
                    if (!Flags.removeUnnecessaryRouteScanning()) {
                        localMediaManager.stopScan()
                    }
                    localMediaManager.unregisterCallback(callback)
                }
            }
+20 −22
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -750,35 +751,31 @@ public class InfoMediaManagerTest {

    @Test
    public void onTransferred_getAvailableRoutes_shouldAddMediaDevice() {
        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
        final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
        routingSessionInfos.add(sessionInfo);
        final List<String> selectedRoutes = new ArrayList<>();
        selectedRoutes.add(TEST_ID);
        when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
        mInfoMediaManager.mRouterManager = mRouterManager;
        // Since test is running in Robolectric, return a fake session to avoid NPE.
        when(mRouterManager.getRoutingSessions(anyString()))
                .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION));
        when(mRouterManager.getSelectedRoutes(any()))
                .thenReturn(List.of(TEST_SELECTED_SYSTEM_ROUTE));

        final MediaRoute2Info info = mock(MediaRoute2Info.class);
        mInfoMediaManager.registerCallback(mCallback);

        when(info.getDeduplicationIds()).thenReturn(Set.of());
        when(info.getId()).thenReturn(TEST_ID);
        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);

        final List<MediaRoute2Info> routes = new ArrayList<>();
        routes.add(info);
        mShadowRouter2Manager.setTransferableRoutes(routes);
        MediaDevice mediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
        assertThat(mediaDevice).isNotNull();
        assertThat(mediaDevice.getId()).isEqualTo(TEST_SYSTEM_ROUTE_ID);

        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
        assertThat(mediaDevice).isNull();
        when(mRouterManager.getRoutingSessions(anyString()))
                .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION));
        when(mRouterManager.getSelectedRoutes(any())).thenReturn(List.of(TEST_REMOTE_ROUTE));

        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
        mInfoMediaManager.mMediaRouterCallback.onTransferred(
                TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION);

        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
        assertThat(infoDevice).isNotNull();
        assertThat(infoDevice.getId()).isEqualTo(TEST_REMOTE_ROUTE.getId());
        assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
        verify(mCallback).onConnectedDeviceChanged(TEST_ID);
        verify(mCallback).onConnectedDeviceChanged(TEST_REMOTE_ROUTE.getId());
    }

    @Test
@@ -794,7 +791,8 @@ public class InfoMediaManagerTest {

        mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(TEST_SYSTEM_ROUTING_SESSION);

        verify(mCallback).onDeviceListAdded(any());
        // Expecting 1st call after registerCallback() and 2nd call after onSessionUpdated().
        verify(mCallback, times(2)).onDeviceListAdded(any());
    }

    @Test
Loading