Return DeviceDescriptor or DeviceVector in Engine.

To remove the limit on the number of audio device types, there is a
need to remove the return value as a combination of audio_devices_t.
Using DeviceDescriptor or DeviceVector in Engine is a starting point.

Test: audiopolicy_tests, smoke test
Test: CTS for AudioTrack, AudioRecord, AudioManager
Bug: 135621476
Change-Id: I0d87fd76385629e6e6b74cdd37dbd334ae02c04f
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index c602f3a..cbc46d5 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -136,27 +136,23 @@
     return EngineBase::setForceUse(usage, config);
 }
 
-audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy,
-                                                DeviceVector availableOutputDevices,
-                                                DeviceVector availableInputDevices,
-                                                const SwAudioOutputCollection &outputs,
-                                                uint32_t outputDeviceTypesToIgnore) const
+DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
+                                              DeviceVector availableOutputDevices,
+                                              DeviceVector availableInputDevices,
+                                              const SwAudioOutputCollection &outputs) const
 {
-    uint32_t device = AUDIO_DEVICE_NONE;
-    uint32_t availableOutputDevicesType =
-            availableOutputDevices.types() & ~outputDeviceTypesToIgnore;
+    DeviceVector devices;
 
     switch (strategy) {
 
     case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
-        device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
+        devices = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER);
         break;
 
     case STRATEGY_SONIFICATION_RESPECTFUL:
         if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
-            device = getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
-                    outputDeviceTypesToIgnore);
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
         } else {
             bool media_active_locally =
                     outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
@@ -165,17 +161,18 @@
                         toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
                         SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
             // routing is same as media without the "remote" device
-            device = getDeviceForStrategyInt(STRATEGY_MEDIA,
+            availableOutputDevices.remove(availableOutputDevices.getDevicesFromTypeMask(
+                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX));
+            devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
                     availableOutputDevices,
-                    availableInputDevices, outputs,
-                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX | outputDeviceTypesToIgnore);
+                    availableInputDevices, outputs);
             // if no media is playing on the device, check for mandatory use of "safe" speaker
             // when media would have played on speaker, and the safe speaker path is available
-            if (!media_active_locally
-                    && (device & AUDIO_DEVICE_OUT_SPEAKER)
-                    && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-                device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-                device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+            if (!media_active_locally) {
+                devices.replaceDevicesByType(
+                        AUDIO_DEVICE_OUT_SPEAKER,
+                        availableOutputDevices.getDevicesFromTypeMask(
+                                AUDIO_DEVICE_OUT_SPEAKER_SAFE));
             }
         }
         break;
@@ -183,9 +180,8 @@
     case STRATEGY_DTMF:
         if (!isInCall()) {
             // when off call, DTMF strategy follows the same rules as MEDIA strategy
-            device = getDeviceForStrategyInt(
-                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs,
-                    outputDeviceTypesToIgnore);
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs);
             break;
         }
         // when in call, DTMF and PHONE strategies follow the same rules
@@ -197,24 +193,27 @@
         //   - cannot route from voice call RX OR
         //   - audio HAL version is < 3.0 and TX device is on the primary HW module
         if (getPhoneState() == AUDIO_MODE_IN_CALL) {
-            audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION);
+            audio_devices_t txDevice = getDeviceForInputSource(
+                    AUDIO_SOURCE_VOICE_COMMUNICATION)->type();
             sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
-            audio_devices_t availPrimaryInputDevices =
-                 availableInputDevices.getDeviceTypesFromHwModule(primaryOutput->getModuleHandle());
+            DeviceVector availPrimaryInputDevices =
+                    availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle());
 
             // TODO: getPrimaryOutput return only devices from first module in
             // audio_policy_configuration.xml, hearing aid is not there, but it's
             // a primary device
             // FIXME: this is not the right way of solving this problem
-            audio_devices_t availPrimaryOutputDevices =
-                (primaryOutput->supportedDevices().types() | AUDIO_DEVICE_OUT_HEARING_AID) &
-                availableOutputDevices.types();
+            DeviceVector availPrimaryOutputDevices = availableOutputDevices.getDevicesFromTypeMask(
+                    primaryOutput->supportedDevices().types());
+            availPrimaryOutputDevices.add(
+                    availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_HEARING_AID));
 
