Merge "audio: Display product name for input device" into main
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 1d17b00..6335e71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -49,19 +49,23 @@
 
     private final boolean mIsVolumeFixed;
 
+    private final String mProductName;
+
     private InputMediaDevice(
             @NonNull Context context,
             @NonNull String id,
             @AudioDeviceType int audioDeviceInfoType,
             int maxVolume,
             int currentVolume,
-            boolean isVolumeFixed) {
+            boolean isVolumeFixed,
+            @Nullable String productName) {
         super(context, /* info= */ null, /* item= */ null);
         mId = id;
         mAudioDeviceInfoType = audioDeviceInfoType;
         mMaxVolume = maxVolume;
         mCurrentVolume = currentVolume;
         mIsVolumeFixed = isVolumeFixed;
+        mProductName = productName;
         initDeviceRecord();
     }
 
@@ -72,13 +76,20 @@
             @AudioDeviceType int audioDeviceInfoType,
             int maxVolume,
             int currentVolume,
-            boolean isVolumeFixed) {
+            boolean isVolumeFixed,
+            @Nullable String productName) {
         if (!isSupportedInputDevice(audioDeviceInfoType)) {
             return null;
         }
 
         return new InputMediaDevice(
-                context, id, audioDeviceInfoType, maxVolume, currentVolume, isVolumeFixed);
+                context,
+                id,
+                audioDeviceInfoType,
+                maxVolume,
+                currentVolume,
+                isVolumeFixed,
+                productName);
     }
 
     public @AudioDeviceType int getAudioDeviceInfoType() {
@@ -98,18 +109,25 @@
         };
     }
 
+    @Nullable
+    public String getProductName() {
+        return mProductName;
+    }
+
     @Override
     public @NonNull String getName() {
-        CharSequence name = switch (mAudioDeviceInfoType) {
-            case TYPE_WIRED_HEADSET -> mContext.getString(
-                    R.string.media_transfer_wired_device_mic_name);
-            case TYPE_USB_DEVICE, TYPE_USB_HEADSET, TYPE_USB_ACCESSORY -> mContext.getString(
-                    R.string.media_transfer_usb_device_mic_name);
-            case TYPE_BLUETOOTH_SCO -> mContext.getString(
-                    R.string.media_transfer_bt_device_mic_name);
+        return switch (mAudioDeviceInfoType) {
+            case TYPE_WIRED_HEADSET ->
+                    mContext.getString(R.string.media_transfer_wired_device_mic_name);
+            case TYPE_USB_DEVICE, TYPE_USB_HEADSET, TYPE_USB_ACCESSORY ->
+                    // The product name is assumed to be a well-formed string if it's not null.
+                    mProductName != null
+                            ? mProductName
+                            : mContext.getString(R.string.media_transfer_usb_device_mic_name);
+            case TYPE_BLUETOOTH_SCO ->
+                    mContext.getString(R.string.media_transfer_bt_device_mic_name);
             default -> mContext.getString(R.string.media_transfer_this_device_name_desktop);
         };
-        return name.toString();
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index a72ba8d..727662b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -151,7 +151,8 @@
                             info.getType(),
                             getMaxInputGain(),
                             getCurrentInputGain(),
-                            isInputGainFixed());
+                            isInputGainFixed(),
+                            getProductNameFromAudioDeviceInfo(info));
             if (mediaDevice != null) {
                 if (info.getType() == selectedInputDeviceAttributesType) {
                     mediaDevice.setState(STATE_SELECTED);
@@ -169,6 +170,25 @@
         }
     }
 
+    /**
+     * Gets the product name for the given {@link AudioDeviceInfo}.
+     *
+     * @return The product name for the given {@link AudioDeviceInfo}, or null if a suitable name
+     *     cannot be found.
+     */
+    @Nullable
+    private String getProductNameFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
+        CharSequence productName = deviceInfo.getProductName();
+        if (productName == null) {
+            return null;
+        }
+        String productNameString = productName.toString();
+        if (productNameString.isBlank()) {
+            return null;
+        }
+        return productNameString;
+    }
+
     public void selectDevice(@NonNull MediaDevice device) {
         if (!(device instanceof InputMediaDevice)) {
             Slog.w(TAG, "This device is not an InputMediaDevice: " + device.getName());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index 30e4637..6c1cb70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -41,6 +41,9 @@
     private final int MAX_VOLUME = 1;
     private final int CURRENT_VOLUME = 0;
     private final boolean IS_VOLUME_FIXED = true;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
+    private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -60,7 +63,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_BUILTIN_MIC);
         assertThat(builtinMediaDevice).isNotNull();
         assertThat(builtinMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_microphone);
     }
