Use routed devices throughout the audio framework

After you open an audio stream, you can call getRoutedDevice() to
get which output device is actually used.

However, if you play an ALARM with a headset attached, audio comes
out of both the speaker and the headset. This is not properly reflected
with our current APIs.

Throughout the audio framework, we should use DeviceIdVector instead
of a single device when getRoutedDevice(s) are called.

This change touches many files because onAudioDeviceUpdate() has many
callers.

BYPASS_LARGE_CHANGE_WARNING

Bug: 367816690
Test: atest AAudioTests
Test: atest audiorecord_tests
Test: atest audiosystem_tests
Test: atest audiotrack_tests
Test: adb shell /data/fuzz/arm64/libaaudio_fuzzer/libaaudio_fuzzer
Test: adb shell /data/fuzz/arm64/mediaplayer_fuzzer/mediaplayer_fuzzer
Test: adb shell /data/fuzz/arm64/mediarecorder_fuzzer/mediarecorder_fuzzer
Test: OboeTester Test Output with Alarm and USB
Flag: EXEMPT refactor
Change-Id: I5e32ac5d1c24229e60112ef00d82af41d8eff6e7
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2322780..b2edaf7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1190,7 +1190,7 @@
                                             adjAttributionSource, &input.config, input.flags,
                                             &selectedDeviceIds, &portId, &secondaryOutputs,
                                             &isSpatialized, &isBitPerfect, &volume, &muted);
-    output.selectedDeviceId = getFirstDeviceId(selectedDeviceIds);
+    output.selectedDeviceIds = selectedDeviceIds;
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
         ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e42b39e..200175b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -10921,7 +10921,7 @@
 
     // For mmap streams, once the routing has changed, they will be disconnected. It should be
     // okay to notify the client earlier before the new patch creation.
-    if (mDeviceIds != deviceIds) {
+    if (!areDeviceIdsEqual(deviceIds, mDeviceIds)) {
         if (const sp<MmapStreamCallback> callback = mCallback.promote()) {
             // The aaudioservice handle the routing changed event asynchronously. In that case,
             // it is safe to hold the lock here.
@@ -10945,7 +10945,7 @@
         *handle = AUDIO_PATCH_HANDLE_NONE;
     }
 
-    if (numDevices == 0 || mDeviceIds != deviceIds) {
+    if (numDevices == 0 || (!areDeviceIdsEqual(deviceIds, mDeviceIds))) {
         if (isOutput()) {
             sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
             mOutDeviceTypeAddrs = sinkDeviceTypeAddrs;
@@ -11110,8 +11110,7 @@
             if (const sp<MmapStreamCallback> callback = mCallback.promote()) {
                 // The aaudioservice handle the routing changed event asynchronously. In that case,
                 // it is safe to hold the lock here.
-                DeviceIdVector emptyDeviceIdVector;
-                callback->onRoutingChanged(emptyDeviceIdVector);
+                callback->onRoutingChanged({});
             } else if (mNoCallbackWarningCount < kMaxNoCallbackWarnings) {
                 ALOGW("Could not notify MMAP stream tear down: no onRoutingChanged callback!");
                 mNoCallbackWarningCount++;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index b5ee2f2..243f1f1 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -119,8 +119,9 @@
         }
     }
 
-    ALOGV("findExclusiveEndpoint_l(), found %p for device = %d, sessionId = %d",
-          endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
+    ALOGV("findExclusiveEndpoint_l(), found %p for devices = %s, sessionId = %d",
+          endpoint.get(), toString(configuration.getDeviceIds()).c_str(),
+          configuration.getSessionId());
     return endpoint;
 }
 
@@ -137,8 +138,9 @@
         }
     }
 
-    ALOGV("findSharedEndpoint_l(), found %p for device = %d, sessionId = %d",
-          endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
+    ALOGV("findSharedEndpoint_l(), found %p for devices = %s, sessionId = %d",
+          endpoint.get(), toString(configuration.getDeviceIds()).c_str(),
+          configuration.getSessionId());
     return endpoint;
 }
 
@@ -192,8 +194,8 @@
     } else {
         const sp<AAudioServiceEndpointMMAP> endpointMMap =
                 new AAudioServiceEndpointMMAP(aaudioService);
-        ALOGV("%s(), no match so try to open MMAP %p for dev %d",
-              __func__, endpointMMap.get(), configuration.getDeviceId());
+        ALOGV("%s(), no match so try to open MMAP %p for devices %s",
+              __func__, endpointMMap.get(), toString(configuration.getDeviceIds()).c_str());
         endpoint = endpointMMap;
 
         const aaudio_result_t result = endpoint->open(request);
@@ -250,8 +252,9 @@
                 mSharedOpenCount++;
             }
         }
