Merge "Add tests for TreeHugger Presubmit." into rvc-dev
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index a9aae0b..8f51458 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -3,5 +3,5 @@
user cameraserver
group audio camera input drmrpc
ioprio rt 4
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
rlimit rtprio 10 10
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 38c2750..6cb0c73 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,7 +5,7 @@
group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelock
capabilities BLOCK_SUSPEND
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
onrestart setprop sys.audio.restart.hal 1
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
index 57b4609..e05b12a 100644
--- a/media/bufferpool/2.0/Accessor.cpp
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -117,6 +117,10 @@
Accessor::Impl::createInvalidator();
}
+void Accessor::createEvictor() {
+ Accessor::Impl::createEvictor();
+}
+
// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
Return<void> Accessor::connect(
const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer,
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
index 8d02519..8b43301 100644
--- a/media/bufferpool/2.0/Accessor.h
+++ b/media/bufferpool/2.0/Accessor.h
@@ -187,6 +187,8 @@
static void createInvalidator();
+ static void createEvictor();
+
private:
class Impl;
std::shared_ptr<Impl> mImpl;
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index cb55b07..6111fea 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -39,6 +39,9 @@
static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
static constexpr size_t kMinBufferCountForEviction = 25;
+
+ static constexpr nsecs_t kEvictGranularityNs = 1000000000; // 1 sec
+ static constexpr nsecs_t kEvictDurationNs = 5000000000; // 5 secs
}
// Buffer structure in bufferpool process
@@ -136,7 +139,7 @@
}
#ifdef __ANDROID_VNDK__
-static constexpr uint32_t kSeqIdVndkBit = 1 << 31;
+static constexpr uint32_t kSeqIdVndkBit = 1U << 31;
#else
static constexpr uint32_t kSeqIdVndkBit = 0;
#endif
@@ -146,7 +149,7 @@
Accessor::Impl::Impl(
const std::shared_ptr<BufferPoolAllocator> &allocator)
- : mAllocator(allocator) {}
+ : mAllocator(allocator), mScheduleEvictTs(0) {}
Accessor::Impl::~Impl() {
}
@@ -184,6 +187,7 @@
}
mBufferPool.processStatusMessages();
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
}
return status;
}
@@ -198,6 +202,7 @@
// Since close# will be called after all works are finished, it is OK to
// evict unused buffers.
mBufferPool.cleanUp(true);
+ scheduleEvictIfNeeded();
return ResultStatus::OK;
}
@@ -224,6 +229,7 @@
mBufferPool.handleOwnBuffer(connectionId, *bufferId);
}
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
return status;
}
@@ -249,6 +255,7 @@
}
}
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
return ResultStatus::CRITICAL_ERROR;
}
@@ -891,6 +898,88 @@
}
}
+void Accessor::Impl::evictorThread(
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv) {
+ std::list<const std::weak_ptr<Accessor::Impl>> evictList;
+ while (true) {
+ int expired = 0;
+ int evicted = 0;
+ {
+ nsecs_t now = systemTime();
+ std::unique_lock<std::mutex> lock(mutex);
+ if (accessors.size() == 0) {
+ cv.wait(lock);
+ }
+ auto it = accessors.begin();
+ while (it != accessors.end()) {
+ if (now > (it->second + kEvictDurationNs)) {
+ ++expired;
+ evictList.push_back(it->first);
+ it = accessors.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ // evict idle accessors;
+ for (auto it = evictList.begin(); it != evictList.end(); ++it) {
+ const std::shared_ptr<Accessor::Impl> accessor = it->lock();
+ if (accessor) {
+ accessor->cleanUp(true);
+ ++evicted;
+ }
+ }
+ if (expired > 0) {
+ ALOGD("evictor expired: %d, evicted: %d", expired, evicted);
+ }
+ evictList.clear();
+ ::usleep(kEvictGranularityNs / 1000);
+ }
+}
+
+Accessor::Impl::AccessorEvictor::AccessorEvictor() {
+ std::thread evictor(
+ evictorThread,
+ std::ref(mAccessors),
+ std::ref(mMutex),
+ std::ref(mCv));
+ evictor.detach();
+}
+
+void Accessor::Impl::AccessorEvictor::addAccessor(
+ const std::weak_ptr<Accessor::Impl> &impl, nsecs_t ts) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ bool notify = mAccessors.empty();
+ auto it = mAccessors.find(impl);
+ if (it == mAccessors.end()) {
+ mAccessors.emplace(impl, ts);
+ } else {
+ it->second = ts;
+ }
+ if (notify) {
+ mCv.notify_one();
+ }
+}
+
+std::unique_ptr<Accessor::Impl::AccessorEvictor> Accessor::Impl::sEvictor;
+
+void Accessor::Impl::createEvictor() {
+ if (!sEvictor) {
+ sEvictor = std::make_unique<Accessor::Impl::AccessorEvictor>();
+ }
+}
+
+void Accessor::Impl::scheduleEvictIfNeeded() {
+ nsecs_t now = systemTime();
+
+ if (now > (mScheduleEvictTs + kEvictGranularityNs)) {
+ mScheduleEvictTs = now;
+ sEvictor->addAccessor(shared_from_this(), now);
+ }
+}
+
} // namespace implementation
} // namespace V2_0
} // namespace bufferpool
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 9888be5..cd1b4d0 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -20,6 +20,7 @@
#include <map>
#include <set>
#include <condition_variable>
+#include <utils/Timers.h>
#include "Accessor.h"
namespace android {
@@ -71,6 +72,8 @@
static void createInvalidator();
+ static void createEvictor();
+
private:
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
@@ -78,6 +81,8 @@
const std::shared_ptr<BufferPoolAllocator> mAllocator;
+ nsecs_t mScheduleEvictTs;
+
/**
* Buffer pool implementation.
*
@@ -389,6 +394,25 @@
std::mutex &mutex,
std::condition_variable &cv,
bool &ready);
+
+ struct AccessorEvictor {
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> mAccessors;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+
+ AccessorEvictor();
+ void addAccessor(const std::weak_ptr<Accessor::Impl> &impl, nsecs_t ts);
+ };
+
+ static std::unique_ptr<AccessorEvictor> sEvictor;
+
+ static void evictorThread(
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv);
+
+ void scheduleEvictIfNeeded();
+
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index 87ee4e8..54a20b9 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -484,6 +484,7 @@
sInstance = new ClientManager();
}
Accessor::createInvalidator();
+ Accessor::createEvictor();
return sInstance;
}
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index c1edb98..d7b9e12 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -549,7 +549,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index e4b911d..23104dc 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -544,7 +544,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index c7ca18c..55dd475 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -618,7 +618,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 01d106f..1e4560c 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1406,6 +1406,9 @@
// TODO: convert err into status_t
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
}
+ // Assure buffers are not owned when stop() was called without flush().
+ std::list<std::unique_ptr<C2Work>> flushedWork;
+ mChannel->flush(flushedWork);
{
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index e902b5d..3773528 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1705,8 +1705,8 @@
}
}
- if (!buffer && !flags) {
- ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
+ if (!buffer && !flags && outputFormat == nullptr) {
+ ALOGV("[%s] onWorkDone: nothing to report from the work (%lld)",
mName, work->input.ordinal.frameIndex.peekull());
return true;
}
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 044c4d0..b88e4e8 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -339,7 +339,12 @@
}
void BlockIterator::advance_l() {
- for (;;) {
+ for (int i = 0;; i++) {
+ if (i == 1000) {
+ ALOGE("no block found after %d iterations, stopping", i);
+ mCluster = NULL;
+ break;
+ }
long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry);
ALOGV("GetEntry returned %ld", res);
@@ -809,11 +814,13 @@
int32_t sampleRate;
if (!AMediaFormat_getInt32(trackInfo->mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE,
&sampleRate)) {
+ mbuf->release();
return AMEDIA_ERROR_MALFORMED;
}
int64_t durationUs;
if (!AMediaFormat_getInt64(trackInfo->mMeta, AMEDIAFORMAT_KEY_DURATION,
&durationUs)) {
+ mbuf->release();
return AMEDIA_ERROR_MALFORMED;
}
// TODO: Explore if this can be handled similar to MPEG4 extractor where padding is
@@ -976,6 +983,7 @@
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
+ buffer->release();
clearPendingFrames();
return err;
}
@@ -995,6 +1003,7 @@
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
+ buffer->release();
clearPendingFrames();
return err;
}
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index a838ae6..5165822 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -227,17 +227,17 @@
private:
static const size_t kMaxFrameSize;
- AMediaFormat *mMeta;
- DataSourceHelper *mDataSource;
- off64_t mFirstFramePos;
- uint32_t mFixedHeader;
- off64_t mCurrentPos;
- int64_t mCurrentTimeUs;
- bool mStarted;
- MP3Seeker *mSeeker;
+ AMediaFormat *mMeta = NULL;
+ DataSourceHelper *mDataSource = NULL;
+ off64_t mFirstFramePos = 0;
+ uint32_t mFixedHeader = 0;
+ off64_t mCurrentPos = 0;
+ int64_t mCurrentTimeUs = 0;
+ bool mStarted = false;
+ MP3Seeker *mSeeker = NULL;
- int64_t mBasisTimeUs;
- int64_t mSamplesRead;
+ int64_t mBasisTimeUs = 0;
+ int64_t mSamplesRead = 0;
MP3Source(const MP3Source &);
MP3Source &operator=(const MP3Source &);
@@ -251,11 +251,7 @@
MP3Extractor::MP3Extractor(
DataSourceHelper *source, Mp3Meta *meta)
- : mInitCheck(NO_INIT),
- mDataSource(source),
- mFirstFramePos(-1),
- mFixedHeader(0),
- mSeeker(NULL) {
+ : mDataSource(source) {
off64_t pos = 0;
off64_t post_id3_pos;
@@ -442,6 +438,7 @@
// (8000 samples/sec * 8 bits/byte)) + 1 padding byte/frame = 2881 bytes/frame.
// Set our max frame size to the nearest power of 2 above this size (aka, 4kB)
const size_t MP3Source::kMaxFrameSize = (1 << 12); /* 4096 bytes */
+
MP3Source::MP3Source(
AMediaFormat *meta, DataSourceHelper *source,
off64_t first_frame_pos, uint32_t fixed_header,
@@ -450,12 +447,7 @@
mDataSource(source),
mFirstFramePos(first_frame_pos),
mFixedHeader(fixed_header),
- mCurrentPos(0),
- mCurrentTimeUs(0),
- mStarted(false),
- mSeeker(seeker),
- mBasisTimeUs(0),
- mSamplesRead(0) {
+ mSeeker(seeker) {
}
MP3Source::~MP3Source() {
diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h
index 1e38ab7..a2345da 100644
--- a/media/extractors/mp3/MP3Extractor.h
+++ b/media/extractors/mp3/MP3Extractor.h
@@ -45,13 +45,13 @@
virtual const char * name() { return "MP3Extractor"; }
private:
- status_t mInitCheck;
+ status_t mInitCheck = NO_INIT;
- DataSourceHelper *mDataSource;
- off64_t mFirstFramePos;
- AMediaFormat *mMeta;
- uint32_t mFixedHeader;
- MP3Seeker *mSeeker;
+ DataSourceHelper *mDataSource = NULL;
+ off64_t mFirstFramePos = -1;
+ AMediaFormat *mMeta = NULL;
+ uint32_t mFixedHeader = 0;
+ MP3Seeker *mSeeker = NULL;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);
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/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index b18571f..36cd73b 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -107,7 +107,7 @@
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
status_t ret = remote()->transact(GETFORMAT, data, &reply);
if (ret == NO_ERROR) {
- AutoMutex _l(mLock);
+ AutoMutex _l(mBpLock);
mMetaData = MetaData::createFromParcel(reply);
return mMetaData;
}
@@ -224,7 +224,7 @@
// XXX: could we use this for caching, or does metadata change on the fly?
sp<MetaData> mMetaData;
// ensure synchronize access to mMetaData
- Mutex mLock;
+ Mutex mBpLock;
// Cache all IMemory objects received from MediaExtractor.
// We gc IMemory objects that are no longer active (referenced by a MediaBuffer).
@@ -301,6 +301,7 @@
CHECK_INTERFACE(IMediaSource, data, reply);
mGroup->signalBufferReturned(nullptr);
status_t status = stop();
+ AutoMutex _l(mBnLock);
mIndexCache.reset();
mBuffersSinceStop = 0;
return status;
@@ -340,6 +341,7 @@
&& len == sizeof(opts)
&& data.read((void *)&opts, len) == NO_ERROR;
+ AutoMutex _l(mBnLock);
mGroup->signalBufferReturned(nullptr);
mIndexCache.gc();
size_t inlineTransferSize = 0;
diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h
index f3fa39b..84310f0 100644
--- a/media/libmedia/include/media/IMediaSource.h
+++ b/media/libmedia/include/media/IMediaSource.h
@@ -135,6 +135,7 @@
private:
uint32_t mBuffersSinceStop; // Buffer tracking variable
+ Mutex mBnLock; // to guard readMultiple against concurrent access to the buffer cache
std::unique_ptr<MediaBufferGroup> mGroup;
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
index 679b091..a11f55e 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
@@ -409,7 +409,9 @@
if (!BitstreamRead1Bits(stream)) return PV_FAIL;
/* video_object_layer_width (13 bits) */
- video->displayWidth = video->width = (int) BitstreamReadBits16(stream, 13);
+ tmpvar = BitstreamReadBits16(stream, 13);
+ if (!tmpvar) return PV_FAIL;
+ video->displayWidth = video->width = tmpvar;
/* round up to a multiple of MB_SIZE. 08/09/2000 */
video->width = (video->width + 15) & -16;
@@ -419,7 +421,9 @@
if (!BitstreamRead1Bits(stream)) return PV_FAIL;
/* video_object_layer_height (13 bits) */
- video->displayHeight = video->height = (int) BitstreamReadBits16(stream, 13);
+ tmpvar = BitstreamReadBits16(stream, 13);
+ if (!tmpvar) return PV_FAIL;
+ video->displayHeight = video->height = tmpvar;
/* round up to a multiple of MB_SIZE. 08/09/2000 */
video->height = (video->height + 15) & -16;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 657144c..4bb21fa 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -1153,7 +1153,7 @@
}
const RangeInfo &info = *mRangeInfos.begin();
- if (mBuffer->size() < info.mLength) {
+ if (info.mLength == 0 || mBuffer->size() < info.mLength) {
return NULL;
}
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index ecb75a9..05373c9 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -6,4 +6,4 @@
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk
index 94e4811..680b077 100644
--- a/services/audiopolicy/service/Android.mk
+++ b/services/audiopolicy/service/Android.mk
@@ -44,7 +44,7 @@
LOCAL_MODULE:= libaudiopolicyservice
LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wall -Werror -Wthread-safety
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 738a279..1ec0c5e 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -928,7 +928,10 @@
loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
- loadDeviceProcessingChain(result.parsedConfig->deviceprocess, mDeviceEffects);
+ {
+ Mutex::Autolock _l(mLock);
+ loadDeviceProcessingChain(result.parsedConfig->deviceprocess, mDeviceEffects);
+ }
// Casting from ssize_t to status_t is probably safe, there should not be more than 2^31 errors
return result.nbSkippedElement;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index a15970a..9b61e74 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -58,8 +58,6 @@
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService(),
- mpAudioPolicyDev(NULL),
- mpAudioPolicy(NULL),
mAudioPolicyManager(NULL),
mAudioPolicyClient(NULL),
mPhoneState(AUDIO_MODE_INVALID),
@@ -78,21 +76,19 @@
mAudioPolicyClient = new AudioPolicyClient(this);
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
-
- mSupportedSystemUsages = std::vector<audio_usage_t> {};
}
// load audio processing modules
- sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
+ sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();
+ sp<UidPolicy> uidPolicy = new UidPolicy(this);
+ sp<SensorPrivacyPolicy> sensorPrivacyPolicy = new SensorPrivacyPolicy(this);
{
Mutex::Autolock _l(mLock);
mAudioPolicyEffects = audioPolicyEffects;
+ mUidPolicy = uidPolicy;
+ mSensorPrivacyPolicy = sensorPrivacyPolicy;
}
-
- mUidPolicy = new UidPolicy(this);
- mUidPolicy->registerSelf();
-
- mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
- mSensorPrivacyPolicy->registerSelf();
+ uidPolicy->registerSelf();
+ sensorPrivacyPolicy->registerSelf();
}
AudioPolicyService::~AudioPolicyService()
@@ -107,9 +103,9 @@
mAudioPolicyEffects.clear();
mUidPolicy->unregisterSelf();
- mUidPolicy.clear();
-
mSensorPrivacyPolicy->unregisterSelf();
+
+ mUidPolicy.clear();
mSensorPrivacyPolicy.clear();
}
@@ -172,20 +168,20 @@
// removeNotificationClient() is called when the client process dies.
void AudioPolicyService::removeNotificationClient(uid_t uid, pid_t pid)
{
+ bool hasSameUid = false;
{
Mutex::Autolock _l(mNotificationClientsLock);
int64_t token = ((int64_t)uid<<32) | pid;
mNotificationClients.removeItem(token);
- }
- {
- Mutex::Autolock _l(mLock);
- bool hasSameUid = false;
for (size_t i = 0; i < mNotificationClients.size(); i++) {
if (mNotificationClients.valueAt(i)->uid() == uid) {
hasSameUid = true;
break;
}
}
+ }
+ {
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager && !hasSameUid) {
// called from binder death notification: no need to clear caller identity
mAudioPolicyManager->releaseResourcesForUid(uid);
@@ -381,10 +377,14 @@
IPCThreadState::self()->getCallingPid());
}
-static bool dumpTryLock(Mutex& mutex)
+static bool dumpTryLock(Mutex& mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
{
- status_t err = mutex.timedLock(kDumpLockTimeoutNs);
- return err == NO_ERROR;
+ return mutex.timedLock(kDumpLockTimeoutNs) == NO_ERROR;
+}
+
+static void dumpReleaseLock(Mutex& mutex, bool locked) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
+{
+ if (locked) mutex.unlock();
}
status_t AudioPolicyService::dumpInternals(int fd)
@@ -564,7 +564,7 @@
bool isTopOrLatestSensitive = topSensitiveActive == nullptr ?
false : current->uid == topSensitiveActive->uid;
- auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) {
+ auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) REQUIRES(mLock) {
bool canCaptureCall = recordClient->canCaptureOutput;
bool canCaptureCommunication = recordClient->canCaptureOutput
|| recordClient->uid == mPhoneStateOwnerUid
@@ -703,7 +703,7 @@
if (!dumpAllowed()) {
dumpPermissionDenial(fd);
} else {
- bool locked = dumpTryLock(mLock);
+ const bool locked = dumpTryLock(mLock);
if (!locked) {
String8 result(kDeadlockedString);
write(fd, result.string(), result.size());
@@ -720,7 +720,7 @@
mPackageManager.dump(fd);
- if (locked) mLock.unlock();
+ dumpReleaseLock(mLock, locked);
}
return NO_ERROR;
}
@@ -839,8 +839,16 @@
return BAD_VALUE;
}
- mUidPolicy->addOverrideUid(uid, active);
- return NO_ERROR;
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
+ }
+ if (uidPolicy) {
+ uidPolicy->addOverrideUid(uid, active);
+ return NO_ERROR;
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::handleResetUidState(Vector<String16>& args, int err) {
@@ -860,8 +868,16 @@
return BAD_VALUE;
}
- mUidPolicy->removeOverrideUid(uid);
- return NO_ERROR;
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
+ }
+ if (uidPolicy) {
+ uidPolicy->removeOverrideUid(uid);
+ return NO_ERROR;
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::handleGetUidState(Vector<String16>& args, int out, int err) {
@@ -881,11 +897,15 @@
return BAD_VALUE;
}
- if (mUidPolicy->isUidActive(uid)) {
- return dprintf(out, "active\n");
- } else {
- return dprintf(out, "idle\n");
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
}
+ if (uidPolicy) {
+ return dprintf(out, uidPolicy->isUidActive(uid) ? "active\n" : "idle\n");
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::printHelp(int out) {
@@ -958,7 +978,7 @@
}
}
ActivityManager am;
- bool active = am.isUidActiveOrForeground(uid, String16("audioserver"));
+ bool active = am.isUidActive(uid, String16("audioserver"));
{
Mutex::Autolock _l(mLock);
mCachedUids.insert(std::pair<uid_t,
@@ -1003,7 +1023,7 @@
}
}
ActivityManager am;
- bool active = am.isUidActiveOrForeground(uid, String16("audioserver"));
+ bool active = am.isUidActive(uid, String16("audioserver"));
int state = ActivityManager::PROCESS_STATE_UNKNOWN;
if (active) {
state = am.getUidProcessState(uid, String16("audioserver"));
@@ -1402,7 +1422,7 @@
result.append(buffer);
write(fd, result.string(), result.size());
- bool locked = dumpTryLock(mLock);
+ const bool locked = dumpTryLock(mLock);
if (!locked) {
String8 result2(kCmdDeadlockedString);
write(fd, result2.string(), result2.size());
@@ -1425,7 +1445,7 @@
write(fd, result.string(), result.size());
- if (locked) mLock.unlock();
+ dumpReleaseLock(mLock, locked);
return NO_ERROR;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index f77a481..869a963 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOPOLICYSERVICE_H
#define ANDROID_AUDIOPOLICYSERVICE_H
+#include <android-base/thread_annotations.h>
#include <cutils/misc.h>
#include <cutils/config_utils.h>
#include <cutils/compiler.h>
@@ -330,13 +331,13 @@
AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
- status_t dumpInternals(int fd);
+ status_t dumpInternals(int fd) REQUIRES(mLock);
// Handles binder shell commands
virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
// Sets whether the given UID records only silence
- virtual void setAppState_l(audio_port_handle_t portId, app_state_t state);
+ virtual void setAppState_l(audio_port_handle_t portId, app_state_t state) REQUIRES(mLock);
// Overrides the UID state as if it is idle
status_t handleSetUidState(Vector<String16>& args, int err);
@@ -361,9 +362,9 @@
status_t validateUsage(audio_usage_t usage, pid_t pid, uid_t uid);
void updateUidStates();
- void updateUidStates_l();
+ void updateUidStates_l() REQUIRES(mLock);
- void silenceAllRecordings_l();
+ void silenceAllRecordings_l() REQUIRES(mLock);
static bool isVirtualSource(audio_source_t source);
@@ -420,13 +421,13 @@
wp<AudioPolicyService> mService;
Mutex mLock;
ActivityManager mAm;
- bool mObserverRegistered;
+ bool mObserverRegistered = false;
std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids;
std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids;
- uid_t mAssistantUid;
+ uid_t mAssistantUid = -1;
std::vector<uid_t> mA11yUids;
- uid_t mCurrentImeUid;
- bool mRttEnabled;
+ uid_t mCurrentImeUid = -1;
+ bool mRttEnabled = false;
};
// If sensor privacy is enabled then all apps, including those that are active, should be
@@ -447,7 +448,7 @@
private:
wp<AudioPolicyService> mService;
- std::atomic_bool mSensorPrivacyEnabled;
+ std::atomic_bool mSensorPrivacyEnabled = false;
};
// Thread used to send audio config commands to audio flinger
@@ -880,26 +881,27 @@
// and possibly back in to audio policy service and acquire mEffectsLock.
sp<AudioCommandThread> mAudioCommandThread; // audio commands thread
sp<AudioCommandThread> mOutputCommandThread; // process stop and release output
- struct audio_policy_device *mpAudioPolicyDev;
- struct audio_policy *mpAudioPolicy;
AudioPolicyInterface *mAudioPolicyManager;
AudioPolicyClient *mAudioPolicyClient;
std::vector<audio_usage_t> mSupportedSystemUsages;
- DefaultKeyedVector< int64_t, sp<NotificationClient> > mNotificationClients;
- Mutex mNotificationClientsLock; // protects mNotificationClients
+ Mutex mNotificationClientsLock;
+ DefaultKeyedVector<int64_t, sp<NotificationClient>> mNotificationClients
+ GUARDED_BY(mNotificationClientsLock);
// Manage all effects configured in audio_effects.conf
// never hold AudioPolicyService::mLock when calling AudioPolicyEffects methods as
// those can call back into AudioPolicyService methods and try to acquire the mutex
- sp<AudioPolicyEffects> mAudioPolicyEffects;
- audio_mode_t mPhoneState;
- uid_t mPhoneStateOwnerUid;
+ sp<AudioPolicyEffects> mAudioPolicyEffects GUARDED_BY(mLock);
+ audio_mode_t mPhoneState GUARDED_BY(mLock);
+ uid_t mPhoneStateOwnerUid GUARDED_BY(mLock);
- sp<UidPolicy> mUidPolicy;
- sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
+ sp<UidPolicy> mUidPolicy GUARDED_BY(mLock);
+ sp<SensorPrivacyPolicy> mSensorPrivacyPolicy GUARDED_BY(mLock);
- DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> > mAudioRecordClients;
- DefaultKeyedVector< audio_port_handle_t, sp<AudioPlaybackClient> > mAudioPlaybackClients;
+ DefaultKeyedVector<audio_port_handle_t, sp<AudioRecordClient>> mAudioRecordClients
+ GUARDED_BY(mLock);
+ DefaultKeyedVector<audio_port_handle_t, sp<AudioPlaybackClient>> mAudioPlaybackClients
+ GUARDED_BY(mLock);
MediaPackageManager mPackageManager; // To check allowPlaybackCapture
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index f92d673..c9d2c68 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -3192,9 +3192,7 @@
// some polling which should happen pretty rarely anyway as the race is hard
// to hit.
active = mActiveUids.find(uid) != mActiveUids.end();
- if (!active) {
- active = am.isUidActiveOrForeground(uid, callingPackage);
- }
+ if (!active) active = am.isUidActive(uid, callingPackage);
if (active) {
break;
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 10b653e..32d118d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -949,6 +949,51 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addPreCorrectionActiveArraySize() {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+
+ auto activeArraySize = c.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ auto preCorrectionActiveArraySize = c.find(
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ if (activeArraySize.count == 4 && preCorrectionActiveArraySize.count == 0) {
+ std::vector<int32_t> preCorrectionArray(
+ activeArraySize.data.i32, activeArraySize.data.i32+4);
+ res = c.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ preCorrectionArray.data(), 4);
+ if (res != OK) {
+ ALOGE("%s: Failed to add ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ } else {
+ return res;
+ }
+
+ auto charTags = c.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ bool hasPreCorrectionActiveArraySize = std::find(charTags.data.i32,
+ charTags.data.i32 + charTags.count,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE) !=
+ (charTags.data.i32 + charTags.count);
+ if (!hasPreCorrectionActiveArraySize) {
+ std::vector<int32_t> supportedCharTags;
+ supportedCharTags.reserve(charTags.count + 1);
+ supportedCharTags.insert(supportedCharTags.end(), charTags.data.i32,
+ charTags.data.i32 + charTags.count);
+ supportedCharTags.push_back(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+
+ res = c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedCharTags.data(),
+ supportedCharTags.size());
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
status_t res = OK;
@@ -2254,7 +2299,11 @@
ALOGE("%s: Unable to add default SCALER_ROTATE_AND_CROP tags: %s (%d)", __FUNCTION__,
strerror(-res), res);
}
-
+ res = addPreCorrectionActiveArraySize();
+ if (OK != res) {
+ ALOGE("%s: Unable to add PRE_CORRECTION_ACTIVE_ARRAY_SIZE: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ }
res = camera3::ZoomRatioMapper::overrideZoomRatioTags(
&mCameraCharacteristics, &mSupportNativeZoomRatio);
if (OK != res) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 50044d8..25d3639 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -566,6 +566,7 @@
status_t addDynamicDepthTags();
status_t deriveHeicTags();
status_t addRotateCropTags();
+ status_t addPreCorrectionActiveArraySize();
static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
android_pixel_format_t format,
diff --git a/services/mediametrics/AnalyticsActions.h b/services/mediametrics/AnalyticsActions.h
index 0151134..897e567 100644
--- a/services/mediametrics/AnalyticsActions.h
+++ b/services/mediametrics/AnalyticsActions.h
@@ -78,8 +78,8 @@
template <typename T, typename U, typename A>
void addAction(T&& url, U&& value, A&& action) {
std::lock_guard l(mLock);
- mFilters[ { std::forward<T>(url), std::forward<U>(value) } ]
- = std::forward<A>(action);
+ mFilters.emplace(Trigger{ std::forward<T>(url), std::forward<U>(value) },
+ std::forward<A>(action));
}
// TODO: remove an action.
@@ -94,36 +94,15 @@
std::vector<Action> actions;
std::lock_guard l(mLock);
- // Essentially the code looks like this:
- /*
- for (auto &[trigger, action] : mFilters) {
- if (isMatch(trigger, item)) {
- actions.push_back(action);
- }
- }
- */
-
- // Optimization: there should only be one match for a non-wildcard url.
- auto it = mFilters.upper_bound( {item->getKey(), std::monostate{} });
- if (it != mFilters.end()) {
- const auto &[trigger, action] = *it;
- if (isMatch(trigger, item)) {
+ for (const auto &[trigger, action] : mFilters) {
+ if (isWildcardMatch(trigger, item) ==
+ mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
actions.push_back(action);
}
}
- // Optimization: for wildcard URLs we go backwards until there is no
- // match with the prefix before the wildcard.
- while (it != mFilters.begin()) { // this walks backwards, cannot start at begin.
- const auto &[trigger, action] = *--it; // look backwards
- int ret = isWildcardMatch(trigger, item);
- if (ret == mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
- actions.push_back(action); // match found.
- } else if (ret == mediametrics::Item::RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD) {
- break; // no match before wildcard.
- }
- // a wildcard was encountered when matching prefix, so we should check again.
- }
+ // TODO: Optimize for prefix search and wildcarding.
+
return actions;
}
@@ -145,7 +124,9 @@
}
mutable std::mutex mLock;
- std::map<Trigger, Action> mFilters GUARDED_BY(mLock);
+
+ using FilterType = std::multimap<Trigger, Action>;
+ FilterType mFilters GUARDED_BY(mLock);
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index fb4022e..8e5bc79 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -76,5 +76,6 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
],
}
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 3f9a42f..fe3a34d 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -19,8 +19,12 @@
#include <utils/Log.h>
#include "AudioAnalytics.h"
+#include "MediaMetricsService.h" // package info
+#include <audio_utils/clock.h> // clock conversions
+#include <statslog.h> // statsd
-#include <audio_utils/clock.h> // clock conversions
+// Enable for testing of delivery to statsd
+// #define STATSD
namespace android::mediametrics {
@@ -87,11 +91,53 @@
// report this for Bluetooth
}
}));
+
+ // Handle device use thread statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceUse.endAudioIntervalGroup(item, false /* isTrack */);
+ }));
+
+ // Handle device use track statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceUse.endAudioIntervalGroup(item, true /* isTrack */);
+ }));
+
+ // Handle device routing statistics
+
+ // We track connections (not disconnections) for the time to connect.
+ // TODO: consider BT requests in their A2dp service
+ // AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+ // AudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+ // AudioDeviceBroker.postA2dpActiveDeviceChange
+ mActions.addAction(
+ "audio.device.a2dp.state",
+ std::string("connected"),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceConnection.a2dpConnected(item);
+ }));
+ // If audio is active, we expect to see a createAudioPatch after the device is connected.
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string("createAudioPatch"),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceConnection.createPatch(item);
+ }));
}
AudioAnalytics::~AudioAnalytics()
{
ALOGD("%s", __func__);
+ mTimedAction.quit(); // ensure no deferred access during destructor.
}
status_t AudioAnalytics::submit(
@@ -151,4 +197,220 @@
return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(threadId_int32);
}
+// DeviceUse helper class.
+void AudioAnalytics::DeviceUse::endAudioIntervalGroup(
+ const std::shared_ptr<const android::mediametrics::Item> &item, bool isTrack) const {
+ const std::string& key = item->getKey();
+ const std::string id = key.substr(
+ (isTrack ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+ : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD))
+ - 1);
+ // deliver statistics
+ int64_t deviceTimeNs = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs);
+ std::string encoding;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ENCODING, &encoding);
+ int32_t frameCount = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
+ int32_t intervalCount = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
+ std::string outputDevices;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+ int32_t sampleRate = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
+ int32_t underrun = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
+
+ // Get connected device name if from bluetooth.
+ bool isBluetooth = false;
+ std::string name;
+ if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
+ isBluetooth = true;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &name);
+ }
+
+ // We may have several devices. We only list the first device.
+ // TODO: consider whether we should list all the devices separated by |
+ std::string firstDevice = "unknown";
+ auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(outputDevices);
+ if (devaddrvec.size() != 0) {
+ firstDevice = devaddrvec[0].first;
+ // DO NOT show the address.
+ }
+
+ if (isTrack) {
+ std::string callerName;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
+ std::string contentType;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentType);
+ double deviceLatencyMs = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICELATENCYMS, &deviceLatencyMs);
+ double deviceStartupMs = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICESTARTUPMS, &deviceStartupMs);
+ double deviceVolume = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume);
+ std::string packageName;
+ int64_t versionCode = 0;
+ int32_t uid = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
+ if (uid != -1) {
+ std::tie(packageName, versionCode) =
+ MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+ }
+ double playbackPitch = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PLAYBACK_PITCH, &playbackPitch);
+ double playbackSpeed = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PLAYBACK_SPEED, &playbackSpeed);
+ int32_t selectedDeviceId = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
+
+ std::string usage;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_USAGE, &usage);
+
+ ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%s "
+ "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+ "sampleRate:%d underrun:%d "
+ "callerName:%s contentType:%s "
+ "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf"
+ "packageName:%s playbackPitch:%lf playbackSpeed:%lf "
+ "selectedDevceId:%d usage:%s",
+ key.c_str(), id.c_str(), firstDevice.c_str(), name.c_str(),
+ (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+ sampleRate, underrun,
+ callerName.c_str(), contentType.c_str(),
+ deviceLatencyMs, deviceStartupMs, deviceVolume,
+ packageName.c_str(), playbackPitch, playbackSpeed,
+ selectedDeviceId, usage.c_str());
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , firstDevice.c_str()
+ , name.c_str()
+ , deviceTimeNs
+ , encoding.c_str()
+ , frameCount
+ , intervalCount
+ , sampleRate
+ , underrun
+
+ , packageName.c_str()
+ , (float)deviceLatencyMs
+ , (float)deviceStartupMs
+ , (float)deviceVolume
+ , selectedDeviceId
+ , usage.c_str()
+ , contentType.c_str()
+ , callerName.c_str()
+ );
+ }
+#endif
+ } else {
+
+ std::string flags;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FLAGS, &flags);
+
+ ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%s "
+ "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+ "sampleRate:%d underrun:%d "
+ "flags:%s",
+ key.c_str(), id.c_str(), firstDevice.c_str(), name.c_str(),
+ (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+ sampleRate, underrun,
+ flags.c_str());
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , firstDevice.c_str()
+ , name.c_str()
+ , deviceTimeNs
+ , encoding.c_str()
+ , frameCount
+ , intervalCount
+ , sampleRate
+ , underrun
+ );
+ }
+#endif
+ }
+
+ // Report this as needed.
+ if (isBluetooth) {
+ // report this for Bluetooth
+ }
+}
+
+// DeviceConnection helper class.
+void AudioAnalytics::DeviceConnection::a2dpConnected(
+ const std::shared_ptr<const android::mediametrics::Item> &item) {
+ const std::string& key = item->getKey();
+
+ const int64_t connectedAtNs = item->getTimestamp();
+ {
+ std::lock_guard l(mLock);
+ mA2dpTimeConnectedNs = connectedAtNs;
+ ++mA2dpConnectedAttempts;
+ }
+ std::string name;
+ item->get(AMEDIAMETRICS_PROP_NAME, &name);
+ ALOGD("(key=%s) a2dp connected device:%s "
+ "connectedAtNs:%lld",
+ key.c_str(), name.c_str(),
+ (long long)connectedAtNs);
+ // Note - we need to be able to cancel a timed event
+ mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
+ // This sets the time we were connected. Now we look for the delta in the future.
+}
+
+void AudioAnalytics::DeviceConnection::createPatch(
+ const std::shared_ptr<const android::mediametrics::Item> &item) {
+ std::lock_guard l(mLock);
+ if (mA2dpTimeConnectedNs == 0) return; // ignore
+ const std::string& key = item->getKey();
+ std::string outputDevices;
+ item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+ if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
+ // TODO compare address
+ const int64_t timeDiff = item->getTimestamp() - mA2dpTimeConnectedNs;
+ ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
+ mA2dpTimeConnectedNs = 0; // reset counter.
+ ++mA2dpConnectedSuccesses;
+ }
+}
+
+void AudioAnalytics::DeviceConnection::expire() {
+ std::lock_guard l(mLock);
+ if (mA2dpTimeConnectedNs == 0) return; // ignore
+
+ // An expiration may occur because there is no audio playing.
+ // TODO: disambiguate this case.
+ ALOGD("A2DP device connection expired");
+ ++mA2dpConnectedFailures; // this is not a true failure.
+ mA2dpTimeConnectedNs = 0;
+}
+
} // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index ba4c3f2..eb9c228 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -16,8 +16,10 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include "AnalyticsActions.h"
#include "AnalyticsState.h"
+#include "TimedAction.h"
#include "Wrap.h"
namespace android::mediametrics {
@@ -89,6 +91,8 @@
*/
std::string getThreadFromTrack(const std::string& track) const;
+ const bool mDeliverStatistics __unused = true;
+
// Actions is individually locked
AnalyticsActions mActions;
@@ -97,6 +101,54 @@
SharedPtrWrap<AnalyticsState> mAnalyticsState;
SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
+
+ TimedAction mTimedAction; // locked internally
+
+ // DeviceUse is a nested class which handles audio device usage accounting.
+ // We define this class at the end to ensure prior variables all properly constructed.
+ // TODO: Track / Thread interaction
+ // TODO: Consider statistics aggregation.
+ class DeviceUse {
+ public:
+ explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
+
+ // Called every time an endAudioIntervalGroup message is received.
+ void endAudioIntervalGroup(
+ const std::shared_ptr<const android::mediametrics::Item> &item,
+ bool isTrack) const;
+ private:
+ AudioAnalytics &mAudioAnalytics;
+ } mDeviceUse{*this};
+
+ // DeviceConnected is a nested class which handles audio device connection
+ // We define this class at the end to ensure prior variables all properly constructed.
+ // TODO: Track / Thread interaction
+ // TODO: Consider statistics aggregation.
+ class DeviceConnection {
+ public:
+ explicit DeviceConnection(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics{audioAnalytics} {}
+
+ // Called every time an endAudioIntervalGroup message is received.
+ void a2dpConnected(
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // Called when we have an AudioFlinger createPatch
+ void createPatch(
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // When the timer expires.
+ void expire();
+
+ private:
+ AudioAnalytics &mAudioAnalytics;
+
+ mutable std::mutex mLock;
+ int64_t mA2dpTimeConnectedNs GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectedAttempts GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectedSuccesses GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectedFailures GUARDED_BY(mLock) = 0;
+ } mDeviceConnection{*this};
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 4b84bea..3b3dc3e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -78,6 +78,74 @@
}
}
+/* static */
+std::pair<std::string, int64_t>
+MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid_t uid) {
+ // Meyer's singleton, initialized on first access.
+ // mUidInfo is locked internally.
+ static mediautils::UidInfo uidInfo;
+
+ // get info.
+ mediautils::UidInfo::Info info = uidInfo.getInfo(uid);
+ if (useUidForPackage(info.package, info.installer)) {
+ return { std::to_string(uid), /* versionCode */ 0 };
+ } else {
+ return { info.package, info.versionCode };
+ }
+}
+
+/* static */
+std::string MediaMetricsService::tokenizer(std::string::const_iterator& it,
+ const std::string::const_iterator& end, const char *reserved) {
+ // consume leading white space
+ for (; it != end && std::isspace(*it); ++it);
+ if (it == end) return {};
+
+ auto start = it;
+ // parse until we hit a reserved keyword or space
+ if (strchr(reserved, *it)) return {start, ++it};
+ for (;;) {
+ ++it;
+ if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
+ }
+}
+
+/* static */
+std::vector<std::pair<std::string, std::string>>
+MediaMetricsService::getDeviceAddressPairs(const std::string& devices) {
+ std::vector<std::pair<std::string, std::string>> result;
+
+ // Currently, the device format is EXACTLY
+ // (device1, addr1)|(device2, addr2)|...
+
+ static constexpr char delim[] = "()|,";
+ for (auto it = devices.begin(); ; ) {
+ auto token = tokenizer(it, devices.end(), delim);
+ if (token != "(") return result;
+
+ auto device = tokenizer(it, devices.end(), delim);
+ if (device.empty() || !std::isalnum(device[0])) return result;
+
+ token = tokenizer(it, devices.end(), delim);
+ if (token != ",") return result;
+
+ // special handling here for empty addresses
+ auto address = tokenizer(it, devices.end(), delim);
+ if (address.empty() || !std::isalnum(device[0])) return result;
+ if (address == ")") { // no address, just the ")"
+ address.clear();
+ } else {
+ token = tokenizer(it, devices.end(), delim);
+ if (token != ")") return result;
+ }
+
+ result.emplace_back(std::move(device), std::move(address));
+
+ token = tokenizer(it, devices.end(), delim);
+ if (token != "|") return result; // this includes end of string detection
+ }
+}
+
MediaMetricsService::MediaMetricsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
@@ -136,16 +204,10 @@
// Overwrite package name and version if the caller was untrusted or empty
if (!isTrusted || item->getPkgName().empty()) {
const uid_t uid = item->getUid();
- mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
- if (useUidForPackage(info.package, info.installer)) {
- // remove uid information of unknown installed packages.
- // TODO: perhaps this can be done just before uploading to Westworld.
- item->setPkgName(std::to_string(uid));
- item->setPkgVersionCode(0);
- } else {
- item->setPkgName(info.package);
- item->setPkgVersionCode(info.versionCode);
- }
+ const auto [ pkgName, version ] =
+ MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+ item->setPkgName(pkgName);
+ item->setPkgVersionCode(version);
}
ALOGV("%s: isTrusted:%d given uid %d; sanitized uid: %d sanitized pkg: %s "
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index faba197..b8eb267 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -69,6 +69,28 @@
*/
static bool useUidForPackage(const std::string& package, const std::string& installer);
+ /**
+ * Returns a std::pair of packageName and versionCode for a given uid.
+ *
+ * The value is sanitized - i.e. if the result is not approved to send,
+ * we use the uid as a string and a version code of 0.
+ */
+ static std::pair<std::string, int64_t> getSanitizedPackageNameAndVersionCode(uid_t uid);
+
+ /**
+ * Return string tokens from iterator, separated by spaces and reserved chars.
+ */
+ static std::string tokenizer(std::string::const_iterator& it,
+ const std::string::const_iterator& end, const char *reserved);
+
+ /**
+ * Parse the devices string and return a vector of device address pairs.
+ *
+ * A failure to parse returns early with the contents that were able to be parsed.
+ */
+ static std::vector<std::pair<std::string, std::string>>
+ getDeviceAddressPairs(const std::string &devices);
+
protected:
// Internal call where release is true if ownership of item is transferred
@@ -100,8 +122,6 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
- mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
-
mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
std::mutex mLock;
diff --git a/services/mediametrics/TimedAction.h b/services/mediametrics/TimedAction.h
new file mode 100644
index 0000000..c7ef585
--- /dev/null
+++ b/services/mediametrics/TimedAction.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <map>
+#include <mutex>
+#include <thread>
+
+namespace android::mediametrics {
+
+class TimedAction {
+public:
+ TimedAction() : mThread{[this](){threadLoop();}} {}
+
+ ~TimedAction() {
+ quit();
+ }
+
+ // TODO: return a handle for cancelling the action?
+ template <typename T> // T is in units of std::chrono::duration.
+ void postIn(const T& time, std::function<void()> f) {
+ postAt(std::chrono::steady_clock::now() + time, f);
+ }
+
+ template <typename T> // T is in units of std::chrono::time_point
+ void postAt(const T& targetTime, std::function<void()> f) {
+ std::lock_guard l(mLock);
+ if (mQuit) return;
+ if (mMap.empty() || targetTime < mMap.begin()->first) {
+ mMap.emplace_hint(mMap.begin(), targetTime, std::move(f));
+ mCondition.notify_one();
+ } else {
+ mMap.emplace(targetTime, std::move(f));
+ }
+ }
+
+ void clear() {
+ std::lock_guard l(mLock);
+ mMap.clear();
+ }
+
+ void quit() {
+ {
+ std::lock_guard l(mLock);
+ if (mQuit) return;
+ mQuit = true;
+ mMap.clear();
+ mCondition.notify_all();
+ }
+ mThread.join();
+ }
+
+ size_t size() const {
+ std::lock_guard l(mLock);
+ return mMap.size();
+ }
+
+private:
+ void threadLoop() NO_THREAD_SAFETY_ANALYSIS { // thread safety doesn't cover unique_lock
+ std::unique_lock l(mLock);
+ while (!mQuit) {
+ auto sleepUntilTime = std::chrono::time_point<std::chrono::steady_clock>::max();
+ if (!mMap.empty()) {
+ sleepUntilTime = mMap.begin()->first;
+ if (sleepUntilTime <= std::chrono::steady_clock::now()) {
+ auto node = mMap.extract(mMap.begin()); // removes from mMap.
+ l.unlock();
+ node.mapped()();
+ l.lock();
+ continue;
+ }
+ }
+ mCondition.wait_until(l, sleepUntilTime);
+ }
+ }
+
+ mutable std::mutex mLock;
+ std::condition_variable mCondition GUARDED_BY(mLock);
+ bool mQuit GUARDED_BY(mLock) = false;
+ std::multimap<std::chrono::time_point<std::chrono::steady_clock>, std::function<void()>>
+ mMap GUARDED_BY(mLock); // multiple functions could execute at the same time.
+
+ // needs to be initialized after the variables above, done in constructor initializer list.
+ std::thread mThread;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/mediametrics.rc b/services/mediametrics/mediametrics.rc
index 1efde5e..2a6c817 100644
--- a/services/mediametrics/mediametrics.rc
+++ b/services/mediametrics/mediametrics.rc
@@ -3,4 +3,4 @@
user media
group media
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index cf0dceb..b465ecd 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -883,6 +883,48 @@
}
}
+TEST(mediametrics_tests, device_parsing) {
+ auto devaddr = android::MediaMetricsService::getDeviceAddressPairs("(DEVICE, )");
+ ASSERT_EQ((size_t)1, devaddr.size());
+ ASSERT_EQ("DEVICE", devaddr[0].first);
+ ASSERT_EQ("", devaddr[0].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ "(DEVICE1, A)|(D, ADDRB)");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("DEVICE1", devaddr[0].first);
+ ASSERT_EQ("A", devaddr[0].second);
+ ASSERT_EQ("D", devaddr[1].first);
+ ASSERT_EQ("ADDRB", devaddr[1].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ "(A,B)|(C,D)");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("A", devaddr[0].first);
+ ASSERT_EQ("B", devaddr[0].second);
+ ASSERT_EQ("C", devaddr[1].first);
+ ASSERT_EQ("D", devaddr[1].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ " ( A1 , B ) | ( C , D2 ) ");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("A1", devaddr[0].first);
+ ASSERT_EQ("B", devaddr[0].second);
+ ASSERT_EQ("C", devaddr[1].first);
+ ASSERT_EQ("D2", devaddr[1].second);
+}
+
+TEST(mediametrics_tests, timed_action) {
+ android::mediametrics::TimedAction timedAction;
+ std::atomic_int value1 = 0;
+
+ timedAction.postIn(std::chrono::seconds(0), [&value1] { ++value1; });
+ timedAction.postIn(std::chrono::seconds(1000), [&value1] { ++value1; });
+ usleep(100000);
+ ASSERT_EQ(1, value1);
+ ASSERT_EQ((size_t)1, timedAction.size());
+}
+
#if 0
// Stress test code for garbage collection, you need to enable AID_SHELL as trusted to run
// in MediaMetricsService.cpp.
diff --git a/services/mediatranscoding/mediatranscoding.rc b/services/mediatranscoding/mediatranscoding.rc
index 2dc547f..5bfef59 100644
--- a/services/mediatranscoding/mediatranscoding.rc
+++ b/services/mediatranscoding/mediatranscoding.rc
@@ -3,4 +3,4 @@
user media
group media
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
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