@@ -74,7 +78,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_BUILTIN_MIC);
         assertThat(builtinMediaDevice).isNotNull();
         assertThat(builtinMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_this_device_name_desktop));
@@ -89,7 +94,8 @@
                         AudioDeviceInfo.TYPE_WIRED_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_WIRED_HEADSET);
         assertThat(wiredMediaDevice).isNotNull();
         assertThat(wiredMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_wired_device_mic_name));
@@ -104,7 +110,23 @@
                         AudioDeviceInfo.TYPE_USB_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_USB_HEADSET);
+        assertThat(usbMediaDevice).isNotNull();
+        assertThat(usbMediaDevice.getName()).isEqualTo(PRODUCT_NAME_USB_HEADSET);
+    }
+
+    @Test
+    public void getName_returnCorrectName_usbHeadset_nullProductName() {
+        InputMediaDevice usbMediaDevice =
+                InputMediaDevice.create(
+                        mContext,
+                        String.valueOf(USB_HEADSET_ID),
+                        AudioDeviceInfo.TYPE_USB_HEADSET,
+                        MAX_VOLUME,
+                        CURRENT_VOLUME,
+                        IS_VOLUME_FIXED,
+                        null);
         assertThat(usbMediaDevice).isNotNull();
         assertThat(usbMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_usb_device_mic_name));
@@ -119,7 +141,8 @@
                         AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        null);
         assertThat(btMediaDevice).isNotNull();
         assertThat(btMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_bt_device_mic_name));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 29cc403..f63bfc7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -58,6 +58,11 @@
     private static final int MAX_VOLUME = 1;
     private static final int CURRENT_VOLUME = 0;
     private static final boolean VOLUME_FIXED_TRUE = true;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
+    private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
+    private static final String PRODUCT_NAME_USB_DEVICE = "My USB Device";
+    private static final String PRODUCT_NAME_USB_ACCESSORY = "My USB Accessory";
 
     private final Context mContext = spy(RuntimeEnvironment.application);
     private InputRouteManager mInputRouteManager;
@@ -75,25 +80,31 @@
         final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
         when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
         when(info1.getId()).thenReturn(BUILTIN_MIC_ID);