-        ALOGV("%s(), created endpoint %p, requested device = %d, dir = %d",
-              __func__, endpoint.get(), configuration.getDeviceId(), (int)direction);
+        ALOGV("%s(), created endpoint %p, requested device = %s, dir = %d",
+              __func__, endpoint.get(), android::toString(configuration.getDeviceIds()).c_str(),
+              (int)direction);
         IPCThreadState::self()->restoreCallingIdentity(token);
     }
 
@@ -289,8 +292,9 @@
 
         serviceEndpoint->close();
         mExclusiveCloseCount++;
-        ALOGV("%s() %p for device %d",
-              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGV("%s() %p for devices %s",
+              __func__, serviceEndpoint.get(),
+              android::toString(serviceEndpoint->getDeviceIds()).c_str());
     }
 }
 
@@ -313,7 +317,8 @@
         serviceEndpoint->close();
 
         mSharedCloseCount++;
-        ALOGV("%s(%p) closed for device %d",
-              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGV("%s(%p) closed for device %s",
+              __func__, serviceEndpoint.get(),
+              android::toString(serviceEndpoint->getDeviceIds()).c_str());
     }
 }
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index e49e9e7..c677619 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -57,7 +57,7 @@
     result << "    Direction:            " << ((getDirection() == AAUDIO_DIRECTION_OUTPUT)
                                    ? "OUTPUT" : "INPUT") << "\n";
     result << "    Requested Device Id:  " << mRequestedDeviceId << "\n";
-    result << "    Device Id:            " << getDeviceId() << "\n";
+    result << "    Device Ids:           " << android::toString(getDeviceIds()).c_str() << "\n";
     result << "    Sample Rate:          " << getSampleRate() << "\n";
     result << "    Channel Count:        " << getSamplesPerFrame() << "\n";
     result << "    Channel Mask:         0x" << std::hex << getChannelMask() << std::dec << "\n";
@@ -155,8 +155,8 @@
     if (configuration.getDirection() != getDirection()) {
         return false;
     }
