Merge changes I7f8339d8,Idff0c394 into rvc-dev
* changes:
aaudio: steal exclusive streams
aaudio test: test stealing an exclusive stream
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 3db0099..79fa5ed 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -354,6 +354,12 @@
drainTimestampsFromService();
aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ // Stealing was added in R. Coerce result to improve backward compatibility.
+ result = AAUDIO_ERROR_DISCONNECTED;
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ }
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
@@ -397,7 +403,12 @@
if (isDataCallbackSet()
&& (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
mCallbackEnabled.store(false);
- return joinThread(NULL); // may temporarily unlock mStreamLock
+ aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ result = AAUDIO_OK;
+ }
+ return result;
} else {
return AAUDIO_OK;
}
@@ -427,7 +438,12 @@
setState(AAUDIO_STREAM_STATE_STOPPING);
mAtomicInternalTimestamp.clear();
- return mServiceInterface.stopStream(mServiceStreamHandle);
+ result = mServiceInterface.stopStream(mServiceStreamHandle);
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ result = AAUDIO_OK;
+ }
+ return result;
}
aaudio_result_t AudioStreamInternal::registerThread() {
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 73fd896..a6e5f70 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -226,3 +226,15 @@
"libutils",
],
}
+
+cc_test {
+ name: "test_steal_exclusive",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_steal_exclusive.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
new file mode 100644
index 0000000..2a05910
--- /dev/null
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This test starts an exclusive stream.
+ * Then a few seconds later it starts a second exclusive stream.
+ * The first stream should get stolen and they should both end up
+ * as SHARED streams.
+ * The test will print PASS or FAIL.
+ *
+ * If you plug in a headset during the test then you can get them to both
+ * open at almost the same time. This can result in a race condition.
+ * Both streams may try to automatically reopen their streams in EXCLUSIVE mode.
+ * The first stream will have its EXCLUSIVE stream stolen by the second stream.
+ * It will usually get disconnected between its Open and Start calls.
+ * This can also occur in normal use. But is unlikely because the window is very narrow.
+ * In this case, where two streams are responding to the same disconnect event,
+ * it will usually happen.
+ *
+ * Because the stream has not started, this condition will not trigger an onError callback.
+ * But the stream will get an error returned from AAudioStream_requestStart().
+ * The test uses this result to trigger a retry in the onError callback.
+ * That is the best practice for any app restarting a stream.
+ *
+ * You should see that both streams are advancing after the disconnect.
+ *
+ * The headset can connect using a 3.5 mm jack, or USB-C or Bluetooth.
+ *
+ * This test can be used with INPUT by using the -i command line option.
+ * Before running the test you will need to enter "adb root" so that
+ * you can have permission to record.
+ * Also the headset needs to have a microphone.
+ * Then the test should behave essentially the same.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <thread>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+#define SOLO_DURATION_MSEC 2000
+#define DUET_DURATION_MSEC 8000
+#define SLEEP_DURATION_MSEC 500
+
+static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
+ return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
+ : ((mode == AAUDIO_SHARING_MODE_SHARED) ? "SHARED"
+ : AAudio_convertResultToText(mode));
+}
+
+static void s_myErrorCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ aaudio_result_t error);
+
+struct AudioEngine {
+ AAudioStream *stream = nullptr;
+ std::thread *thread = nullptr;
+ aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+
+ // These counters are read and written by the callback and the main thread.
+ std::atomic<int32_t> framesRead{};
+ std::atomic<int32_t> framesCalled{};
+ std::atomic<int32_t> callbackCount{};
+
+ void reset() {
+ framesRead.store(0);
+ framesCalled.store(0);
+ callbackCount.store(0);
+ }
+};
+
+// Callback function that fills the audio output buffer.
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void *audioData,
+ int32_t numFrames
+) {
+ (void) audioData;
+ (void) numFrames;
+ AudioEngine *engine = (struct AudioEngine *)userData;
+ engine->callbackCount++;
+
+ engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
+ engine->framesCalled += numFrames;
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
+ aaudio_direction_t direction) {
+ AAudioStreamBuilder *builder = nullptr;
+ engine->direction = direction;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+ if (result != AAUDIO_OK) {
+ printf("AAudio_createStreamBuilder returned %s",
+ AAudio_convertResultToText(result));
+ return result;
+ }
+
+ // Request stream properties.
+ AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+ AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &engine->stream);
+ AAudioStreamBuilder_delete(builder);
+ builder = nullptr;
+ if (result != AAUDIO_OK) {
+ printf("AAudioStreamBuilder_openStream returned %s",
+ AAudio_convertResultToText(result));
+ }
+
+ // See see what kind of stream we actually opened.
+ int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
+ aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
+ printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
+ deviceId,
+ s_sharingModeToText(actualSharingMode));
+
+ return result;
+}
+
+static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
+ aaudio_result_t result = AAUDIO_OK;
+ if (engine->stream != nullptr) {
+ result = AAudioStream_close(engine->stream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_close returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ engine->stream = nullptr;
+ }
+ return result;
+}
+
+static void s_myRestartStreamProc(void *userData) {
+ printf("%s() - restart in separate thread\n", __func__);
+ AudioEngine *engine = (AudioEngine *) userData;
+ int retriesLeft = 1;
+ aaudio_result_t result;
+ do {
+ s_CloseAudioStream(engine);
+ s_OpenAudioStream(engine, engine->direction);
+ // It is possible for the stream to be disconnected, or stolen between the time
+ // it is opened and when it is started. If that happens then try again.
+ // If it was stolen then it should succeed the second time because there will already be
+ // a SHARED stream, which will not get stolen.
+ result = AAudioStream_requestStart(engine->stream);
+ printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
+ AAudio_convertResultToText(result));
+ } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+}
+
+static void s_myErrorCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ aaudio_result_t error) {
+ printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
+ // Handle error on a separate thread.
+ std::thread t(s_myRestartStreamProc, userData);
+ t.detach();
+}
+
+static void s_usage() {
+ printf("test_steal_exclusive [-i]\n");
+ printf(" -i direction INPUT, otherwise OUTPUT\n");
+}
+
+/**
+ * @return 0 is OK, -1 for error
+ */
+static int s_checkEnginePositions(AudioEngine *engine) {
+ if (engine->stream == nullptr) return 0; // race condition with onError procs!
+
+ const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
+ const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
+ const int32_t delta = (int32_t)(framesWritten - framesRead);
+ printf("playing framesRead = %7d, framesWritten = %7d"
+ ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+ (int32_t) framesRead,
+ (int32_t) framesWritten,
+ delta,
+ engine->framesCalled.load(),
+ engine->callbackCount.load()
+ );
+ if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
+ printf("ERROR - delta > capacity\n");
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void) argv;
+ struct AudioEngine victim;
+ struct AudioEngine thief;
+ aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+ aaudio_result_t result = AAUDIO_OK;
+ int errorCount = 0;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("Test Stealing an EXCLUSIVE stream V1.0\n");
+ printf("\n");
+
+ for (int i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (arg[0] == '-') {
+ char option = arg[1];
+ switch (option) {
+ case 'i':
+ direction = AAUDIO_DIRECTION_INPUT;
+ break;
+ default:
+ s_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ } else {
+ s_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ result = s_OpenAudioStream(&victim, direction);
+ if (result != AAUDIO_OK) {
+ printf("s_OpenAudioStream victim returned %s\n",
+ AAudio_convertResultToText(result));
+ errorCount++;
+ }
+ victim.reset();
+
+ // Start stream.
+ result = AAudioStream_requestStart(victim.stream);
+ printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+
+ if (result == AAUDIO_OK) {
+ const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
+ for (int i = watchLoops; i > 0; i--) {
+ errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ usleep(SLEEP_DURATION_MSEC * 1000);
+ }
+ }
+
+ printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
+ result = s_OpenAudioStream(&thief, direction);
+ if (result != AAUDIO_OK) {
+ printf("s_OpenAudioStream victim returned %s\n",
+ AAudio_convertResultToText(result));
+ errorCount++;
+ }
+ thief.reset();
+
+ // Start stream.
+ result = AAudioStream_requestStart(thief.stream);
+ printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+ printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+ if (result == AAUDIO_OK) {
+ const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
+ for (int i = watchLoops; i > 0; i--) {
+ printf("victim: ");
+ errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ printf(" thief: ");
+ errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+ usleep(SLEEP_DURATION_MSEC * 1000);
+ }
+ }
+
+ // Check for PASS/FAIL
+ aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
+ aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
+ printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
+ s_sharingModeToText(victimSharingMode),
+ s_sharingModeToText(thiefSharingMode));
+ if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
+ && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
+ printf("Both modes are SHARED => PASS\n");
+ } else {
+ errorCount++;
+ printf("Both modes should be SHARED => FAIL!!\n");
+ }
+
+ const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
+ const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
+ printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
+ (int)victimFramesRead, (int)thiefFramesRead);
+ if (victimFramesRead > 0 && thiefFramesRead > 0) {
+ printf("Both streams are running => PASS\n");
+ } else {
+ errorCount++;
+ printf("Both streams should be running => FAIL!!\n");
+ }
+
+ result = AAudioStream_requestStop(victim.stream);
+ printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+ result = AAudioStream_requestStop(thief.stream);
+ printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+
+ s_CloseAudioStream(&victim);
+ s_CloseAudioStream(&thief);
+
+ printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
+ printf("test %s\n", errorCount ? "FAILED" : "PASSED");
+
+ return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 82cc90e..c9bf72f 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -76,6 +76,7 @@
result << " ExclusiveFoundCount: " << mExclusiveFoundCount << "\n";
result << " ExclusiveOpenCount: " << mExclusiveOpenCount << "\n";
result << " ExclusiveCloseCount: " << mExclusiveCloseCount << "\n";
+ result << " ExclusiveStolenCount: " << mExclusiveStolenCount << "\n";
result << "\n";
if (isExclusiveLocked) {
@@ -142,7 +143,13 @@
sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
const aaudio::AAudioStreamRequest &request) {
if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
- return openExclusiveEndpoint(audioService, request);
+ sp<AAudioServiceEndpoint> endpointToSteal;
+ sp<AAudioServiceEndpoint> foundEndpoint =
+ openExclusiveEndpoint(audioService, request, endpointToSteal);
+ if (endpointToSteal.get()) {
+ endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource
+ }
+ return foundEndpoint;
} else {
return openSharedEndpoint(audioService, request);
}
@@ -150,7 +157,8 @@
sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
AAudioService &aaudioService,
- const aaudio::AAudioStreamRequest &request) {
+ const aaudio::AAudioStreamRequest &request,
+ sp<AAudioServiceEndpoint> &endpointToSteal) {
std::lock_guard<std::mutex> lock(mExclusiveLock);
@@ -161,18 +169,22 @@
// If we find an existing one then this one cannot be exclusive.
if (endpoint.get() != nullptr) {
- ALOGW("openExclusiveEndpoint() already in use");
- // Already open so do not allow a second stream.
+ if (kStealingEnabled
+ && !endpoint->isForSharing() // not currently SHARED
+ && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
+ ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
+ mExclusiveStolenCount++;
+ endpointToSteal = endpoint;
+ }
return nullptr;
} else {
sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
- ALOGV("openExclusiveEndpoint(), no match so try to open MMAP %p for dev %d",
- endpointMMap.get(), configuration.getDeviceId());
+ ALOGV("%s(), no match so try to open MMAP %p for dev %d",
+ __func__, endpointMMap.get(), configuration.getDeviceId());
endpoint = endpointMMap;
aaudio_result_t result = endpoint->open(request);
if (result != AAUDIO_OK) {
- ALOGV("openExclusiveEndpoint(), open failed");
endpoint.clear();
} else {
mExclusiveStreams.push_back(endpointMMap);
@@ -183,7 +195,9 @@
if (endpoint.get() != nullptr) {
// Increment the reference count under this lock.
endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+ endpoint->setForSharing(request.isSharingModeMatchRequired());
}
+
return endpoint;
}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index ba17853..ae776b1 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -19,6 +19,7 @@
#include <map>
#include <mutex>
+#include <sys/types.h>
#include <utils/Singleton.h>
#include "binding/AAudioServiceMessage.h"
@@ -62,7 +63,8 @@
private:
android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
- const aaudio::AAudioStreamRequest &request);
+ const aaudio::AAudioStreamRequest &request,
+ sp<AAudioServiceEndpoint> &endpointToSteal);
android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
const aaudio::AAudioStreamRequest &request);
@@ -91,11 +93,16 @@
int32_t mExclusiveFoundCount = 0; // number of times we FOUND an exclusive endpoint
int32_t mExclusiveOpenCount = 0; // number of times we OPENED an exclusive endpoint
int32_t mExclusiveCloseCount = 0; // number of times we CLOSED an exclusive endpoint
+ int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint
+
// Same as above but for SHARED endpoints.
int32_t mSharedSearchCount = 0;
int32_t mSharedFoundCount = 0;
int32_t mSharedOpenCount = 0;
int32_t mSharedCloseCount = 0;
+
+ // For easily disabling the stealing of exclusive streams.
+ static constexpr bool kStealingEnabled = true;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index cac7453..ecbcb7e 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -85,6 +85,17 @@
aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
+ // A lock in is used to order the opening of endpoints when an
+ // EXCLUSIVE endpoint is stolen. We want the order to be:
+ // 1) Thread A opens exclusive MMAP endpoint
+ // 2) Thread B wants to open an exclusive MMAP endpoint so it steals the one from A
+ // under this lock.
+ // 3) Thread B opens a shared MMAP endpoint.
+ // 4) Thread A can then get the lock and also open a shared stream.
+ // Without the lock. Thread A might sneak in and reallocate an exclusive stream
+ // before B can open the shared stream.
+ std::unique_lock<std::recursive_mutex> lock(mOpenLock);
+
aaudio_result_t result = AAUDIO_OK;
sp<AAudioServiceStreamBase> serviceStream;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
@@ -139,7 +150,6 @@
return result;
} else {
aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
- ALOGV("openStream(): handle = 0x%08X", handle);
serviceStream->setHandle(handle);
pid_t pid = request.getProcessId();
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
@@ -147,6 +157,7 @@
// Log open in MediaMetrics after we have the handle because we need the handle to
// create the metrics ID.
serviceStream->logOpen(handle);
+ ALOGV("%s(): return handle = 0x%08X", __func__, handle);
return handle;
}
}
@@ -180,7 +191,10 @@
ALOGE("closeStream(0x%0x), illegal stream handle", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
+ return closeStream(serviceStream);
+}
+aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
pid_t pid = serviceStream->getOwnerProcessId();
AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 7167868..6a2ac1f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -55,6 +55,10 @@
aaudio::AAudioStreamConfiguration &configurationOutput)
override;
+ /*
+ * This is called from Binder. It checks for permissions
+ * and converts the handle passed through Binder to a stream pointer.
+ */
aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;
aaudio_result_t getStreamDescription(
@@ -84,8 +88,18 @@
aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
audio_port_handle_t clientHandle) override;
+ // ===============================================================================
+ // The following public methods are only called from the service and NOT by Binder.
+ // ===============================================================================
+
aaudio_result_t disconnectStreamByPortHandle(audio_port_handle_t portHandle);
+ /*
+ * This is only called from within the Service.
+ * It bypasses the permission checks in closeStream(handle).
+ */
+ aaudio_result_t closeStream(sp<aaudio::AAudioServiceStreamBase> serviceStream);
+
private:
/** @return true if the client is the audioserver
@@ -100,8 +114,6 @@
sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
aaudio::aaudio_handle_t streamHandle);
-
-
bool releaseStream(const sp<aaudio::AAudioServiceStreamBase> &serviceStream);
aaudio_result_t checkForPendingClose(const sp<aaudio::AAudioServiceStreamBase> &serviceStream,
@@ -111,6 +123,10 @@
aaudio::AAudioStreamTracker mStreamTracker;
+ // We use a lock to prevent thread A from reopening an exclusive stream
+ // after thread B steals thread A's exclusive MMAP resource stream.
+ std::recursive_mutex mOpenLock;
+
// TODO Extract the priority constants from services/audioflinger/Threads.cpp
// and share them with this code. Look for "kPriorityFastMixer".
static constexpr int32_t kRealTimeAudioPriorityClient = 2;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 647dcf7..b09cbf4 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -27,13 +27,13 @@
#include <utils/Singleton.h>
-#include "AAudioEndpointManager.h"
-#include "AAudioServiceEndpoint.h"
#include "core/AudioStreamBuilder.h"
+
+#include "AAudioEndpointManager.h"
+#include "AAudioClientTracker.h"
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceEndpointShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
@@ -87,16 +87,31 @@
return false;
}
-void AAudioServiceEndpoint::disconnectRegisteredStreams() {
+std::vector<android::sp<AAudioServiceStreamBase>>
+ AAudioServiceEndpoint::disconnectRegisteredStreams() {
+ std::vector<android::sp<AAudioServiceStreamBase>> streamsDisconnected;
std::lock_guard<std::mutex> lock(mLockStreams);
mConnected.store(false);
- for (const auto& stream : mRegisteredStreams) {
- ALOGD("disconnectRegisteredStreams() stop and disconnect port %d",
- stream->getPortHandle());
+ for (const auto &stream : mRegisteredStreams) {
+ ALOGD("%s() - stop and disconnect port %d", __func__, stream->getPortHandle());
stream->stop();
stream->disconnect();
}
- mRegisteredStreams.clear();
+ mRegisteredStreams.swap(streamsDisconnected);
+ return streamsDisconnected;
+}
+
+void AAudioServiceEndpoint::releaseRegisteredStreams() {
+ // List of streams to be closed after we disconnect everything.
+ std::vector<android::sp<AAudioServiceStreamBase>> streamsToClose
+ = disconnectRegisteredStreams();
+
+ // Close outside the lock to avoid recursive locks.
+ AAudioService *aaudioService = AAudioClientTracker::getInstance().getAAudioService();
+ for (const auto& serviceStream : streamsToClose) {
+ ALOGD("%s() - close stream 0x%08X", __func__, serviceStream->getHandle());
+ aaudioService->closeStream(serviceStream);
+ }
}
aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index ad30087..a171cb0 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -111,6 +111,21 @@
static audio_attributes_t getAudioAttributesFrom(const AAudioStreamParameters *params);
+ // Stop, disconnect and release any streams registered on this endpoint.
+ void releaseRegisteredStreams();
+
+ bool isForSharing() const {
+ return mForSharing;
+ }
+
+ /**
+ *
+ * @param flag true if this endpoint is to be shared between multiple streams
+ */
+ void setForSharing(bool flag) {
+ mForSharing = flag;
+ }
+
protected:
/**
@@ -119,7 +134,7 @@
*/
bool isStreamRegistered(audio_port_handle_t portHandle);
- void disconnectRegisteredStreams();
+ std::vector<android::sp<AAudioServiceStreamBase>> disconnectRegisteredStreams();
mutable std::mutex mLockStreams;
std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
@@ -132,7 +147,11 @@
int32_t mOpenCount = 0;
int32_t mRequestedDeviceId = 0;
+ // True if this will be shared by one or more other streams.
+ bool mForSharing = false;
+
std::atomic<bool> mConnected{true};
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index af2710d..9dab770 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -229,7 +229,7 @@
}
aaudio_result_t AAudioServiceEndpointMMAP::close() {
- if (mMmapStream != 0) {
+ if (mMmapStream != nullptr) {
// Needs to be explicitly cleared or CTS will fail but it is not clear why.
mMmapStream.clear();
// Apparently the above close is asynchronous. An attempt to open a new device