Introduce device(s) role for strategy.

Adding interface for set/remove/getDevicesRoleForStrategy. This will be
used for setting device(s) role, such as preferred, for strategy.
Set/remove/getPreferreDeviceForStrategy are reimplemented by calling
the new interfaces.

Test: atest AudioHostTest
Test: atest AudioServiceHostTest
Test: atest AudioServiceTest
Test: atest audiopolicy_tests
Bug: 160352965
Change-Id: I2ba02c6b50af80846a25010ead05c4ae364868de
diff --git a/api/system-current.txt b/api/system-current.txt
index 9447427..1315a33 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4195,7 +4195,8 @@
 
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
@@ -4207,13 +4208,15 @@
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4223,6 +4226,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4246,8 +4250,12 @@
     method public void onAudioServerUp();
   }
 
-  public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
-    method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+    method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  }
+
+  public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
+    method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
   }
 
   public abstract static class AudioManager.VolumeGroupCallback {
diff --git a/api/test-current.txt b/api/test-current.txt
index 963ef7c..0ebbcd7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1779,6 +1779,9 @@
     method public static float getMasterBalance();
     method public static final int getNumStreamTypes();
     method public static int setMasterBalance(float);
+    field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
+    field public static final int DEVICE_ROLE_NONE = 0; // 0x0
+    field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
     field public static final int STREAM_DEFAULT = -1; // 0xffffffff
   }
 
diff --git a/config/preloaded-classes b/config/preloaded-classes
index e43c7d4..f56656b 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -3709,9 +3709,9 @@
 android.media.IRingtonePlayer$Stub$Proxy
 android.media.IRingtonePlayer$Stub
 android.media.IRingtonePlayer
-android.media.IStrategyPreferredDeviceDispatcher$Stub$Proxy
-android.media.IStrategyPreferredDeviceDispatcher$Stub
-android.media.IStrategyPreferredDeviceDispatcher
+android.media.IStrategyPreferredDevicesDispatcher$Stub$Proxy
+android.media.IStrategyPreferredDevicesDispatcher$Stub
+android.media.IStrategyPreferredDevicesDispatcher
 android.media.IVolumeController$Stub$Proxy
 android.media.IVolumeController$Stub
 android.media.IVolumeController
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index e79c95e..2a16dce 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -31,7 +31,7 @@
                                  const AudioDeviceTypeAddr *devTypeAddr) {
     jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
     jint jNativeType = (jint)devTypeAddr->mType;
-    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
 
     *jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
             jNativeType, jAddress.get());
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index aad6835..3f39478 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -172,6 +172,8 @@
     jmethodID postRecordConfigEventFromNative;
 } gAudioPolicyEventHandlerMethods;
 
+static struct { jmethodID add; } gListMethods;
+
 //
 // JNI Initialization for OpenSLES routing
 //
@@ -2361,46 +2363,48 @@
     return AudioSystem::isCallScreenModeSupported();
 }
 
-static jint
-android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
-        jint strategy, jint deviceType, jstring deviceAddress) {
-
-    const char *c_address = env->GetStringUTFChars(deviceAddress, NULL);
+static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+                                                                jint strategy, jint role,
+                                                                jintArray jDeviceTypes,
+                                                                jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
     int status = check_AudioSystem_Command(
-            AudioSystem::setPreferredDeviceForStrategy((product_strategy_t) strategy,
-                    AudioDeviceTypeAddr(deviceType, c_address)));
-    env->ReleaseStringUTFChars(deviceAddress, c_address);
+            AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy,
+                                                   (device_role_t)role, nDevices));
     return (jint) status;
 }
 
-static jint
-android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy)
-{
-    return (jint) check_AudioSystem_Command(
-            AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy));
+static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+                                                                   jint strategy, jint role) {
+    return (jint)check_AudioSystem_Command(
+            AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy,
+                                                      (device_role_t)role));
 }
 
-static jint
-android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
-        jint strategy, jobjectArray jDeviceArray)
-{
-    if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
-        ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__);
-        return (jint)AUDIO_JAVA_BAD_VALUE;
-    }
-
-    AudioDeviceTypeAddr elDevice;
+static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
+                                                                   jint strategy, jint role,
+                                                                   jobject jDevices) {
+    AudioDeviceTypeAddrVector nDevices;
     status_t status = check_AudioSystem_Command(
-            AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice));
+            AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy,
+                                                      (device_role_t)role, nDevices));
     if (status != NO_ERROR) {
         return (jint) status;
     }