-    if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
-        configuration.getDeviceId() != getDeviceId()) {
+    if (!configuration.getDeviceIds().empty() &&
+        !android::areDeviceIdsEqual(configuration.getDeviceIds(), getDeviceIds())) {
         return false;
     }
     if (configuration.getSessionId() != AAUDIO_SESSION_ID_ALLOCATE &&
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 59bb98e..66918c1 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -105,7 +105,7 @@
     aaudio_result_t result = AAUDIO_OK;
     mAudioDataWrapper = std::make_unique<SharedMemoryWrapper>();
     copyFrom(request.getConstantConfiguration());
-    mRequestedDeviceId = getDeviceId();
+    mRequestedDeviceId = android::getFirstDeviceId(getDeviceIds());
 
     mMmapClient.attributionSource = request.getAttributionSource();
     // TODO b/182392769: use attribution source util
@@ -173,11 +173,13 @@
         audio_config_base_t* config) {
     aaudio_result_t result = AAUDIO_OK;
     audio_config_base_t currentConfig = *config;
-    audio_port_handle_t deviceId;
+    android::DeviceIdVector deviceIds;
 
     const audio_attributes_t attributes = getAudioAttributesFrom(this);
 
-    deviceId = mRequestedDeviceId;
+    if (mRequestedDeviceId != AAUDIO_UNSPECIFIED) {
+        deviceIds.push_back(mRequestedDeviceId);
+    }
 
     const aaudio_direction_t direction = getDirection();
 
@@ -202,14 +204,9 @@
 
     // Open HAL stream. Set mMmapStream
     ALOGD("%s trying to open MMAP stream with format=%#x, "
-          "sample_rate=%u, channel_mask=%#x, device=%d",
+          "sample_rate=%u, channel_mask=%#x, device=%s",
           __func__, config->format, config->sample_rate,
-          config->channel_mask, deviceId);
-
-    android::DeviceIdVector deviceIds;
-    if (deviceId != AAUDIO_UNSPECIFIED) {
-        deviceIds.push_back(deviceId);
-    }
+          config->channel_mask, android::toString(deviceIds).c_str());
 
     const std::lock_guard<std::mutex> lock(mMmapStreamLock);
     const status_t status = MmapStreamInterface::openMmapStream(streamDirection,
@@ -233,12 +230,11 @@
         config->channel_mask = currentConfig.channel_mask;
         return AAUDIO_ERROR_UNAVAILABLE;
     }
-    deviceId = android::getFirstDeviceId(deviceIds);
 
-    if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGW("%s() - openMmapStream() failed to set deviceId", __func__);
+    if (deviceIds.empty()) {
+        ALOGW("%s() - openMmapStream() failed to set deviceIds", __func__);
     }
-    setDeviceId(deviceId);
+    setDeviceIds(deviceIds);
 
     if (sessionId == AUDIO_SESSION_ALLOCATE) {
         ALOGW("%s() - openMmapStream() failed to set sessionId", __func__);
@@ -250,8 +246,8 @@
             : (aaudio_session_id_t) sessionId;
     setSessionId(actualSessionId);
 
-    ALOGD("%s(format = 0x%X) deviceId = %d, sessionId = %d",
-          __func__, config->format, getDeviceId(), getSessionId());
+    ALOGD("%s(format = 0x%X) deviceIds = %s, sessionId = %d",
+          __func__, config->format, toString(getDeviceIds()).c_str(), getSessionId());
 
     // Create MMAP/NOIRQ buffer.
     result = createMmapBuffer_l();
@@ -280,9 +276,9 @@
 
     mDataReportOffsetNanos = ((int64_t)mTimestampGracePeriodMs) * AAUDIO_NANOS_PER_MILLISECOND;
 
-    ALOGD("%s() got rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
+    ALOGD("%s() got rate = %d, channels = %d channelMask = %#x, deviceIds = %s, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
-          deviceId, getBufferCapacity());
+          android::toString(deviceIds).c_str(), getBufferCapacity());
 
     ALOGD("%s() got format = 0x%X = %s, frame size = %d, burst size = %d",
           __func__, getFormat(), audio_format_to_string(getFormat()),
@@ -293,7 +289,11 @@
 error:
     close_l();
     // restore original requests
-    setDeviceId(mRequestedDeviceId);
+    android::DeviceIdVector requestedDeviceIds;
+    if (mRequestedDeviceId != AAUDIO_UNSPECIFIED) {
+        requestedDeviceIds.push_back(mRequestedDeviceId);
+    }
+    setDeviceIds(requestedDeviceIds);
     setSessionId(requestedSessionId);
     return result;
 }
@@ -491,27 +491,26 @@
 };
 
 void AAudioServiceEndpointMMAP::onRoutingChanged(const android::DeviceIdVector& deviceIds) {
-    const auto deviceId = android::getFirstDeviceId(deviceIds);
-    // TODO(b/367816690): Compare the new and saved device sets.
-    ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
-    if (getDeviceId() != deviceId) {
-        if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+    ALOGD("%s() called with dev %s, old = %s", __func__, android::toString(deviceIds).c_str(),
+          android::toString(getDeviceIds()).c_str());
+    if (!android::areDeviceIdsEqual(getDeviceIds(), deviceIds)) {
+        if (!getDeviceIds().empty()) {
             // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
-            // as false here so that there won't be a new stream connect to this endpoint.
+            // as false here so that there won't be a new stream connected to this endpoint.
             mConnected.store(false);
             const android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
-            std::thread asyncTask([holdEndpoint, deviceId]() {
+            std::thread asyncTask([holdEndpoint, deviceIds]() {
                 ALOGD("onRoutingChanged() asyncTask launched");
                 // When routing changed, the stream is disconnected and cannot be used except for
                 // closing. In that case, it should be safe to release all registered streams.
                 // This can help release service side resource in case the client doesn't close
                 // the stream after receiving disconnect event.
                 holdEndpoint->releaseRegisteredStreams();
-                holdEndpoint->setDeviceId(deviceId);
+                holdEndpoint->setDeviceIds(deviceIds);
             });
             asyncTask.detach();
         } else {
-            setDeviceId(deviceId);
+            setDeviceIds(deviceIds);
         }
     }
 };
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 5e1e594..f54de5e 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -64,7 +64,7 @@
     const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
 
     copyFrom(configuration);
-    mRequestedDeviceId = configuration.getDeviceId();
+    mRequestedDeviceId = android::getFirstDeviceId(configuration.getDeviceIds());
 
     AudioStreamBuilder builder;
     builder.copyFrom(configuration);
@@ -79,7 +79,7 @@
 
     setSampleRate(mStreamInternal->getSampleRate());
     setChannelMask(mStreamInternal->getChannelMask());
-    setDeviceId(mStreamInternal->getDeviceId());
+    setDeviceIds(mStreamInternal->getDeviceIds());
     setSessionId(mStreamInternal->getSessionId());
     setFormat(AUDIO_FORMAT_PCM_FLOAT); // force for mixer
     setHardwareSampleRate(mStreamInternal->getHardwareSampleRate());
@@ -220,7 +220,7 @@
 void AAudioServiceEndpointShared::handleDisconnectRegisteredStreamsAsync() {
     android::sp<AAudioServiceEndpointShared> holdEndpoint(this);
     // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
-    // as false here so that there won't be a new stream connect to this endpoint.
+    // as false here so that there won't be a new stream connected to this endpoint.
     mConnected.store(false);
     std::thread asyncTask([holdEndpoint]() {
         // When handling disconnection, the service side has disconnected. In that case,
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 78cf706..1c24f18 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -127,7 +127,8 @@
         .set(AMEDIAMETRICS_PROP_DIRECTION,
                 AudioGlobal_convertDirectionToText(getDirection()))
         .set(AMEDIAMETRICS_PROP_ENCODING, toString(getFormat()).c_str())
-        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)getDeviceId())
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, android::getFirstDeviceId(getDeviceIds()))
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS, android::toString(getDeviceIds()).c_str())
         .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)getSampleRate())
         .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)getSessionId())
         .set(AMEDIAMETRICS_PROP_SOURCE, toString(attributes.source).c_str())
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
index f5c2e6c..e80f51d 100644
--- a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -372,7 +372,8 @@
     request.setAttributionSource(attributionSource);
     request.setInService(fdp.ConsumeBool());
 
-    request.getConfiguration().setDeviceId(fdp.ConsumeIntegral<int32_t>());
+    android::DeviceIdVector DeviceIdVector = { fdp.ConsumeIntegral<int32_t>() };
+    request.getConfiguration().setDeviceIds(DeviceIdVector);
     request.getConfiguration().setSampleRate(fdp.ConsumeIntegral<int32_t>());
     request.getConfiguration().setChannelMask((aaudio_channel_mask_t)(
         fdp.ConsumeBool()