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/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index e566332..1e3119c 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -115,7 +115,11 @@
 
     ALOGD("AudioStreamTrack::open(), request notificationFrames = %d, frameCount = %u",
           notificationFrames, (uint)frameCount);
-    mAudioTrack = new AudioTrack(
+    mAudioTrack = new AudioTrack();
+    if (getDeviceId() != AAUDIO_UNSPECIFIED) {
+        mAudioTrack->setOutputDevice(getDeviceId());
+    }
+    mAudioTrack->set(
             (audio_stream_type_t) AUDIO_STREAM_MUSIC,
             getSampleRate(),
             format,
@@ -125,6 +129,8 @@
             callback,
             callbackData,
             notificationFrames,
+            0 /*sharedBuffer*/,
+            false /*threadCanCallJava*/,
             AUDIO_SESSION_ALLOCATE,
             streamTransferType
             );
@@ -160,6 +166,7 @@
 
     setState(AAUDIO_STREAM_STATE_OPEN);
     setDeviceId(mAudioTrack->getRoutedDeviceId());
+    mAudioTrack->addAudioDeviceCallback(mDeviceCallback);
 
     // Update performance mode based on the actual stream.
     // For example, if the sample rate is not allowed then you won't get a FAST track.
@@ -229,6 +236,7 @@
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
     } else {
+        onStart();
         setState(AAUDIO_STREAM_STATE_STARTING);
     }
     return AAUDIO_OK;
@@ -246,6 +254,7 @@
               AAudio_convertStreamStateToText(getState()));
         return AAUDIO_ERROR_INVALID_STATE;
     }
+    onStop();
     setState(AAUDIO_STREAM_STATE_PAUSING);
     mAudioTrack->pause();
     status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
@@ -276,6 +285,7 @@
     if (mAudioTrack.get() == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
+    onStop();
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
     mAudioTrack->stop();
@@ -339,6 +349,10 @@
         return result;
     }
 
+    if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+        return AAUDIO_ERROR_DISCONNECTED;
+    }
+
     // TODO add timeout to AudioTrack
     bool blocking = timeoutNanoseconds > 0;
     ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
@@ -346,6 +360,12 @@
         return 0;
     } else if (bytesWritten < 0) {
         ALOGE("invalid write, returned %d", (int)bytesWritten);
+        // in this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
+        // AudioTrack invalidation
+        if (bytesWritten == DEAD_OBJECT) {
+            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+            return AAUDIO_ERROR_DISCONNECTED;
+        }
         return AAudioConvert_androidToAAudioResult(bytesWritten);
     }
     int32_t framesWritten = (int32_t)(bytesWritten / bytesPerFrame);