Merge "Non-functionally simplify getRoutingSession() and fix nullability" into main
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a376c1f..645eecd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -199,6 +199,12 @@
     @NonNull
     protected abstract List<RoutingSessionInfo> getRemoteSessions();
 
+    /**
+     * Returns a non-empty list containing the routing sessions associated to the target media app.
+     *
+     * <p> The first item of the list is always the {@link RoutingSessionInfo#isSystemSession()
+     * system session}, followed other remote sessions linked to the target media app.
+     */
     @NonNull
     protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
 
@@ -256,8 +262,8 @@
      * @return If add device successful return {@code true}, otherwise return {@code false}
      */
     boolean addDeviceToPlayMedia(MediaDevice device) {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
+        final RoutingSessionInfo info = getActiveRoutingSession();
+        if (!info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
             Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
                     + device.getName());
             return false;
@@ -267,13 +273,11 @@
         return true;
     }
 
-    private RoutingSessionInfo getRoutingSessionInfo() {
-        final List<RoutingSessionInfo> sessionInfos = getRoutingSessionsForPackage();
-
-        if (sessionInfos.isEmpty()) {
-            return null;
-        }
-        return sessionInfos.get(sessionInfos.size() - 1);
+    @NonNull
+    private RoutingSessionInfo getActiveRoutingSession() {
+        // List is never empty.
+        final List<RoutingSessionInfo> sessions = getRoutingSessionsForPackage();
+        return sessions.get(sessions.size() - 1);
     }
 
     boolean isRoutingSessionAvailableForVolumeControl() {
@@ -311,8 +315,8 @@
      * @return If device stop successful return {@code true}, otherwise return {@code false}
      */
     boolean removeDeviceFromPlayMedia(MediaDevice device) {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
+        final RoutingSessionInfo info = getActiveRoutingSession();
+        if (!info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
             Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
                     + device.getName());
             return false;
@@ -326,13 +330,7 @@
      * Release session to stop playing media on MediaDevice.
      */
     boolean releaseSession() {
-        final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
-        if (sessionInfo == null) {
-            Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
-            return false;
-        }
-
-        releaseSession(sessionInfo);
+        releaseSession(getActiveRoutingSession());
         return true;
     }
 
@@ -342,12 +340,7 @@
      */
     @NonNull
     List<MediaDevice> getSelectableMediaDevices() {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
-                    + mPackageName);
-            return Collections.emptyList();
-        }
+        final RoutingSessionInfo info = getActiveRoutingSession();
 
         final List<MediaDevice> deviceList = new ArrayList<>();
         for (MediaRoute2Info route : getSelectableRoutes(info)) {
@@ -364,12 +357,7 @@
      */
     @NonNull
     List<MediaDevice> getDeselectableMediaDevices() {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
-                    + mPackageName);
-            return Collections.emptyList();
-        }
+        final RoutingSessionInfo info = getActiveRoutingSession();
 
         final List<MediaDevice> deviceList = new ArrayList<>();
         for (MediaRoute2Info route : getDeselectableRoutes(info)) {
@@ -387,13 +375,7 @@
      */
     @NonNull
     List<MediaDevice> getSelectedMediaDevices() {
-        RoutingSessionInfo info = getRoutingSessionInfo();
-
-        if (info == null) {
-            Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
-                    + mPackageName);
-            return Collections.emptyList();
-        }
+        RoutingSessionInfo info = getActiveRoutingSession();
 
         final List<MediaDevice> deviceList = new ArrayList<>();
         for (MediaRoute2Info route : getSelectedRoutes(info)) {
@@ -427,15 +409,8 @@
      * @param volume the value of volume
      */
     void adjustSessionVolume(int volume) {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
-                    + mPackageName);
-            return;
-        }
-
         Log.d(TAG, "adjustSessionVolume() adjust volume: " + volume + ", with : " + mPackageName);
-        setSessionVolume(info, volume);
+        setSessionVolume(getActiveRoutingSession(), volume);
     }
 
     /**
@@ -444,14 +419,7 @@
      * @return  maximum volume of the session, and return -1 if not found.
      */
     public int getSessionVolumeMax() {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
-                    + mPackageName);
-            return -1;
-        }
-
-        return info.getVolumeMax();
+        return getActiveRoutingSession().getVolumeMax();
     }
 
     /**
@@ -460,24 +428,11 @@
      * @return current volume of the session, and return -1 if not found.
      */
     public int getSessionVolume() {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
-                    + mPackageName);
-            return -1;
-        }
-
-        return info.getVolume();
+        return getActiveRoutingSession().getVolume();
     }
 
     CharSequence getSessionName() {
-        final RoutingSessionInfo info = getRoutingSessionInfo();
-        if (info == null) {
-            Log.w(TAG, "Unable to get session name for package: " + mPackageName);
-            return null;
-        }
-
-        return info.getName();
+        return getActiveRoutingSession().getName();
     }
 
     @TargetApi(Build.VERSION_CODES.R)
