Merge "C2BqPool: move pool data destruction out of lock"
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index bd274d8..84a6d6b 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -107,8 +107,11 @@
if (useMic) {
// talk into the appropriate microphone for the duration
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+
source = new AudioSource(
- AUDIO_SOURCE_MIC,
+ &attr,
String16(),
sampleRate,
channels);
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index e0b3939..071bb74 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -50,13 +50,8 @@
],
shared_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.media.bufferpool@2.0",
"libbase",
"libcutils",
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index 78ac355..3ac3d89 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -18,19 +18,21 @@
#define LOG_TAG "C2AllocatorGralloc"
#include <utils/Log.h>
-#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
-#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/3.0/IMapper.h>
-#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <mutex>
+
+#include <android/hardware/graphics/common/1.2/types.h>
#include <cutils/native_handle.h>
#include <hardware/gralloc.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
#include <C2AllocatorGralloc.h>
#include <C2Buffer.h>
#include <C2PlatformSupport.h>
+using ::android::hardware::hidl_handle;
+using PixelFormat4 = ::android::hardware::graphics::common::V1_2::PixelFormat;
+
namespace android {
namespace /* unnamed */ {
@@ -63,82 +65,9 @@
(expected & PASSTHROUGH_USAGE_MASK));
}
-using ::android::hardware::hidl_handle;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::graphics::common::V1_0::BufferUsage;
-using PixelFormat2 = ::android::hardware::graphics::common::V1_0::PixelFormat;
-using PixelFormat3 = ::android::hardware::graphics::common::V1_2::PixelFormat;
-using PixelFormat4 = ::android::hardware::graphics::common::V1_2::PixelFormat;
-
-using IAllocator2 = ::android::hardware::graphics::allocator::V2_0::IAllocator;
-using BufferDescriptor2 = ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
-using Error2 = ::android::hardware::graphics::mapper::V2_0::Error;
-using IMapper2 = ::android::hardware::graphics::mapper::V2_0::IMapper;
-
-using IAllocator3 = ::android::hardware::graphics::allocator::V3_0::IAllocator;
-using BufferDescriptor3 = ::android::hardware::graphics::mapper::V3_0::BufferDescriptor;
-using Error3 = ::android::hardware::graphics::mapper::V3_0::Error;
-using IMapper3 = ::android::hardware::graphics::mapper::V3_0::IMapper;
-
-using IAllocator4 = ::android::hardware::graphics::allocator::V4_0::IAllocator;
-using BufferDescriptor4 = ::android::hardware::graphics::mapper::V4_0::BufferDescriptor;
-using Error4 = ::android::hardware::graphics::mapper::V4_0::Error;
-using IMapper4 = ::android::hardware::graphics::mapper::V4_0::IMapper;
-
namespace /* unnamed */ {
-struct BufferDescriptorInfo2 {
- IMapper2::BufferDescriptorInfo mapperInfo;
- uint32_t stride;
-};
-
-struct BufferDescriptorInfo3 {
- IMapper3::BufferDescriptorInfo mapperInfo;
- uint32_t stride;
-};
-
-struct BufferDescriptorInfo4 {
- IMapper4::BufferDescriptorInfo mapperInfo;
- uint32_t stride;
-};
-
/* ===================================== GRALLOC ALLOCATION ==================================== */
-c2_status_t maperr2error(Error2 maperr) {
- switch (maperr) {
- case Error2::NONE: return C2_OK;
- case Error2::BAD_DESCRIPTOR: return C2_BAD_VALUE;
- case Error2::BAD_BUFFER: return C2_BAD_VALUE;
- case Error2::BAD_VALUE: return C2_BAD_VALUE;
- case Error2::NO_RESOURCES: return C2_NO_MEMORY;
- case Error2::UNSUPPORTED: return C2_CANNOT_DO;
- }
- return C2_CORRUPTED;
-}
-
-c2_status_t maperr2error(Error3 maperr) {
- switch (maperr) {
- case Error3::NONE: return C2_OK;
- case Error3::BAD_DESCRIPTOR: return C2_BAD_VALUE;
- case Error3::BAD_BUFFER: return C2_BAD_VALUE;
- case Error3::BAD_VALUE: return C2_BAD_VALUE;
- case Error3::NO_RESOURCES: return C2_NO_MEMORY;
- case Error3::UNSUPPORTED: return C2_CANNOT_DO;
- }
- return C2_CORRUPTED;
-}
-
-c2_status_t maperr2error(Error4 maperr) {
- switch (maperr) {
- case Error4::NONE: return C2_OK;
- case Error4::BAD_DESCRIPTOR: return C2_BAD_VALUE;
- case Error4::BAD_BUFFER: return C2_BAD_VALUE;
- case Error4::BAD_VALUE: return C2_BAD_VALUE;
- case Error4::NO_RESOURCES: return C2_NO_MEMORY;
- case Error4::UNSUPPORTED: return C2_CANNOT_DO;
- }
- return C2_CORRUPTED;
-}
-
bool native_handle_is_invalid(const native_handle_t *const handle) {
// perform basic validation of a native handle
if (handle == nullptr) {
@@ -334,21 +263,11 @@
// internal methods
// |handle| will be moved.
+
C2AllocationGralloc(
- const BufferDescriptorInfo2 &info,
- const sp<IMapper2> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId);
- C2AllocationGralloc(
- const BufferDescriptorInfo3 &info,
- const sp<IMapper3> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId);
- C2AllocationGralloc(
- const BufferDescriptorInfo4 &info,
- const sp<IMapper4> &mapper,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t layerCount,
+ uint64_t grallocUsage, uint32_t stride,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle,
C2Allocator::id_t allocatorId);
@@ -356,12 +275,12 @@
c2_status_t status() const;
private:
- const BufferDescriptorInfo2 mInfo2{};
- const sp<IMapper2> mMapper2{nullptr};
- const BufferDescriptorInfo3 mInfo3{};
- const sp<IMapper3> mMapper3{nullptr};
- const BufferDescriptorInfo4 mInfo4{};
- const sp<IMapper4> mMapper4{nullptr};
+ const uint32_t mWidth;
+ const uint32_t mHeight;
+ const uint32_t mFormat;
+ const uint32_t mLayerCount;
+ const uint64_t mGrallocUsage;
+ const uint32_t mStride;
const hidl_handle mHidlHandle;
const C2HandleGralloc *mHandle;
buffer_handle_t mBuffer;
@@ -372,48 +291,19 @@
};
C2AllocationGralloc::C2AllocationGralloc(
- const BufferDescriptorInfo2 &info,
- const sp<IMapper2> &mapper,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t layerCount,
+ uint64_t grallocUsage, uint32_t stride,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle,
C2Allocator::id_t allocatorId)
- : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
- mInfo2(info),
- mMapper2(mapper),
- mHidlHandle(std::move(hidlHandle)),
- mHandle(handle),
- mBuffer(nullptr),
- mLockedHandle(nullptr),
- mLocked(false),
- mAllocatorId(allocatorId) {
-}
-
-C2AllocationGralloc::C2AllocationGralloc(
- const BufferDescriptorInfo3 &info,
- const sp<IMapper3> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId)
- : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
- mInfo3(info),
- mMapper3(mapper),
- mHidlHandle(std::move(hidlHandle)),
- mHandle(handle),
- mBuffer(nullptr),
- mLockedHandle(nullptr),
- mLocked(false),
- mAllocatorId(allocatorId) {
-}
-
-C2AllocationGralloc::C2AllocationGralloc(
- const BufferDescriptorInfo4 &info,
- const sp<IMapper4> &mapper,
- hidl_handle &hidlHandle,
- const C2HandleGralloc *const handle,
- C2Allocator::id_t allocatorId)
- : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
- mInfo4(info),
- mMapper4(mapper),
+ : C2GraphicAllocation(width, height),
+ mWidth(width),
+ mHeight(height),
+ mFormat(format),
+ mLayerCount(layerCount),
+ mGrallocUsage(grallocUsage),
+ mStride(stride),
mHidlHandle(std::move(hidlHandle)),
mHandle(handle),
mBuffer(nullptr),
@@ -429,23 +319,10 @@
unmap(addr, C2Rect(), nullptr);
}
if (mBuffer) {
- if (mMapper2) {
- if (!mMapper2->freeBuffer(const_cast<native_handle_t *>(
- mBuffer)).isOk()) {
- ALOGE("failed transaction: freeBuffer");
- }
- } else if (mMapper3) {
- if (!mMapper3->freeBuffer(const_cast<native_handle_t *>(
- mBuffer)).isOk()) {
- ALOGE("failed transaction: freeBuffer");
- }
- } else {
- if (!mMapper4->freeBuffer(const_cast<native_handle_t *>(
- mBuffer)).isOk()) {
- ALOGE("failed transaction: freeBuffer");
- }
+ status_t err = GraphicBufferMapper::get().freeBuffer(mBuffer);
+ if (err) {
+ ALOGE("failed transaction: freeBuffer");
}
-
}
if (mHandle) {
native_handle_delete(
@@ -466,7 +343,7 @@
(long long)usage.expected, (long long)grallocUsage);
// TODO
- (void) fence;
+ (void)fence;
std::lock_guard<std::mutex> lock(mMappedLock);
if (mBuffer && mLocked) {
@@ -478,45 +355,13 @@
return C2_BAD_VALUE;
}
- c2_status_t err = C2_OK;
if (!mBuffer) {
- if (mMapper2) {
- if (!mMapper2->importBuffer(
- mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- mBuffer = static_cast<buffer_handle_t>(buffer);
- }
- }).isOk()) {
- ALOGE("failed transaction: importBuffer");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->importBuffer(
- mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- mBuffer = static_cast<buffer_handle_t>(buffer);
- }
- }).isOk()) {
- ALOGE("failed transaction: importBuffer (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper4->importBuffer(
- mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- mBuffer = static_cast<buffer_handle_t>(buffer);
- }
- }).isOk()) {
- ALOGE("failed transaction: importBuffer (@4.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("importBuffer failed: %d", err);
- return err;
+ status_t err = GraphicBufferMapper::get().importBuffer(
+ mHidlHandle.getNativeHandle(), mWidth, mHeight, mLayerCount,
+ mFormat, mGrallocUsage, mStride, &mBuffer);
+ if (err) {
+ ALOGE("failed transaction: importBuffer");
+ return C2_CORRUPTED;
}
if (mBuffer == nullptr) {
ALOGD("importBuffer returned null buffer");
@@ -528,96 +373,26 @@
if (mHandle) {
mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
}
- if (mMapper2) {
- mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
- mBuffer, mInfo2.mapperInfo.width, mInfo2.mapperInfo.height,
- (uint32_t)mInfo2.mapperInfo.format, mInfo2.mapperInfo.usage,
- mInfo2.stride, generation, igbp_id, igbp_slot);
- } else if (mMapper3) {
- mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
- mBuffer, mInfo3.mapperInfo.width, mInfo3.mapperInfo.height,
- (uint32_t)mInfo3.mapperInfo.format, mInfo3.mapperInfo.usage,
- mInfo3.stride, generation, igbp_id, igbp_slot);
- } else {
- mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
- mBuffer, mInfo4.mapperInfo.width, mInfo4.mapperInfo.height,
- (uint32_t)mInfo4.mapperInfo.format, mInfo4.mapperInfo.usage,
- mInfo4.stride, generation, igbp_id, igbp_slot);
- }
- }
- PixelFormat4 format;
- if (mMapper2) {
- format = PixelFormat4(mInfo2.mapperInfo.format);
- } else if (mMapper3) {
- format = PixelFormat4(mInfo3.mapperInfo.format);
- } else {
- format = PixelFormat4(mInfo4.mapperInfo.format);
+ mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
+ mBuffer, mWidth, mHeight, mFormat, mGrallocUsage,
+ mStride, generation, igbp_id, igbp_slot);
}
- switch (format) {
- case PixelFormat4::RGBA_1010102: {
+ switch (mFormat) {
+ case static_cast<uint32_t>(PixelFormat4::RGBA_1010102): {
// TRICKY: this is used for media as YUV444 in the case when it is queued directly to a
// Surface. In all other cases it is RGBA. We don't know which case it is here, so
// default to YUV for now.
void *pointer = nullptr;
- if (mMapper2) {
- if (!mMapper2->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_1010102)");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer,
- int32_t bytesPerPixel, int32_t bytesPerStride) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- (void)bytesPerPixel;
- (void)bytesPerStride;
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_1010102) (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper4->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_1010102) (@4.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("lock failed: %d", err);
- return err;
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().lock(
+ const_cast<native_handle_t *>(mBuffer), grallocUsage,
+ { (int32_t)rect.left, (int32_t)rect.top,
+ (int32_t)rect.width, (int32_t)rect.height },
+ &pointer);
+ if (err) {
+ ALOGE("failed transaction: lock(RGBA_1010102)");
+ return C2_CORRUPTED;
}
// treat as 32-bit values
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)pointer;
@@ -627,18 +402,10 @@
layout->type = C2PlanarLayout::TYPE_YUVA;
layout->numPlanes = 4;
layout->rootPlanes = 1;
- int32_t stride;
- if (mMapper2) {
- stride = int32_t(mInfo2.stride);
- } else if (mMapper3) {
- stride = int32_t(mInfo3.stride);
- } else {
- stride = int32_t(mInfo4.stride);
- }
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -651,7 +418,7 @@
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -664,7 +431,7 @@
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -677,7 +444,7 @@
layout->planes[C2PlanarLayout::PLANE_A] = {
C2PlaneInfo::CHANNEL_A, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
32, // allocatedDepth
@@ -690,69 +457,20 @@
break;
}
- case PixelFormat4::RGBA_8888:
+ case static_cast<uint32_t>(PixelFormat4::RGBA_8888):
// TODO: alpha channel
// fall-through
- case PixelFormat4::RGBX_8888: {
+ case static_cast<uint32_t>(PixelFormat4::RGBX_8888): {
void *pointer = nullptr;
- if (mMapper2) {
- if (!mMapper2->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_8888)");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer,
- int32_t bytesPerPixel, int32_t bytesPerStride) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- (void)bytesPerPixel;
- (void)bytesPerStride;
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_8888) (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper4->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(RGBA_8888) (@4.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("lock failed: %d", err);
- return err;
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().lock(
+ const_cast<native_handle_t*>(mBuffer), grallocUsage,
+ { (int32_t)rect.left, (int32_t)rect.top,
+ (int32_t)rect.width, (int32_t)rect.height },
+ &pointer);
+ if (err) {
+ ALOGE("failed transaction: lock(RGBA_8888)");
+ return C2_CORRUPTED;
}
addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
@@ -760,18 +478,10 @@
layout->type = C2PlanarLayout::TYPE_RGB;
layout->numPlanes = 3;
layout->rootPlanes = 1;
- int32_t stride;
- if (mMapper2) {
- stride = int32_t(mInfo2.stride);
- } else if (mMapper3) {
- stride = int32_t(mInfo3.stride);
- } else {
- stride = int32_t(mInfo4.stride);
- }
layout->planes[C2PlanarLayout::PLANE_R] = {
C2PlaneInfo::CHANNEL_R, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -784,7 +494,7 @@
layout->planes[C2PlanarLayout::PLANE_G] = {
C2PlaneInfo::CHANNEL_G, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -797,7 +507,7 @@
layout->planes[C2PlanarLayout::PLANE_B] = {
C2PlaneInfo::CHANNEL_B, // channel
4, // colInc
- 4 * stride, // rowInc
+ static_cast<int32_t>(4 * mStride), // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -810,142 +520,23 @@
break;
}
- case PixelFormat4::BLOB: {
- void* pointer = nullptr;
- if (mMapper2) {
- if (!mMapper2->lock(const_cast<native_handle_t*>(mBuffer), grallocUsage,
- {(int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width,
- (int32_t)rect.height},
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto& maperr, const auto& mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(BLOB)");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->lock(
- const_cast<native_handle_t*>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer,
- int32_t bytesPerPixel, int32_t bytesPerStride) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- (void)bytesPerPixel;
- (void)bytesPerStride;
- }).isOk()) {
- ALOGE("failed transaction: lock(BLOB) (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper4->lock(
- const_cast<native_handle_t *>(mBuffer),
- grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &pointer](const auto &maperr, const auto &mapPointer) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- pointer = mapPointer;
- }
- }).isOk()) {
- ALOGE("failed transaction: lock(BLOB) (@4.0)");
- return C2_CORRUPTED;
- }
- }
- if (err != C2_OK) {
- ALOGD("lock failed: %d", err);
- return err;
- }
-
- *addr = static_cast<uint8_t*>(pointer);
- break;
- }
-
- case PixelFormat4::YCBCR_420_888:
+ case static_cast<uint32_t>(PixelFormat4::YCBCR_420_888):
// fall-through
- case PixelFormat4::YV12:
+ case static_cast<uint32_t>(PixelFormat4::YV12):
// fall-through
default: {
- struct YCbCrLayout {
- void* y;
- void* cb;
- void* cr;
- uint32_t yStride;
- uint32_t cStride;
- uint32_t chromaStep;
- };
- YCbCrLayout ycbcrLayout;
- if (mMapper2) {
- if (!mMapper2->lockYCbCr(
- const_cast<native_handle_t *>(mBuffer), grallocUsage,
+ android_ycbcr ycbcrLayout;
+
+ status_t err = GraphicBufferMapper::get().lockYCbCr(
+ const_cast<native_handle_t*>(mBuffer), grallocUsage,
{ (int32_t)rect.left, (int32_t)rect.top,
(int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- ycbcrLayout = YCbCrLayout{
- mapLayout.y,
- mapLayout.cb,
- mapLayout.cr,
- mapLayout.yStride,
- mapLayout.cStride,
- mapLayout.chromaStep};
- }
- }).isOk()) {
- ALOGE("failed transaction: lockYCbCr");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->lockYCbCr(
- const_cast<native_handle_t *>(mBuffer), grallocUsage,
- { (int32_t)rect.left, (int32_t)rect.top,
- (int32_t)rect.width, (int32_t)rect.height },
- // TODO: fence
- hidl_handle(),
- [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- ycbcrLayout = YCbCrLayout{
- mapLayout.y,
- mapLayout.cb,
- mapLayout.cr,
- mapLayout.yStride,
- mapLayout.cStride,
- mapLayout.chromaStep};
- }
- }).isOk()) {
- ALOGE("failed transaction: lockYCbCr (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- // No device currently supports IMapper 4.0 so it is safe to just return an error
- // code here.
- //
- // This will be supported by a combination of lock and BufferMetadata getters.
- // We are going to refactor all the IAllocator/IMapper versioning code into a
- // shared library. We will then add the IMapper 4.0 lockYCbCr support then.
- ALOGE("failed transaction: lockYCbCr (@4.0)");
+ &ycbcrLayout);
+ if (err) {
+ ALOGE("failed transaction: lockYCbCr");
return C2_CORRUPTED;
}
- if (err != C2_OK) {
- ALOGD("lockYCbCr failed: %d", err);
- return err;
- }
+
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
@@ -955,7 +546,7 @@
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
1, // colInc
- (int32_t)ycbcrLayout.yStride, // rowInc
+ (int32_t)ycbcrLayout.ystride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
@@ -967,8 +558,8 @@
};
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
- (int32_t)ycbcrLayout.chromaStep, // colInc
- (int32_t)ycbcrLayout.cStride, // rowInc
+ (int32_t)ycbcrLayout.chroma_step, // colInc
+ (int32_t)ycbcrLayout.cstride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
@@ -980,8 +571,8 @@
};
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
- (int32_t)ycbcrLayout.chromaStep, // colInc
- (int32_t)ycbcrLayout.cStride, // rowInc
+ (int32_t)ycbcrLayout.chroma_step, // colInc
+ (int32_t)ycbcrLayout.cstride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
@@ -993,11 +584,11 @@
};
// handle interleaved formats
intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
- if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) {
+ if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chroma_step) {
layout->rootPlanes = 2;
layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
- } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) {
+ } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chroma_step) {
layout->rootPlanes = 2;
layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
@@ -1015,59 +606,18 @@
// TODO: check addr and size, use fence
(void)addr;
(void)rect;
+ (void)fence;
std::lock_guard<std::mutex> lock(mMappedLock);
- c2_status_t err = C2_OK;
- if (mMapper2) {
- if (!mMapper2->unlock(
- const_cast<native_handle_t *>(mBuffer),
- [&err, &fence](const auto &maperr, const auto &releaseFence) {
- // TODO
- (void) fence;
- (void) releaseFence;
- err = maperr2error(maperr);
- if (err == C2_OK) {
- // TODO: fence
- }
- }).isOk()) {
- ALOGE("failed transaction: unlock");
- return C2_CORRUPTED;
- }
- } else if (mMapper3) {
- if (!mMapper3->unlock(
- const_cast<native_handle_t *>(mBuffer),
- [&err, &fence](const auto &maperr, const auto &releaseFence) {
- // TODO
- (void) fence;
- (void) releaseFence;
- err = maperr2error(maperr);
- if (err == C2_OK) {
- // TODO: fence
- }
- }).isOk()) {
- ALOGE("failed transaction: unlock (@3.0)");
- return C2_CORRUPTED;
- }
- } else {
- if (!mMapper4->unlock(
- const_cast<native_handle_t *>(mBuffer),
- [&err, &fence](const auto &maperr, const auto &releaseFence) {
- // TODO
- (void) fence;
- (void) releaseFence;
- err = maperr2error(maperr);
- if (err == C2_OK) {
- // TODO: fence
- }
- }).isOk()) {
- ALOGE("failed transaction: unlock (@4.0)");
- return C2_CORRUPTED;
- }
+ // TODO: fence
+ status_t err = GraphicBufferMapper::get().unlock(mBuffer);
+ if (err) {
+ ALOGE("failed transaction: unlock");
+ return C2_CORRUPTED;
}
- if (err == C2_OK) {
- mLocked = false;
- }
- return err;
+
+ mLocked = false;
+ return C2_OK;
}
bool C2AllocationGralloc::equals(const std::shared_ptr<const C2GraphicAllocation> &other) const {
@@ -1104,12 +654,6 @@
private:
std::shared_ptr<C2Allocator::Traits> mTraits;
c2_status_t mInit;
- sp<IAllocator2> mAllocator2;
- sp<IMapper2> mMapper2;
- sp<IAllocator3> mAllocator3;
- sp<IMapper3> mMapper3;
- sp<IAllocator4> mAllocator4;
- sp<IMapper4> mMapper4;
const bool mBufferQueue;
};
@@ -1127,27 +671,6 @@
C2MemoryUsage minUsage = { 0, 0 }, maxUsage = { ~(uint64_t)0, ~(uint64_t)0 };
Traits traits = { "android.allocator.gralloc", id, C2Allocator::GRAPHIC, minUsage, maxUsage };
mTraits = std::make_shared<C2Allocator::Traits>(traits);
-
- // gralloc allocator is a singleton, so all objects share a global service
- mAllocator4 = IAllocator4::getService();
- mMapper4 = IMapper4::getService();
- if (!mAllocator4 || !mMapper4) {
- mAllocator4 = nullptr;
- mMapper4 = nullptr;
- mAllocator3 = IAllocator3::getService();
- mMapper3 = IMapper3::getService();
- if (!mAllocator3 || !mMapper3) {
- mAllocator3 = nullptr;
- mMapper3 = nullptr;
- mAllocator2 = IAllocator2::getService();
- mMapper2 = IMapper2::getService();
- if (!mAllocator2 || !mMapper2) {
- mAllocator2 = nullptr;
- mMapper2 = nullptr;
- mInit = C2_CORRUPTED;
- }
- }
- }
}
c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
@@ -1157,257 +680,59 @@
ALOGV("allocating buffer with usage %#llx => %#llx",
(long long)usage.expected, (long long)grallocUsage);
- c2_status_t err = C2_OK;
- hidl_handle buffer{};
+ buffer_handle_t buffer;
- if (mMapper2) {
- BufferDescriptorInfo2 info = {
- {
- width,
- height,
- 1u, // layerCount
- PixelFormat2(format),
- grallocUsage,
- },
- 0u, // stride placeholder
- };
- BufferDescriptor2 desc;
- if (!mMapper2->createDescriptor(
- info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- desc = descriptor;
- }
- }).isOk()) {
- ALOGE("failed transaction: createDescriptor");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
+ uint32_t stride = 0;
- // IAllocator shares IMapper error codes.
- if (!mAllocator2->allocate(
- desc,
- 1u,
- [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
- err = maperr2error(maperr);
- if (err != C2_OK) {
- return;
- }
- if (buffers.size() != 1u) {
- err = C2_CORRUPTED;
- return;
- }
- info.stride = stride;
- buffer = buffers[0];
- }).isOk()) {
- ALOGE("failed transaction: allocate");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
- allocation->reset(new C2AllocationGralloc(
- info, mMapper2, buffer,
- C2HandleGralloc::WrapAndMoveNativeHandle(
- buffer.getNativeHandle(),
- width, height,
- format, grallocUsage, info.stride,
- 0, 0, mBufferQueue ? ~0 : 0),
- mTraits->id));
- return C2_OK;
- } else if (mMapper3) {
- BufferDescriptorInfo3 info = {
- {
- width,
- height,
- 1u, // layerCount
- PixelFormat4(format),
- grallocUsage,
- },
- 0u, // stride placeholder
- };
- BufferDescriptor3 desc;
- if (!mMapper3->createDescriptor(
- info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- desc = descriptor;
- }
- }).isOk()) {
- ALOGE("failed transaction: createDescriptor");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
-
- // IAllocator shares IMapper error codes.
- if (!mAllocator3->allocate(
- desc,
- 1u,
- [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
- err = maperr2error(maperr);
- if (err != C2_OK) {
- return;
- }
- if (buffers.size() != 1u) {
- err = C2_CORRUPTED;
- return;
- }
- info.stride = stride;
- buffer = buffers[0];
- }).isOk()) {
- ALOGE("failed transaction: allocate");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
- allocation->reset(new C2AllocationGralloc(
- info, mMapper3, buffer,
- C2HandleGralloc::WrapAndMoveNativeHandle(
- buffer.getNativeHandle(),
- width, height,
- format, grallocUsage, info.stride,
- 0, 0, mBufferQueue ? ~0 : 0),
- mTraits->id));
- return C2_OK;
- } else {
- BufferDescriptorInfo4 info = {
- {
- "C2GrallocAllocation",
- width,
- height,
- 1u, // layerCount
- PixelFormat4(format),
- grallocUsage,
- },
- 0u, // stride placeholder
- };
- BufferDescriptor4 desc;
- if (!mMapper4->createDescriptor(
- info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
- err = maperr2error(maperr);
- if (err == C2_OK) {
- desc = descriptor;
- }
- }).isOk()) {
- ALOGE("failed transaction: createDescriptor");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
-
- // IAllocator shares IMapper error codes.
- if (!mAllocator4->allocate(
- desc,
- 1u,
- [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
- err = maperr2error(maperr);
- if (err != C2_OK) {
- return;
- }
- if (buffers.size() != 1u) {
- err = C2_CORRUPTED;
- return;
- }
- info.stride = stride;
- buffer = buffers[0];
- }).isOk()) {
- ALOGE("failed transaction: allocate");
- return C2_CORRUPTED;
- }
- if (err != C2_OK) {
- return err;
- }
- allocation->reset(new C2AllocationGralloc(
- info, mMapper4, buffer,
- C2HandleGralloc::WrapAndMoveNativeHandle(
- buffer.getNativeHandle(),
- width, height,
- format, grallocUsage, info.stride,
- 0, 0, mBufferQueue ? ~0 : 0),
- mTraits->id));
- return C2_OK;
+ status_t err = GraphicBufferAllocator::get().allocateRawHandle(width, height, format,
+ 1u /* layer count */, grallocUsage, &buffer, &stride, "C2GrallocAllocation");
+ if (err) {
+ ALOGE("failed transaction: allocate");
+ return C2_CORRUPTED;
}
+
+ hidl_handle hidlHandle;
+ hidlHandle.setTo(const_cast<native_handle_t*>(buffer), true);
+
+ allocation->reset(new C2AllocationGralloc(
+ width, height, format, 1u /* layer count */, grallocUsage, stride, hidlHandle,
+ C2HandleGralloc::WrapAndMoveNativeHandle(
+ hidlHandle, width, height,
+ format, grallocUsage, stride,
+ 0, 0, mBufferQueue ? ~0 : 0),
+ mTraits->id));
+ return C2_OK;
}
c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
const C2Handle *handle,
std::shared_ptr<C2GraphicAllocation> *allocation) {
- if (mMapper2) {
- BufferDescriptorInfo2 info;
- info.mapperInfo.layerCount = 1u;
- uint32_t generation;
- uint64_t igbp_id;
- uint32_t igbp_slot;
- const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
- handle,
- &info.mapperInfo.width, &info.mapperInfo.height,
- (uint32_t *)&info.mapperInfo.format,
- (uint64_t *)&info.mapperInfo.usage,
- &info.stride,
- &generation, &igbp_id, &igbp_slot);
- if (grallocHandle == nullptr) {
- return C2_BAD_VALUE;
- }
- hidl_handle hidlHandle;
- hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+ uint32_t generation;
+ uint64_t igbp_id;
+ uint32_t igbp_slot;
- allocation->reset(new C2AllocationGralloc(
- info, mMapper2, hidlHandle, grallocHandle, mTraits->id));
- return C2_OK;
- } else if (mMapper3) {
- BufferDescriptorInfo3 info;
- info.mapperInfo.layerCount = 1u;
- uint32_t generation;
- uint64_t igbp_id;
- uint32_t igbp_slot;
- const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
- handle,
- &info.mapperInfo.width, &info.mapperInfo.height,
- (uint32_t *)&info.mapperInfo.format,
- (uint64_t *)&info.mapperInfo.usage,
- &info.stride,
- &generation, &igbp_id, &igbp_slot);
- if (grallocHandle == nullptr) {
- return C2_BAD_VALUE;
- }
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ uint32_t layerCount = 1;
+ uint64_t grallocUsage;
+ uint32_t stride;
- hidl_handle hidlHandle;
- hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
-
- allocation->reset(new C2AllocationGralloc(
- info, mMapper3, hidlHandle, grallocHandle, mTraits->id));
- return C2_OK;
- } else {
- BufferDescriptorInfo4 info;
- info.mapperInfo.layerCount = 1u;
- uint32_t generation;
- uint64_t igbp_id;
- uint32_t igbp_slot;
- const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
- handle,
- &info.mapperInfo.width, &info.mapperInfo.height,
- (uint32_t *)&info.mapperInfo.format,
- (uint64_t *)&info.mapperInfo.usage,
- &info.stride,
- &generation, &igbp_id, &igbp_slot);
- if (grallocHandle == nullptr) {
- return C2_BAD_VALUE;
- }
-
- hidl_handle hidlHandle;
- hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
-
- allocation->reset(new C2AllocationGralloc(
- info, mMapper4, hidlHandle, grallocHandle, mTraits->id));
- return C2_OK;
+ const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
+ handle, &width, &height, &format, &grallocUsage, &stride,
+ &generation, &igbp_id, &igbp_slot);
+ if (grallocHandle == nullptr) {
+ return C2_BAD_VALUE;
}
+
+ hidl_handle hidlHandle;
+ hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+
+ allocation->reset(new C2AllocationGralloc(
+ width, height, format, layerCount,
+ grallocUsage, stride, hidlHandle, grallocHandle, mTraits->id));
+ return C2_OK;
}
C2AllocatorGralloc::C2AllocatorGralloc(id_t id, bool bufferQueue)
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 9115778..4bba436 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -38,12 +38,15 @@
aaudio_input_preset_t inputPreset) = nullptr;
static void (*s_setAllowedCapturePolicy)(AAudioStreamBuilder* builder,
aaudio_allowed_capture_policy_t usage) = nullptr;
+static void (*s_setPrivacySensitive)(AAudioStreamBuilder* builder,
+ bool privacySensitive) = nullptr;
static bool s_loadAttempted = false;
static aaudio_usage_t (*s_getUsage)(AAudioStream *stream) = nullptr;
static aaudio_content_type_t (*s_getContentType)(AAudioStream *stream) = nullptr;
static aaudio_input_preset_t (*s_getInputPreset)(AAudioStream *stream) = nullptr;
static aaudio_allowed_capture_policy_t (*s_getAllowedCapturePolicy)(AAudioStream *stream) = nullptr;
+static bool (*s_isPrivacySensitive)(AAudioStream *stream) = nullptr;
// Link to test functions in shared library.
static void loadFutureFunctions() {
@@ -68,6 +71,10 @@
dlsym(handle, "AAudioStreamBuilder_setAllowedCapturePolicy");
if (s_setAllowedCapturePolicy == nullptr) goto error;
+ s_setPrivacySensitive = (void (*)(AAudioStreamBuilder *, bool))
+ dlsym(handle, "AAudioStreamBuilder_setPrivacySensitive");
+ if (s_setPrivacySensitive == nullptr) goto error;
+
s_getUsage = (aaudio_usage_t (*)(AAudioStream *))
dlsym(handle, "AAudioStream_getUsage");
if (s_getUsage == nullptr) goto error;
@@ -83,6 +90,10 @@
s_getAllowedCapturePolicy = (aaudio_input_preset_t (*)(AAudioStream *))
dlsym(handle, "AAudioStream_getAllowedCapturePolicy");
if (s_getAllowedCapturePolicy == nullptr) goto error;
+
+ s_isPrivacySensitive = (bool (*)(AAudioStream *))
+ dlsym(handle, "AAudioStream_isPrivacySensitive");
+ if (s_isPrivacySensitive == nullptr) goto error;
}
return;
@@ -91,9 +102,11 @@
s_setUsage = nullptr;
s_setContentType = nullptr;
s_setInputPreset = nullptr;
+ s_setPrivacySensitive = nullptr;
s_getUsage = nullptr;
s_getContentType = nullptr;
s_getInputPreset = nullptr;
+ s_isPrivacySensitive = nullptr;
dlclose(handle);
return;
}
@@ -211,6 +224,14 @@
mFramesPerCallback = size;
}
+ int32_t isPrivacySensitive() const {
+ return mPrivacySensitive;
+ }
+
+ void setPrivacySensitive(int32_t privacySensitive) {
+ mPrivacySensitive = privacySensitive;
+ }
+
/**
* Apply these parameters to a stream builder.
* @param builder
@@ -234,12 +255,12 @@
}
if (s_setContentType != nullptr) {
s_setContentType(builder, mContentType);
- } else if (mUsage != AAUDIO_UNSPECIFIED){
+ } else if (mContentType != AAUDIO_UNSPECIFIED){
printf("WARNING: setContentType not supported");
}
if (s_setInputPreset != nullptr) {
s_setInputPreset(builder, mInputPreset);
- } else if (mUsage != AAUDIO_UNSPECIFIED){
+ } else if (mInputPreset != AAUDIO_UNSPECIFIED){
printf("WARNING: setInputPreset not supported");
}
@@ -249,6 +270,15 @@
} else if (mAllowedCapturePolicy != AAUDIO_UNSPECIFIED){
printf("WARNING: setAllowedCapturePolicy not supported");
}
+
+ if (mPrivacySensitive != PRIVACY_SENSITIVE_DEFAULT) {
+ if (s_setPrivacySensitive != nullptr) {
+ s_setPrivacySensitive(builder,
+ mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED);
+ } else {
+ printf("WARNING: setPrivacySensitive not supported");
+ }
+ }
}
static constexpr int32_t kDefaultNumberOfBursts = 2;
@@ -270,6 +300,13 @@
int32_t mNumberOfBursts = kDefaultNumberOfBursts;
int32_t mFramesPerCallback = AAUDIO_UNSPECIFIED;
+
+ enum {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+ int32_t mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
};
class AAudioArgsParser : public AAudioParameters {
@@ -341,6 +378,9 @@
case 'z':
setFramesPerCallback(atoi(&arg[2]));
break;
+ case 'S':
+ setPrivacySensitive(atoi(&arg[2]));
+ break;
default:
unrecognized = true;
break;
@@ -399,6 +439,9 @@
printf(" -x to use EXCLUSIVE mode\n");
printf(" -y{contentType} eg. 1 for AAUDIO_CONTENT_TYPE_SPEECH\n");
printf(" -z{callbackSize} or block size, in frames, default = 0\n");
+ printf(" -S{0|1} set privacy Sensitive enabled or disabled\n");
+ printf(" 0 = disabled\n");
+ printf(" 1 = enabled\n");
}
static aaudio_performance_mode_t parseAllowedCapturePolicy(char c) {
@@ -506,10 +549,15 @@
getContentType(), s_getContentType(stream));
}
- if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT
- && s_getInputPreset != nullptr) {
+ if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT) {
+ if (s_getInputPreset != nullptr) {
printf(" InputPreset: requested = %d, actual = %d\n",
getInputPreset(), s_getInputPreset(stream));
+ }
+ if (s_isPrivacySensitive != nullptr) {
+ printf(" Privacy Sensitive: requested = %d, actual = %d\n",
+ isPrivacySensitive(), s_isPrivacySensitive(stream));
+ }
}
printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index a4322a1..5bebd61 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -759,6 +759,28 @@
AAUDIO_API void AAudioStreamBuilder_setSessionId(AAudioStreamBuilder* builder,
aaudio_session_id_t sessionId) __INTRODUCED_IN(28);
+
+/** Indicates whether this input stream must be marked as privacy sensitive or not.
+ *
+ * When true, this input stream is privacy sensitive and any concurrent capture
+ * is not permitted.
+ *
+ * This is off (false) by default except when the input preset is {@link #AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION}
+ * or {@link #AAUDIO_INPUT_PRESET_CAMCORDER}.
+ *
+ * Always takes precedence over default from input preset when set explicitly.
+ *
+ * Only relevant if the stream direction is {@link #AAUDIO_DIRECTION_INPUT}.
+ *
+ * Added in API level 30.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param privacySensitive true if capture from this stream must be marked as privacy sensitive,
+ * false otherwise.
+ */
+AAUDIO_API void AAudioStreamBuilder_setPrivacySensitive(AAudioStreamBuilder* builder,
+ bool privacySensitive) __INTRODUCED_IN(30);
+
/**
* Return one of these values from the data callback function.
*/
@@ -1444,6 +1466,20 @@
AAUDIO_API aaudio_allowed_capture_policy_t AAudioStream_getAllowedCapturePolicy(
AAudioStream* stream) __INTRODUCED_IN(29);
+
+/**
+ * Return whether this input stream is marked as privacy sensitive or not.
+ *
+ * See {@link #AAudioStreamBuilder_setPrivacySensitive()}.
+ *
+ * Added in API level 30.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return true if privacy sensitive, false otherwise
+ */
+AAUDIO_API bool AAudioStream_isPrivacySensitive(AAudioStream* stream)
+ __INTRODUCED_IN(30);
+
#ifdef __cplusplus
}
#endif
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index a987fab..b785f88 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -66,6 +66,8 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32(getSessionId());
if (status != NO_ERROR) goto error;
+ status = parcel->writeInt32(isPrivacySensitive() ? 1 : 0);
+ if (status != NO_ERROR) goto error;
return NO_ERROR;
error:
ALOGE("%s(): write failed = %d", __func__, status);
@@ -111,7 +113,9 @@
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
setSessionId(value);
-
+ status = parcel->readInt32(&value);
+ if (status != NO_ERROR) goto error;
+ setPrivacySensitive(value == 1);
return NO_ERROR;
error:
ALOGE("%s(): read failed = %d", __func__, status);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7481daa..bfad254 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -116,6 +116,7 @@
request.getConfiguration().setUsage(getUsage());
request.getConfiguration().setContentType(getContentType());
request.getConfiguration().setInputPreset(getInputPreset());
+ request.getConfiguration().setPrivacySensitive(isPrivacySensitive());
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 8040e6a..184e9cb 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -149,6 +149,12 @@
streamBuilder->setInputPreset(inputPreset);
}
+AAUDIO_API void AAudioStreamBuilder_setPrivacySensitive(AAudioStreamBuilder* builder,
+ bool privacySensitive) {
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ streamBuilder->setPrivacySensitiveRequest(privacySensitive);
+}
+
AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder,
int32_t frames)
{
@@ -507,3 +513,9 @@
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
return audioStream->isMMap();
}
+
+AAUDIO_API bool AAudioStream_isPrivacySensitive(AAudioStream* stream)
+{
+ AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ return audioStream->isPrivacySensitive();
+}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index c9711da..58058f5 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -47,6 +47,7 @@
mContentType = other.mContentType;
mInputPreset = other.mInputPreset;
mAllowedCapturePolicy = other.mAllowedCapturePolicy;
+ mIsPrivacySensitive = other.mIsPrivacySensitive;
}
static aaudio_result_t isFormatValid(audio_format_t format) {
@@ -195,4 +196,5 @@
ALOGD("mContentType = %6d", mContentType);
ALOGD("mInputPreset = %6d", mInputPreset);
ALOGD("mAllowedCapturePolicy = %6d", mAllowedCapturePolicy);
+ ALOGD("mIsPrivacySensitive = %s", mIsPrivacySensitive ? "true" : "false");
}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index 2e21a8d..3e65b37 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -128,6 +128,14 @@
mSessionId = sessionId;
}
+ bool isPrivacySensitive() const {
+ return mIsPrivacySensitive;
+ }
+
+ void setPrivacySensitive(bool privacySensitive) {
+ mIsPrivacySensitive = privacySensitive;
+ }
+
/**
* @return bytes per frame of getFormat()
*/
@@ -158,6 +166,7 @@
int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED;
aaudio_session_id_t mSessionId = AAUDIO_SESSION_ID_NONE;
+ bool mIsPrivacySensitive = false;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 5303631..d928222 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -91,6 +91,7 @@
if (mAllowedCapturePolicy == AAUDIO_UNSPECIFIED) {
mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
}
+ mIsPrivacySensitive = builder.isPrivacySensitive();
// callbacks
mFramesPerDataCallback = builder.getFramesPerDataCallback();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 044c979..b4ffcf2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -234,6 +234,10 @@
return mSessionId;
}
+ bool isPrivacySensitive() const {
+ return mIsPrivacySensitive;
+ }
+
/**
* This is only valid after setSamplesPerFrame() and setFormat() have been called.
*/
@@ -543,6 +547,13 @@
mAllowedCapturePolicy = policy;
}
+ /**
+ * This should not be called after the open() call.
+ */
+ void setPrivacySensitive(bool privacySensitive) {
+ mIsPrivacySensitive = privacySensitive;
+ }
+
private:
aaudio_result_t safeStop();
@@ -565,6 +576,7 @@
aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED;
aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED;
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
+ bool mIsPrivacySensitive = false;
int32_t mSessionId = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 44f45b3..af28a59 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -158,6 +158,19 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
+ setPrivacySensitive(false);
+ if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_DEFAULT) {
+ // When not explicitly requested, set privacy sensitive mode according to input preset:
+ // communication and camcorder captures are considered privacy sensitive by default.
+ aaudio_input_preset_t preset = getInputPreset();
+ if (preset == AAUDIO_INPUT_PRESET_CAMCORDER
+ || preset == AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION) {
+ setPrivacySensitive(true);
+ }
+ } else if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_ENABLED) {
+ setPrivacySensitive(true);
+ }
+
result = builder_createStream(getDirection(), sharingMode, allowMMap, &audioStream);
if (result == AAUDIO_OK) {
// Open the stream using the parameters from the builder.
@@ -257,4 +270,5 @@
mFramesPerDataCallback);
ALOGI("usage = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d",
getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy());
+ ALOGI("privacy sensitive = %s", isPrivacySensitive() ? "true" : "false");
}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 8149af2..d5fb80d 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -98,6 +98,12 @@
return this;
}
+ AudioStreamBuilder* setPrivacySensitiveRequest(bool privacySensitive) {
+ mPrivacySensitiveReq =
+ privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+ return this;
+ }
+
aaudio_result_t build(AudioStream **streamPtr);
virtual aaudio_result_t validate() const override;
@@ -114,6 +120,14 @@
AAudioStream_errorCallback mErrorCallbackProc = nullptr;
void *mErrorCallbackUserData = nullptr;
+
+ enum {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+ typedef int32_t privacy_sensitive_t;
+ privacy_sensitive_t mPrivacySensitiveReq = PRIVACY_SENSITIVE_DEFAULT;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 71efc30..54af580 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -142,11 +142,13 @@
const audio_source_t source =
AAudioConvert_inputPresetToAudioSource(builder.getInputPreset());
+ const audio_flags_mask_t attrFlags =
+ AAudioConvert_privacySensitiveToAudioFlagsMask(builder.isPrivacySensitive());
const audio_attributes_t attributes = {
.content_type = contentType,
.usage = AUDIO_USAGE_UNKNOWN, // only used for output
.source = source,
- .flags = AUDIO_FLAG_NONE, // Different than the AUDIO_INPUT_FLAGS
+ .flags = attrFlags, // Different than the AUDIO_INPUT_FLAGS
.tags = ""
};
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index c2f7fd0..ef89697 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -234,6 +234,11 @@
}
}
+audio_flags_mask_t AAudioConvert_privacySensitiveToAudioFlagsMask(
+ bool privacySensitive) {
+ return privacySensitive ? AUDIO_FLAG_CAPTURE_PRIVATE : AUDIO_FLAG_NONE;
+}
+
int32_t AAudioConvert_framesToBytes(int32_t numFrames,
int32_t bytesPerFrame,
int32_t *sizeInBytes) {
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 5dcddf3..d2e4805 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -92,6 +92,9 @@
audio_flags_mask_t AAudioConvert_allowCapturePolicyToAudioFlagsMask(
aaudio_allowed_capture_policy_t policy);
+audio_flags_mask_t AAudioConvert_privacySensitiveToAudioFlagsMask(
+ bool privacySensitive);
+
// Note that this code may be replaced by Settings or by some other system configuration tool.
/**
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 0f2d48e..c22db59 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -262,8 +262,12 @@
}
if (pAttributes == NULL) {
- memset(&mAttributes, 0, sizeof(audio_attributes_t));
+ mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
mAttributes.source = inputSource;
+ if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || inputSource == AUDIO_SOURCE_CAMCORDER) {
+ mAttributes.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ }
} else {
// stream type shouldn't be looked at, this track has audio attributes
memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index a354ce1..ac86f72 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -67,7 +67,9 @@
GET_ACTIVE_MICROPHONES,
GET_PORT_ID,
SET_PREFERRED_MICROPHONE_DIRECTION,
- SET_PREFERRED_MICROPHONE_FIELD_DIMENSION
+ SET_PREFERRED_MICROPHONE_FIELD_DIMENSION,
+ SET_PRIVACY_SENSITIVE,
+ GET_PRIVACY_SENSITIVE
};
class BpMediaRecorder: public BpInterface<IMediaRecorder>
@@ -151,6 +153,36 @@
return reply.readInt32();
}
+ status_t setPrivacySensitive(bool privacySensitive)
+ {
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ data.writeInt32(privacySensitive ? 1 : 0);
+ status_t status = remote()->transact(SET_PRIVACY_SENSITIVE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return reply.readInt32();
+ }
+
+ status_t isPrivacySensitive(bool *privacySensitive) const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ *privacySensitive = false;
+ status_t status = remote()->transact(GET_PRIVACY_SENSITIVE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (status == NO_ERROR) {
+ *privacySensitive = reply.readInt32() == 1;
+ }
+ ALOGV("%s status %d enabled: %s", __func__, status, *privacySensitive ? "true" : "false");
+ return status;
+ }
+
status_t setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
@@ -537,6 +569,24 @@
reply->writeInt32(setAudioSource(as));
return NO_ERROR;
} break;
+ case SET_PRIVACY_SENSITIVE: {
+ ALOGV("SET_PRIVACY_SENSITIVE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ bool privacySensitive = data.readInt32() == 1;
+ reply->writeInt32(setPrivacySensitive(privacySensitive));
+ return NO_ERROR;
+ } break;
+ case GET_PRIVACY_SENSITIVE: {
+ ALOGV("GET_PRIVACY_SENSITIVE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ bool privacySensitive = false;
+ status_t status = isPrivacySensitive(&privacySensitive);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(privacySensitive ? 1 : 0);
+ }
+ return NO_ERROR;
+ } break;
case SET_OUTPUT_FORMAT: {
ALOGV("SET_OUTPUT_FORMAT");
CHECK_INTERFACE(IMediaRecorder, data, reply);
diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h
index f9c557c..651bd5e 100644
--- a/media/libmedia/include/media/IMediaRecorder.h
+++ b/media/libmedia/include/media/IMediaRecorder.h
@@ -44,6 +44,8 @@
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0;
virtual status_t setVideoSource(int vs) = 0;
virtual status_t setAudioSource(int as) = 0;
+ virtual status_t setPrivacySensitive(bool privacySensitive) = 0;
+ virtual status_t isPrivacySensitive(bool *privacySensitive) const = 0;
virtual status_t setOutputFormat(int of) = 0;
virtual status_t setVideoEncoder(int ve) = 0;
virtual status_t setAudioEncoder(int ae) = 0;
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index a2dff31..8493f64 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -39,6 +39,8 @@
virtual status_t init() = 0;
virtual status_t setAudioSource(audio_source_t as) = 0;
+ virtual status_t setPrivacySensitive(bool privacySensitive) = 0 ;
+ virtual status_t isPrivacySensitive(bool *privacySensitive) const = 0;
virtual status_t setVideoSource(video_source vs) = 0;
virtual status_t setOutputFormat(output_format of) = 0;
virtual status_t setAudioEncoder(audio_encoder ae) = 0;
@@ -79,6 +81,7 @@
protected:
+
String16 mOpPackageName;
private:
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 2dd4b7f..6e2d94d 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -236,6 +236,8 @@
status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
status_t setVideoSource(int vs);
status_t setAudioSource(int as);
+ status_t setPrivacySensitive(bool privacySensitive);
+ status_t isPrivacySensitive(bool *privacySensitive) const;
status_t setOutputFormat(int of);
status_t setVideoEncoder(int ve);
status_t setAudioEncoder(int ae);
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 4570af9..70655d5 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -178,6 +178,47 @@
return ret;
}
+status_t MediaRecorder::setPrivacySensitive(bool privacySensitive)
+{
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ if (mMediaRecorder == NULL) {
+ ALOGE("%s: media recorder is not initialized yet", __func__);
+ return INVALID_OPERATION;
+ }
+
+ if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED) || !mIsAudioSourceSet) {
+ ALOGE("%s called in an invalid state(%d) or audio source not (%d)",
+ __func__, mCurrentState, mIsAudioSourceSet);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->setPrivacySensitive(privacySensitive);
+ if (OK != ret) {
+ ALOGV("%s failed: %d", __func__, ret);
+ mCurrentState = MEDIA_RECORDER_ERROR;
+ return ret;
+ }
+ return ret;
+}
+
+status_t MediaRecorder::isPrivacySensitive(bool *privacySensitive) const
+{
+ if (mMediaRecorder == NULL) {
+ ALOGE("%s: media recorder is not initialized yet", __func__);
+ return INVALID_OPERATION;
+ }
+
+ if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED) || !mIsAudioSourceSet) {
+ ALOGE("%s called in an invalid state(%d) or audio source not (%d)",
+ __func__, mCurrentState, mIsAudioSourceSet);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->isPrivacySensitive(privacySensitive);
+ ALOGV("%s status: %d eanbled %s", __func__, ret, *privacySensitive ? "enabled" : "disabled");
+ return ret;
+}
+
status_t MediaRecorder::setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index 817aadf..6ea6172 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -395,6 +395,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_MEDIA_PROJECTION),
MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_SYSTEM_CAPTURE),
+ MAKE_STRING_FROM_ENUM(AUDIO_FLAG_CAPTURE_PRIVATE),
TERMINATOR
};
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index c61ed1b..9b1974b 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -138,6 +138,29 @@
return mRecorder->setAudioSource((audio_source_t)as);
}
+status_t MediaRecorderClient::setPrivacySensitive(bool privacySensitive)
+{
+ ALOGV("%s(%s)", __func__, privacySensitive ? "true" : "false");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("%s: recorder is not initialized", __func__);
+ return NO_INIT;
+ }
+ return mRecorder->setPrivacySensitive(privacySensitive);
+}
+
+status_t MediaRecorderClient::isPrivacySensitive(bool *privacySensitive) const
+{
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("%s: recorder is not initialized", __func__);
+ return NO_INIT;
+ }
+ status_t status = mRecorder->isPrivacySensitive(privacySensitive);
+ ALOGV("%s: status: %d enabled: %s", __func__, status, *privacySensitive ? "true" : "false");
+ return status;
+}
+
status_t MediaRecorderClient::setOutputFormat(int of)
{
ALOGV("setOutputFormat(%d)", of);
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 9e0f877..12257e5 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -51,6 +51,8 @@
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
virtual status_t setVideoSource(int vs);
virtual status_t setAudioSource(int as);
+ status_t setPrivacySensitive(bool privacySensitive) override;
+ status_t isPrivacySensitive(bool *privacySensitive) const override;
virtual status_t setOutputFormat(int of);
virtual status_t setVideoEncoder(int ve);
virtual status_t setAudioEncoder(int ae);
@@ -98,7 +100,7 @@
sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedNotifier;
pid_t mPid;
- Mutex mLock;
+ mutable Mutex mLock;
MediaRecorderBase *mRecorder;
sp<MediaPlayerService> mMediaPlayerService;
};
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 954ccc9..d36c6e3 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -115,6 +115,7 @@
mWriter(NULL),
mOutputFd(-1),
mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
+ mPrivacySensitive(PRIVACY_SENSITIVE_DEFAULT),
mVideoSource(VIDEO_SOURCE_LIST_END),
mStarted(false),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
@@ -238,7 +239,31 @@
} else {
mAudioSource = as;
}
+ // Reset privacy sensitive in case this is the second time audio source is set
+ mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
+ return OK;
+}
+status_t StagefrightRecorder::setPrivacySensitive(bool privacySensitive) {
+ // privacy sensitive cannot be set before audio source is set
+ if (mAudioSource == AUDIO_SOURCE_CNT) {
+ return INVALID_OPERATION;
+ }
+ mPrivacySensitive = privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+ return OK;
+}
+
+status_t StagefrightRecorder::isPrivacySensitive(bool *privacySensitive) const {
+ *privacySensitive = false;
+ if (mAudioSource == AUDIO_SOURCE_CNT) {
+ return INVALID_OPERATION;
+ }
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+ *privacySensitive = mAudioSource == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || mAudioSource == AUDIO_SOURCE_CAMCORDER;
+ } else {
+ *privacySensitive = mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED;
+ }
return OK;
}
@@ -1082,9 +1107,35 @@
}
}
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = mAudioSource;
+ // attr.flags AUDIO_FLAG_CAPTURE_PRIVATE is cleared by default
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+ if (attr.source == AUDIO_SOURCE_VOICE_COMMUNICATION
+ || attr.source == AUDIO_SOURCE_CAMCORDER) {
+ attr.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ mPrivacySensitive = PRIVACY_SENSITIVE_ENABLED;
+ } else {
+ mPrivacySensitive = PRIVACY_SENSITIVE_DISABLED;
+ }
+ } else {
+ if (mAudioSource == AUDIO_SOURCE_REMOTE_SUBMIX
+ || mAudioSource == AUDIO_SOURCE_FM_TUNER
+ || mAudioSource == AUDIO_SOURCE_VOICE_DOWNLINK
+ || mAudioSource == AUDIO_SOURCE_VOICE_UPLINK
+ || mAudioSource == AUDIO_SOURCE_VOICE_CALL
+ || mAudioSource == AUDIO_SOURCE_ECHO_REFERENCE) {
+ ALOGE("Cannot request private capture with source: %d", mAudioSource);
+ return NULL;
+ }
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
+ attr.flags |= AUDIO_FLAG_CAPTURE_PRIVATE;
+ }
+ }
+
sp<AudioSource> audioSource =
new AudioSource(
- mAudioSource,
+ &attr,
mOpPackageName,
sourceSampleRate,
mAudioChannels,
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 8bf083a..9e0f4b7 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -46,6 +46,8 @@
virtual ~StagefrightRecorder();
virtual status_t init();
virtual status_t setAudioSource(audio_source_t as);
+ status_t setPrivacySensitive(bool privacySensitive) override;
+ status_t isPrivacySensitive(bool *privacySensitive) const override;
virtual status_t setVideoSource(video_source vs);
virtual status_t setOutputFormat(output_format of);
virtual status_t setAudioEncoder(audio_encoder ae);
@@ -82,6 +84,13 @@
status_t getPortId(audio_port_handle_t *portId) const override;
private:
+
+ enum privacy_sensitive_t {
+ PRIVACY_SENSITIVE_DEFAULT = -1,
+ PRIVACY_SENSITIVE_DISABLED = 0,
+ PRIVACY_SENSITIVE_ENABLED = 1,
+ };
+
mutable Mutex mLock;
sp<hardware::ICamera> mCamera;
sp<ICameraRecordingProxy> mCameraProxy;
@@ -101,6 +110,7 @@
void updateMetrics();
audio_source_t mAudioSource;
+ privacy_sensitive_t mPrivacySensitive;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d78d729..00c5b40 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -50,7 +50,7 @@
}
AudioSource::AudioSource(
- audio_source_t inputSource, const String16 &opPackageName,
+ const audio_attributes_t *attr, const String16 &opPackageName,
uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
@@ -92,7 +92,7 @@
}
mRecord = new AudioRecord(
- inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
opPackageName,
(size_t) (bufCount * frameCount),
@@ -104,7 +104,7 @@
AUDIO_INPUT_FLAG_NONE,
uid,
pid,
- NULL /*pAttributes*/,
+ attr,
selectedDeviceId,
selectedMicDirection,
selectedMicFieldDimension);
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index af04dad..ad4ab47 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -37,7 +37,7 @@
// Note that the "channels" parameter _is_ the number of channels,
// _not_ a bitmask of audio_channels_t constants.
AudioSource(
- audio_source_t inputSource,
+ const audio_attributes_t *attr,
const String16 &opPackageName,
uint32_t sampleRate,
uint32_t channels,
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 1823fbc..8fb6a87 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -268,6 +268,9 @@
int64_t timeoutUs) __INTRODUCED_IN(21);
/**
+ * Returns the format of the codec's output.
+ * The caller must free the returned format.
+ *
* Available since API level 21.
*/
AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*) __INTRODUCED_IN(21);
@@ -397,6 +400,7 @@
/**
* Get format of the buffer. The specified buffer index must have been previously obtained from
* dequeueOutputBuffer.
+ * The caller must free the returned format.
*
* Available since API level 28.
*/
@@ -455,6 +459,7 @@
* Call this after AMediaCodec_configure() returns successfully to get the input
* format accepted by the codec. Do this to determine what optional configuration
* parameters were supported by the codec.
+ * The caller must free the returned format.
*
* Available since API level 28.
*/
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e3cabcd..63bbcc6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1825,7 +1825,7 @@
// TODO: We may also match on address as well as device type for
// AUDIO_DEVICE_OUT_BUS, AUDIO_DEVICE_OUT_ALL_A2DP, AUDIO_DEVICE_OUT_REMOTE_SUBMIX
- if (type == MIXER || type == DIRECT) {
+ if (type == MIXER || type == DIRECT || type == OFFLOAD) {
// TODO: This property should be ensure that only contains one single device type.
mTimestampCorrectedDevice = (audio_devices_t)property_get_int64(
"audio.timestamp.corrected_output_device",
@@ -5741,7 +5741,7 @@
int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || !last ||
track->presentationComplete(framesWritten, audioHALFrames) ||
- track->isPaused()) {
+ track->isPaused() || mHwPaused) {
if (track->isStopping_2()) {
track->mState = TrackBase::STOPPED;
}
diff --git a/services/audiopolicy/engine/common/Android.bp b/services/audiopolicy/engine/common/Android.bp
old mode 100644
new mode 100755
index b87c71d..a1c69f2
--- a/services/audiopolicy/engine/common/Android.bp
+++ b/services/audiopolicy/engine/common/Android.bp
@@ -25,6 +25,7 @@
"src/ProductStrategy.cpp",
"src/VolumeCurve.cpp",
"src/VolumeGroup.cpp",
+ "src/LastRemovableMediaDevices.cpp",
],
cflags: [
"-Wall",
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
old mode 100644
new mode 100755
index fca9a60..4cf0b90
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -20,6 +20,7 @@
#include <EngineInterface.h>
#include <ProductStrategy.h>
#include <VolumeGroup.h>
+#include <LastRemovableMediaDevices.h>
namespace android {
namespace audio_policy {
@@ -49,10 +50,8 @@
return mForceUse[usage];
}
android::status_t setDeviceConnectionState(const sp<DeviceDescriptor> /*devDesc*/,
- audio_policy_dev_state_t /*state*/) override
- {
- return NO_ERROR;
- }
+ audio_policy_dev_state_t /*state*/) override;
+
product_strategy_t getProductStrategyForAttributes(
const audio_attributes_t &attr) const override;
@@ -86,6 +85,12 @@
status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override;
+ std::vector<audio_devices_t> getLastRemovableMediaDevices(
+ device_out_group_t group = GROUP_NONE) const
+ {
+ return mLastRemovableMediaDevices.getLastRemovableMediaDevices(group);
+ }
+
void dump(String8 *dst) const override;
@@ -115,11 +120,12 @@
status_t restoreOriginVolumeCurve(audio_stream_type_t stream);
- private:
+private:
AudioPolicyManagerObserver *mApmObserver = nullptr;
ProductStrategyMap mProductStrategies;
VolumeGroupMap mVolumeGroups;
+ LastRemovableMediaDevices mLastRemovableMediaDevices;
audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */
/** current forced use configuration. */
diff --git a/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h b/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h
new file mode 100755
index 0000000..a3053a4
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_LAST_REMOVABLE_MEDIA_DEVICES_H
+#define ANDROID_LAST_REMOVABLE_MEDIA_DEVICES_H
+
+#include <vector>
+#include <HwModule.h>
+#include <system/audio_policy.h>
+
+namespace android {
+
+typedef enum {
+ GROUP_NONE = -1,
+ GROUP_WIRED,
+ GROUP_BT_A2DP,
+ NUM_GROUP
+} device_out_group_t;
+
+class LastRemovableMediaDevices
+{
+public:
+ void setRemovableMediaDevices(sp<DeviceDescriptor> desc, audio_policy_dev_state_t state);
+ std::vector<audio_devices_t> getLastRemovableMediaDevices(
+ device_out_group_t group = GROUP_NONE) const;
+
+private:
+ struct DeviceGroupDescriptor {
+ sp<DeviceDescriptor> desc;
+ device_out_group_t group;
+ };
+ std::vector<DeviceGroupDescriptor> mMediaDevices;
+
+ device_out_group_t getDeviceOutGroup(audio_devices_t device) const;
+};
+
+} // namespace android
+
+#endif // ANDROID_LAST_REMOVABLE_MEDIA_DEVICES_H
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 840eb34..45c43d8 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -63,6 +63,17 @@
return NO_ERROR;
}
+status_t EngineBase::setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
+ audio_policy_dev_state_t state)
+{
+ audio_devices_t deviceType = devDesc->type();
+ if ((deviceType != AUDIO_DEVICE_NONE) && audio_is_output_device(deviceType)) {
+ mLastRemovableMediaDevices.setRemovableMediaDevices(devDesc, state);
+ }
+
+ return NO_ERROR;
+}
+
product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attributes_t &attr) const
{
return mProductStrategies.getProductStrategyForAttributes(attr);
diff --git a/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
new file mode 100755
index 0000000..87b6aaf
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "APM::AudioPolicyEngine/LastRemovableMediaDevices"
+//#define LOG_NDEBUG 0
+
+#include "LastRemovableMediaDevices.h"
+#include <log/log.h>
+
+namespace android {
+
+void LastRemovableMediaDevices::setRemovableMediaDevices(sp<DeviceDescriptor> desc,
+ audio_policy_dev_state_t state)
+{
+ if (desc == nullptr) {
+ return;
+ } else {
+ if ((state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) &&
+ (getDeviceOutGroup(desc->type()) != GROUP_NONE)) {
+ setRemovableMediaDevices(desc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE);
+ mMediaDevices.insert(mMediaDevices.begin(), {desc, getDeviceOutGroup(desc->type())});
+ } else if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
+ for (auto iter = mMediaDevices.begin(); iter != mMediaDevices.end(); ++iter) {
+ if ((iter->desc)->equals(desc)) {
+ mMediaDevices.erase(iter);
+ break;
+ }
+ }
+ }
+ }
+}
+
+std::vector<audio_devices_t> LastRemovableMediaDevices::getLastRemovableMediaDevices(
+ device_out_group_t group) const
+{
+ std::vector<audio_devices_t> ret;
+ for (auto iter = mMediaDevices.begin(); iter != mMediaDevices.end(); ++iter) {
+ if ((group == GROUP_NONE) || (group == getDeviceOutGroup((iter->desc)->type()))) {
+ ret.push_back((iter->desc)->type());
+ }
+ }
+ return ret;
+}
+
+device_out_group_t LastRemovableMediaDevices::getDeviceOutGroup(audio_devices_t device) const
+{
+ switch (device) {
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+ case AUDIO_DEVICE_OUT_LINE:
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+ case AUDIO_DEVICE_OUT_USB_HEADSET:
+ case AUDIO_DEVICE_OUT_USB_ACCESSORY:
+ case AUDIO_DEVICE_OUT_USB_DEVICE:
+ case AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET:
+ return GROUP_WIRED;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+ return GROUP_BT_A2DP;
+ default:
+ return GROUP_NONE;
+ }
+}
+
+} // namespace android
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 0a88685..752ba92 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -176,7 +176,7 @@
return mPolicyParameterMgr->setAvailableInputDevices(
deviceTypesToBitMask(getApmObserver()->getAvailableInputDevices().types()));
}
- return BAD_TYPE;
+ return EngineBase::setDeviceConnectionState(devDesc, state);
}
status_t Engine::loadAudioPolicyEngineConfig()
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
old mode 100644
new mode 100755
index 2a5cd49..808d100
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -387,22 +387,20 @@
devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
}
if ((devices2.isEmpty()) &&
- (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
- outputs.isA2dpSupported()) {
- devices2 = availableOutputDevices.getFirstDevicesFromTypes({
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER});
- }
- if ((devices2.isEmpty()) &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
}
- if (devices2.isEmpty()) {
- devices2 = availableOutputDevices.getFirstDevicesFromTypes({
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE, AUDIO_DEVICE_OUT_LINE,
- AUDIO_DEVICE_OUT_WIRED_HEADSET, AUDIO_DEVICE_OUT_USB_HEADSET,
- AUDIO_DEVICE_OUT_USB_ACCESSORY, AUDIO_DEVICE_OUT_USB_DEVICE,
- AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET});
+ if (devices2.isEmpty() && (getLastRemovableMediaDevices().size() > 0)) {
+ if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
+ outputs.isA2dpSupported()) {
+ // Get the last connected device of wired and bluetooth a2dp
+ devices2 = availableOutputDevices.getFirstDevicesFromTypes(
+ getLastRemovableMediaDevices());
+ } else {
+ // Get the last connected device of wired except bluetooth a2dp
+ devices2 = availableOutputDevices.getFirstDevicesFromTypes(
+ getLastRemovableMediaDevices(GROUP_WIRED));
+ }
}
if ((devices2.isEmpty()) && (strategy != STRATEGY_SONIFICATION)) {
// no sonification on aux digital (e.g. HDMI)
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index c8d7d0c..c83512f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -489,7 +489,7 @@
latestActive = current;
latestStartNs = current->startTimeNs;
}
- if (isPrivacySensitiveSource(current->attributes.source)) {
+ if ((current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0) {
if (current->startTimeNs > latestSensitiveStartNs) {
latestSensitiveActive = current;
latestSensitiveStartNs = current->startTimeNs;
@@ -602,19 +602,6 @@
}
/* static */
-bool AudioPolicyService::isPrivacySensitiveSource(audio_source_t source)
-{
- switch (source) {
- case AUDIO_SOURCE_CAMCORDER:
- case AUDIO_SOURCE_VOICE_COMMUNICATION:
- return true;
- default:
- break;
- }
- return false;
-}
-
-/* static */
bool AudioPolicyService::isVirtualSource(audio_source_t source)
{
switch (source) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 2bd02c8..850226e 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -338,7 +338,6 @@
void silenceAllRecordings_l();
- static bool isPrivacySensitiveSource(audio_source_t source);
static bool isVirtualSource(audio_source_t source);
// If recording we need to make sure the UID is allowed to do that. If the UID is idle
diff --git a/services/mediaanalytics/tests/mediametrics_tests.cpp b/services/mediaanalytics/tests/mediametrics_tests.cpp
deleted file mode 100644
index fc02767..0000000
--- a/services/mediaanalytics/tests/mediametrics_tests.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#define LOG_TAG "mediametrics_tests"
-#include <utils/Log.h>
-
-#include "MediaAnalyticsService.h"
-
-#include <stdio.h>
-
-#include <gtest/gtest.h>
-#include <media/MediaAnalyticsItem.h>
-
-using namespace android;
-
-TEST(mediametrics_tests, instantiate) {
- sp mediaMetrics = new MediaAnalyticsService();
- status_t status;
-
- // random keys ignored when empty
- std::unique_ptr<MediaAnalyticsItem> random_key(MediaAnalyticsItem::create("random_key"));
- status = mediaMetrics->submit(random_key.get());
- ASSERT_EQ(PERMISSION_DENIED, status);
-
- // random keys ignored with data
- random_key->setInt32("foo", 10);
- status = mediaMetrics->submit(random_key.get());
- ASSERT_EQ(PERMISSION_DENIED, status);
-
- // known keys ignored if empty
- std::unique_ptr<MediaAnalyticsItem> audiotrack_key(MediaAnalyticsItem::create("audiotrack"));
- status = mediaMetrics->submit(audiotrack_key.get());
- ASSERT_EQ(BAD_VALUE, status);
-
- // known keys not ignored if not empty
- audiotrack_key->addInt32("foo", 10);
- status = mediaMetrics->submit(audiotrack_key.get());
- ASSERT_EQ(NO_ERROR, status);
-
-
- /*
- // fluent style that goes directly to mediametrics
- ASSERT_EQ(true, MediaAnalyticsItem("audiorecord")
- .setInt32("value", 2)
- .addInt32("bar", 1)
- .addInt32("value", 3)
- .selfrecord());
- */
-
- mediaMetrics->dump(fileno(stdout), {} /* args */);
-}
-
-TEST(mediametrics_tests, item_manipulation) {
- MediaAnalyticsItem item("audiorecord");
-
- item.setInt32("value", 2).addInt32("bar", 3).addInt32("value", 4);
-
- int32_t i32;
- ASSERT_TRUE(item.getInt32("value", &i32));
- ASSERT_EQ(6, i32);
-
- ASSERT_TRUE(item.getInt32("bar", &i32));
- ASSERT_EQ(3, i32);
-
- item.setInt64("big", INT64_MAX).setInt64("smaller", INT64_MAX - 1).addInt64("smaller", -2);
-
- int64_t i64;
- ASSERT_TRUE(item.getInt64("big", &i64));
- ASSERT_EQ(INT64_MAX, i64);
-
- ASSERT_TRUE(item.getInt64("smaller", &i64));
- ASSERT_EQ(INT64_MAX - 3, i64);
-
- item.setDouble("precise", 10.5).setDouble("small", 0.125).addDouble("precise", 0.25);
-
- double d;
- ASSERT_TRUE(item.getDouble("precise", &d));
- ASSERT_EQ(10.75, d);
-
- ASSERT_TRUE(item.getDouble("small", &d));
- ASSERT_EQ(0.125, d);
-
- char *s;
- item.setCString("name", "Frank").setCString("mother", "June").setCString("mother", "July");
- ASSERT_TRUE(item.getCString("name", &s));
- ASSERT_EQ(0, strcmp(s, "Frank"));
- free(s);
-
- ASSERT_TRUE(item.getCString("mother", &s));
- ASSERT_EQ(0, strcmp(s, "July")); // "July" overwrites "June"
- free(s);
-
- item.setRate("burgersPerHour", 5, 2);
- int64_t b, h;
- ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
- ASSERT_EQ(5, b);
- ASSERT_EQ(2, h);
- ASSERT_EQ(2.5, d);
-
- item.addRate("burgersPerHour", 4, 2);
- ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
- ASSERT_EQ(9, b);
- ASSERT_EQ(4, h);
- ASSERT_EQ(2.25, d);
-
- printf("item: %s\n", item.toString().c_str());
- fflush(stdout);
-
- sp mediaMetrics = new MediaAnalyticsService();
- status_t status = mediaMetrics->submit(&item);
- ASSERT_EQ(NO_ERROR, status);
- mediaMetrics->dump(fileno(stdout), {} /* args */);
-}
-
-TEST(mediametrics_tests, superbig_item) {
- MediaAnalyticsItem item("TheBigOne");
- constexpr size_t count = 10000;
-
- for (size_t i = 0; i < count; ++i) {
- item.setInt32(std::to_string(i).c_str(), i);
- }
- for (size_t i = 0; i < count; ++i) {
- int32_t i32;
- ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
- ASSERT_EQ((int32_t)i, i32);
- }
-}
-
-TEST(mediametrics_tests, superbig_item_removal) {
- MediaAnalyticsItem item("TheOddBigOne");
- constexpr size_t count = 10000;
-
- for (size_t i = 0; i < count; ++i) {
- item.setInt32(std::to_string(i).c_str(), i);
- }
- for (size_t i = 0; i < count; i += 2) {
- item.filter(std::to_string(i).c_str()); // filter out all the evens.
- }
- for (size_t i = 0; i < count; ++i) {
- int32_t i32;
- if (i & 1) { // check to see that only the odds are left.
- ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
- ASSERT_EQ((int32_t)i, i32);
- } else {
- ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
- }
- }
-}
-
-TEST(mediametrics_tests, superbig_item_removal2) {
- MediaAnalyticsItem item("TheOne");
- constexpr size_t count = 10000;
-
- for (size_t i = 0; i < count; ++i) {
- item.setInt32(std::to_string(i).c_str(), i);
- }
- static const char *attrs[] = { "1", };
- item.filterNot(1, attrs);
-
- for (size_t i = 0; i < count; ++i) {
- int32_t i32;
- if (i == 1) { // check to see that there is only one
- ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
- ASSERT_EQ((int32_t)i, i32);
- } else {
- ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
- }
- }
-}
-
-TEST(mediametrics_tests, item_transmutation) {
- MediaAnalyticsItem item("Alchemist's Stone");
-
- item.setInt64("convert", 123);
- int64_t i64;
- ASSERT_TRUE(item.getInt64("convert", &i64));
- ASSERT_EQ(123, i64);
-
- item.addInt32("convert", 2); // changes type of 'convert' from i64 to i32 (and re-init).
- ASSERT_FALSE(item.getInt64("convert", &i64)); // should be false, no value in i64.
-
- int32_t i32;
- ASSERT_TRUE(item.getInt32("convert", &i32)); // check it is i32 and 2 (123 is discarded).
- ASSERT_EQ(2, i32);
-}
-
-TEST(mediametrics_tests, item_binderization) {
- MediaAnalyticsItem item;
- item.setInt32("i32", 1)
- .setInt64("i64", 2)
- .setDouble("double", 3.1)
- .setCString("string", "abc")
- .setRate("rate", 11, 12);
-
- Parcel p;
- item.writeToParcel(&p);
-
- p.setDataPosition(0); // rewind for reading
- MediaAnalyticsItem item2;
- item2.readFromParcel(p);
-
- ASSERT_EQ(item, item2);
-}
-
-TEST(mediametrics_tests, item_byteserialization) {
- MediaAnalyticsItem item;
- item.setInt32("i32", 1)
- .setInt64("i64", 2)
- .setDouble("double", 3.1)
- .setCString("string", "abc")
- .setRate("rate", 11, 12);
-
- char *data;
- size_t length;
- ASSERT_EQ(0, item.writeToByteString(&data, &length));
- ASSERT_GT(length, (size_t)0);
-
- MediaAnalyticsItem item2;
- item2.readFromByteString(data, length);
-
- printf("item: %s\n", item.toString().c_str());
- printf("item2: %s\n", item2.toString().c_str());
- ASSERT_EQ(item, item2);
-
- free(data);
-}
-
-TEST(mediametrics_tests, item_iteration) {
- MediaAnalyticsItem item;
- item.setInt32("i32", 1)
- .setInt64("i64", 2)
- .setDouble("double", 3.125)
- .setCString("string", "abc")
- .setRate("rate", 11, 12);
-
- int mask = 0;
- for (auto &prop : item) {
- const char *name = prop.getName();
- if (!strcmp(name, "i32")) {
- int32_t i32;
- ASSERT_TRUE(prop.get(&i32));
- ASSERT_EQ(1, i32);
- mask |= 1;
- } else if (!strcmp(name, "i64")) {
- int64_t i64;
- ASSERT_TRUE(prop.get(&i64));
- ASSERT_EQ(2, i64);
- mask |= 2;
- } else if (!strcmp(name, "double")) {
- double d;
- ASSERT_TRUE(prop.get(&d));
- ASSERT_EQ(3.125, d);
- mask |= 4;
- } else if (!strcmp(name, "string")) {
- const char *s;
- ASSERT_TRUE(prop.get(&s));
- ASSERT_EQ(0, strcmp(s, "abc"));
- mask |= 8;
- } else if (!strcmp(name, "rate")) {
- std::pair<int64_t, int64_t> r;
- ASSERT_TRUE(prop.get(&r));
- ASSERT_EQ(11, r.first);
- ASSERT_EQ(12, r.second);
- mask |= 16;
- } else {
- FAIL();
- }
- }
- ASSERT_EQ(31, mask);
-}
-
-TEST(mediametrics_tests, item_expansion) {
- mediametrics::Item<1> item("I");
- item.set("i32", (int32_t)1)
- .set("i64", (int64_t)2)
- .set("double", (double)3.125)
- .set("string", "abcdefghijklmnopqrstuvwxyz")
- .set("rate", std::pair<int64_t, int64_t>(11, 12));
- ASSERT_TRUE(item.updateHeader());
-
- MediaAnalyticsItem item2;
- item2.readFromByteString(item.getBuffer(), item.getLength());
- ASSERT_EQ((pid_t)-1, item2.getPid());
- ASSERT_EQ((uid_t)-1, item2.getUid());
- int mask = 0;
- for (auto &prop : item2) {
- const char *name = prop.getName();
- if (!strcmp(name, "i32")) {
- int32_t i32;
- ASSERT_TRUE(prop.get(&i32));
- ASSERT_EQ(1, i32);
- mask |= 1;
- } else if (!strcmp(name, "i64")) {
- int64_t i64;
- ASSERT_TRUE(prop.get(&i64));
- ASSERT_EQ(2, i64);
- mask |= 2;
- } else if (!strcmp(name, "double")) {
- double d;
- ASSERT_TRUE(prop.get(&d));
- ASSERT_EQ(3.125, d);
- mask |= 4;
- } else if (!strcmp(name, "string")) {
- const char *s;
- ASSERT_TRUE(prop.get(&s));
- ASSERT_EQ(0, strcmp(s, "abcdefghijklmnopqrstuvwxyz"));
- mask |= 8;
- } else if (!strcmp(name, "rate")) {
- std::pair<int64_t, int64_t> r;
- ASSERT_TRUE(prop.get(&r));
- ASSERT_EQ(11, r.first);
- ASSERT_EQ(12, r.second);
- mask |= 16;
- } else {
- FAIL();
- }
- }
- ASSERT_EQ(31, mask);
-}
-
-TEST(mediametrics_tests, item_expansion2) {
- mediametrics::Item<1> item("Bigly");
- item.setPid(123)
- .setUid(456);
- constexpr size_t count = 10000;
-
- for (size_t i = 0; i < count; ++i) {
- // printf("recording %zu, %p, len:%zu of %zu remaining:%zu \n", i, item.getBuffer(), item.getLength(), item.getCapacity(), item.getRemaining());
- item.set(std::to_string(i).c_str(), (int32_t)i);
- }
- ASSERT_TRUE(item.updateHeader());
-
- MediaAnalyticsItem item2;
- printf("begin buffer:%p length:%zu\n", item.getBuffer(), item.getLength());
- fflush(stdout);
- item2.readFromByteString(item.getBuffer(), item.getLength());
-
- ASSERT_EQ((pid_t)123, item2.getPid());
- ASSERT_EQ((uid_t)456, item2.getUid());
- for (size_t i = 0; i < count; ++i) {
- int32_t i32;
- ASSERT_TRUE(item2.getInt32(std::to_string(i).c_str(), &i32));
- ASSERT_EQ((int32_t)i, i32);
- }
-}
diff --git a/services/mediaanalytics/Android.bp b/services/mediametrics/Android.bp
similarity index 88%
rename from services/mediaanalytics/Android.bp
rename to services/mediametrics/Android.bp
index dc72064..0840f31 100644
--- a/services/mediaanalytics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -11,7 +11,7 @@
shared_libs: [
"libbinder",
"liblog",
- "libmediaanalyticsservice",
+ "libmediametricsservice",
"libutils",
],
@@ -27,11 +27,12 @@
}
cc_library_shared {
- name: "libmediaanalyticsservice",
+ name: "libmediametricsservice",
srcs: [
+ "AudioAnalytics.cpp",
"iface_statsd.cpp",
- "MediaAnalyticsService.cpp",
+ "MediaMetricsService.cpp",
"statsd_audiopolicy.cpp",
"statsd_audiorecord.cpp",
"statsd_audiothread.cpp",
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
new file mode 100644
index 0000000..638c4ab
--- /dev/null
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioAnalytics"
+#include <utils/Log.h>
+
+#include "AudioAnalytics.h"
+
+#include <audio_utils/clock.h> // clock conversions
+
+namespace android::mediametrics {
+
+AudioAnalytics::AudioAnalytics()
+{
+ ALOGD("%s", __func__);
+}
+
+AudioAnalytics::~AudioAnalytics()
+{
+ ALOGD("%s", __func__);
+}
+
+status_t AudioAnalytics::submit(
+ const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted)
+{
+ if (startsWith(item->getKey(), "audio.")) {
+ return mTimeMachine.put(item, isTrusted)
+ ?: mTransactionLog.put(item);
+ }
+ return BAD_VALUE;
+}
+
+std::pair<std::string, int32_t> AudioAnalytics::dump(int32_t lines) const
+{
+ std::stringstream ss;
+ int32_t ll = lines;
+
+ if (ll > 0) {
+ ss << "TransactionLog:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTransactionLog.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ if (ll > 0) {
+ ss << "TimeMachine:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTimeMachine.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ return { ss.str(), lines - ll };
+}
+
+} // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
new file mode 100644
index 0000000..366a809
--- /dev/null
+++ b/services/mediametrics/AudioAnalytics.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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 "TimeMachine.h"
+#include "TransactionLog.h"
+
+namespace android::mediametrics {
+
+class AudioAnalytics
+{
+public:
+ AudioAnalytics();
+ ~AudioAnalytics();
+
+ // TODO: update with conditions for keys.
+ /**
+ * Returns success if AudioAnalytics recognizes item.
+ *
+ * AudioAnalytics requires the item key to start with "audio.".
+ *
+ * A trusted source can create a new key, an untrusted source
+ * can only modify the key if the uid will match that authorized
+ * on the existing key.
+ *
+ * \param item the item to be submitted.
+ * \param isTrusted whether the transaction comes from a trusted source.
+ * In this case, a trusted source is verified by binder
+ * UID to be a system service by MediaMetrics service.
+ * Do not use true if you haven't really checked!
+ */
+ status_t submit(const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted);
+
+ /**
+ * Returns a pair consisting of the dump string, and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * The TimeMachine and the TransactionLog are dumped separately under
+ * different locks, so may not be 100% consistent with the last data
+ * delivered.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
+
+private:
+ // The following are locked internally
+ TimeMachine mTimeMachine;
+ TransactionLog mTransactionLog;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediametrics/MediaMetricsService.cpp
similarity index 91%
rename from services/mediaanalytics/MediaAnalyticsService.cpp
rename to services/mediametrics/MediaMetricsService.cpp
index f1f4761..b735b81 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -18,12 +18,12 @@
#define LOG_TAG "MediaAnalyticsService"
#include <utils/Log.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include <pwd.h> //getpwuid
-#include <audio_utils/clock.h> // clock conversions
#include <android/content/pm/IPackageManagerNative.h> // package info
+#include <audio_utils/clock.h> // clock conversions
#include <binder/IPCThreadState.h> // get calling uid
#include <cutils/properties.h> // for property_get
#include <private/android_filesystem_config.h> // UID
@@ -51,6 +51,12 @@
// TODO: need to look at tuning kMaxRecords and friends for low-memory devices
+/* static */
+nsecs_t MediaAnalyticsService::roundTime(nsecs_t timeNs)
+{
+ return (timeNs + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
+}
+
MediaAnalyticsService::MediaAnalyticsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
@@ -70,38 +76,38 @@
status_t MediaAnalyticsService::submitInternal(MediaAnalyticsItem *item, bool release)
{
- // we control these, generally not trusting user input
- nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- // round nsecs to seconds
- now = (now + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
- // TODO: if we convert to boot time, do we need to round timestamp?
- item->setTimestamp(now);
+ // calling PID is 0 for one-way calls.
+ const pid_t pid = IPCThreadState::self()->getCallingPid();
+ const pid_t pid_given = item->getPid();
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ const uid_t uid_given = item->getUid();
- const int pid = IPCThreadState::self()->getCallingPid();
- const int uid = IPCThreadState::self()->getCallingUid();
- const int uid_given = item->getUid();
- const int pid_given = item->getPid();
+ //ALOGD("%s: caller pid=%d uid=%d, item pid=%d uid=%d", __func__,
+ // (int)pid, (int)uid, (int) pid_given, (int)uid_given);
- ALOGV("%s: caller has uid=%d, embedded uid=%d", __func__, uid, uid_given);
bool isTrusted;
switch (uid) {
+ case AID_AUDIOSERVER:
+ case AID_BLUETOOTH:
+ case AID_CAMERA:
case AID_DRM:
case AID_MEDIA:
case AID_MEDIA_CODEC:
case AID_MEDIA_EX:
case AID_MEDIA_DRM:
+ case AID_SYSTEM:
// trusted source, only override default values
isTrusted = true;
- if (uid_given == -1) {
+ if (uid_given == (uid_t)-1) {
item->setUid(uid);
}
- if (pid_given == -1) {
- item->setPid(pid);
+ if (pid_given == (pid_t)-1) {
+ item->setPid(pid); // if one-way then this is 0.
}
break;
default:
isTrusted = false;
- item->setPid(pid);
+ item->setPid(pid); // always use calling pid, if one-way then this is 0.
item->setUid(uid);
break;
}
@@ -140,9 +146,21 @@
return BAD_VALUE;
}
+ if (!isTrusted || item->getTimestamp() == 0) {
+ // WestWorld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
+ // WallClockTimeNs (REALTIME). The new audio keys use BOOTTIME.
+ //
+ // TODO: Reevaluate time base with other teams.
+ const bool useBootTime = startsWith(item->getKey(), "audio.");
+ const int64_t now = systemTime(useBootTime ? SYSTEM_TIME_BOOTTIME : SYSTEM_TIME_REALTIME);
+ item->setTimestamp(now);
+ }
+
// now attach either the item or its dup to a const shared pointer
std::shared_ptr<const MediaAnalyticsItem> sitem(release ? item : item->dup());
+ (void)mAudioAnalytics.submit(sitem, isTrusted);
+
extern bool dump2Statsd(const std::shared_ptr<const MediaAnalyticsItem>& item);
(void)dump2Statsd(sitem); // failure should be logged in function.
saveItem(sitem);
@@ -247,6 +265,9 @@
mItems.clear();
// shall we clear the summary data too?
}
+ // TODO: maybe consider a better way of dumping audio analytics info.
+ constexpr int32_t linesToDump = 1000;
+ result.append(mAudioAnalytics.dump(linesToDump).first.c_str());
}
write(fd, result.string(), result.size());
@@ -403,11 +424,14 @@
if (isTrusted) return true;
// untrusted uids can only send us a limited set of keys
const std::string &key = item->getKey();
+ if (startsWith(key, "audio.")) return true;
for (const char *allowedKey : {
+ // legacy audio
"audiopolicy",
"audiorecord",
"audiothread",
"audiotrack",
+ // other media
"codec",
"extractor",
"nuplayer",
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediametrics/MediaMetricsService.h
similarity index 95%
rename from services/mediaanalytics/MediaAnalyticsService.h
rename to services/mediametrics/MediaMetricsService.h
index 4a8b971..5bdc48f 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -26,6 +26,8 @@
#include <media/IMediaAnalyticsService.h>
#include <utils/String8.h>
+#include "AudioAnalytics.h"
+
namespace android {
class MediaAnalyticsService : public BnMediaAnalyticsService
@@ -55,6 +57,11 @@
static constexpr const char * const kServiceName = "media.metrics";
+ /**
+ * Rounds time to the nearest second.
+ */
+ static nsecs_t roundTime(nsecs_t timeNs);
+
protected:
// Internal call where release is true if ownership of item is transferred
@@ -110,6 +117,8 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
+ mediametrics::AudioAnalytics mAudioAnalytics;
+
std::mutex mLock;
// statistics about our analytics
int64_t mItemsFinalized = 0; // GUARDED_BY(mLock)
diff --git a/services/mediaanalytics/OWNERS b/services/mediametrics/OWNERS
similarity index 100%
rename from services/mediaanalytics/OWNERS
rename to services/mediametrics/OWNERS
diff --git a/services/mediaanalytics/TEST_MAPPING b/services/mediametrics/TEST_MAPPING
similarity index 100%
rename from services/mediaanalytics/TEST_MAPPING
rename to services/mediametrics/TEST_MAPPING
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
new file mode 100644
index 0000000..578b838
--- /dev/null
+++ b/services/mediametrics/TimeMachine.h
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2019 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 <any>
+#include <map>
+#include <sstream>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Timers.h>
+
+namespace android::mediametrics {
+
+// define a way of printing the monostate
+inline std::ostream & operator<< (std::ostream& s,
+ std::monostate const& v __unused) {
+ s << "none_item";
+ return s;
+}
+
+// define a way of printing a variant
+// see https://en.cppreference.com/w/cpp/utility/variant/visit
+template <typename T0, typename ... Ts>
+std::ostream & operator<< (std::ostream& s,
+ std::variant<T0, Ts...> const& v) {
+ std::visit([&s](auto && arg){ s << std::forward<decltype(arg)>(arg); }, v);
+ return s;
+}
+
+/**
+ * The TimeMachine is used to record timing changes of MediaAnalyticItem
+ * properties.
+ *
+ * Any URL that ends with '!' will have a time sequence that keeps duplicates.
+ *
+ * The TimeMachine is NOT thread safe.
+ */
+class TimeMachine {
+
+ using Elem = std::variant<std::monostate, int32_t, int64_t, double, std::string>;
+ using PropertyHistory = std::multimap<int64_t /* time */, Elem>;
+
+ // KeyHistory contains no lock.
+ // Access is through the TimeMachine, and a hash-striped lock is used
+ // before calling into KeyHistory.
+ class KeyHistory {
+ public:
+ template <typename T>
+ KeyHistory(T key, pid_t pid, uid_t uid, int64_t time)
+ : mKey(key)
+ , mPid(pid)
+ , mUid(uid)
+ , mCreationTime(time)
+ , mLastModificationTime(time)
+ {
+ putValue("_pid", (int32_t)pid, time);
+ putValue("_uid", (int32_t)uid, time);
+ }
+
+ status_t checkPermission(uid_t uidCheck) const {
+ return uidCheck != (uid_t)-1 && uidCheck != mUid ? PERMISSION_DENIED : NO_ERROR;
+ }
+
+ template <typename T>
+ status_t getValue(const std::string &property, T* value, int64_t time = 0) const {
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ const auto tsptr = mPropertyMap.find(property);
+ if (tsptr == mPropertyMap.end()) return BAD_VALUE;
+ const auto& timeSequence = tsptr->second;
+ auto eptr = timeSequence.upper_bound(time);
+ if (eptr == timeSequence.begin()) return BAD_VALUE;
+ --eptr;
+ if (eptr == timeSequence.end()) return BAD_VALUE;
+ const T* vptr = std::get_if<T>(&eptr->second);
+ if (vptr == nullptr) return BAD_VALUE;
+ *value = *vptr;
+ return NO_ERROR;
+ }
+
+ template <typename T>
+ status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const {
+ T value;
+ return getValue(property, &value, time) != NO_ERROR ? defaultValue : value;
+ }
+
+ void putProp(
+ const std::string &name, const MediaAnalyticsItem::Prop &prop, int64_t time = 0) {
+ prop.visit([&](auto value) { putValue(name, value, time); });
+ }
+
+ template <typename T>
+ void putValue(const std::string &property,
+ T&& e, int64_t time = 0) {
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ mLastModificationTime = time;
+ auto& timeSequence = mPropertyMap[property];
+ Elem el{std::forward<T>(e)};
+ if (timeSequence.empty() // no elements
+ || property.back() == '!' // keep duplicates TODO: remove?
+ || timeSequence.rbegin()->second != el) { // value changed
+ timeSequence.emplace(time, std::move(el));
+ }
+ }
+
+ // Explicitly ignore rate properties - we don't expose them for now.
+ void putValue(
+ const std::string &property __unused,
+ std::pair<int64_t, int64_t>& e __unused,
+ int64_t time __unused) {
+ }
+
+ std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+ for (auto& tsPair : mPropertyMap) {
+ if (ll <= 0) break;
+ ss << dump(mKey, tsPair, time);
+ --ll;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ int64_t getLastModificationTime() const { return mLastModificationTime; }
+
+ private:
+ static std::string dump(
+ const std::string &key,
+ const std::pair<std::string /* prop */, PropertyHistory>& tsPair,
+ int64_t time) {
+ const auto timeSequence = tsPair.second;
+ auto eptr = timeSequence.lower_bound(time);
+ if (eptr == timeSequence.end()) {
+ return tsPair.first + "={};\n";
+ }
+ std::stringstream ss;
+ ss << key << "." << tsPair.first << "={";
+ do {
+ ss << eptr->first << ":" << eptr->second << ",";
+ } while (++eptr != timeSequence.end());
+ ss << "};\n";
+ return ss.str();
+ }
+
+ const std::string mKey;
+ const pid_t mPid __unused;
+ const uid_t mUid;
+ const int64_t mCreationTime __unused;
+
+ int64_t mLastModificationTime;
+ std::map<std::string /* property */, PropertyHistory> mPropertyMap;
+ };
+
+ using History = std::map<std::string /* key */, std::shared_ptr<KeyHistory>>;
+
+ static inline constexpr size_t kKeyLowWaterMark = 500;
+ static inline constexpr size_t kKeyHighWaterMark = 1000;
+
+ // Estimated max data space usage is 3KB * kKeyHighWaterMark.
+
+public:
+
+ TimeMachine() = default;
+ TimeMachine(size_t keyLowWaterMark, size_t keyHighWaterMark)
+ : mKeyLowWaterMark(keyLowWaterMark)
+ , mKeyHighWaterMark(keyHighWaterMark) {
+ LOG_ALWAYS_FATAL_IF(keyHighWaterMark <= keyLowWaterMark,
+ "%s: required that keyHighWaterMark:%zu > keyLowWaterMark:%zu",
+ __func__, keyHighWaterMark, keyLowWaterMark);
+ }
+
+ /**
+ * Put all the properties from an item into the Time Machine log.
+ */
+ status_t put(const std::shared_ptr<const MediaAnalyticsItem>& item, bool isTrusted = false) {
+ const int64_t time = item->getTimestamp();
+ const std::string &key = item->getKey();
+
+ std::shared_ptr<KeyHistory> keyHistory;
+ {
+ std::vector<std::any> garbage;
+ std::lock_guard lock(mLock);
+
+ auto it = mHistory.find(key);
+ if (it == mHistory.end()) {
+ if (!isTrusted) return PERMISSION_DENIED;
+
+ (void)gc_l(garbage);
+
+ // no keylock needed here as we are sole owner
+ // until placed on mHistory.
+ keyHistory = std::make_shared<KeyHistory>(
+ key, item->getPid(), item->getUid(), time);
+ mHistory[key] = keyHistory;
+ } else {
+ keyHistory = it->second;
+ }
+ }
+
+ // deferred contains remote properties (for other keys) to do later.
+ std::vector<const MediaAnalyticsItem::Prop *> deferred;
+ {
+ // handle local properties
+ std::lock_guard lock(getLockForKey(key));
+ if (!isTrusted) {
+ status_t status = keyHistory->checkPermission(item->getUid());
+ if (status != NO_ERROR) return status;
+ }
+
+ for (const auto &prop : *item) {
+ const std::string &name = prop.getName();
+ if (name.size() == 0 || name[0] == '_') continue;
+
+ // Cross key settings are with [key]property
+ if (name[0] == '[') {
+ if (!isTrusted) continue;
+ deferred.push_back(&prop);
+ } else {
+ keyHistory->putProp(name, prop, time);
+ }
+ }
+ }
+
+ // handle remote properties, if any
+ for (const auto propptr : deferred) {
+ const auto &prop = *propptr;
+ const std::string &name = prop.getName();
+ size_t end = name.find_first_of(']'); // TODO: handle nested [] or escape?
+ if (end == 0) continue;
+ std::string remoteKey = name.substr(1, end - 1);
+ std::string remoteName = name.substr(end + 1);
+ if (remoteKey.size() == 0 || remoteName.size() == 0) continue;
+ std::shared_ptr<KeyHistory> remoteKeyHistory;
+ {
+ std::lock_guard lock(mLock);
+ auto it = mHistory.find(remoteKey);
+ if (it == mHistory.end()) continue;
+ remoteKeyHistory = it->second;
+ }
+ std::lock_guard(getLockForKey(remoteKey));
+ remoteKeyHistory->putProp(remoteName, prop, time);
+ }
+ return NO_ERROR;
+ }
+
+ template <typename T>
+ status_t get(const std::string &key, const std::string &property,
+ T* value, int32_t uidCheck = -1, int64_t time = 0) const {
+ std::shared_ptr<KeyHistory> keyHistory;
+ {
+ std::lock_guard lock(mLock);
+ const auto it = mHistory.find(key);
+ if (it == mHistory.end()) return BAD_VALUE;
+ keyHistory = it->second;
+ }
+ std::lock_guard lock(getLockForKey(key));
+ return keyHistory->checkPermission(uidCheck)
+ ?: keyHistory->getValue(property, value, time);
+ }
+
+ /**
+ * Individual property put.
+ *
+ * Put takes in a time (if none is provided then BOOTTIME is used).
+ */
+ template <typename T>
+ status_t put(const std::string &url, T &&e, int64_t time = 0) {
+ std::string key;
+ std::string prop;
+ std::shared_ptr<KeyHistory> keyHistory =
+ getKeyHistoryFromUrl(url, &key, &prop);
+ if (keyHistory == nullptr) return BAD_VALUE;
+ if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
+ std::lock_guard lock(getLockForKey(key));
+ keyHistory->putValue(prop, std::forward<T>(e), time);
+ return NO_ERROR;
+ }
+
+ /**
+ * Individual property get
+ */
+ template <typename T>
+ status_t get(const std::string &url, T* value, int32_t uidCheck, int64_t time = 0) const {
+ std::string key;
+ std::string prop;
+ std::shared_ptr<KeyHistory> keyHistory =
+ getKeyHistoryFromUrl(url, &key, &prop);
+ if (keyHistory == nullptr) return BAD_VALUE;
+
+ std::lock_guard lock(getLockForKey(key));
+ return keyHistory->checkPermission(uidCheck)
+ ?: keyHistory->getValue(prop, value, time);
+ }
+
+ /**
+ * Individual property get with default
+ */
+ template <typename T>
+ T get(const std::string &url, const T &defaultValue, int32_t uidCheck,
+ int64_t time = 0) const {
+ T value;
+ return get(url, &value, uidCheck, time) == NO_ERROR
+ ? value : defaultValue;
+ }
+
+ /**
+ * Returns number of keys in the Time Machine.
+ */
+ size_t size() const {
+ std::lock_guard lock(mLock);
+ return mHistory.size();
+ }
+
+ /**
+ * Clears all properties from the Time Machine.
+ */
+ void clear() {
+ std::lock_guard lock(mLock);
+ mHistory.clear();
+ }
+
+ /**
+ * Returns a pair consisting of the TimeMachine state as a string
+ * and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ * \param key selects only that key.
+ * \param time to start the dump from.
+ */
+ std::pair<std::string, int32_t> dump(
+ int32_t lines = INT32_MAX, const std::string &key = {}, int64_t time = 0) const {
+ std::lock_guard lock(mLock);
+ if (!key.empty()) { // use std::regex
+ const auto it = mHistory.find(key);
+ if (it == mHistory.end()) return {};
+ std::lock_guard lock(getLockForKey(it->first));
+ return it->second->dump(lines, time);
+ }
+
+ std::stringstream ss;
+ int32_t ll = lines;
+ for (const auto &keyPair : mHistory) {
+ std::lock_guard lock(getLockForKey(keyPair.first));
+ if (lines <= 0) break;
+ auto [s, l] = keyPair.second->dump(ll, time);
+ ss << s;
+ ll -= l;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+private:
+
+ // Obtains the lock for a KeyHistory.
+ std::mutex &getLockForKey(const std::string &key) const {
+ return mKeyLocks[std::hash<std::string>{}(key) % std::size(mKeyLocks)];
+ }
+
+ // Finds a KeyHistory from a URL. Returns nullptr if not found.
+ std::shared_ptr<KeyHistory> getKeyHistoryFromUrl(
+ std::string url, std::string* key, std::string *prop) const {
+ std::lock_guard lock(mLock);
+
+ auto it = mHistory.upper_bound(url);
+ if (it == mHistory.begin()) {
+ return nullptr;
+ }
+ --it; // go to the actual key, if it exists.
+
+ const std::string& itKey = it->first;
+ if (strncmp(itKey.c_str(), url.c_str(), itKey.size())) {
+ return nullptr;
+ }
+ if (key) *key = itKey;
+ if (prop) *prop = url.substr(itKey.size() + 1);
+ return it->second;
+ }
+
+ // GUARDED_BY mLock
+ /**
+ * Garbage collects if the TimeMachine size exceeds the high water mark.
+ *
+ * \param garbage a type-erased vector of elements to be destroyed
+ * outside of lock. Move large items to be destroyed here.
+ *
+ * \return true if garbage collection was done.
+ */
+ bool gc_l(std::vector<std::any>& garbage) {
+ // TODO: something better than this for garbage collection.
+ if (mHistory.size() < mKeyHighWaterMark) return false;
+
+ ALOGD("%s: garbage collection", __func__);
+
+ // erase everything explicitly expired.
+ std::multimap<int64_t, std::string> accessList;
+ // use a stale vector with precise type to avoid type erasure overhead in garbage
+ std::vector<std::shared_ptr<KeyHistory>> stale;
+
+ for (auto it = mHistory.begin(); it != mHistory.end();) {
+ const std::string& key = it->first;
+ std::shared_ptr<KeyHistory> &keyHist = it->second;
+
+ std::lock_guard lock(getLockForKey(it->first));
+ int64_t expireTime = keyHist->getValue("_expire", -1 /* default */);
+ if (expireTime != -1) {
+ stale.emplace_back(std::move(it->second));
+ it = mHistory.erase(it);
+ } else {
+ accessList.emplace(keyHist->getLastModificationTime(), key);
+ ++it;
+ }
+ }
+
+ if (mHistory.size() > mKeyLowWaterMark) {
+ const size_t toDelete = mHistory.size() - mKeyLowWaterMark;
+ auto it = accessList.begin();
+ for (size_t i = 0; i < toDelete; ++i) {
+ auto it2 = mHistory.find(it->second);
+ stale.emplace_back(std::move(it2->second));
+ mHistory.erase(it2);
+ ++it;
+ }
+ }
+ garbage.emplace_back(std::move(accessList));
+ garbage.emplace_back(std::move(stale));
+
+ ALOGD("%s(%zu, %zu): key size:%zu",
+ __func__, mKeyLowWaterMark, mKeyHighWaterMark,
+ mHistory.size());
+ return true;
+ }
+
+ const size_t mKeyLowWaterMark = kKeyLowWaterMark;
+ const size_t mKeyHighWaterMark = kKeyHighWaterMark;
+
+ /**
+ * Locking Strategy
+ *
+ * Each key in the History has a KeyHistory. To get a shared pointer to
+ * the KeyHistory requires a lookup of mHistory under mLock. Once the shared
+ * pointer to KeyHistory is obtained, the mLock for mHistory can be released.
+ *
+ * Once the shared pointer to the key's KeyHistory is obtained, the KeyHistory
+ * can be locked for read and modification through the method getLockForKey().
+ *
+ * Instead of having a mutex per KeyHistory, we use a hash striped lock
+ * which assigns a mutex based on the hash of the key string.
+ *
+ * Once the last shared pointer reference to KeyHistory is released, it is
+ * destroyed. This is done through the garbage collection method.
+ *
+ * This two level locking allows multiple threads to access the TimeMachine
+ * in parallel.
+ */
+
+ mutable std::mutex mLock; // Lock for mHistory
+ History mHistory; // GUARDED_BY mLock
+
+ // KEY_LOCKS is the number of mutexes for keys.
+ // It need not be a power of 2, but faster that way.
+ static inline constexpr size_t KEY_LOCKS = 256;
+ mutable std::mutex mKeyLocks[KEY_LOCKS]; // Hash-striped lock for KeyHistory based on key.
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
new file mode 100644
index 0000000..ca37862
--- /dev/null
+++ b/services/mediametrics/TransactionLog.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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 <any>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <media/MediaAnalyticsItem.h>
+
+namespace android::mediametrics {
+
+/**
+ * The TransactionLog is used to record MediaAnalyticsItems to present
+ * different views on the time information (selected by audio, and sorted by key).
+ *
+ * The TransactionLog will always present data in timestamp order. (Perhaps we
+ * just make this submit order).
+ *
+ * These Views have a cost in shared pointer storage, so they aren't quite free.
+ *
+ * The TransactionLog is NOT thread safe.
+ */
+class TransactionLog {
+public:
+ // In long term run, the garbage collector aims to keep the
+ // Transaction Log between the Low Water Mark and the High Water Mark.
+
+ // low water mark
+ static inline constexpr size_t kLogItemsLowWater = 5000;
+ // high water mark
+ static inline constexpr size_t kLogItemsHighWater = 10000;
+
+ // Estimated max data usage is 1KB * kLogItemsHighWater.
+
+ TransactionLog() = default;
+
+ TransactionLog(size_t lowWaterMark, size_t highWaterMark)
+ : mLowWaterMark(lowWaterMark)
+ , mHighWaterMark(highWaterMark) {
+ LOG_ALWAYS_FATAL_IF(highWaterMark <= lowWaterMark,
+ "%s: required that highWaterMark:%zu > lowWaterMark:%zu",
+ __func__, highWaterMark, lowWaterMark);
+ }
+
+ /**
+ * Put an item in the TransactionLog.
+ */
+ status_t put(const std::shared_ptr<const MediaAnalyticsItem>& item) {
+ const std::string& key = item->getKey();
+ const int64_t time = item->getTimestamp();
+
+ std::vector<std::any> garbage; // objects destroyed after lock.
+ std::lock_guard lock(mLock);
+
+ (void)gc_l(garbage);
+ mLog.emplace(time, item);
+ mItemMap[key].emplace(time, item);
+ return NO_ERROR; // no errors for now.
+ }
+
+ /**
+ * Returns all records within [startTime, endTime]
+ */
+ std::vector<std::shared_ptr<const MediaAnalyticsItem>> get(
+ int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
+ std::lock_guard lock(mLock);
+ return getItemsInRange_l(mLog, startTime, endTime);
+ }
+
+ /**
+ * Returns all records for a key within [startTime, endTime]
+ */
+ std::vector<std::shared_ptr<const MediaAnalyticsItem>> get(
+ const std::string& key,
+ int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
+ std::lock_guard lock(mLock);
+ auto mapIt = mItemMap.find(key);
+ if (mapIt == mItemMap.end()) return {};
+ return getItemsInRange_l(mapIt->second, startTime, endTime);
+ }
+
+ /**
+ * Returns a pair consisting of the Transaction Log as a string
+ * and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+ std::lock_guard lock(mLock);
+
+ // All audio items in time order.
+ if (ll > 0) {
+ ss << "Consolidated:\n";
+ --ll;
+ }
+ for (const auto &log : mLog) {
+ if (ll <= 0) break;
+ ss << " " << log.second->toString() << "\n";
+ --ll;
+ }
+
+ // Grouped by item key (category)
+ if (ll > 0) {
+ ss << "Categorized:\n";
+ --ll;
+ }
+ for (const auto &itemMap : mItemMap) {
+ if (ll <= 0) break;
+ ss << " " << itemMap.first << "\n";
+ --ll;
+ for (const auto &item : itemMap.second) {
+ if (ll <= 0) break;
+ ss << " { " << item.first << ", " << item.second->toString() << " }\n";
+ --ll;
+ }
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ /**
+ * Returns number of Items in the TransactionLog.
+ */
+ size_t size() const {
+ std::lock_guard lock(mLock);
+ return mLog.size();
+ }
+
+ /**
+ * Clears all Items from the TransactionLog.
+ */
+ // TODO: Garbage Collector, sweep and expire old values
+ void clear() {
+ std::lock_guard lock(mLock);
+ mLog.clear();
+ mItemMap.clear();
+ }
+
+private:
+ using MapTimeItem =
+ std::multimap<int64_t /* time */, std::shared_ptr<const MediaAnalyticsItem>>;
+
+ // GUARDED_BY mLock
+ /**
+ * Garbage collects if the TimeMachine size exceeds the high water mark.
+ *
+ * \param garbage a type-erased vector of elements to be destroyed
+ * outside of lock. Move large items to be destroyed here.
+ *
+ * \return true if garbage collection was done.
+ */
+ bool gc_l(std::vector<std::any>& garbage) {
+ if (mLog.size() < mHighWaterMark) return false;
+
+ ALOGD("%s: garbage collection", __func__);
+
+ auto eraseEnd = mLog.begin();
+ size_t toRemove = mLog.size() - mLowWaterMark;
+ // remove at least those elements.
+
+ // use a stale vector with precise type to avoid type erasure overhead in garbage
+ std::vector<std::shared_ptr<const MediaAnalyticsItem>> stale;
+
+ for (size_t i = 0; i < toRemove; ++i) {
+ stale.emplace_back(std::move(eraseEnd->second));
+ ++eraseEnd; // amortized O(1)
+ }
+ // ensure that eraseEnd is an lower bound on timeToErase.
+ const int64_t timeToErase = eraseEnd->first;
+ while (eraseEnd != mLog.end()) {
+ auto it = eraseEnd;
+ --it; // amortized O(1)
+ if (it->first != timeToErase) {
+ break; // eraseEnd represents a unique time jump.
+ }
+ stale.emplace_back(std::move(eraseEnd->second));
+ ++eraseEnd;
+ }
+
+ mLog.erase(mLog.begin(), eraseEnd); // O(ptr_diff)
+
+ size_t itemMapCount = 0;
+ for (auto it = mItemMap.begin(); it != mItemMap.end();) {
+ auto &keyHist = it->second;
+ auto it2 = keyHist.lower_bound(timeToErase);
+ if (it2 == keyHist.end()) {
+ garbage.emplace_back(std::move(keyHist)); // directly move keyhist to garbage
+ it = mItemMap.erase(it);
+ } else {
+ for (auto it3 = keyHist.begin(); it3 != it2; ++it3) {
+ stale.emplace_back(std::move(it3->second));
+ }
+ keyHist.erase(keyHist.begin(), it2);
+ itemMapCount += keyHist.size();
+ ++it;
+ }
+ }
+
+ garbage.emplace_back(std::move(stale));
+
+ ALOGD("%s(%zu, %zu): log size:%zu item map size:%zu, item map items:%zu",
+ __func__, mLowWaterMark, mHighWaterMark,
+ mLog.size(), mItemMap.size(), itemMapCount);
+ return true;
+ }
+
+ static std::vector<std::shared_ptr<const MediaAnalyticsItem>> getItemsInRange_l(
+ const MapTimeItem& map,
+ int64_t startTime = 0, int64_t endTime = INT64_MAX) {
+ auto it = map.lower_bound(startTime);
+ if (it == map.end()) return {};
+
+ auto it2 = map.upper_bound(endTime);
+
+ std::vector<std::shared_ptr<const MediaAnalyticsItem>> ret;
+ while (it != it2) {
+ ret.push_back(it->second);
+ ++it;
+ }
+ return ret;
+ }
+
+ const size_t mLowWaterMark = kLogItemsHighWater;
+ const size_t mHighWaterMark = kLogItemsHighWater;
+
+ mutable std::mutex mLock;
+
+ // GUARDED_BY mLock
+ MapTimeItem mLog;
+
+ // GUARDED_BY mLock
+ std::map<std::string /* item_key */, MapTimeItem> mItemMap;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediaanalytics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
similarity index 98%
rename from services/mediaanalytics/iface_statsd.cpp
rename to services/mediametrics/iface_statsd.cpp
index dccc76a..962cafe 100644
--- a/services/mediaanalytics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -31,7 +31,7 @@
#include <string.h>
#include <pwd.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "iface_statsd.h"
#include <statslog.h>
diff --git a/services/mediaanalytics/iface_statsd.h b/services/mediametrics/iface_statsd.h
similarity index 100%
rename from services/mediaanalytics/iface_statsd.h
rename to services/mediametrics/iface_statsd.h
diff --git a/services/mediaanalytics/main_mediametrics.cpp b/services/mediametrics/main_mediametrics.cpp
similarity index 97%
rename from services/mediaanalytics/main_mediametrics.cpp
rename to services/mediametrics/main_mediametrics.cpp
index 6833fe2..4b2a9fa 100644
--- a/services/mediaanalytics/main_mediametrics.cpp
+++ b/services/mediametrics/main_mediametrics.cpp
@@ -18,7 +18,7 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
diff --git a/services/mediaanalytics/mediametrics.rc b/services/mediametrics/mediametrics.rc
similarity index 100%
rename from services/mediaanalytics/mediametrics.rc
rename to services/mediametrics/mediametrics.rc
diff --git a/services/mediaanalytics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_audiopolicy.cpp
rename to services/mediametrics/statsd_audiopolicy.cpp
index 2c11100..2f934f8 100644
--- a/services/mediaanalytics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_audiorecord.cpp
rename to services/mediametrics/statsd_audiorecord.cpp
index 6a80dd0..4e2829d 100644
--- a/services/mediaanalytics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
similarity index 98%
rename from services/mediaanalytics/statsd_audiothread.cpp
rename to services/mediametrics/statsd_audiothread.cpp
index e62899b..a5bfcba 100644
--- a/services/mediaanalytics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_audiotrack.cpp
rename to services/mediametrics/statsd_audiotrack.cpp
index 10bf298..9d76d43 100644
--- a/services/mediaanalytics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_codec.cpp
rename to services/mediametrics/statsd_codec.cpp
index 519091f..fde0bfa 100644
--- a/services/mediaanalytics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
similarity index 93%
rename from services/mediaanalytics/statsd_drm.cpp
rename to services/mediametrics/statsd_drm.cpp
index 5c81ebf..78d0a22 100644
--- a/services/mediaanalytics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -30,7 +30,7 @@
#include <string.h>
#include <pwd.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "iface_statsd.h"
#include <statslog.h>
@@ -42,7 +42,7 @@
{
if (item == NULL) return false;
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
@@ -79,7 +79,7 @@
{
if (item == NULL) return false;
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
@@ -109,7 +109,7 @@
{
if (item == NULL) return false;
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
similarity index 95%
rename from services/mediaanalytics/statsd_extractor.cpp
rename to services/mediametrics/statsd_extractor.cpp
index 55d5c29..cc62241 100644
--- a/services/mediaanalytics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
similarity index 97%
rename from services/mediaanalytics/statsd_nuplayer.cpp
rename to services/mediametrics/statsd_nuplayer.cpp
index 791a125..9db1e81 100644
--- a/services/mediaanalytics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -46,7 +46,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
similarity index 98%
rename from services/mediaanalytics/statsd_recorder.cpp
rename to services/mediametrics/statsd_recorder.cpp
index e6383e6..972a221 100644
--- a/services/mediaanalytics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -31,7 +31,7 @@
#include <statslog.h>
-#include "MediaAnalyticsService.h"
+#include "MediaMetricsService.h"
#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
@@ -42,7 +42,7 @@
if (item == NULL) return false;
// these go into the statsd wrapper
- nsecs_t timestamp = item->getTimestamp();
+ const nsecs_t timestamp = MediaAnalyticsService::roundTime(item->getTimestamp());
std::string pkgName = item->getPkgName();
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
diff --git a/services/mediaanalytics/tests/Android.bp b/services/mediametrics/tests/Android.bp
similarity index 80%
rename from services/mediaanalytics/tests/Android.bp
rename to services/mediametrics/tests/Android.bp
index a8a330c..9eb2d89 100644
--- a/services/mediaanalytics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -9,14 +9,14 @@
],
include_dirs: [
- "frameworks/av/services/mediaanalytics",
+ "frameworks/av/services/mediametrics",
],
shared_libs: [
"libbinder",
"liblog",
- "libmediaanalyticsservice",
"libmediametrics",
+ "libmediametricsservice",
"libutils",
],
diff --git a/services/mediaanalytics/tests/build_and_run_all_unit_tests.sh b/services/mediametrics/tests/build_and_run_all_unit_tests.sh
similarity index 100%
rename from services/mediaanalytics/tests/build_and_run_all_unit_tests.sh
rename to services/mediametrics/tests/build_and_run_all_unit_tests.sh
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
new file mode 100644
index 0000000..808da7b
--- /dev/null
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "mediametrics_tests"
+#include <utils/Log.h>
+
+#include "MediaMetricsService.h"
+
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+#include <media/MediaAnalyticsItem.h>
+
+using namespace android;
+
+static size_t countNewlines(const char *s) {
+ size_t count = 0;
+ while ((s = strchr(s, '\n')) != nullptr) {
+ ++s;
+ ++count;
+ }
+ return count;
+}
+
+TEST(mediametrics_tests, instantiate) {
+ sp mediaMetrics = new MediaAnalyticsService();
+ status_t status;
+
+ // random keys ignored when empty
+ std::unique_ptr<MediaAnalyticsItem> random_key(MediaAnalyticsItem::create("random_key"));
+ status = mediaMetrics->submit(random_key.get());
+ ASSERT_EQ(PERMISSION_DENIED, status);
+
+ // random keys ignored with data
+ random_key->setInt32("foo", 10);
+ status = mediaMetrics->submit(random_key.get());
+ ASSERT_EQ(PERMISSION_DENIED, status);
+
+ // known keys ignored if empty
+ std::unique_ptr<MediaAnalyticsItem> audiotrack_key(MediaAnalyticsItem::create("audiotrack"));
+ status = mediaMetrics->submit(audiotrack_key.get());
+ ASSERT_EQ(BAD_VALUE, status);
+
+ // known keys not ignored if not empty
+ audiotrack_key->addInt32("foo", 10);
+ status = mediaMetrics->submit(audiotrack_key.get());
+ ASSERT_EQ(NO_ERROR, status);
+
+
+ /*
+ // fluent style that goes directly to mediametrics
+ ASSERT_EQ(true, MediaAnalyticsItem("audiorecord")
+ .setInt32("value", 2)
+ .addInt32("bar", 1)
+ .addInt32("value", 3)
+ .selfrecord());
+ */
+
+ mediaMetrics->dump(fileno(stdout), {} /* args */);
+}
+
+TEST(mediametrics_tests, item_manipulation) {
+ MediaAnalyticsItem item("audiorecord");
+
+ item.setInt32("value", 2).addInt32("bar", 3).addInt32("value", 4);
+
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32("value", &i32));
+ ASSERT_EQ(6, i32);
+
+ ASSERT_TRUE(item.getInt32("bar", &i32));
+ ASSERT_EQ(3, i32);
+
+ item.setInt64("big", INT64_MAX).setInt64("smaller", INT64_MAX - 1).addInt64("smaller", -2);
+
+ int64_t i64;
+ ASSERT_TRUE(item.getInt64("big", &i64));
+ ASSERT_EQ(INT64_MAX, i64);
+
+ ASSERT_TRUE(item.getInt64("smaller", &i64));
+ ASSERT_EQ(INT64_MAX - 3, i64);
+
+ item.setDouble("precise", 10.5).setDouble("small", 0.125).addDouble("precise", 0.25);
+
+ double d;
+ ASSERT_TRUE(item.getDouble("precise", &d));
+ ASSERT_EQ(10.75, d);
+
+ ASSERT_TRUE(item.getDouble("small", &d));
+ ASSERT_EQ(0.125, d);
+
+ char *s;
+ item.setCString("name", "Frank").setCString("mother", "June").setCString("mother", "July");
+ ASSERT_TRUE(item.getCString("name", &s));
+ ASSERT_EQ(0, strcmp(s, "Frank"));
+ free(s);
+
+ ASSERT_TRUE(item.getCString("mother", &s));
+ ASSERT_EQ(0, strcmp(s, "July")); // "July" overwrites "June"
+ free(s);
+
+ item.setRate("burgersPerHour", 5, 2);
+ int64_t b, h;
+ ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
+ ASSERT_EQ(5, b);
+ ASSERT_EQ(2, h);
+ ASSERT_EQ(2.5, d);
+
+ item.addRate("burgersPerHour", 4, 2);
+ ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
+ ASSERT_EQ(9, b);
+ ASSERT_EQ(4, h);
+ ASSERT_EQ(2.25, d);
+
+ printf("item: %s\n", item.toString().c_str());
+ fflush(stdout);
+
+ sp mediaMetrics = new MediaAnalyticsService();
+ status_t status = mediaMetrics->submit(&item);
+ ASSERT_EQ(NO_ERROR, status);
+ mediaMetrics->dump(fileno(stdout), {} /* args */);
+}
+
+TEST(mediametrics_tests, superbig_item) {
+ MediaAnalyticsItem item("TheBigOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ }
+}
+
+TEST(mediametrics_tests, superbig_item_removal) {
+ MediaAnalyticsItem item("TheOddBigOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ for (size_t i = 0; i < count; i += 2) {
+ item.filter(std::to_string(i).c_str()); // filter out all the evens.
+ }
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ if (i & 1) { // check to see that only the odds are left.
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ } else {
+ ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
+ }
+ }
+}
+
+TEST(mediametrics_tests, superbig_item_removal2) {
+ MediaAnalyticsItem item("TheOne");
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ item.setInt32(std::to_string(i).c_str(), i);
+ }
+ static const char *attrs[] = { "1", };
+ item.filterNot(1, attrs);
+
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ if (i == 1) { // check to see that there is only one
+ ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ } else {
+ ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
+ }
+ }
+}
+
+TEST(mediametrics_tests, item_transmutation) {
+ MediaAnalyticsItem item("Alchemist's Stone");
+
+ item.setInt64("convert", 123);
+ int64_t i64;
+ ASSERT_TRUE(item.getInt64("convert", &i64));
+ ASSERT_EQ(123, i64);
+
+ item.addInt32("convert", 2); // changes type of 'convert' from i64 to i32 (and re-init).
+ ASSERT_FALSE(item.getInt64("convert", &i64)); // should be false, no value in i64.
+
+ int32_t i32;
+ ASSERT_TRUE(item.getInt32("convert", &i32)); // check it is i32 and 2 (123 is discarded).
+ ASSERT_EQ(2, i32);
+}
+
+TEST(mediametrics_tests, item_binderization) {
+ MediaAnalyticsItem item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.1)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ Parcel p;
+ item.writeToParcel(&p);
+
+ p.setDataPosition(0); // rewind for reading
+ MediaAnalyticsItem item2;
+ item2.readFromParcel(p);
+
+ ASSERT_EQ(item, item2);
+}
+
+TEST(mediametrics_tests, item_byteserialization) {
+ MediaAnalyticsItem item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.1)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ char *data;
+ size_t length;
+ ASSERT_EQ(0, item.writeToByteString(&data, &length));
+ ASSERT_GT(length, (size_t)0);
+
+ MediaAnalyticsItem item2;
+ item2.readFromByteString(data, length);
+
+ printf("item: %s\n", item.toString().c_str());
+ printf("item2: %s\n", item2.toString().c_str());
+ ASSERT_EQ(item, item2);
+
+ free(data);
+}
+
+TEST(mediametrics_tests, item_iteration) {
+ MediaAnalyticsItem item;
+ item.setInt32("i32", 1)
+ .setInt64("i64", 2)
+ .setDouble("double", 3.125)
+ .setCString("string", "abc")
+ .setRate("rate", 11, 12);
+
+ int mask = 0;
+ for (auto &prop : item) {
+ const char *name = prop.getName();
+ if (!strcmp(name, "i32")) {
+ int32_t i32;
+ ASSERT_TRUE(prop.get(&i32));
+ ASSERT_EQ(1, i32);
+ mask |= 1;
+ } else if (!strcmp(name, "i64")) {
+ int64_t i64;
+ ASSERT_TRUE(prop.get(&i64));
+ ASSERT_EQ(2, i64);
+ mask |= 2;
+ } else if (!strcmp(name, "double")) {
+ double d;
+ ASSERT_TRUE(prop.get(&d));
+ ASSERT_EQ(3.125, d);
+ mask |= 4;
+ } else if (!strcmp(name, "string")) {
+ const char *s;
+ ASSERT_TRUE(prop.get(&s));
+ ASSERT_EQ(0, strcmp(s, "abc"));
+ mask |= 8;
+ } else if (!strcmp(name, "rate")) {
+ std::pair<int64_t, int64_t> r;
+ ASSERT_TRUE(prop.get(&r));
+ ASSERT_EQ(11, r.first);
+ ASSERT_EQ(12, r.second);
+ mask |= 16;
+ } else {
+ FAIL();
+ }
+ }
+ ASSERT_EQ(31, mask);
+}
+
+TEST(mediametrics_tests, item_expansion) {
+ mediametrics::Item<1> item("I");
+ item.set("i32", (int32_t)1)
+ .set("i64", (int64_t)2)
+ .set("double", (double)3.125)
+ .set("string", "abcdefghijklmnopqrstuvwxyz")
+ .set("rate", std::pair<int64_t, int64_t>(11, 12));
+ ASSERT_TRUE(item.updateHeader());
+
+ MediaAnalyticsItem item2;
+ item2.readFromByteString(item.getBuffer(), item.getLength());
+ ASSERT_EQ((pid_t)-1, item2.getPid());
+ ASSERT_EQ((uid_t)-1, item2.getUid());
+ int mask = 0;
+ for (auto &prop : item2) {
+ const char *name = prop.getName();
+ if (!strcmp(name, "i32")) {
+ int32_t i32;
+ ASSERT_TRUE(prop.get(&i32));
+ ASSERT_EQ(1, i32);
+ mask |= 1;
+ } else if (!strcmp(name, "i64")) {
+ int64_t i64;
+ ASSERT_TRUE(prop.get(&i64));
+ ASSERT_EQ(2, i64);
+ mask |= 2;
+ } else if (!strcmp(name, "double")) {
+ double d;
+ ASSERT_TRUE(prop.get(&d));
+ ASSERT_EQ(3.125, d);
+ mask |= 4;
+ } else if (!strcmp(name, "string")) {
+ const char *s;
+ ASSERT_TRUE(prop.get(&s));
+ ASSERT_EQ(0, strcmp(s, "abcdefghijklmnopqrstuvwxyz"));
+ mask |= 8;
+ } else if (!strcmp(name, "rate")) {
+ std::pair<int64_t, int64_t> r;
+ ASSERT_TRUE(prop.get(&r));
+ ASSERT_EQ(11, r.first);
+ ASSERT_EQ(12, r.second);
+ mask |= 16;
+ } else {
+ FAIL();
+ }
+ }
+ ASSERT_EQ(31, mask);
+}
+
+TEST(mediametrics_tests, item_expansion2) {
+ mediametrics::Item<1> item("Bigly");
+ item.setPid(123)
+ .setUid(456);
+ constexpr size_t count = 10000;
+
+ for (size_t i = 0; i < count; ++i) {
+ // printf("recording %zu, %p, len:%zu of %zu remaining:%zu \n", i, item.getBuffer(), item.getLength(), item.getCapacity(), item.getRemaining());
+ item.set(std::to_string(i).c_str(), (int32_t)i);
+ }
+ ASSERT_TRUE(item.updateHeader());
+
+ MediaAnalyticsItem item2;
+ printf("begin buffer:%p length:%zu\n", item.getBuffer(), item.getLength());
+ fflush(stdout);
+ item2.readFromByteString(item.getBuffer(), item.getLength());
+
+ ASSERT_EQ((pid_t)123, item2.getPid());
+ ASSERT_EQ((uid_t)456, item2.getUid());
+ for (size_t i = 0; i < count; ++i) {
+ int32_t i32;
+ ASSERT_TRUE(item2.getInt32(std::to_string(i).c_str(), &i32));
+ ASSERT_EQ((int32_t)i, i32);
+ }
+}
+
+TEST(mediametrics_tests, time_machine_storage) {
+ auto item = std::make_shared<MediaAnalyticsItem>("Key");
+ (*item).set("i32", (int32_t)1)
+ .set("i64", (int64_t)2)
+ .set("double", (double)3.125)
+ .set("string", "abcdefghijklmnopqrstuvwxyz")
+ .set("rate", std::pair<int64_t, int64_t>(11, 12));
+
+ // Let's put the item in
+ android::mediametrics::TimeMachine timeMachine;
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "i32", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ int64_t i64;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "i64", &i64, -1));
+ ASSERT_EQ(2, i64);
+
+ double d;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "double", &d, -1));
+ ASSERT_EQ(3.125, d);
+
+ std::string s;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key", "string", &s, -1));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
+
+ // Using fully qualified name?
+ i32 = 0;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.i32", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ i64 = 0;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.i64", &i64, -1));
+ ASSERT_EQ(2, i64);
+
+ d = 0.;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.double", &d, -1));
+ ASSERT_EQ(3.125, d);
+
+ s.clear();
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key.string", &s, -1));
+ ASSERT_EQ("abcdefghijklmnopqrstuvwxyz", s);
+}
+
+TEST(mediametrics_tests, time_machine_remote_key) {
+ auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2);
+
+ android::mediametrics::TimeMachine timeMachine;
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
+
+ auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ (*item2).set("three", (int32_t)3)
+ .set("[Key1]four", (int32_t)4) // affects Key1
+ .set("[Key1]five", (int32_t)5); // affects key1
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item2, true));
+
+ auto item3 = std::make_shared<MediaAnalyticsItem>("Key2");
+ (*item3).set("six", (int32_t)6)
+ .set("[Key1]seven", (int32_t)7); // affects Key1
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item3, false)); // remote keys not allowed.
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.one", &i32, -1));
+ ASSERT_EQ(1, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.two", &i32, -1));
+ ASSERT_EQ(2, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.three", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.three", &i32, -1));
+ ASSERT_EQ(3, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.four", &i32, -1));
+ ASSERT_EQ(4, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.four", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key1.five", &i32, -1));
+ ASSERT_EQ(5, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.five", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.six", &i32, -1));
+ ASSERT_EQ(6, i32);
+
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key2.seven", &i32, -1));
+}
+
+TEST(mediametrics_tests, time_machine_gc) {
+ auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ android::mediametrics::TimeMachine timeMachine(1, 2); // keep at most 2 keys.
+
+ ASSERT_EQ((size_t)0, timeMachine.size());
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item, true));
+
+ ASSERT_EQ((size_t)1, timeMachine.size());
+
+ auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ (*item2).set("three", (int32_t)3)
+ .set("[Key1]three", (int32_t)3)
+ .setTimestamp(11);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item2, true));
+ ASSERT_EQ((size_t)2, timeMachine.size());
+
+ //printf("Before\n%s\n\n", timeMachine.dump().c_str());
+
+ auto item3 = std::make_shared<MediaAnalyticsItem>("Key3");
+ (*item3).set("six", (int32_t)6)
+ .set("[Key1]four", (int32_t)4) // affects Key1
+ .set("[Key1]five", (int32_t)5) // affects key1
+ .setTimestamp(12);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.put(item3, true));
+
+ ASSERT_EQ((size_t)2, timeMachine.size());
+
+ // Can we read the values?
+ int32_t i32;
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.one", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.two", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.three", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.four", &i32, -1));
+ ASSERT_EQ(BAD_VALUE, timeMachine.get("Key1.five", &i32, -1));
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key2.three", &i32, -1));
+ ASSERT_EQ(3, i32);
+
+ ASSERT_EQ(NO_ERROR, timeMachine.get("Key3.six", &i32, -1));
+ ASSERT_EQ(6, i32);
+
+ printf("After\n%s\n", timeMachine.dump().first.c_str());
+}
+
+TEST(mediametrics_tests, transaction_log_gc) {
+ auto item = std::make_shared<MediaAnalyticsItem>("Key1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ android::mediametrics::TransactionLog transactionLog(1, 2); // keep at most 2 items
+ ASSERT_EQ((size_t)0, transactionLog.size());
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item));
+ ASSERT_EQ((size_t)1, transactionLog.size());
+
+ auto item2 = std::make_shared<MediaAnalyticsItem>("Key2");
+ (*item2).set("three", (int32_t)3)
+ .set("[Key1]three", (int32_t)3)
+ .setTimestamp(11);
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item2));
+ ASSERT_EQ((size_t)2, transactionLog.size());
+
+ auto item3 = std::make_shared<MediaAnalyticsItem>("Key3");
+ (*item3).set("six", (int32_t)6)
+ .set("[Key1]four", (int32_t)4) // affects Key1
+ .set("[Key1]five", (int32_t)5) // affects key1
+ .setTimestamp(12);
+
+ ASSERT_EQ(NO_ERROR, transactionLog.put(item3));
+ ASSERT_EQ((size_t)2, transactionLog.size());
+}
+
+TEST(mediametrics_tests, audio_analytics_permission) {
+ auto item = std::make_shared<MediaAnalyticsItem>("audio.1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ auto item2 = std::make_shared<MediaAnalyticsItem>("audio.1");
+ (*item2).set("three", (int32_t)3)
+ .setTimestamp(11);
+
+ auto item3 = std::make_shared<MediaAnalyticsItem>("audio.2");
+ (*item3).set("four", (int32_t)4)
+ .setTimestamp(12);
+
+ android::mediametrics::AudioAnalytics audioAnalytics;
+
+ // untrusted entities cannot create a new key.
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item, false /* isTrusted */));
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item2, false /* isTrusted */));
+
+ // TODO: Verify contents of AudioAnalytics.
+ // Currently there is no getter API in AudioAnalytics besides dump.
+ ASSERT_EQ(4, audioAnalytics.dump(1000).second /* lines */);
+
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
+ // untrusted entities can add to an existing key
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item2, false /* isTrusted */));
+
+ // Check that we have some info in the dump.
+ ASSERT_LT(4, audioAnalytics.dump(1000).second /* lines */);
+}
+
+TEST(mediametrics_tests, audio_analytics_dump) {
+ auto item = std::make_shared<MediaAnalyticsItem>("audio.1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .setTimestamp(10);
+
+ auto item2 = std::make_shared<MediaAnalyticsItem>("audio.1");
+ (*item2).set("three", (int32_t)3)
+ .setTimestamp(11);
+
+ auto item3 = std::make_shared<MediaAnalyticsItem>("audio.2");
+ (*item3).set("four", (int32_t)4)
+ .setTimestamp(12);
+
+ android::mediametrics::AudioAnalytics audioAnalytics;
+
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
+ // untrusted entities can add to an existing key
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item2, false /* isTrusted */));
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item3, true /* isTrusted */));
+
+ // find out how many lines we have.
+ auto [string, lines] = audioAnalytics.dump(1000);
+ ASSERT_EQ(lines, (int32_t) countNewlines(string.c_str()));
+
+ printf("AudioAnalytics: %s", string.c_str());
+ // ensure that dump operates over those lines.
+ for (int32_t ll = 0; ll < lines; ++ll) {
+ auto [s, l] = audioAnalytics.dump(ll);
+ ASSERT_EQ(ll, l);
+ ASSERT_EQ(ll, (int32_t) countNewlines(s.c_str()));
+ }
+}
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 8572561..6e14434 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -75,10 +75,10 @@
std::lock_guard<std::mutex> lock(mLock);
if (mNotificationClients.count(pid) == 0) {
- sp<NotificationClient> notificationClient = new NotificationClient(pid);
+ sp<IBinder> binder = IInterface::asBinder(client);
+ sp<NotificationClient> notificationClient = new NotificationClient(pid, binder);
mNotificationClients[pid] = notificationClient;
- sp<IBinder> binder = IInterface::asBinder(client);
status_t status = binder->linkToDeath(notificationClient);
ALOGW_IF(status != NO_ERROR, "registerClient() linkToDeath = %d\n", status);
return AAudioConvert_androidToAAudioResult(status);
@@ -113,7 +113,7 @@
if (notificationClient == 0) {
// This will get called the first time the audio server registers an internal stream.
ALOGV("registerClientStream(%d,) unrecognized pid\n", pid);
- notificationClient = new NotificationClient(pid);
+ notificationClient = new NotificationClient(pid, nullptr);
mNotificationClients[pid] = notificationClient;
}
notificationClient->registerClientStream(serviceStream);
@@ -136,8 +136,8 @@
return AAUDIO_OK;
}
-AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid)
- : mProcessId(pid) {
+AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid, const sp<IBinder>& binder)
+ : mProcessId(pid), mBinder(binder) {
}
AAudioClientTracker::NotificationClient::~NotificationClient() {
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index accf1a7..00ff467 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -73,7 +73,7 @@
*/
class NotificationClient : public IBinder::DeathRecipient {
public:
- NotificationClient(pid_t pid);
+ NotificationClient(pid_t pid, const android::sp<IBinder>& binder);
virtual ~NotificationClient();
int32_t getStreamCount();
@@ -91,6 +91,8 @@
mutable std::mutex mLock;
const pid_t mProcessId;
std::set<android::sp<AAudioServiceStreamBase>> mStreams;
+ // hold onto binder to receive death notifications
+ android::sp<IBinder> mBinder;
};
mutable std::mutex mLock;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 553754e..2753f1f 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -62,6 +62,7 @@
result << " InputPreset: " << getInputPreset() << "\n";
result << " Reference Count: " << mOpenCount << "\n";
result << " Session Id: " << getSessionId() << "\n";
+ result << " Privacy Sensitive: " << isPrivacySensitive() << "\n";
result << " Connected: " << mConnected.load() << "\n";
result << " Registered Streams:" << "\n";
result << AAudioServiceStreamShared::dumpHeader() << "\n";
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index b05baa4..5bdb8eb 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -90,9 +90,14 @@
const audio_source_t source = (direction == AAUDIO_DIRECTION_INPUT)
? AAudioConvert_inputPresetToAudioSource(getInputPreset())
: AUDIO_SOURCE_DEFAULT;
- const audio_flags_mask_t flags = AUDIO_FLAG_LOW_LATENCY |
- AAudioConvert_allowCapturePolicyToAudioFlagsMask(getAllowedCapturePolicy());
-
+ audio_flags_mask_t flags;
+ if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ flags = AUDIO_FLAG_LOW_LATENCY
+ | AAudioConvert_allowCapturePolicyToAudioFlagsMask(getAllowedCapturePolicy());
+ } else {
+ flags = AUDIO_FLAG_LOW_LATENCY
+ | AAudioConvert_privacySensitiveToAudioFlagsMask(isPrivacySensitive());
+ }
const audio_attributes_t attributes = {
.content_type = contentType,
.usage = usage,