+        when(info1.getProductName()).thenReturn(PRODUCT_NAME_BUILTIN_MIC);
 
         final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
         when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
         when(info2.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(info2.getProductName()).thenReturn(PRODUCT_NAME_WIRED_HEADSET);
 
         final AudioDeviceInfo info3 = mock(AudioDeviceInfo.class);
         when(info3.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_DEVICE);
         when(info3.getId()).thenReturn(INPUT_USB_DEVICE_ID);
+        when(info3.getProductName()).thenReturn(PRODUCT_NAME_USB_DEVICE);
 
         final AudioDeviceInfo info4 = mock(AudioDeviceInfo.class);
         when(info4.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_HEADSET);
         when(info4.getId()).thenReturn(INPUT_USB_HEADSET_ID);
+        when(info4.getProductName()).thenReturn(PRODUCT_NAME_USB_HEADSET);
 
         final AudioDeviceInfo info5 = mock(AudioDeviceInfo.class);
         when(info5.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_ACCESSORY);
         when(info5.getId()).thenReturn(INPUT_USB_ACCESSORY_ID);
+        when(info5.getProductName()).thenReturn(PRODUCT_NAME_USB_ACCESSORY);
 
         final AudioDeviceInfo unsupportedInfo = mock(AudioDeviceInfo.class);
         when(unsupportedInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_HDMI);
+        when(unsupportedInfo.getProductName()).thenReturn("HDMI device");
 
         final AudioManager audioManager = mock(AudioManager.class);
         AudioDeviceInfo[] devices = {info1, info2, info3, info4, info5, unsupportedInfo};
@@ -142,10 +153,12 @@
         final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
         when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
         when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(info1.getProductName()).thenReturn(PRODUCT_NAME_WIRED_HEADSET);
 
         final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
         when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
         when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
+        when(info2.getProductName()).thenReturn(PRODUCT_NAME_BUILTIN_MIC);
 
         final AudioManager audioManager = mock(AudioManager.class);
         AudioDeviceInfo[] devices = {info1, info2};
@@ -171,10 +184,12 @@
         final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
         when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
         when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(info1.getProductName()).thenReturn(PRODUCT_NAME_WIRED_HEADSET);
 
         final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
         when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
         when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
+        when(info2.getProductName()).thenReturn(PRODUCT_NAME_BUILTIN_MIC);
 
         final AudioManager audioManager = mock(AudioManager.class);
         AudioDeviceInfo[] devices = {info1, info2};
@@ -204,10 +219,12 @@
         final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
         when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
         when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(info1.getProductName()).thenReturn(PRODUCT_NAME_WIRED_HEADSET);
 
         final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
         when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
         when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
+        when(info2.getProductName()).thenReturn(PRODUCT_NAME_BUILTIN_MIC);
 
         final AudioManager audioManager = mock(AudioManager.class);
         AudioDeviceInfo[] devices = {info1, info2};
@@ -239,7 +256,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
         inputRouteManager.selectDevice(inputMediaDevice);
 
         AudioDeviceAttributes deviceAttributes =
@@ -267,4 +285,51 @@
     public void isInputGainFixed() {
         assertThat(mInputRouteManager.isInputGainFixed()).isTrue();
     }
+
+    @Test
+    public void onAudioDevicesAdded_shouldSetProductNameCorrectly() {
+        final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
+        when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+        when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        String firstProductName = "My first headset";
+        when(info1.getProductName()).thenReturn(firstProductName);
+
+        final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
+        when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+        when(info2.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        String secondProductName = "My second headset";
+        when(info2.getProductName()).thenReturn(secondProductName);
+
+        final AudioDeviceInfo infoWithNullProductName = mock(AudioDeviceInfo.class);
+        when(infoWithNullProductName.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+        when(infoWithNullProductName.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(infoWithNullProductName.getProductName()).thenReturn(null);
+
+        final AudioDeviceInfo infoWithBlankProductName = mock(AudioDeviceInfo.class);
+        when(infoWithBlankProductName.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+        when(infoWithBlankProductName.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(infoWithBlankProductName.getProductName()).thenReturn("");
+
+        final AudioManager audioManager = mock(AudioManager.class);
+        AudioDeviceInfo[] devices = {
+            info1, info2, infoWithNullProductName, infoWithBlankProductName
+        };
+        when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
+
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+
+        assertThat(inputRouteManager.mInputMediaDevices).isEmpty();
+
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+        assertThat(getProductNameAtIndex(inputRouteManager, 1)).isEqualTo(firstProductName);
+        assertThat(getProductNameAtIndex(inputRouteManager, 2)).isEqualTo(secondProductName);
+        assertThat(getProductNameAtIndex(inputRouteManager, 3)).isNull();
+        assertThat(getProductNameAtIndex(inputRouteManager, 4)).isNull();
+    }
+
+    private String getProductNameAtIndex(InputRouteManager inputRouteManager, int index) {
+        return ((InputMediaDevice) inputRouteManager.mInputMediaDevices.get(index))
+                .getProductName();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 07e48b9..bf4ef50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -125,6 +125,8 @@
     private static final boolean VOLUME_FIXED_TRUE = true;
     private static final int LATCH_COUNT_DOWN_TIME_IN_SECOND = 5;
     private static final int LATCH_TIME_OUT_TIME_IN_SECOND = 10;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
 
     @Mock
     private DialogTransitionAnimator mDialogTransitionAnimator;
@@ -568,7 +570,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
         final MediaDevice mediaDevice4 =
                 InputMediaDevice.create(
                         mContext,
@@ -576,7 +579,8 @@
                         AudioDeviceInfo.TYPE_WIRED_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_WIRED_HEADSET);
         final List<MediaDevice> inputDevices = new ArrayList<>();
         inputDevices.add(mediaDevice3);
         inputDevices.add(mediaDevice4);
@@ -1355,7 +1359,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
         mMediaSwitchingController.connectDevice(inputMediaDevice);
 
         CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND);