aaudio: fix device switch detection in legacy path

Implement device switch detection on legacy path (AudioTrack and
AudioRecord) based on audio routing callbacks forcing the stream state
to disconnected.

Bug: 33355262
Bug: 62090113
Test: tested with write_sine and input_monitor command line tools.
Change-Id: I9e0421fee233964b1bf318acb640569196a00f13
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index f89234a..dfac4fb 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -30,7 +30,7 @@
 using namespace aaudio;
 
 AudioStreamLegacy::AudioStreamLegacy()
-        : AudioStream() {
+        : AudioStream(), mDeviceCallback(new StreamDeviceCallback(this)) {
 }
 
 AudioStreamLegacy::~AudioStreamLegacy() {
@@ -60,44 +60,54 @@
 
 void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
     aaudio_data_callback_result_t callbackResult;
+
+    if (!mCallbackEnabled.load()) {
+        return;
+    }
+
     switch (opcode) {
         case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
-            // Note that this code assumes an AudioTrack::Buffer is the same as AudioRecord::Buffer
-            // TODO define our own AudioBuffer and pass it from the subclasses.
-            AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
-            if (audioBuffer->frameCount == 0) return;
+            if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+                // Note that this code assumes an AudioTrack::Buffer is the same as
+                // AudioRecord::Buffer
+                // TODO define our own AudioBuffer and pass it from the subclasses.
+                AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
+                if (audioBuffer->frameCount == 0) return;
 
-            // If the caller specified an exact size then use a block size adapter.
-            if (mBlockAdapter != nullptr) {
-                int32_t byteCount = audioBuffer->frameCount * getBytesPerFrame();
-                callbackResult = mBlockAdapter->processVariableBlock((uint8_t *) audioBuffer->raw,
-                                                                     byteCount);
-            } else {
-                // Call using the AAudio callback interface.
-                callbackResult = (*getDataCallbackProc())(
-                        (AAudioStream *) this,
-                        getDataCallbackUserData(),
-                        audioBuffer->raw,
-                        audioBuffer->frameCount
-                        );
-            }
-            if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
-                audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
-                incrementClientFrameCounter(audioBuffer->frameCount);
-            } else {
-                audioBuffer->size = 0;
+                // If the caller specified an exact size then use a block size adapter.
+                if (mBlockAdapter != nullptr) {
+                    int32_t byteCount = audioBuffer->frameCount * getBytesPerFrame();
+                    callbackResult = mBlockAdapter->processVariableBlock(
+                            (uint8_t *) audioBuffer->raw, byteCount);
+                } else {
+                    // Call using the AAudio callback interface.
+                    callbackResult = (*getDataCallbackProc())(
+                            (AAudioStream *) this,
+                            getDataCallbackUserData(),
+                            audioBuffer->raw,
+                            audioBuffer->frameCount
+                            );
+                }
+                if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
+                    audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
+                    incrementClientFrameCounter(audioBuffer->frameCount);
+                } else {
+                    audioBuffer->size = 0;
+                }
+                break;
             }
         }
-            break;
+        /// FALL THROUGH
 
             // Stream got rerouted so we disconnect.
         case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
-            ALOGD("AudioStreamAAudio(): callbackLoop() stream disconnected");
+            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+            ALOGD("processCallbackCommon() stream disconnected");
             if (getErrorCallbackProc() != nullptr) {
                 (*getErrorCallbackProc())(
                         (AAudioStream *) this,
                         getErrorCallbackUserData(),
-                        AAUDIO_OK
+                        AAUDIO_ERROR_DISCONNECTED
                         );
             }
             mCallbackEnabled.store(false);
@@ -129,3 +139,22 @@
     status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
     return AAudioConvert_androidToAAudioResult(status);
 }
+
+void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
+{
+    ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId);
+    if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
+            getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+        // if we have a data callback and the stream is active, send the error callback from
+        // data callback thread when it sees the DISCONNECTED state
+        if (!isDataCallbackActive() && getErrorCallbackProc() != nullptr) {
+            (*getErrorCallbackProc())(
+                    (AAudioStream *) this,
+                    getErrorCallbackUserData(),
+                    AAUDIO_ERROR_DISCONNECTED
+                    );
+        }
+    }
+    setDeviceId(deviceId);
+}