@@ -503,26 +458,24 @@
         }
     }
     private synchronized List<MediaRoute2Info> getAvailableRoutes() {
-        List<MediaRoute2Info> infos = new ArrayList<>();
-        RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo();
-        List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
-        if (routingSessionInfo != null) {
-            selectedRouteInfos = getSelectedRoutes(routingSessionInfo);
-            infos.addAll(selectedRouteInfos);
-            infos.addAll(getSelectableRoutes(routingSessionInfo));
-        }
-        final List<MediaRoute2Info> transferableRoutes =
-                getTransferableRoutes(mPackageName);
+        List<MediaRoute2Info> availableRoutes = new ArrayList<>();
+        RoutingSessionInfo activeSession = getActiveRoutingSession();
+
+        List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(activeSession);
+        availableRoutes.addAll(selectedRoutes);
+        availableRoutes.addAll(getSelectableRoutes(activeSession));
+
+        final List<MediaRoute2Info> transferableRoutes = getTransferableRoutes(mPackageName);
         for (MediaRoute2Info transferableRoute : transferableRoutes) {
             boolean alreadyAdded = false;
-            for (MediaRoute2Info mediaRoute2Info : infos) {
+            for (MediaRoute2Info mediaRoute2Info : availableRoutes) {
                 if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
                     alreadyAdded = true;
                     break;
                 }
             }
             if (!alreadyAdded) {
-                infos.add(transferableRoute);
+                availableRoutes.add(transferableRoute);
             }
         }
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -531,13 +484,13 @@
                 final List<RouteListingPreference.Item> preferenceRouteListing =
                         Api34Impl.composePreferenceRouteListing(
                                 routeListingPreference);
-                infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
+                availableRoutes = Api34Impl.arrangeRouteListByPreference(selectedRoutes,
                         getAvailableRoutesFromRouter(),
                                 preferenceRouteListing);
             }
-            return Api34Impl.filterDuplicatedIds(infos);
+            return Api34Impl.filterDuplicatedIds(availableRoutes);
         } else {
-            return infos;
+            return availableRoutes;
         }
     }
 
@@ -610,7 +563,7 @@
         }
 
         if (mediaDevice != null
-                && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
+                && getActiveRoutingSession().getSelectedRoutes().contains(route.getId())) {
             mediaDevice.setState(STATE_SELECTED);
             if (mCurrentConnectedDevice == null) {
                 mCurrentConnectedDevice = mediaDevice;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 290e63ca..382e9dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -30,6 +30,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -124,7 +125,11 @@
 
     @Test
     public void stopScan_startFirst_callsUnregister() {
+        RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
         mInfoMediaManager.mRouterManager = mRouterManager;
+        // Since test is running in Robolectric, return a fake session to avoid NPE.
+        when(mRouterManager.getRoutingSessions(anyString())).thenReturn(List.of(sessionInfo));
+
         mInfoMediaManager.startScan();
         mInfoMediaManager.stopScan();
 
@@ -678,17 +683,6 @@
     }
 
     @Test
-    public void getSessionVolumeMax_routeSessionInfoIsNull_returnNotFound() {
-        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
-        final RoutingSessionInfo info = null;
-        routingSessionInfos.add(info);
-
-        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
-        assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
-    }
-
-    @Test
     public void getSessionVolume_containPackageName_returnMaxVolume() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -703,17 +697,6 @@
     }
 
     @Test
-    public void getSessionVolume_routeSessionInfoIsNull_returnNotFound() {
-        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
-        final RoutingSessionInfo info = null;
-        routingSessionInfos.add(info);
-
-        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
-        assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
-    }
-
-    @Test
     public void getRemoteSessions_returnsRemoteSessions() {
         final List<RoutingSessionInfo> infos = new ArrayList<>();
         infos.add(mock(RoutingSessionInfo.class));
@@ -735,17 +718,6 @@
     }
 
     @Test
-    public void getSessionName_routeSessionInfoIsNull_returnNull() {
-        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
-        final RoutingSessionInfo info = null;
-        routingSessionInfos.add(info);
-
-        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
-        assertThat(mInfoMediaManager.getSessionName()).isNull();
-    }
-
-    @Test
     public void getSessionName_containPackageName_returnName() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f7873aa..ca403e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -47,6 +48,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.LocalMediaManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -126,6 +128,13 @@
                 mNotifCollection, mDialogTransitionAnimator,
                 mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
+
+        // Using a fake package will cause routing operations to fail, so we intercept
+        // scanning-related operations.
+        mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class);
+        doNothing().when(mMediaOutputController.mLocalMediaManager).startScan();
+        doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan();
+
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
                 mMediaOutputController);
         mMediaOutputBaseDialogImpl.onCreate(new Bundle());