-    jobject jAudioDeviceAttributes = NULL;
-    jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &elDevice);
-    if (jStatus == AUDIO_JAVA_SUCCESS) {
-        env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAttributes);
+    for (const auto &device : nDevices) {
+        jobject jAudioDeviceAttributes = NULL;
+        jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+        env->DeleteLocalRef(jAudioDeviceAttributes);
     }
-    return jStatus;
+    return AUDIO_JAVA_SUCCESS;
 }
 
 static jint
@@ -2548,12 +2552,12 @@
          {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
          {"isCallScreeningModeSupported", "()Z",
           (void *)android_media_AudioSystem_isCallScreeningModeSupported},
-         {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
-          (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
-         {"removePreferredDeviceForStrategy", "(I)I",
-          (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
-         {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAttributes;)I",
-          (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+         {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setDevicesRoleForStrategy},
+         {"removeDevicesRoleForStrategy", "(II)I",
+          (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
+         {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+          (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
          {"getDevicesForAttributes",
           "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
           (void *)android_media_AudioSystem_getDevicesForAttributes},
@@ -2755,6 +2759,9 @@
     gMidAudioRecordRoutingProxy_release =
             android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
 
+    jclass listClass = FindClassOrDie(env, "java/util/List");
+    gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z");
+
     AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
 
     RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 408f34b..e0da5d9 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -75,6 +75,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -1599,21 +1600,14 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
             @NonNull AudioDeviceAttributes device) {
-        Objects.requireNonNull(strategy);
-        Objects.requireNonNull(device);
-        try {
-            final int status =
-                    getService().setPreferredDeviceForStrategy(strategy.getId(), device);
-            return status == AudioSystem.SUCCESS;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
     }
 
     /**
      * @hide
-     * Removes the preferred audio device previously set with
-     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}.
+     * Removes the preferred audio device(s) previously set with
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
      * @param strategy the audio strategy whose routing will be affected
      * @return true if the operation was successful, false otherwise (invalid strategy, or no
      *     device set for example)
@@ -1624,7 +1618,7 @@
         Objects.requireNonNull(strategy);
         try {
             final int status =
-                    getService().removePreferredDeviceForStrategy(strategy.getId());
+                    getService().removePreferredDevicesForStrategy(strategy.getId());
             return status == AudioSystem.SUCCESS;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1634,18 +1628,74 @@
     /**
      * @hide
      * Return the preferred device for an audio strategy, previously set with
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+     * @param strategy the strategy to query
+     * @return the preferred device for that strategy, if multiple devices are set as preferred
+     *    devices, the first one in the list will be returned. Null will be returned if none was
+     *    ever set or if the strategy is invalid
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Nullable
+    public AudioDeviceAttributes getPreferredDeviceForStrategy(
+            @NonNull AudioProductStrategy strategy) {
+        List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
+        return devices.isEmpty() ? null : devices.get(0);
+    }
+
+    /**
+     * @hide
+     * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
+     * this audio strategy. Note that the devices may not be available at the time the preferred
+     * devices is set, but it will be used once made available.
+     * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
+     * this preference for this strategy.</p>
+     * Note that the list of devices is not a list ranked by preference, but a list of one or more
+     * devices used simultaneously to output the same audio signal.
+     * @param strategy the audio strategy whose routing will be affected
+     * @param devices a non-empty list of the audio devices to route to when available
+     * @return true if the operation was successful, false otherwise
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
+                                                  @NonNull List<AudioDeviceAttributes> devices) {
+        Objects.requireNonNull(strategy);
+        Objects.requireNonNull(devices);
+        if (devices.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Tried to set preferred devices for strategy with a empty list");
+        }
+        for (AudioDeviceAttributes device : devices) {
+            Objects.requireNonNull(device);
+        }
+        try {
+            final int status =
+                    getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Return the preferred devices for an audio strategy, previously set with
      * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
      * @param strategy the strategy to query
      * @return the preferred device for that strategy, or null if none was ever set or if the
      *    strategy is invalid
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public @Nullable AudioDeviceAttributes getPreferredDeviceForStrategy(
+    @NonNull
+    public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
             @NonNull AudioProductStrategy strategy) {
         Objects.requireNonNull(strategy);
         try {
-            return getService().getPreferredDeviceForStrategy(strategy.getId());
+            return getService().getPreferredDevicesForStrategy(strategy.getId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1657,6 +1707,7 @@
      * strategy.
      * <p>Note that this listener will only be invoked whenever
      * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
      * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
      * preferred device. It will not be invoked directly after registration with
      * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
@@ -1664,8 +1715,10 @@
      * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
      * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
      * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @deprecated use #OnPreferredDevicesForStrategyChangedListener
      */
     @SystemApi
+    @Deprecated
     public interface OnPreferredDeviceForStrategyChangedListener {
         /**
          * Called on the listener to indicate that the preferred audio device for the given
@@ -1680,6 +1733,70 @@
 
     /**
      * @hide
+     * Interface to be notified of changes in the preferred audio devices set for a given audio
+     * strategy.
+     * <p>Note that this listener will only be invoked whenever
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+     * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
+     * preferred device(s). It will not be invoked directly after registration with
+     * {@link #addOnPreferredDevicesForStrategyChangedListener(
+     * Executor, OnPreferredDevicesForStrategyChangedListener)}
+     * to indicate which strategies had preferred devices at the time of registration.</p>
+     * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+     * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
+     * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
+     * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
+     */
+    @SystemApi
+    public interface OnPreferredDevicesForStrategyChangedListener {
+        /**
+         * Called on the listener to indicate that the preferred audio devices for the given
+         * strategy has changed.
+         * @param strategy the {@link AudioProductStrategy} whose preferred device changed
+         * @param devices a list of newly set preferred audio devices
+         */
+        void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
+                                                  @NonNull List<AudioDeviceAttributes> devices);
+    }
+
+    /**
+     * @hide
+     * Adds a listener for being notified of changes to the strategy-preferred audio device.
+     * @param executor
+     * @param listener
+     * @throws SecurityException if the caller doesn't hold the required permission
+     * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
+     *             Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Deprecated
+    public void addOnPreferredDeviceForStrategyChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+            throws SecurityException {
+        // No-op, the method is deprecated.
+    }
+
+    /**
+     * @hide
+     * Removes a previously added listener of changes to the strategy-preferred audio device.
+     * @param listener
+     * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
+     *             AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Deprecated
+    public void removeOnPreferredDeviceForStrategyChangedListener(
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+        // No-op, the method is deprecated.
+    }
+
+    /**
+     * @hide
      * Adds a listener for being notified of changes to the strategy-preferred audio device.
      * @param executor
      * @param listener
@@ -1687,16 +1804,16 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void addOnPreferredDeviceForStrategyChangedListener(
+    public void addOnPreferredDevicesForStrategyChangedListener(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+            @NonNull OnPreferredDevicesForStrategyChangedListener listener)
             throws SecurityException {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(listener);
         synchronized (mPrefDevListenerLock) {
             if (hasPrefDevListener(listener)) {
                 throw new IllegalArgumentException(
-                        "attempt to call addOnPreferredDeviceForStrategyChangedListener() "
+                        "attempt to call addOnPreferredDevicesForStrategyChangedListener() "
                                 + "on a previously registered listener");
             }
             // lazy initialization of the list of strategy-preferred device listener
@@ -1708,10 +1825,10 @@
             if (oldCbCount == 0 && mPrefDevListeners.size() > 0) {
                 // register binder for callbacks
                 if (mPrefDevDispatcherStub == null) {
-                    mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub();
+                    mPrefDevDispatcherStub = new StrategyPreferredDevicesDispatcherStub();
                 }
                 try {
-                    getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub);
+                    getService().registerStrategyPreferredDevicesDispatcher(mPrefDevDispatcherStub);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -1726,8 +1843,8 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void removeOnPreferredDeviceForStrategyChangedListener(
-            @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+    public void removeOnPreferredDevicesForStrategyChangedListener(
+            @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
         Objects.requireNonNull(listener);
         synchronized (mPrefDevListenerLock) {
             if (!removePrefDevListener(listener)) {
@@ -1738,7 +1855,7 @@
             if (mPrefDevListeners.size() == 0) {
                 // unregister binder for callbacks
                 try {
-                    getService().unregisterStrategyPreferredDeviceDispatcher(
+                    getService().unregisterStrategyPreferredDevicesDispatcher(
                             mPrefDevDispatcherStub);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -1760,23 +1877,23 @@
     private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners;
 
     private static class PrefDevListenerInfo {
-        final @NonNull OnPreferredDeviceForStrategyChangedListener mListener;
+        final @NonNull OnPreferredDevicesForStrategyChangedListener mListener;
         final @NonNull Executor mExecutor;
-        PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) {
+        PrefDevListenerInfo(OnPreferredDevicesForStrategyChangedListener listener, Executor exe) {
             mListener = listener;
             mExecutor = exe;
         }
     }
 
     @GuardedBy("mPrefDevListenerLock")
-    private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub;
+    private StrategyPreferredDevicesDispatcherStub mPrefDevDispatcherStub;
 
-    private final class StrategyPreferredDeviceDispatcherStub
-            extends IStrategyPreferredDeviceDispatcher.Stub {
+    private final class StrategyPreferredDevicesDispatcherStub
+            extends IStrategyPreferredDevicesDispatcher.Stub {
 
         @Override
-        public void dispatchPrefDeviceChanged(int strategyId,
-                                              @Nullable AudioDeviceAttributes device) {
+        public void dispatchPrefDevicesChanged(int strategyId,
+                                               @NonNull List<AudioDeviceAttributes> devices) {
             // make a shallow copy of listeners so callback is not executed under lock
             final ArrayList<PrefDevListenerInfo> prefDevListeners;
             synchronized (mPrefDevListenerLock) {
@@ -1791,7 +1908,7 @@
             try {
                 for (PrefDevListenerInfo info : prefDevListeners) {
                     info.mExecutor.execute(() ->
-                            info.mListener.onPreferredDeviceForStrategyChanged(strategy, device));
+                            info.mListener.onPreferredDevicesForStrategyChanged(strategy, devices));
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -1801,7 +1918,7 @@
 
     @GuardedBy("mPrefDevListenerLock")
     private @Nullable PrefDevListenerInfo getPrefDevListenerInfo(
-            OnPreferredDeviceForStrategyChangedListener listener) {
+            OnPreferredDevicesForStrategyChangedListener listener) {
         if (mPrefDevListeners == null) {
             return null;
         }
@@ -1814,7 +1931,7 @@
     }
 
     @GuardedBy("mPrefDevListenerLock")
-    private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+    private boolean hasPrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
         return getPrefDevListenerInfo(listener) != null;
     }
 
@@ -1822,7 +1939,7 @@
     /**
      * @return true if the listener was removed from the list
      */
-    private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+    private boolean removePrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
         final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener);
         if (infoToRemove != null) {
             mPrefDevListeners.remove(infoToRemove);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index da52cfe..dbf2542 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -32,6 +32,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -1347,6 +1348,11 @@
     /** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
     private static final int NUM_FORCE_USE = 8;
 
+    // Device role in audio policy
+    public static final int DEVICE_ROLE_NONE = 0;
+    public static final int DEVICE_ROLE_PREFERRED = 1;
+    public static final int DEVICE_ROLE_DISABLED = 2;
+
     /** @hide */
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
@@ -1667,47 +1673,58 @@
 
     /**
      * @hide
-     * Sets the preferred device to use for a given audio strategy in the audio policy engine
+     * Set device as role for product strategy.
      * @param strategy the id of the strategy to configure
-     * @param device the device type and address to route to when available
+     * @param role the role of the devices
+     * @param devices the list of devices to be set as role for the given strategy
      * @return {@link #SUCCESS} if successfully set
      */
-    public static int setPreferredDeviceForStrategy(
-            int strategy, @NonNull AudioDeviceAttributes device) {
-        return setPreferredDeviceForStrategy(strategy,
-                AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
-                device.getAddress());
+    public static int setDevicesRoleForStrategy(
+            int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        int[] types = new int[devices.size()];
+        String[] addresses = new String[devices.size()];
+        for (int i = 0; i < devices.size(); ++i) {
+            types[i] = AudioDeviceInfo.convertDeviceTypeToInternalDevice(devices.get(i).getType());
+            addresses[i] = devices.get(i).getAddress();
+        }
+        return setDevicesRoleForStrategy(strategy, role, types, addresses);
     }
-    /**
-     * @hide
-     * Set device routing per product strategy.
-     * @param strategy the id of the strategy to configure
-     * @param deviceType the native device type, NOT AudioDeviceInfo types
-     * @param deviceAddress the address of the device
-     * @return {@link #SUCCESS} if successfully set
-     */
-    private static native int setPreferredDeviceForStrategy(
-            int strategy, int deviceType, String deviceAddress);
 
     /**
      * @hide
-     * Remove preferred routing for the strategy
+     * Set device as role for product strategy.
      * @param strategy the id of the strategy to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int setDevicesRoleForStrategy(
+            int strategy, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove devices as role for the strategy
+     * @param strategy the id of the strategy to configure
+     * @param role the role of the devices
      * @return {@link #SUCCESS} if successfully removed
      */
-    public static native int removePreferredDeviceForStrategy(int strategy);
+    public static native int removeDevicesRoleForStrategy(int strategy, int role);
 
     /**
      * @hide
-     * Query previously set preferred device for a strategy
+     * Query previously set devices as role for a strategy
      * @param strategy the id of the strategy to query for
-     * @param device an array of size 1 that will contain the preferred device, or null if
-     *               none was set
+     * @param role the role of the devices
+     * @param devices a list that will contain the devices of role
      * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
      *     and written to the array
      */
-    public static native int getPreferredDeviceForStrategy(int strategy,
-                                                           AudioDeviceAttributes[] device);
+    public static native int getDevicesForRoleAndStrategy(
+            int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
 
     // Items shared with audio service
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 4cf236a..ef8b0ed 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -29,7 +29,7 @@
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.IVolumeController;
 import android.media.IVolumeController;
 import android.media.PlayerBase;
@@ -279,11 +279,11 @@
 
     boolean isCallScreeningModeSupported();
 
-    int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAttributes device);
+    int setPreferredDevicesForStrategy(in int strategy, in List<AudioDeviceAttributes> device);
 
-    int removePreferredDeviceForStrategy(in int strategy);
+    int removePreferredDevicesForStrategy(in int strategy);
 
-    AudioDeviceAttributes getPreferredDeviceForStrategy(in int strategy);
+    List<AudioDeviceAttributes> getPreferredDevicesForStrategy(in int strategy);
 
     List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
 
@@ -291,10 +291,10 @@
 
     int getAllowedCapturePolicy();
 
-    void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher);
+    void registerStrategyPreferredDevicesDispatcher(IStrategyPreferredDevicesDispatcher dispatcher);
 
-    oneway void unregisterStrategyPreferredDeviceDispatcher(
-            IStrategyPreferredDeviceDispatcher dispatcher);
+    oneway void unregisterStrategyPreferredDevicesDispatcher(
+            IStrategyPreferredDevicesDispatcher dispatcher);
 
     oneway void setRttEnabled(in boolean rttEnabled);
 
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
similarity index 82%
rename from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
rename to media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
index b1f99e6..db674c3 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
@@ -19,12 +19,12 @@
 import android.media.AudioDeviceAttributes;
 
 /**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio strategy-preferred devices updates.
  *
  * {@hide}
  */
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface IStrategyPreferredDevicesDispatcher {
 
-    void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+    void dispatchPrefDevicesChanged(int strategyId, in List<AudioDeviceAttributes> devices);
 
 }
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 96961ac2..45c49e5 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -333,7 +333,7 @@
     if (deviceType != AUDIO_DEVICE_NONE) {
         device.mType = deviceType;
         ScopedUtfChars address(env, deviceAddress);
-        device.mAddress = address.c_str();
+        device.setAddress(address.c_str());
     }
 
     // create the native AudioEffect object
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 0906a06..4d41fa5 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4135,7 +4135,8 @@
 
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
@@ -4147,13 +4148,15 @@
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4163,6 +4166,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4186,8 +4190,12 @@
     method public void onAudioServerUp();
   }
 
-  public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
-    method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+    method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  }
+
+  public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
+    method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
   }
 
   public abstract static class AudioManager.VolumeGroupCallback {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2bbbbf1..06ef58f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -28,7 +28,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
 import android.os.Binder;
 import android.os.Handler;
@@ -47,6 +47,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -526,23 +527,23 @@
         }
     }
 
-    /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAttributes device) {
-        return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
+    /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
     }
 
-    /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
-        return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
+    /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
+        return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
     }
 
-    /*package*/ void registerStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
-        mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher);
+    /*package*/ void registerStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
-    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
-        mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+    /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
     //---------------------------------------------------------------------
@@ -683,14 +684,14 @@
         sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
     }
 
-    /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy,
-                                                           AudioDeviceAttributes device)
+    /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
+                                                            List<AudioDeviceAttributes> devices)
     {
-        sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
+        sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices);
     }
 
-    /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) {
-        sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+    /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) {
+        sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
     }
 
     //---------------------------------------------------------------------
@@ -1082,14 +1083,15 @@
                                 info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
                     }
                 } break;
-                case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
+                case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
-                    final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
-                    mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
+                    final List<AudioDeviceAttributes> devices =
+                            (List<AudioDeviceAttributes>) msg.obj;
+                    mDeviceInventory.onSaveSetPreferredDevices(strategy, devices);
                 } break;
-                case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
+                case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
-                    mDeviceInventory.onSaveRemovePreferredDevice(strategy);
+                    mDeviceInventory.onSaveRemovePreferredDevices(strategy);
                 } break;
                 case MSG_CHECK_MUTE_MUSIC:
                     checkMessagesMuteMusic(0);
@@ -1163,8 +1165,8 @@
 
     // a ScoClient died in BtHelper
     private static final int MSG_L_SCOCLIENT_DIED = 32;
-    private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
-    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
+    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
 
     private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
     private static final int MSG_CHECK_MUTE_MUSIC = 36;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 02a846e..fbf07cc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,7 +16,6 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
@@ -32,7 +31,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
 import android.os.Binder;
 import android.os.RemoteCallbackList;
@@ -51,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -137,7 +137,8 @@
     private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
 
     // List of preferred devices for strategies
-    private final ArrayMap<Integer, AudioDeviceAttributes> mPreferredDevices = new ArrayMap<>();
+    private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
+            new ArrayMap<>();
 
     // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
     private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -150,8 +151,8 @@
             new RemoteCallbackList<IAudioRoutesObserver>();
 
     // Monitoring of strategy-preferred device
-    final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers =
-            new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>();
+    final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
+            new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
 
     /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
         mDeviceBroker = broker;
@@ -265,8 +266,9 @@
             }
         }
         synchronized (mPreferredDevices) {
-            mPreferredDevices.forEach((strategy, device) -> {
-                mAudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+            mPreferredDevices.forEach((strategy, devices) -> {
+                mAudioSystem.setDevicesRoleForStrategy(
+                        strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
         }
     }
 
@@ -600,49 +602,52 @@
         mmi.record();
     }
 
-    /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
-        mPreferredDevices.put(strategy, device);
-        dispatchPreferredDevice(strategy, device);
+    /*package*/ void onSaveSetPreferredDevices(int strategy,
+                                               @NonNull List<AudioDeviceAttributes> devices) {
+        mPreferredDevices.put(strategy, devices);
+        dispatchPreferredDevice(strategy, devices);
     }
 
-    /*package*/ void onSaveRemovePreferredDevice(int strategy) {
+    /*package*/ void onSaveRemovePreferredDevices(int strategy) {
         mPreferredDevices.remove(strategy);
-        dispatchPreferredDevice(strategy, null);
+        dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
     }
 
     //------------------------------------------------------------
     //
 
-    /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAttributes device) {
+    /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+            @NonNull List<AudioDeviceAttributes> devices) {
         final long identity = Binder.clearCallingIdentity();
-        final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
+        final int status = mAudioSystem.setDevicesRoleForStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
         Binder.restoreCallingIdentity(identity);
 
         if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device);
+            mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
         }
         return status;
     }
 
-    /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+    /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
         final long identity = Binder.clearCallingIdentity();
-        final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy);
+        final int status = mAudioSystem.removeDevicesRoleForStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
         Binder.restoreCallingIdentity(identity);
 
         if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy);
+            mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
         }
         return status;
     }
 
-    /*package*/ void registerStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+    /*package*/ void registerStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
         mPrefDevDispatchers.register(dispatcher);
     }
 
-    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+    /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
         mPrefDevDispatchers.unregister(dispatcher);
     }
 
@@ -1288,11 +1293,13 @@
         }
     }
 
-    private void dispatchPreferredDevice(int strategy, @Nullable AudioDeviceAttributes device) {
+    private void dispatchPreferredDevice(int strategy,
+                                         @NonNull List<AudioDeviceAttributes> devices) {
         final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
-                mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device);
+                mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
+                        strategy, devices);
             } catch (RemoteException e) {
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 23b0929..0a179e8 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -83,7 +83,7 @@
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.IVolumeController;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -167,6 +167,7 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 /**
  * The implementation of the audio service for volume, audio focus, device management...
@@ -1777,22 +1778,28 @@
     ///////////////////////////////////////////////////////////////////////////
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
-    /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
-    public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) {
-        if (device == null) {
+    /**
+     * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+     * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
+     *                                                  List<AudioDeviceAttributes>)
+     */
+    public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
+        if (devices == null) {
             return AudioSystem.ERROR;
         }
         enforceModifyAudioRoutingPermission();
         final String logString = String.format(
                 "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
-                Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
+                Binder.getCallingUid(), Binder.getCallingPid(), strategy,
+                devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
-        if (device.getRole() == AudioDeviceAttributes.ROLE_INPUT) {
+        if (devices.stream().anyMatch(device ->
+                device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
             Log.e(TAG, "Unsupported input routing in " + logString);
             return AudioSystem.ERROR;
         }
 
-        final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device);
+        final int status = mDeviceBroker.setPreferredDevicesForStrategySync(strategy, devices);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
@@ -1801,53 +1808,61 @@
     }
 
     /** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
-    public int removePreferredDeviceForStrategy(int strategy) {
+    public int removePreferredDevicesForStrategy(int strategy) {
         enforceModifyAudioRoutingPermission();
         final String logString =
                 String.format("removePreferredDeviceForStrategy strat:%d", strategy);
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
 
-        final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy);
+        final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
         return status;
     }
 
-    /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
-    public AudioDeviceAttributes getPreferredDeviceForStrategy(int strategy) {
+    /**
+     * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
+     */
+    public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(int strategy) {
         enforceModifyAudioRoutingPermission();
-        AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+        List<AudioDeviceAttributes> devices = new ArrayList<>();
         final long identity = Binder.clearCallingIdentity();
-        final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
+        final int status = AudioSystem.getDevicesForRoleAndStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
         Binder.restoreCallingIdentity(identity);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
                     status, strategy));
-            return null;
+            return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return devices[0];
+            return devices;
         }
     }
 