-            if (((availableInputDevices.types() &
-                    AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) ||
-                    (((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
-                         (primaryOutput->getAudioPort()->getModuleVersionMajor() < 3))) {
-                availableOutputDevicesType = availPrimaryOutputDevices;
+            if ((availableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX,
+                    String8(""), AUDIO_FORMAT_DEFAULT) == nullptr) ||
+                    ((availPrimaryInputDevices.getDevice(
+                            txDevice, String8(""), AUDIO_FORMAT_DEFAULT) != nullptr) &&
+                            (primaryOutput->getAudioPort()->getModuleVersionMajor() < 3))) {
+                availableOutputDevices = availPrimaryOutputDevices;
             }
         }
         // for phone strategy, we first consider the forced use and then the available devices by
@@ -222,49 +221,40 @@
         switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) {
         case AUDIO_POLICY_FORCE_BT_SCO:
             if (!isInCall() || strategy != STRATEGY_DTMF) {
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-                if (device) break;
+                devices = availableOutputDevices.getDevicesFromTypeMask(
+                        AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
+                if (!devices.isEmpty()) break;
             }
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
-            if (device) break;
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
-            if (device) break;
+            devices = availableOutputDevices.getFirstDevicesFromTypes({
+                    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_OUT_BLUETOOTH_SCO});
+            if (!devices.isEmpty()) break;
             // if SCO device is requested but no SCO device is available, fall back to default case
             FALLTHROUGH_INTENDED;
 
         default:    // FORCE_NONE
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_HEARING_AID;
-            if (device) break;
+            devices = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_HEARING_AID);
+            if (!devices.isEmpty()) break;
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
             if (!isInCall() &&
                     (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
                      outputs.isA2dpSupported()) {
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
-                if (device) break;
+                devices = availableOutputDevices.getFirstDevicesFromTypes({
+                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES});
+                if (!devices.isEmpty()) break;
             }
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
-            if (device) break;
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET;
-            if (device) break;
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE;
-            if (device) break;
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_HEADSET;
-            if (device) break;
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE;
-            if (device) break;
+            devices = availableOutputDevices.getFirstDevicesFromTypes({
+                    AUDIO_DEVICE_OUT_WIRED_HEADPHONE, AUDIO_DEVICE_OUT_WIRED_HEADSET,
+                    AUDIO_DEVICE_OUT_LINE, AUDIO_DEVICE_OUT_USB_HEADSET,
+                    AUDIO_DEVICE_OUT_USB_DEVICE});
+            if (!devices.isEmpty()) break;
             if (!isInCall()) {
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
-                if (device) break;
+                devices = availableOutputDevices.getFirstDevicesFromTypes({
+                        AUDIO_DEVICE_OUT_USB_ACCESSORY, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
+                        AUDIO_DEVICE_OUT_AUX_DIGITAL, AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET});
+                if (!devices.isEmpty()) break;
             }
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_EARPIECE;
+            devices = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_EARPIECE);
             break;
 
         case AUDIO_POLICY_FORCE_SPEAKER:
@@ -273,22 +263,18 @@
             if (!isInCall() &&
                     (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
                      outputs.isA2dpSupported()) {
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
-                if (device) break;
+                devices = availableOutputDevices.getDevicesFromTypeMask(
+                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+                if (!devices.isEmpty()) break;
             }
             if (!isInCall()) {
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL;
-                if (device) break;
-                device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
-                if (device) break;
+                devices = availableOutputDevices.getFirstDevicesFromTypes({
+                        AUDIO_DEVICE_OUT_USB_ACCESSORY, AUDIO_DEVICE_OUT_USB_DEVICE,
+                        AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, AUDIO_DEVICE_OUT_AUX_DIGITAL,
+                        AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET});
+                if (!devices.isEmpty()) break;
             }
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
+            devices = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER);
             break;
         }
     break;
@@ -298,9 +284,8 @@
         // If incall, just select the STRATEGY_PHONE device
         if (isInCall() ||
                 outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
-            device = getDeviceForStrategyInt(
-                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
-                    outputDeviceTypesToIgnore);
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
             break;
         }
         FALLTHROUGH_INTENDED;
@@ -313,41 +298,37 @@
 
         if ((strategy == STRATEGY_SONIFICATION) ||
                 (getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) {
-            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
+            devices = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER);
         }
 
         // if SCO headset is connected and we are told to use it, play ringtone over
         // speaker and BT SCO
-        if ((availableOutputDevicesType & AUDIO_DEVICE_OUT_ALL_SCO) != 0) {
-            uint32_t device2 = AUDIO_DEVICE_NONE;
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-            if (device2 == AUDIO_DEVICE_NONE) {
-                device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
-            }
-            if (device2 == AUDIO_DEVICE_NONE) {
-                device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
-            }
+        if (!availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_ALL_SCO).isEmpty()) {
+            DeviceVector devices2;
+            devices2 = availableOutputDevices.getFirstDevicesFromTypes({
+                    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+                    AUDIO_DEVICE_OUT_BLUETOOTH_SCO});
             // Use ONLY Bluetooth SCO output when ringing in vibration mode
             if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
                     && (strategy == STRATEGY_ENFORCED_AUDIBLE))) {
                 if (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING)
                         == AUDIO_POLICY_FORCE_BT_SCO) {
-                    if (device2 != AUDIO_DEVICE_NONE) {
-                        device = device2;
+                    if (!devices2.isEmpty()) {
+                        devices = devices2;
                         break;
                     }
                 }
             }
             // Use both Bluetooth SCO and phone default output when ringing in normal mode
             if (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION) == AUDIO_POLICY_FORCE_BT_SCO) {
-                if ((strategy == STRATEGY_SONIFICATION) &&
-                        (device & AUDIO_DEVICE_OUT_SPEAKER) &&
-                        (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-                    device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-                    device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+                if (strategy == STRATEGY_SONIFICATION) {
+                    devices.replaceDevicesByType(
+                            AUDIO_DEVICE_OUT_SPEAKER,
+                            availableOutputDevices.getDevicesFromTypeMask(
+                                    AUDIO_DEVICE_OUT_SPEAKER_SAFE));
                 }
-                if (device2 != AUDIO_DEVICE_NONE) {
-                    device |= device2;
+                if (!devices2.isEmpty()) {
+                    devices.add(devices2);
                     break;
                 }
             }
@@ -361,25 +342,20 @@
             // compressed format as they would likely not be mixed and dropped.
             for (size_t i = 0; i < outputs.size(); i++) {
                 sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
-                audio_devices_t devices = desc->devices().types() &
-                    (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC);
-                if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) &&
-                        devices != AUDIO_DEVICE_NONE) {
-                    availableOutputDevicesType = availableOutputDevices.types() & ~devices;
+                if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat)) {
+                    availableOutputDevices.remove(desc->devices().getDevicesFromTypeMask(
+                            AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF
+                            | AUDIO_DEVICE_OUT_HDMI_ARC));
                 }
             }
