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

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

Merge "Move suggestion device chip state management into a separate class" into main

parents ca6f78a9 db07bfcb
Loading
Loading
Loading
Loading
+21 −129
Original line number Diff line number Diff line
@@ -18,10 +18,7 @@ package com.android.settingslib.media;
import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
import static android.media.session.MediaController.PlaybackInfo;

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;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
import static com.android.settingslib.media.MediaDeviceUtilKt.isBluetoothMediaDevice;
import static com.android.settingslib.media.MediaDeviceUtilKt.isComplexMediaDevice;
@@ -64,7 +61,6 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
@@ -112,9 +108,14 @@ public abstract class InfoMediaManager {
         */
        void onRequestFailed(int reason);

        /** Callback for notifying that the suggested device has been updated. */
        default void onSuggestedDeviceUpdated(@Nullable SuggestedDeviceState suggestedDevice) {}
        ;
        /**
         * Callback for changes to the suggested device list.
         *
         * @param deviceSuggestions the list of suggested devices.
         */
        default void onDeviceSuggestionsUpdated(
                @NonNull List<SuggestedDeviceInfo> deviceSuggestions) {
        }
    }


@@ -141,8 +142,6 @@ public abstract class InfoMediaManager {
    private final LocalBluetoothManager mBluetoothManager;
    @GuardedBy("mLock")
    private final Map<String, List<SuggestedDeviceInfo>> mSuggestedDeviceMap = new HashMap<>();
    @GuardedBy("mLock")
    @Nullable private SuggestedDeviceState mSuggestedDeviceState;

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

@@ -286,7 +285,7 @@ public abstract class InfoMediaManager {

    protected final void rebuildDeviceList() {
        buildAvailableRoutes();
        updateDeviceSuggestion();
        updateMediaDevicesSuggestionState();
    }

    protected final void notifyCurrentConnectedDeviceChanged() {
@@ -608,13 +607,6 @@ public abstract class InfoMediaManager {
        return getActiveRoutingSession().getName();
    }

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

    /** Requests a suggestion from other routers. */
    public abstract void requestDeviceSuggestion();

@@ -626,6 +618,9 @@ public abstract class InfoMediaManager {

    protected void notifyDeviceSuggestionUpdated(
            String suggestingPackageName, @Nullable List<SuggestedDeviceInfo> suggestions) {
        if (!com.android.media.flags.Flags.enableSuggestedDeviceApi()) {
            return;
        }
        synchronized (mLock) {
            if (suggestions == null) {
                mSuggestedDeviceMap.remove(suggestingPackageName);
@@ -633,116 +628,21 @@ public abstract class InfoMediaManager {
                mSuggestedDeviceMap.put(suggestingPackageName, suggestions);
            }
        }
        updateDeviceSuggestion();
    }

    private void updateDeviceSuggestion() {
        if (!com.android.media.flags.Flags.enableSuggestedDeviceApi()) {
            return;
        }
        if (updateSuggestedDeviceState()) {
            dispatchOnSuggestedDeviceUpdated();
        }
        if (updateMediaDevicesSuggestionState()) {
            dispatchDeviceListAdded(getMediaDevices());
        }
        dispatchOnDeviceSuggestionsUpdated();
    }

    private boolean updateSuggestedDeviceState() {
        if (!com.android.media.flags.Flags.enableSuggestedDeviceApi()) {
            return false;
        }
        SuggestedDeviceInfo topSuggestion = null;
        SuggestedDeviceState newSuggestedDeviceState = null;
        SuggestedDeviceState previousState = getSuggestedDevice();
        List<SuggestedDeviceInfo> suggestions = getSuggestions();
        if (suggestions != null && !suggestions.isEmpty()) {
            topSuggestion = suggestions.get(0);
        }
        if (topSuggestion != null) {
            synchronized (mLock) {
                for (MediaDevice device : mMediaDevices) {
                    if (Objects.equals(device.getId(), topSuggestion.getRouteId())) {
                        newSuggestedDeviceState =
                                new SuggestedDeviceState(topSuggestion, device.getState());
                        break;
                    }
                }
            }
            if (newSuggestedDeviceState == null) {
                if (previousState != null
                        && topSuggestion
                                .getRouteId()
                                .equals(previousState.getSuggestedDeviceInfo().getRouteId())) {
                    return false;
                }
                newSuggestedDeviceState = new SuggestedDeviceState(topSuggestion);
            }
        }
        if (newSuggestedDeviceState != null && isSuggestedDeviceSelected(newSuggestedDeviceState)) {
            newSuggestedDeviceState = null;
        }
        if (!Objects.equals(previousState, newSuggestedDeviceState)) {
            synchronized (mLock) {
                mSuggestedDeviceState = newSuggestedDeviceState;
            }
            return true;
        }
        return false;
    }

    private boolean isSuggestedDeviceSelected(
            @NonNull SuggestedDeviceState newSuggestedDeviceState) {
        synchronized (mLock) {
            return mMediaDevices.stream().anyMatch(device ->
                    device.isSelected()
                            && Objects.equals(
                            device.getId(),
                            newSuggestedDeviceState
                                    .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) {
                return;
            }
            mSuggestedDeviceState =
                    new SuggestedDeviceState(
                            mSuggestedDeviceState.getSuggestedDeviceInfo(), STATE_CONNECTING);
        }
        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() {
        SuggestedDeviceState state = getSuggestedDevice();
        Log.i(TAG, "dispatchOnSuggestedDeviceUpdated(), state: " + state);
    private void dispatchOnDeviceSuggestionsUpdated() {
        Log.i(TAG, "dispatchDeviceSuggestionsUpdated()");
        for (MediaDeviceCallback callback : mCallbacks) {
            callback.onSuggestedDeviceUpdated(state);
            callback.onDeviceSuggestionsUpdated(getSuggestions());
        }
    }

    @Nullable
    private List<SuggestedDeviceInfo> getSuggestions() {
    @NonNull
    List<SuggestedDeviceInfo> getSuggestions() {
        // Give suggestions in the following order
        // 1. Suggestions from the local router
        // 2. Suggestions from the proxy router if only one proxy router is providing suggestions
@@ -760,7 +660,7 @@ public abstract class InfoMediaManager {
                }
            }
        }
        return null;
        return List.of();
    }

    // Go through all current MediaDevices, and update the ones that are suggested.
@@ -770,12 +670,9 @@ public abstract class InfoMediaManager {
        }
        Set<String> suggestedDevices = new HashSet<>();
        // Prioritize suggestions from the package, otherwise pick any.
        List<SuggestedDeviceInfo> suggestions = getSuggestions();
        if (suggestions != null) {
            for (SuggestedDeviceInfo suggestion : suggestions) {
        for (SuggestedDeviceInfo suggestion : getSuggestions()) {
            suggestedDevices.add(suggestion.getRouteId());
        }
        }
        boolean didUpdate = false;
        synchronized (mLock) {
            for (MediaDevice device : mMediaDevices) {
@@ -947,11 +844,6 @@ public abstract class InfoMediaManager {
            return;
        }
        device.setState(state);
        if (device.isSuggestedDevice()) {
            if (updateSuggestedDeviceState()) {
                dispatchOnSuggestedDeviceUpdated();
            }
        }
    }

    @RequiresApi(34)
+50 −19
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.media.AudioManager;
import android.media.RoutingChangeInfo;
import android.media.RoutingChangeInfo.EntryPoint;
import android.media.RoutingSessionInfo;
import android.media.SuggestedDeviceInfo;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
@@ -55,7 +56,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

/**
@@ -255,11 +255,6 @@ public class LocalMediaManager implements BluetoothCallback {
            if (mConnectingSuggestedDeviceState != null) {
                return;
            }
            SuggestedDeviceState currentSuggestion = mInfoMediaManager.getSuggestedDevice();
            if (!Objects.equals(suggestion, currentSuggestion)) {
                Log.w(TAG, "Suggestion got changed, aborting connection.");
                return;
            }
            for (MediaDevice device : mMediaDevices) {
                if (suggestion.getSuggestedDeviceInfo().getRouteId().equals(device.getId())) {
                    Log.i(TAG, "Suggestion: device is available, connecting. deviceId = "
@@ -270,7 +265,7 @@ public class LocalMediaManager implements BluetoothCallback {
            }
            mConnectingSuggestedDeviceState =
                    new ConnectingSuggestedDeviceState(
                            currentSuggestion, routingChangeInfo.getEntryPoint());
                            suggestion, routingChangeInfo.getEntryPoint());
            mConnectingSuggestedDeviceState.tryConnect();
        }
    }
@@ -292,9 +287,9 @@ public class LocalMediaManager implements BluetoothCallback {
        }
    }

    @Nullable
    public SuggestedDeviceState getSuggestedDevice() {
        return mInfoMediaManager.getSuggestedDevice();
    @NonNull
    public List<SuggestedDeviceInfo> getSuggestions() {
        return mInfoMediaManager.getSuggestions();
    }

    void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
@@ -356,9 +351,21 @@ public class LocalMediaManager implements BluetoothCallback {
        }
    }

    void dispatchOnSuggestedDeviceUpdated(@Nullable SuggestedDeviceState device) {
    void dispatchDeviceSuggestionsUpdated(List<SuggestedDeviceInfo> deviceSuggestions) {
        for (DeviceCallback callback : getCallbacks()) {
            callback.onDeviceSuggestionsUpdated(deviceSuggestions);
        }
    }

    void dispatchConnectSuggestedDeviceFinished(SuggestedDeviceState state, boolean success) {
        for (DeviceCallback callback : getCallbacks()) {
            callback.onConnectSuggestedDeviceFinished(state, success);
        }
    }

    void dispatchConnectionAttemptedForSuggestion(SuggestedDeviceState state) {
        for (DeviceCallback callback : getCallbacks()) {
            callback.onSuggestedDeviceUpdated(device);
            callback.onConnectionAttemptedForSuggestion(state);
        }
    }

@@ -410,6 +417,17 @@ public class LocalMediaManager implements BluetoothCallback {
        return null;
    }

    /**
     * Returns a list of MediaDevice objects.
     *
     * @return a list of media devices
     */
    public List<MediaDevice> getMediaDevices() {
        synchronized (mMediaDevicesLock) {
            return new ArrayList<>(mMediaDevices);
        }
    }

    /**
     * Find the current connected MediaDevice.
     *
@@ -769,8 +787,9 @@ public class LocalMediaManager implements BluetoothCallback {
        }

        @Override
        public void onSuggestedDeviceUpdated(@Nullable SuggestedDeviceState device) {
            dispatchOnSuggestedDeviceUpdated(device);
        public void onDeviceSuggestionsUpdated(
                @NonNull List<SuggestedDeviceInfo> deviceSuggestions) {
            dispatchDeviceSuggestionsUpdated(deviceSuggestions);
        }
    }

@@ -850,8 +869,20 @@ public class LocalMediaManager implements BluetoothCallback {
         */
        default void onAboutToConnectDeviceRemoved() {}

        /** Callback for notifying that the suggested device has been updated. */
        default void onSuggestedDeviceUpdated(@Nullable SuggestedDeviceState device) {}
        /** Callback for notifying that the suggested device list has been updated. */
        default void onDeviceSuggestionsUpdated(
                @NonNull List<SuggestedDeviceInfo> deviceSuggestions) {
        }

        /** Callback for notifying that connection to suggested device is finished. */
        default void onConnectSuggestedDeviceFinished(
                @NonNull SuggestedDeviceState suggestedDeviceState, boolean success) {
        }

        /** Callback for notifying that connection to suggested device is started. */
        default void onConnectionAttemptedForSuggestion(
                @NonNull SuggestedDeviceState suggestedDeviceState) {
        }
    }

    /**
@@ -951,8 +982,8 @@ public class LocalMediaManager implements BluetoothCallback {
                        stopScan();
                        Log.i(TAG, "Suggestion: scan stopped. success = "
                                + mDidAttemptCompleteSuccessfully);
                        mInfoMediaManager.onConnectionAttemptCompletedForSuggestion(
                                mSuggestedDeviceState, mDidAttemptCompleteSuccessfully);
                        dispatchConnectSuggestedDeviceFinished(mSuggestedDeviceState,
                                mDidAttemptCompleteSuccessfully);
                    };
        }

@@ -968,7 +999,7 @@ public class LocalMediaManager implements BluetoothCallback {
            startScan();
            mConnectSuggestedDeviceHandler.postDelayed(
                    mConnectionAttemptFinishedRunnable, SCAN_DURATION_MS);
            mInfoMediaManager.onConnectionAttemptedForSuggestion(mSuggestedDeviceState);
            dispatchConnectionAttemptedForSuggestion(mSuggestedDeviceState);
        }
    }
}
+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.media

import android.media.RoutingChangeInfo
import android.media.SuggestedDeviceInfo
import android.util.Log
import androidx.annotation.GuardedBy
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTED
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTING
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED
import java.util.concurrent.CopyOnWriteArraySet

private const val TAG = "SuggestedDeviceManager"

/**
 * Provides data to render and handles user interactions for the suggested device chip within the
 * Android Media Controls.
 *
 * This class exposes the [SuggestedDeviceState] which is calculated based on:
 * - Lists of device suggestions and media routes (media devices) provided by the Media Router.
 * - User interactions with the suggested device chip.
 * - The results of user-initiated connection attempts to these devices.
 */
class SuggestedDeviceManager(private val localMediaManager: LocalMediaManager) {
  private val lock: Any = Object()
  private val listeners = CopyOnWriteArraySet<Listener>()
  @GuardedBy("lock") private var mediaDevices: List<MediaDevice> = listOf()
  @GuardedBy("lock") private var suggestions: List<SuggestedDeviceInfo> = listOf()
  @GuardedBy("lock") private var suggestedDeviceState: SuggestedDeviceState? = null

  private val localMediaManagerDeviceCallback =
    object : LocalMediaManager.DeviceCallback {
      override fun onDeviceListUpdate(newDevices: List<MediaDevice>?) {
        val stateChanged = synchronized(lock) {
          mediaDevices = newDevices?.toList() ?: listOf()
          updateSuggestedDeviceStateLocked()
        }
        if (stateChanged) {
          dispatchOnSuggestedDeviceUpdated()
        }
      }

      override fun onDeviceSuggestionsUpdated(newSuggestions: List<SuggestedDeviceInfo>) {
        val stateChanged = synchronized(lock) {
          suggestions = newSuggestions
          updateSuggestedDeviceStateLocked()
        }
        if (stateChanged) {
          dispatchOnSuggestedDeviceUpdated()
        }
      }

      override fun onConnectionAttemptedForSuggestion(
        newSuggestedDeviceState: SuggestedDeviceState
      ) {
        synchronized(lock) {
          if (!isCurrentSuggestion(newSuggestedDeviceState.suggestedDeviceInfo)) {
            Log.w(TAG, "onConnectionAttemptedForSuggestion. Suggestion got changed.")
            return
          }
          if (
            suggestedDeviceState?.connectionState != STATE_DISCONNECTED &&
              suggestedDeviceState?.connectionState != STATE_CONNECTING_FAILED
          ) {
            return
          }
          suggestedDeviceState = suggestedDeviceState?.copy(connectionState = STATE_CONNECTING)
        }
        dispatchOnSuggestedDeviceUpdated()
      }

      override fun onConnectSuggestedDeviceFinished(
        newSuggestedDeviceState: SuggestedDeviceState,
        success: Boolean,
      ) {
        if (!isCurrentSuggestion(newSuggestedDeviceState.suggestedDeviceInfo)) {
          Log.w(TAG, "onConnectSuggestedDeviceFinished. Suggestion got changed.")
          return
        }
        synchronized(lock) {
          val connectionState = if (success) STATE_CONNECTED else STATE_CONNECTING_FAILED
          suggestedDeviceState = suggestedDeviceState?.copy(connectionState = connectionState)
        }
        dispatchOnSuggestedDeviceUpdated()
      }
    }

  fun addListener(listener: Listener) {
    val shouldRegisterCallback = synchronized(lock) {
      val wasSetEmpty = listeners.isEmpty()
      listeners.add(listener)
      wasSetEmpty
    }

    if (shouldRegisterCallback) {
      eagerlyUpdateState()
      localMediaManager.registerCallback(localMediaManagerDeviceCallback)
    }
  }

  fun removeListener(listener: Listener) {
    val shouldUnregisterCallback = synchronized(lock) {
      listeners.remove(listener)
      listeners.isEmpty()
    }

    if (shouldUnregisterCallback) {
      localMediaManager.unregisterCallback(localMediaManagerDeviceCallback)
    }
  }

  fun requestDeviceSuggestion() {
    localMediaManager.requestDeviceSuggestion()
  }

  fun getSuggestedDevice(): SuggestedDeviceState? {
    if (listeners.isEmpty()) {
      // If there were no callbacks set, recalculate the state before returning the result.
      eagerlyUpdateState()
    }
    return suggestedDeviceState
  }

  fun connectSuggestedDevice(
    suggestedDeviceState: SuggestedDeviceState,
    routingChangeInfo: RoutingChangeInfo,
  ) {
    if (!isCurrentSuggestion(suggestedDeviceState.suggestedDeviceInfo)) {
      Log.w(TAG, "Suggestion got changed, aborting connection.")
      return
    }
    localMediaManager.connectSuggestedDevice(suggestedDeviceState, routingChangeInfo)
  }

  private fun eagerlyUpdateState() {
    synchronized(lock) {
      mediaDevices = localMediaManager.mediaDevices
      suggestions = localMediaManager.suggestions
      updateSuggestedDeviceStateLocked()
    }
  }

  @GuardedBy("lock")
  private fun updateSuggestedDeviceStateLocked(): Boolean {
    var newSuggestedDeviceState: SuggestedDeviceState? = null
    val previousState = suggestedDeviceState
    val topSuggestion = suggestions.firstOrNull()
    if (topSuggestion != null) {
      val matchedDevice = getDeviceById(mediaDevices, topSuggestion.routeId)
      if (matchedDevice != null) {
        newSuggestedDeviceState = SuggestedDeviceState(topSuggestion, matchedDevice.state)
      }
      if (newSuggestedDeviceState == null) {
        if (previousState != null
          && (topSuggestion.routeId == previousState.suggestedDeviceInfo.routeId)) {
          return false
        }
        newSuggestedDeviceState = SuggestedDeviceState(topSuggestion)
      }
    }

    if (newSuggestedDeviceState != null && isSuggestedDeviceSelected(newSuggestedDeviceState)) {
      newSuggestedDeviceState = null
    }
    if (previousState != newSuggestedDeviceState) {
      synchronized(lock) { suggestedDeviceState = newSuggestedDeviceState }
      return true
    }
    return false
  }

  private fun isSuggestedDeviceSelected(newSuggestedDeviceState: SuggestedDeviceState): Boolean {
    synchronized(lock) {
      return mediaDevices.any { device ->
        device.isSelected() && device.getId() == newSuggestedDeviceState.suggestedDeviceInfo.routeId
      }
    }
  }

  private fun getDeviceById(mediaDevices: List<MediaDevice>, routeId: String): MediaDevice? =
    mediaDevices.find { it.id == routeId }

  private fun isCurrentSuggestion(suggestedDeviceInfo: SuggestedDeviceInfo) =
    synchronized(lock) {
      suggestedDeviceState?.suggestedDeviceInfo?.routeId == suggestedDeviceInfo.routeId
    }

  private fun dispatchOnSuggestedDeviceUpdated() {
    val state = synchronized(lock) { suggestedDeviceState }
    Log.i(TAG, "dispatchOnSuggestedDeviceUpdated(), state: $state")
    listeners.forEach { it.onSuggestedDeviceStateUpdated(state) }
  }

  interface Listener {
    fun onSuggestedDeviceStateUpdated(state: SuggestedDeviceState?)
  }
}
+7 −263

File changed.

Preview size limit exceeded, changes collapsed.

+2 −9
Original line number Diff line number Diff line
@@ -643,7 +643,6 @@ public class LocalMediaManagerTest {

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

@@ -657,7 +656,6 @@ public class LocalMediaManagerTest {

    @Test
    public void connectSuggestedDevice_deviceIsNotDiscovered_scanStarted() {
        when(mInfoMediaManager.getSuggestedDevice()).thenReturn(mSuggestedDeviceState);
        when(mSuggestedDeviceInfo.getRouteId()).thenReturn(TEST_DEVICE_ID_2);
        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);

@@ -671,7 +669,6 @@ public class LocalMediaManagerTest {

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

@@ -687,7 +684,6 @@ public class LocalMediaManagerTest {

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

@@ -701,13 +697,11 @@ public class LocalMediaManagerTest {

        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

        verify(mInfoMediaManager)
                .onConnectionAttemptCompletedForSuggestion(mSuggestedDeviceState, false);
        verify(mCallback).onConnectSuggestedDeviceFinished(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);

@@ -721,8 +715,7 @@ public class LocalMediaManagerTest {

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

    @Test
Loading