-    /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */
-    public void registerStrategyPreferredDeviceDispatcher(
-            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+    /** @see AudioManager#addOnPreferredDevicesForStrategyChangedListener(
+     *               Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)
+     */
+    public void registerStrategyPreferredDevicesDispatcher(
+            @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
         if (dispatcher == null) {
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher);
+        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
-    /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */
-    public void unregisterStrategyPreferredDeviceDispatcher(
-            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+    /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
+     *               AudioManager.OnPreferredDevicesForStrategyChangedListener)
+     */
+    public void unregisterStrategyPreferredDevicesDispatcher(
+            @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
         if (dispatcher == null) {
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+        mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index e60243f..a0e1ca7 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -20,6 +20,8 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioSystem;
 
+import java.util.List;
+
 /**
  * Provides an adapter to access functionality of the android.media.AudioSystem class for device
  * related functionality.
@@ -77,22 +79,25 @@
     }
 
     /**
-     * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAttributes)}
+     * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)}
      * @param strategy
-     * @param device
+     * @param role
+     * @param devices
      * @return
      */
-    public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAttributes device) {
-        return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+    public int setDevicesRoleForStrategy(int strategy, int role,
+                                         @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices);
     }
 
     /**
-     * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)}
+     * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)}
      * @param strategy
+     * @param role
      * @return
      */
-    public int removePreferredDeviceForStrategy(int strategy) {
-        return AudioSystem.removePreferredDeviceForStrategy(strategy);
+    public int removeDevicesRoleForStrategy(int strategy, int role) {
+        return AudioSystem.removeDevicesRoleForStrategy(strategy, role);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index a9cef20..609af8d 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -21,6 +21,8 @@
 import android.media.AudioSystem;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * Provides an adapter for AudioSystem that does nothing.
  * Overridden methods can be configured.
@@ -66,13 +68,13 @@
     }
 
     @Override
-    public int setPreferredDeviceForStrategy(int strategy,
-            @NonNull AudioDeviceAttributes device) {
+    public int setDevicesRoleForStrategy(int strategy, int role,
+            @NonNull List<AudioDeviceAttributes> devices) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
 
     @Override
-    public int removePreferredDeviceForStrategy(int strategy) {
+    public int removeDevicesRoleForStrategy(int strategy, int role) {
         return AudioSystem.AUDIO_STATUS_OK;
     }