-            availableOutputDevices =
-                    availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType);
             if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
                     outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
-                return getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
-                    outputDeviceTypesToIgnore);
+                return getDevicesForStrategyInt(
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
             }
             if (isInCall()) {
-                return getDeviceForStrategyInt(
-                        STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
-                        outputDeviceTypesToIgnore);
+                return getDevicesForStrategyInt(
+                        STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
             }
         }
         // For other cases, STRATEGY_ACCESSIBILITY behaves like STRATEGY_MEDIA
@@ -388,128 +364,116 @@
     // FIXME: STRATEGY_REROUTING follow STRATEGY_MEDIA for now
     case STRATEGY_REROUTING:
     case STRATEGY_MEDIA: {
-        uint32_t device2 = AUDIO_DEVICE_NONE;
+        DeviceVector devices2;
         if (strategy != STRATEGY_SONIFICATION) {
             // no sonification on remote submix (e.g. WFD)
-            if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                                                 String8("0"), AUDIO_FORMAT_DEFAULT) != 0) {
-                device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+            sp<DeviceDescriptor> remoteSubmix;
+            if ((remoteSubmix = availableOutputDevices.getDevice(
+                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0"),
+                    AUDIO_FORMAT_DEFAULT)) != nullptr) {
+                devices2.add(remoteSubmix);
             }
         }
         if (isInCall() && (strategy == STRATEGY_MEDIA)) {
-            device = getDeviceForStrategyInt(
-                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
-                    outputDeviceTypesToIgnore);
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
             break;
         }
         // FIXME: Find a better solution to prevent routing to BT hearing aid(b/122931261).
