Add standby mode for aaudio service stream.
When the stream is stopped, everything will remain open but just stop
writing data. But this will keep the DSP running and using power
until the stream is closed.
To resolve this issue, the solution is to put the stream into standby
mode so that the HAL can release the corresponding resource.
When the HAL releases the resource, the shared file descriptor will also
be released. In that case, when the stream is restarted, AAudioService
needs to recreate shared buffer and the client needs to replace the new
shared buffer.
Test: atest AAudioTests
Test: test_steal_exclusive
Bug: 201000721
Bug: 196394385
Bug: 167345722
Bug: 208619472
Change-Id: Ib4f98e7aee72c2e56acd7f2f0ac378a94ec26241
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index afdc2ac..9f0564f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -81,8 +81,6 @@
aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AAUDIO_OK;
- int32_t framesPerBurst;
- int32_t framesPerHardwareBurst;
AAudioStreamRequest request;
AAudioStreamConfiguration configurationOutput;
@@ -97,9 +95,6 @@
return result;
}
- const int32_t burstMinMicros = android::AudioSystem::getAAudioHardwareBurstMinUsec();
- int32_t burstMicros = 0;
-
const audio_format_t requestedFormat = getFormat();
// We have to do volume scaling. So we prefer FLOAT format.
if (requestedFormat == AUDIO_FORMAT_DEFAULT) {
@@ -215,12 +210,28 @@
goto error;
}
- framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
+ if ((result = configureDataInformation(builder.getFramesPerDataCallback())) != AAUDIO_OK) {
+ goto error;
+ }
+
+ setState(AAUDIO_STREAM_STATE_OPEN);
+
+ return result;
+
+error:
+ safeReleaseClose();
+ return result;
+}
+
+aaudio_result_t AudioStreamInternal::configureDataInformation(int32_t callbackFrames) {
+ int32_t framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
// Scale up the burst size to meet the minimum equivalent in microseconds.
// This is to avoid waking the CPU too often when the HW burst is very small
// or at high sample rates.
- framesPerBurst = framesPerHardwareBurst;
+ int32_t framesPerBurst = framesPerHardwareBurst;
+ int32_t burstMicros = 0;
+ const int32_t burstMinMicros = android::AudioSystem::getAAudioHardwareBurstMinUsec();
do {
if (burstMicros > 0) { // skip first loop
framesPerBurst *= 2;
@@ -233,8 +244,7 @@
// Validate final burst size.
if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst);
- result = AAUDIO_ERROR_OUT_OF_RANGE;
- goto error;
+ return AAUDIO_ERROR_OUT_OF_RANGE;
}
setFramesPerBurst(framesPerBurst); // only save good value
@@ -242,26 +252,21 @@
if (mBufferCapacityInFrames < getFramesPerBurst()
|| mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
- result = AAUDIO_ERROR_OUT_OF_RANGE;
- goto error;
+ return AAUDIO_ERROR_OUT_OF_RANGE;
}
mClockModel.setSampleRate(getSampleRate());
mClockModel.setFramesPerBurst(framesPerHardwareBurst);
if (isDataCallbackSet()) {
- mCallbackFrames = builder.getFramesPerDataCallback();
+ mCallbackFrames = callbackFrames;
if (mCallbackFrames > getBufferCapacity() / 2) {
ALOGW("%s - framesPerCallback too big = %d, capacity = %d",
__func__, mCallbackFrames, getBufferCapacity());
- result = AAUDIO_ERROR_OUT_OF_RANGE;
- goto error;
-
+ return AAUDIO_ERROR_OUT_OF_RANGE;
} else if (mCallbackFrames < 0) {
ALOGW("%s - framesPerCallback negative", __func__);
- result = AAUDIO_ERROR_OUT_OF_RANGE;
- goto error;
-
+ return AAUDIO_ERROR_OUT_OF_RANGE;
}
if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
mCallbackFrames = getFramesPerBurst();
@@ -302,14 +307,7 @@
}
setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q
-
- setState(AAUDIO_STREAM_STATE_OPEN);
-
- return result;
-
-error:
- safeReleaseClose();
- return result;
+ return AAUDIO_OK;
}
// This must be called under mStreamLock.
@@ -357,6 +355,60 @@
}
}
+aaudio_result_t AudioStreamInternal::exitStandby_l() {
+ AudioEndpointParcelable endpointParcelable;
+ // The stream is in standby mode, copy all available data and then close the duplicated
+ // shared file descriptor so that it won't cause issue when the HAL try to reallocate new
+ // shared file descriptor when exiting from standby.
+ // Cache current read counter, which will be reset to new read and write counter
+ // when the new data queue and endpoint are reconfigured.
+ const android::fifo_counter_t readCounter = mAudioEndpoint->getDataReadCounter();
+ // Cache the buffer size which may be from client.
+ const int32_t previousBufferSize = mBufferSizeInFrames;
+ // Copy all available data from current data queue.
+ uint8_t buffer[getBufferCapacity() * getBytesPerFrame()];
+ android::fifo_frames_t fullFramesAvailable =
+ mAudioEndpoint->read(buffer, getBufferCapacity());
+ mEndPointParcelable.closeDataFileDescriptor();
+ aaudio_result_t result = mServiceInterface.exitStandby(
+ mServiceStreamHandle, endpointParcelable);
+ if (result != AAUDIO_OK) {
+ ALOGE("Failed to exit standby, error=%d", result);
+ goto exit;
+ }
+ // Reconstruct data queue descriptor using new shared file descriptor.
+ mEndPointParcelable.updateDataFileDescriptor(&endpointParcelable);
+ result = mEndPointParcelable.resolveDataQueue(&mEndpointDescriptor.dataQueueDescriptor);
+ if (result != AAUDIO_OK) {
+ ALOGE("Failed to resolve data queue after exiting standby, error=%d", result);
+ goto exit;
+ }
+ // Reconfigure audio endpoint with new data queue descriptor.
+ mAudioEndpoint->configureDataQueue(
+ mEndpointDescriptor.dataQueueDescriptor, getDirection());
+ // Set read and write counters with previous read counter, the later write action
+ // will make the counter at the correct place.
+ mAudioEndpoint->setDataReadCounter(readCounter);
+ mAudioEndpoint->setDataWriteCounter(readCounter);
+ result = configureDataInformation(mCallbackFrames);
+ if (result != AAUDIO_OK) {
+ ALOGE("Failed to configure data information after exiting standby, error=%d", result);
+ goto exit;
+ }
+ // Write data from previous data buffer to new endpoint.
+ if (android::fifo_frames_t framesWritten =
+ mAudioEndpoint->write(buffer, fullFramesAvailable);
+ framesWritten != fullFramesAvailable) {
+ ALOGW("Some data lost after exiting standby, frames written: %d, "
+ "frames to write: %d", framesWritten, fullFramesAvailable);
+ }
+ // Reset previous buffer size as it may be requested by the client.
+ setBufferSize(previousBufferSize);
+
+exit:
+ return result;
+}
+
/*
* It normally takes about 20-30 msec to start a stream on the server.
* But the first time can take as much as 200-300 msec. The HW
@@ -393,8 +445,15 @@
prepareBuffersForStart(); // tell subclasses to get ready
aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
- if (result == AAUDIO_ERROR_INVALID_HANDLE) {
- ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ if (result == AAUDIO_ERROR_STANDBY) {
+ // The stream is at standby mode. Need to exit standby before starting the stream.
+ result = exitStandby_l();
+ if (result == AAUDIO_OK) {
+ result = mServiceInterface.startStream(mServiceStreamHandle);
+ }
+ }
+ if (result != AAUDIO_OK) {
+ ALOGD("%s() error = %d, stream was probably stolen", __func__, result);
// Stealing was added in R. Coerce result to improve backward compatibility.
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
@@ -414,6 +473,7 @@
result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
}
if (result != AAUDIO_OK) {
+ // TODO(b/214607638): Do we want to roll back to original state or keep as disconnected?
setState(originalState);
}
return result;