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

Commit 7dd71282 authored by Alexandr Shabalin's avatar Alexandr Shabalin Committed by Android (Google) Code Review
Browse files

Merge changes Ib044a079,I9449f860,I8b757bee into main

* changes:
  Put all access to suggested device state within the synchronized block.
  Put all access to the MediaDevices within the synchronized block.
  Remove InfoMediaManager synchronized block on class instance.
parents 4b6172cb 358556d6
Loading
Loading
Loading
Loading
+104 −69
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -82,6 +83,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
@@ -210,20 +212,23 @@ public abstract class InfoMediaManager {

    private static final String TAG = "InfoMediaManager";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private final Object mLock = new Object();
    protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
    protected final Object mLock = new Object();
    @GuardedBy("mLock")
    protected final List<MediaDevice> mMediaDevices = new ArrayList<>();
    @NonNull protected final Context mContext;
    @NonNull protected final String mPackageName;
    @NonNull protected final UserHandle mUserHandle;
    private final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
    @GuardedBy("mLock")
    private MediaDevice mCurrentConnectedDevice;
    private MediaController mMediaController;
    private PlaybackInfo mLastKnownPlaybackInfo;
    private final LocalBluetoothManager mBluetoothManager;
    private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
            new ConcurrentHashMap<>();
    private final Map<String, List<SuggestedDeviceInfo>> mSuggestedDeviceMap =
            new ConcurrentHashMap<>();
    @GuardedBy("mLock")
    private final Map<String, List<SuggestedDeviceInfo>> mSuggestedDeviceMap = new HashMap<>();
    @GuardedBy("mLock")
    @Nullable private SuggestedDeviceState mSuggestedDeviceState;

    private final MediaController.Callback mMediaControllerCallback = new MediaControllerCallback();
@@ -373,14 +378,13 @@ public abstract class InfoMediaManager {
    protected abstract List<MediaRoute2Info> getTransferableRoutes(@NonNull String packageName);

    protected final void rebuildDeviceList() {
        synchronized (mLock) {
        buildAvailableRoutes();
        }
        updateDeviceSuggestion();
    }

    protected final void notifyCurrentConnectedDeviceChanged() {
        final String id = mCurrentConnectedDevice != null ? mCurrentConnectedDevice.getId() : null;
        MediaDevice device = getCurrentConnectedDevice();
        final String id = device != null ? device.getId() : null;
        dispatchConnectedDeviceChanged(id);
    }

@@ -390,14 +394,20 @@ public abstract class InfoMediaManager {
        Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
    }

    @VisibleForTesting
    @Nullable
    protected final MediaDevice findMediaDevice(@NonNull String id) {
        for (MediaDevice mediaDevice : mMediaDevices) {
            if (mediaDevice.getId().equals(id)) {
                return mediaDevice;
        return getMediaDevices().stream()
                .filter(device -> device.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

    @NonNull
    private List<MediaDevice> getMediaDevices() {
        synchronized (mLock) {
            return new ArrayList<>(mMediaDevices);
        }
        Log.e(TAG, "findMediaDevice() can't find device with id: " + id);
        return null;
    }

    /**
@@ -414,7 +424,9 @@ public abstract class InfoMediaManager {
        if (!mCallbacks.contains(callback)) {
            mCallbacks.add(callback);
            if (wasEmpty) {
                synchronized (mLock) {
                    mMediaDevices.clear();
                }
                registerRouter();
                if (mMediaController != null) {
                    mMediaController.registerCallback(mMediaControllerCallback);
@@ -474,8 +486,10 @@ public abstract class InfoMediaManager {
     * @return MediaDevice
     */
    MediaDevice getCurrentConnectedDevice() {
        synchronized (mLock) {
            return mCurrentConnectedDevice;
        }
    }

    /* package */ void connectToDevice(MediaDevice device, RoutingChangeInfo routingChangeInfo) {
        Log.i(TAG, "connectToDevice(), device = " + device.getName() + "/" + device.getId());
@@ -661,8 +675,10 @@ public abstract class InfoMediaManager {

    @Nullable
    public SuggestedDeviceState getSuggestedDevice() {
        synchronized (mLock) {
            return mSuggestedDeviceState;
        }
    }

    /** Requests a suggestion from other routers. */
    public abstract void requestDeviceSuggestion();
@@ -675,11 +691,13 @@ public abstract class InfoMediaManager {

    protected void notifyDeviceSuggestionUpdated(
            String suggestingPackageName, @Nullable List<SuggestedDeviceInfo> suggestions) {
        synchronized (mLock) {
            if (suggestions == null) {
                mSuggestedDeviceMap.remove(suggestingPackageName);
            } else {
                mSuggestedDeviceMap.put(suggestingPackageName, suggestions);
            }
        }
        updateDeviceSuggestion();
    }

@@ -691,7 +709,7 @@ public abstract class InfoMediaManager {
            dispatchOnSuggestedDeviceUpdated();
        }
        if (updateMediaDevicesSuggestionState()) {
            dispatchDeviceListAdded(mMediaDevices);
            dispatchDeviceListAdded(getMediaDevices());
        }
    }

@@ -701,7 +719,7 @@ public abstract class InfoMediaManager {
        }
        SuggestedDeviceInfo topSuggestion = null;
        SuggestedDeviceState newSuggestedDeviceState = null;
        SuggestedDeviceState previousState = mSuggestedDeviceState;
        SuggestedDeviceState previousState = getSuggestedDevice();
        List<SuggestedDeviceInfo> suggestions = getSuggestions();
        if (suggestions != null && !suggestions.isEmpty()) {
            topSuggestion = suggestions.get(0);
@@ -730,7 +748,9 @@ public abstract class InfoMediaManager {
            newSuggestedDeviceState = null;
        }
        if (!Objects.equals(previousState, newSuggestedDeviceState)) {
            synchronized (mLock) {
                mSuggestedDeviceState = newSuggestedDeviceState;
            }
            return true;
        }
        return false;
@@ -738,6 +758,7 @@ public abstract class InfoMediaManager {

    private boolean isSuggestedDeviceSelected(
            @NonNull SuggestedDeviceState newSuggestedDeviceState) {
        synchronized (mLock) {
            return mMediaDevices.stream().anyMatch(device ->
                    device.isSelected()
                            && Objects.equals(
@@ -746,35 +767,42 @@ public abstract class InfoMediaManager {
                                    .getSuggestedDeviceInfo()
                                    .getRouteId()));
        }
    }

    final void onConnectionAttemptedForSuggestion(@NonNull SuggestedDeviceState suggestion) {
        synchronized (mLock) {
            if (!Objects.equals(suggestion, mSuggestedDeviceState)) {
                return;
            }
        if (mSuggestedDeviceState.getConnectionState() == STATE_DISCONNECTED
                || mSuggestedDeviceState.getConnectionState() == STATE_CONNECTING_FAILED) {
            if (mSuggestedDeviceState.getConnectionState() != STATE_DISCONNECTED
                    && mSuggestedDeviceState.getConnectionState() != STATE_CONNECTING_FAILED) {
                return;
            }
            mSuggestedDeviceState =
                    new SuggestedDeviceState(
                            mSuggestedDeviceState.getSuggestedDeviceInfo(), STATE_CONNECTING);
            dispatchOnSuggestedDeviceUpdated();
        }
        dispatchOnSuggestedDeviceUpdated();
    }

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

    private void dispatchOnSuggestedDeviceUpdated() {
        Log.i(TAG, "dispatchOnSuggestedDeviceUpdated(), state: " + mSuggestedDeviceState);
        SuggestedDeviceState state = getSuggestedDevice();
        Log.i(TAG, "dispatchOnSuggestedDeviceUpdated(), state: " + state);
        for (MediaDeviceCallback callback : getCallbacks()) {
            callback.onSuggestedDeviceUpdated(mSuggestedDeviceState);
            callback.onSuggestedDeviceUpdated(state);
        }
    }

@@ -784,6 +812,7 @@ public abstract class InfoMediaManager {
        // 1. Suggestions from the local router
        // 2. Suggestions from the proxy router if only one proxy router is providing suggestions
        // 3. No suggestion at all if multiple proxy routers are providing suggestions.
        synchronized (mLock) {
            List<SuggestedDeviceInfo> suggestions = mSuggestedDeviceMap.get(mPackageName);
            if (suggestions != null) {
                return suggestions;
@@ -795,6 +824,7 @@ public abstract class InfoMediaManager {
                    }
                }
            }
        }
        return null;
    }

@@ -847,19 +877,20 @@ public abstract class InfoMediaManager {
        return didUpdate;
    }

    protected final synchronized void refreshDevices() {
    protected final void refreshDevices() {
        rebuildDeviceList();
        dispatchDeviceListAdded(mMediaDevices);
        dispatchDeviceListAdded(getMediaDevices());
    }

    // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
    @SuppressWarnings("NewApi")
    private synchronized void buildAvailableRoutes() {
    private void buildAvailableRoutes() {
        synchronized (mLock) {
            mMediaDevices.clear();
            RoutingSessionInfo activeSession = getActiveRoutingSession();

            for (MediaRoute2Info route : getAvailableRoutes(activeSession)) {
            addMediaDevice(route, activeSession);
                addMediaDeviceLocked(route, activeSession);
            }

            // In practice, mMediaDevices should always have at least one route.
@@ -868,8 +899,9 @@ public abstract class InfoMediaManager {
                mCurrentConnectedDevice = mMediaDevices.get(0);
            }
        }
    }

    private synchronized List<MediaRoute2Info> getAvailableRoutes(
    private List<MediaRoute2Info> getAvailableRoutes(
            RoutingSessionInfo activeSession) {
        List<MediaRoute2Info> availableRoutes = new ArrayList<>();

@@ -905,11 +937,13 @@ public abstract class InfoMediaManager {

    // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
    @SuppressWarnings("NewApi")
    @GuardedBy("mLock")
    @VisibleForTesting
    void addMediaDevice(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo activeSession) {
    void addMediaDeviceLocked(@NonNull MediaRoute2Info route,
            @NonNull RoutingSessionInfo activeSession) {
        DynamicRouteAttributes dynamicRouteAttributes =
                getDynamicRouteAttributes(activeSession, route);
        MediaDevice mediaDevice = createMediaDeviceFromRoute(route, dynamicRouteAttributes);
        MediaDevice mediaDevice = createMediaDeviceFromRouteLocked(route, dynamicRouteAttributes);
        if (mediaDevice != null) {
            if (mediaDevice.isSelected()) {
                mediaDevice.setState(STATE_SELECTED);
@@ -918,8 +952,9 @@ public abstract class InfoMediaManager {
        }
    }

    @GuardedBy("mLock")
    @Nullable
    private MediaDevice createMediaDeviceFromRoute(@NonNull MediaRoute2Info route,
    private MediaDevice createMediaDeviceFromRouteLocked(@NonNull MediaRoute2Info route,
            @NonNull DynamicRouteAttributes dynamicRouteAttributes) {
        final int deviceType = route.getType();
        MediaDevice mediaDevice = null;
@@ -1058,7 +1093,7 @@ public abstract class InfoMediaManager {
    @RequiresApi(34)
    static class Api34Impl {
        @DoNotInline
        static synchronized List<MediaRoute2Info> filterDuplicatedIds(List<MediaRoute2Info> infos) {
        static List<MediaRoute2Info> filterDuplicatedIds(List<MediaRoute2Info> infos) {
            List<MediaRoute2Info> filteredInfos = new ArrayList<>();
            Set<String> foundDeduplicationIds = new HashSet<>();
            for (MediaRoute2Info mediaRoute2Info : infos) {
+3 −3
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
                public void onSuggestionsRequested() {} // no-op
            };

    @GuardedBy("this")
    @GuardedBy("InfoMediaManager.this.fieldName")
    @Nullable
    private MediaRouter2.ScanToken mScanToken;

@@ -150,7 +150,7 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
    @Override
    protected void startScanOnRouter() {
        if (Flags.enableScreenOffScanning()) {
            synchronized (this) {
            synchronized (super.mLock) {
                if (mScanToken == null) {
                    MediaRouter2.ScanRequest request =
                            new MediaRouter2.ScanRequest.Builder().build();
@@ -182,7 +182,7 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
    @Override
    protected void stopScanOnRouter() {
        if (Flags.enableScreenOffScanning()) {
            synchronized (this) {
            synchronized (super.mLock) {
                if (mScanToken != null) {
                    mRouter.cancelScanRequest(mScanToken);
                    mScanToken = null;
+7 −7
Original line number Diff line number Diff line
@@ -864,19 +864,19 @@ public class InfoMediaManagerTest {

        when(route2Info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
        when(route2Info.getId()).thenReturn(TEST_ID);
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof InfoMediaDevice).isTrue();

        when(route2Info.getType()).thenReturn(TYPE_USB_DEVICE);
        when(route2Info.getId()).thenReturn(TEST_ID);
        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();

        when(route2Info.getType()).thenReturn(TYPE_WIRED_HEADSET);
        when(route2Info.getId()).thenReturn(TEST_ID);
        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();

        when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
@@ -887,12 +887,12 @@ public class InfoMediaManagerTest {
        when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
                .thenReturn(cachedDevice);
        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof BluetoothMediaDevice).isTrue();

        when(route2Info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
    }

@@ -910,7 +910,7 @@ public class InfoMediaManagerTest {
                .thenReturn(null);

        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(route2Info, TEST_SYSTEM_ROUTING_SESSION);

        assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
    }
@@ -927,7 +927,7 @@ public class InfoMediaManagerTest {
        when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class))).thenReturn(null);

        mInfoMediaManager.mMediaDevices.clear();
        mInfoMediaManager.addMediaDevice(bluetoothRoute, TEST_SYSTEM_ROUTING_SESSION);
        mInfoMediaManager.addMediaDeviceLocked(bluetoothRoute, TEST_SYSTEM_ROUTING_SESSION);

        assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
    }