-        if ((device2 == AUDIO_DEVICE_NONE) &&
+        if ((devices2.isEmpty()) &&
                 (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HEARING_AID;
+            devices2 = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_HEARING_AID);
         }
-        if ((device2 == AUDIO_DEVICE_NONE) &&
+        if ((devices2.isEmpty()) &&
                 (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
                  outputs.isA2dpSupported()) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
-            if (device2 == AUDIO_DEVICE_NONE) {
-                device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
-            }
-            if (device2 == AUDIO_DEVICE_NONE) {
-                device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
-            }
+            devices2 = availableOutputDevices.getFirstDevicesFromTypes({
+                    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
+                    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER});
         }
-        if ((device2 == AUDIO_DEVICE_NONE) &&
+        if ((devices2.isEmpty()) &&
             (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
+            devices2 = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER);
         }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+        if (devices2.isEmpty()) {
+            devices2 = availableOutputDevices.getFirstDevicesFromTypes({
+                    AUDIO_DEVICE_OUT_WIRED_HEADPHONE, AUDIO_DEVICE_OUT_LINE,
+                    AUDIO_DEVICE_OUT_WIRED_HEADSET, AUDIO_DEVICE_OUT_USB_HEADSET,
+                    AUDIO_DEVICE_OUT_USB_ACCESSORY, AUDIO_DEVICE_OUT_USB_DEVICE,
+                    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET});
         }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE;
-        }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET;
-        }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_HEADSET;
-        }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY;
-        }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE;
-        }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
-        }
-        if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) {
+        if ((devices2.isEmpty()) && (strategy != STRATEGY_SONIFICATION)) {
             // no sonification on aux digital (e.g. HDMI)
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL;
+            devices2 = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_AUX_DIGITAL);
         }
-        if ((device2 == AUDIO_DEVICE_NONE) &&
+        if ((devices2.isEmpty()) &&
                 (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_ANALOG_DOCK)) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
+            devices2 = availableOutputDevices.getDevicesFromTypeMask(
+                    AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET);
         }
-        if (device2 == AUDIO_DEVICE_NONE) {
-            device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
+        if (devices2.isEmpty()) {
+            devices2 = availableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER);
         }
-        int device3 = AUDIO_DEVICE_NONE;
+        DeviceVector devices3;
         if (strategy == STRATEGY_MEDIA) {
             // ARC, SPDIF and AUX_LINE can co-exist with others.
-            device3 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HDMI_ARC;
-            device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPDIF);
-            device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_LINE);
+            devices3 = availableOutputDevices.getDevicesFromTypeMask(
+                    AUDIO_DEVICE_OUT_HDMI_ARC | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_AUX_LINE);
         }
 
-        device2 |= device3;
+        devices2.add(devices3);
         // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
         // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
-        device |= device2;
+        devices.add(devices2);
 
         // If hdmi system audio mode is on, remove speaker out of output list.
         if ((strategy == STRATEGY_MEDIA) &&
             (getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) ==
                 AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) {
-            device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+            devices.remove(devices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER));
         }
 
         // for STRATEGY_SONIFICATION:
         // if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead
-        if ((strategy == STRATEGY_SONIFICATION) &&
-                (device & AUDIO_DEVICE_OUT_SPEAKER) &&
-                (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-            device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-            device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+        if (strategy == STRATEGY_SONIFICATION) {
+            devices.replaceDevicesByType(
+                    AUDIO_DEVICE_OUT_SPEAKER,
+                    availableOutputDevices.getDevicesFromTypeMask(
+                            AUDIO_DEVICE_OUT_SPEAKER_SAFE));
         }
         } break;
 
     default:
-        ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
+        ALOGW("getDevicesForStrategy() unknown strategy: %d", strategy);
         break;
     }
 
-    if (device == AUDIO_DEVICE_NONE) {
-        ALOGV("getDeviceForStrategy() no device found for strategy %d", strategy);
-        device = getApmObserver()->getDefaultOutputDevice()->type();
-        ALOGE_IF(device == AUDIO_DEVICE_NONE,
-                 "getDeviceForStrategy() no default device defined");
+    if (devices.isEmpty()) {
+        ALOGV("getDevicesForStrategy() no device found for strategy %d", strategy);
+        sp<DeviceDescriptor> defaultOutputDevice = getApmObserver()->getDefaultOutputDevice();
+        if (defaultOutputDevice != nullptr) {
+            devices.add(defaultOutputDevice);
+        }
+        ALOGE_IF(devices.isEmpty(),
+                 "getDevicesForStrategy() no default device defined");
     }
-    ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
-    return device;
+
+    ALOGVV("getDevices"
+           "ForStrategy() strategy %d, device %x", strategy, devices.types());
+    return devices;
 }
 
 
-audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
+sp<DeviceDescriptor> Engine::getDeviceForInputSource(audio_source_t inputSource) const
 {
     const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
     const DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
     const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
-    audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
+    DeviceVector availableDevices = availableInputDevices;
     sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
-    audio_devices_t availablePrimaryDeviceTypes = availableInputDevices.getDeviceTypesFromHwModule(
-        primaryOutput->getModuleHandle()) & ~AUDIO_DEVICE_BIT_IN;
-    uint32_t device = AUDIO_DEVICE_NONE;
+    DeviceVector availablePrimaryDevices = availableInputDevices.getDevicesFromHwModule(
+            primaryOutput->getModuleHandle());
+    sp<DeviceDescriptor> device;
 
     // when a call is active, force device selection to match source VOICE_COMMUNICATION
     // for most other input sources to avoid rerouting call TX audio
@@ -532,57 +496,47 @@
     switch (inputSource) {
     case AUDIO_SOURCE_DEFAULT:
     case AUDIO_SOURCE_MIC:
-    if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
-        device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
-    } else if ((getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) &&
-        (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) {
-        device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-    } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
-        device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-    } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
-        device = AUDIO_DEVICE_IN_USB_HEADSET;
-    } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
-        device = AUDIO_DEVICE_IN_USB_DEVICE;
-    } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-        device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-    }
-    break;
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_BLUETOOTH_A2DP, String8(""), AUDIO_FORMAT_DEFAULT);
+        if (device != nullptr) break;
+        if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) {
+            device = availableDevices.getDevice(
+                    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
+            if (device != nullptr) break;
+        }
+        device = availableDevices.getFirstExistingDevice({
+                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
+                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
+        break;
 
     case AUDIO_SOURCE_VOICE_COMMUNICATION:
         // Allow only use of devices on primary input if in call and HAL does not support routing
         // to voice call path.
         if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
-                (availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) {
-            availableDeviceTypes = availablePrimaryDeviceTypes;
+                (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX,
+                        String8(""), AUDIO_FORMAT_DEFAULT)) == nullptr) {
+            availableDevices = availablePrimaryDevices;
         }
 
         switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) {
         case AUDIO_POLICY_FORCE_BT_SCO:
             // if SCO device is requested but no SCO device is available, fall back to default case
-            if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-                device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+            device = availableDevices.getDevice(
+                    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
+            if (device != nullptr) {
                 break;
             }
             FALLTHROUGH_INTENDED;
 
         default:    // FORCE_NONE
-            if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
-                device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
-                device = AUDIO_DEVICE_IN_USB_HEADSET;
-            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
-                device = AUDIO_DEVICE_IN_USB_DEVICE;
-            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-                device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-            }
+            device = availableDevices.getFirstExistingDevice({
+                    AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
+                    AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
             break;
 
         case AUDIO_POLICY_FORCE_SPEAKER:
-            if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
-                device = AUDIO_DEVICE_IN_BACK_MIC;
-            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-                device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-            }
+            device = availableDevices.getFirstExistingDevice({
+                    AUDIO_DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC});
             break;
         }
         break;
@@ -591,77 +545,60 @@
     case AUDIO_SOURCE_UNPROCESSED:
     case AUDIO_SOURCE_HOTWORD:
         if (inputSource == AUDIO_SOURCE_HOTWORD) {
-            availableDeviceTypes = availablePrimaryDeviceTypes;
+            availableDevices = availablePrimaryDevices;
         }
-        if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO &&
-                availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-            device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
-            device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
-            device = AUDIO_DEVICE_IN_USB_HEADSET;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
-            device = AUDIO_DEVICE_IN_USB_DEVICE;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+        if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) {
+            device = availableDevices.getDevice(
+                    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
+            if (device != nullptr) break;
         }
+        device = availableDevices.getFirstExistingDevice({
+                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
+                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
         break;
     case AUDIO_SOURCE_CAMCORDER:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
-            device = AUDIO_DEVICE_IN_BACK_MIC;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
-            // This is specifically for a device without built-in mic
-            device = AUDIO_DEVICE_IN_USB_DEVICE;
-        }
+        // For a device without built-in mic, adding usb device
+        device = availableDevices.getFirstExistingDevice({
+                AUDIO_DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC,
+                AUDIO_DEVICE_IN_USB_DEVICE});
         break;
     case AUDIO_SOURCE_VOICE_DOWNLINK:
     case AUDIO_SOURCE_VOICE_CALL:
     case AUDIO_SOURCE_VOICE_UPLINK:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
-            device = AUDIO_DEVICE_IN_VOICE_CALL;
-        }
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_VOICE_CALL, String8(""), AUDIO_FORMAT_DEFAULT);
         break;
     case AUDIO_SOURCE_VOICE_PERFORMANCE:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
-            device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
-            device = AUDIO_DEVICE_IN_USB_HEADSET;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
-            device = AUDIO_DEVICE_IN_USB_DEVICE;
-        } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-        }
+        device = availableDevices.getFirstExistingDevice({
+                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
+                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
         break;
     case AUDIO_SOURCE_REMOTE_SUBMIX:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
-            device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
-        }
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8(""), AUDIO_FORMAT_DEFAULT);
         break;
     case AUDIO_SOURCE_FM_TUNER:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) {
-            device = AUDIO_DEVICE_IN_FM_TUNER;
-        }
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_FM_TUNER, String8(""), AUDIO_FORMAT_DEFAULT);
         break;
     case AUDIO_SOURCE_ECHO_REFERENCE:
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_ECHO_REFERENCE) {
-            device = AUDIO_DEVICE_IN_ECHO_REFERENCE;
-        }
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_ECHO_REFERENCE, String8(""), AUDIO_FORMAT_DEFAULT);
         break;
     default:
         ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
         break;
     }
