Release all connected streams when routing device is changed.

When there is a routing change, the existing mmap endpoint is
invalidated and can no longer be used. In that case, release all
registered streams and set the endpoint as disconnected so that it won't
be reused by another new request. That also releases the resources from
the service side so that it won't be harmful if the clients don't close
the stream when receiving disconnect event.

Bug: 299360979
Test: atest AAudioTests
Test: test_idle_disconnected_shared_stream
Change-Id: I4d7c68d5c0d15b75cc2f07471f50bfedb6292da9
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index d9b75da..f1fe416 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -295,8 +295,10 @@
         if ((result != mCallbackFrames)) {
             ALOGE("callbackLoop: read() returned %d", result);
             if (result >= 0) {
-                // Only read some of the frames requested. Must have timed out.
-                result = AAUDIO_ERROR_TIMEOUT;
+                // Only read some of the frames requested. The stream can be disconnected
+                // or timed out.
+                processCommands();
+                result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
             }
             maybeCallErrorCallback(result);
             break;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 3badb0b..d98cf56 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -353,8 +353,10 @@
             result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
             if ((result != mCallbackFrames)) {
                 if (result >= 0) {
-                    // Only wrote some of the frames requested. Must have timed out.
-                    result = AAUDIO_ERROR_TIMEOUT;
+                    // Only wrote some of the frames requested. The stream can be disconnected
+                    // or timed out.
+                    processCommands();
+                    result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
                 }
                 maybeCallErrorCallback(result);
                 break;
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 2ef6fe5..12b77c5 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -421,10 +421,17 @@
     ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
     if (getDeviceId() != deviceId) {
         if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+            // 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.
+            mConnected.store(false);
             const android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
             std::thread asyncTask([holdEndpoint, deviceId]() {
                 ALOGD("onRoutingChanged() asyncTask launched");
-                holdEndpoint->disconnectRegisteredStreams();
+                // 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);
             });
             asyncTask.detach();
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 1dd0c3a..5e1e594 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -219,9 +219,13 @@
 
 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.
+    mConnected.store(false);
     std::thread asyncTask([holdEndpoint]() {
-        // We do not need the returned vector.
-        holdEndpoint->disconnectRegisteredStreams();
+        // When handling disconnection, the service side has disconnected. In that case,
+        // it should be safe to release all registered streams.
+        holdEndpoint->releaseRegisteredStreams();
     });
     asyncTask.detach();
 }