-    if (device == AUDIO_DEVICE_NONE) {
+    if (device == nullptr) {
         ALOGV("getDeviceForInputSource() no device found for source %d", inputSource);
-        if (availableDeviceTypes & AUDIO_DEVICE_IN_STUB) {
-            device = AUDIO_DEVICE_IN_STUB;
-        }
-        ALOGE_IF(device == AUDIO_DEVICE_NONE,
+        device = availableDevices.getDevice(
+                AUDIO_DEVICE_IN_STUB, String8(""), AUDIO_FORMAT_DEFAULT);
+        ALOGE_IF(device == nullptr,
                  "getDeviceForInputSource() no default device defined");
     }
-    ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
+    ALOGV_IF(device != nullptr,
+             "getDeviceForInputSource()input source %d, device %08x",
+             inputSource, device->type());
     return device;
 }
 
@@ -684,11 +621,9 @@
 
     auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
                 mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
-    audio_devices_t devices = getDeviceForStrategyInt(legacyStrategy,
-                                                      availableOutputDevices,
-                                                      availableInputDevices, outputs,
-                                                      (uint32_t)AUDIO_DEVICE_NONE);
-    return availableOutputDevices.getDevicesFromTypeMask(devices);
+    return getDevicesForStrategyInt(legacyStrategy,
+                                    availableOutputDevices,
+                                    availableInputDevices, outputs);
 }
 
 DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
@@ -747,17 +682,21 @@
     if (device != nullptr) {
         return device;
     }
-    audio_devices_t deviceType = getDeviceForInputSource(attr.source);
 
-    if (audio_is_remote_submix_device(deviceType)) {
-        address = "0";
-        std::size_t pos;
-        std::string tags { attr.tags };
-        if ((pos = tags.find("addr=")) != std::string::npos) {
-            address = tags.substr(pos + std::strlen("addr="));
-        }
+    device = getDeviceForInputSource(attr.source);
+    if (device == nullptr || !audio_is_remote_submix_device(device->type())) {
+        // Return immediately if the device is null or it is not a remote submix device.
+        return device;
     }
-    return availableInputDevices.getDevice(deviceType,
+
+    // For remote submix device, try to find the device by address.
+    address = "0";
+    std::size_t pos;
+    std::string tags { attr.tags };
+    if ((pos = tags.find("addr=")) != std::string::npos) {
+        address = tags.substr(pos + std::strlen("addr="));
+    }
+    return availableInputDevices.getDevice(device->type(),
                                            String8(address.c_str()),
                                            AUDIO_FORMAT_DEFAULT);
 }