[automerger skipped] RESTRICT AUTOMERGE - resolve merge conflicts of dab37c25e3337387809fd35c7cd46abf76088b83 to qt-qpr1-dev am: ec0754c9b5 -s ours am: 8537cde345 -s ours am: b60b4588d5 -s ours am: d4e19b2899 -s ours am: 946443b1ed -s ours am: d344dffc38 -s ours am: 9a970aeaf8 -s ours am: 4b3c6730cb -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/19823968
Change-Id: Ic9a0707cfd76ccfdeb01776cea7d41790e709201
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index ee609e1..0e0ea1f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,7 +57,7 @@
min_sdk_version: "29",
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
@@ -86,7 +86,7 @@
min_sdk_version: "29",
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
diff --git a/OWNERS b/OWNERS
index 40c65e7..87bc809 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,12 @@
-# Bug component: 1344
+## Media team top-level OWNERS, bug component: 1344
+## Only contact for camera changes as a fallback
elaurent@google.com
-etalvala@google.com
lajos@google.com
# go/android-fwk-media-solutions for info on areas of ownership.
include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
+## Camera team top-level OWNERS, bug component: 41727
+## Only contact for media changes as a fallback
+etalvala@google.com
+shuzhenwang@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 716b550..1f7083b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,3 +11,4 @@
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
media/libmediatranscoding/
services/mediatranscoding/
+ media/libaudioclient/tests/
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index be47898..bb880d1 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -52,7 +52,10 @@
parcel->readInt64(&lastCompletedRegularFrameNumber);
parcel->readInt64(&lastCompletedReprocessFrameNumber);
parcel->readInt64(&lastCompletedZslFrameNumber);
-
+ parcel->readBool(&hasReadoutTimestamp);
+ if (hasReadoutTimestamp) {
+ parcel->readInt64(&readoutTimestamp);
+ }
return OK;
}
@@ -82,6 +85,10 @@
parcel->writeInt64(lastCompletedRegularFrameNumber);
parcel->writeInt64(lastCompletedReprocessFrameNumber);
parcel->writeInt64(lastCompletedZslFrameNumber);
+ parcel->writeBool(hasReadoutTimestamp);
+ if (hasReadoutTimestamp) {
+ parcel->writeInt64(readoutTimestamp);
+ }
return OK;
}
diff --git a/camera/OWNERS b/camera/OWNERS
index 385c163..b705548 100644
--- a/camera/OWNERS
+++ b/camera/OWNERS
@@ -1,7 +1,11 @@
# Bug component: 41727
etalvala@google.com
arakesh@google.com
+borgera@google.com
+bkukreja@google.com
epeev@google.com
jchowdhary@google.com
-shuzhenwang@google.com
+rdhanjal@google.com
ruchamk@google.com
+shuzhenwang@google.com
+
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index b37803a..151b653 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -598,7 +598,6 @@
status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) {
status_t res = OK;
Mutex::Autolock al(sLock);
- sGlobalVendorTagDescriptor = desc;
vendor_tag_ops_t* opsPtr = NULL;
if (desc != NULL) {
@@ -613,6 +612,9 @@
ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)."
, __FUNCTION__, strerror(-res), res);
}
+
+ sGlobalVendorTagDescriptor = desc;
+
return res;
}
@@ -631,7 +633,6 @@
const sp<VendorTagDescriptorCache>& cache) {
status_t res = OK;
Mutex::Autolock al(sLock);
- sGlobalVendorTagDescriptorCache = cache;
struct vendor_tag_cache_ops* opsPtr = NULL;
if (cache != NULL) {
@@ -646,6 +647,9 @@
ALOGE("%s: Could not set vendor tag cache, received error %s (%d)."
, __FUNCTION__, strerror(-res), res);
}
+
+ sGlobalVendorTagDescriptorCache = cache;
+
return res;
}
diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
index 88783fb..fefea13 100644
--- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
@@ -48,5 +48,5 @@
/**
* Checks if the camera has been disabled via device policy.
*/
- boolean isCameraDisabled();
+ boolean isCameraDisabled(int userId);
}
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index 8f51458..6a311f2 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -5,3 +5,4 @@
ioprio rt 4
task_profiles CameraServiceCapacity MaxPerformance
rlimit rtprio 10 10
+ onrestart class_restart cameraWatchdog
diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h
index f163c1e..de534ab 100644
--- a/camera/include/camera/CaptureResult.h
+++ b/camera/include/camera/CaptureResult.h
@@ -103,6 +103,17 @@
*/
int64_t lastCompletedZslFrameNumber;
+ /**
+ * Whether the readoutTimestamp variable is valid and should be used.
+ */
+ bool hasReadoutTimestamp;
+
+ /**
+ * The readout timestamp of the capture. Its value is equal to the
+ * start-of-exposure timestamp plus the exposure time (and a possible fixed
+ * offset due to sensor crop).
+ */
+ int64_t readoutTimestamp;
/**
* Constructor initializes object as invalid by setting requestId to be -1.
@@ -118,7 +129,9 @@
errorPhysicalCameraId(),
lastCompletedRegularFrameNumber(-1),
lastCompletedReprocessFrameNumber(-1),
- lastCompletedZslFrameNumber(-1) {
+ lastCompletedZslFrameNumber(-1),
+ hasReadoutTimestamp(false),
+ readoutTimestamp(0) {
}
/**
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index b842885..b7c7f7f 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -43,7 +43,9 @@
TIMESTAMP_BASE_SENSOR = 1,
TIMESTAMP_BASE_MONOTONIC = 2,
TIMESTAMP_BASE_REALTIME = 3,
- TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4
+ TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4,
+ TIMESTAMP_BASE_READOUT_SENSOR = 5,
+ TIMESTAMP_BASE_MAX = TIMESTAMP_BASE_READOUT_SENSOR,
};
enum MirrorModeType {
MIRROR_MODE_AUTO = 0,
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index b6f8552..0d156a5 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2198,6 +2198,10 @@
* <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#turnOnTorchWithStrengthLevel">CameraManager#turnOnTorchWithStrengthLevel</a>.
* If this value is equal to 1, flashlight brightness control is not supported.
* The value for this key will be null for devices with no flash unit.</p>
+ * <p>The maximum value is guaranteed to be safe to use for an indefinite duration in
+ * terms of device flashlight lifespan, but may be too bright for comfort for many
+ * use cases. Use the default torch brightness value to avoid problems with an
+ * over-bright flashlight.</p>
*/
ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL = // int32
ACAMERA_FLASH_INFO_START + 2,
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 2e0b678..79b1263 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <algorithm>
+#include <string_view>
+#include <type_traits>
#include <assert.h>
#include <ctype.h>
@@ -85,6 +88,7 @@
using android::Vector;
using android::sp;
using android::status_t;
+using android::SurfaceControl;
using android::INVALID_OPERATION;
using android::NAME_NOT_FOUND;
@@ -100,7 +104,6 @@
static const uint32_t kFallbackHeight = 720;
static const char* kMimeTypeAvc = "video/avc";
static const char* kMimeTypeApplicationOctetstream = "application/octet-stream";
-static const char* kWinscopeMagicString = "#VV1NSC0PET1ME!#";
// Command-line parameters.
static bool gVerbose = false; // chatty on stdout
@@ -339,13 +342,20 @@
static status_t prepareVirtualDisplay(
const ui::DisplayState& displayState,
const sp<IGraphicBufferProducer>& bufferProducer,
- sp<IBinder>* pDisplayHandle) {
+ sp<IBinder>* pDisplayHandle, sp<SurfaceControl>* mirrorRoot) {
sp<IBinder> dpy = SurfaceComposerClient::createDisplay(
String8("ScreenRecorder"), false /*secure*/);
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(dpy, bufferProducer);
setDisplayProjection(t, dpy, displayState);
- t.setDisplayLayerStack(dpy, displayState.layerStack);
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(std::rand());
+ t.setDisplayLayerStack(dpy, layerStack);
+ *mirrorRoot = SurfaceComposerClient::getDefault()->mirrorDisplay(gPhysicalDisplayId);
+ if (*mirrorRoot == nullptr) {
+ ALOGE("Failed to create a mirror for screenrecord");
+ return UNKNOWN_ERROR;
+ }
+ t.setLayerStack(*mirrorRoot, layerStack);
t.apply();
*pDisplayHandle = dpy;
@@ -354,14 +364,15 @@
}
/*
- * Writes an unsigned integer byte-by-byte in little endian order regardless
+ * Writes an unsigned/signed integer byte-by-byte in little endian order regardless
* of the platform endianness.
*/
-template <typename UINT>
-static void writeValueLE(UINT value, uint8_t* buffer) {
- for (int i = 0; i < sizeof(UINT); ++i) {
- buffer[i] = static_cast<uint8_t>(value);
- value >>= 8;
+template <typename T>
+static void writeValueLE(T value, uint8_t* buffer) {
+ std::remove_const_t<T> temp = value;
+ for (int i = 0; i < sizeof(T); ++i) {
+ buffer[i] = static_cast<std::uint8_t>(temp & 0xff);
+ temp >>= 8;
}
}
@@ -377,16 +388,18 @@
* - for every frame its presentation time relative to the elapsed realtime clock in microseconds
* (as little endian uint64).
*/
-static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps,
+static status_t writeWinscopeMetadataLegacy(const Vector<int64_t>& timestamps,
const ssize_t metaTrackIdx, AMediaMuxer *muxer) {
- ALOGV("Writing metadata");
+ static constexpr auto kWinscopeMagicStringLegacy = "#VV1NSC0PET1ME!#";
+
+ ALOGV("Writing winscope metadata legacy");
int64_t systemTimeToElapsedTimeOffsetMicros = (android::elapsedRealtimeNano()
- systemTime(SYSTEM_TIME_MONOTONIC)) / 1000;
sp<ABuffer> buffer = new ABuffer(timestamps.size() * sizeof(int64_t)
- + sizeof(uint32_t) + strlen(kWinscopeMagicString));
+ + sizeof(uint32_t) + strlen(kWinscopeMagicStringLegacy));
uint8_t* pos = buffer->data();
- strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicString);
- pos += strlen(kWinscopeMagicString);
+ strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicStringLegacy);
+ pos += strlen(kWinscopeMagicStringLegacy);
writeValueLE<uint32_t>(timestamps.size(), pos);
pos += sizeof(uint32_t);
for (size_t idx = 0; idx < timestamps.size(); ++idx) {
@@ -395,10 +408,68 @@
pos += sizeof(uint64_t);
}
AMediaCodecBufferInfo bufferInfo = {
- 0,
+ 0 /* offset */,
static_cast<int32_t>(buffer->size()),
- timestamps[0],
- 0
+ timestamps[0] /* presentationTimeUs */,
+ 0 /* flags */
+ };
+ return AMediaMuxer_writeSampleData(muxer, metaTrackIdx, buffer->data(), &bufferInfo);
+}
+
+/*
+ * Saves metadata needed by Winscope to synchronize the screen recording playback with other traces.
+ *
+ * The metadata (version 1) is written as a binary array with the following format:
+ * - winscope magic string (#VV1NSC0PET1ME2#, 16B).
+ * - the metadata version number (4B).
+ * - Realtime-to-monotonic time offset in nanoseconds (8B).
+ * - the recorded frames count (8B)
+ * - for each recorded frame:
+ * - System time in monotonic clock timebase in nanoseconds (8B).
+ *
+ * All numbers are Little Endian encoded.
+ */
+static status_t writeWinscopeMetadata(const Vector<std::int64_t>& timestampsMonotonicUs,
+ const ssize_t metaTrackIdx, AMediaMuxer *muxer) {
+ ALOGV("Writing winscope metadata");
+
+ static constexpr auto kWinscopeMagicString = std::string_view {"#VV1NSC0PET1ME2#"};
+ static constexpr std::uint32_t metadataVersion = 1;
+ const std::int64_t realToMonotonicTimeOffsetNs =
+ systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC);
+ const std::uint32_t framesCount = static_cast<std::uint32_t>(timestampsMonotonicUs.size());
+
+ sp<ABuffer> buffer = new ABuffer(
+ kWinscopeMagicString.size() +
+ sizeof(decltype(metadataVersion)) +
+ sizeof(decltype(realToMonotonicTimeOffsetNs)) +
+ sizeof(decltype(framesCount)) +
+ framesCount * sizeof(std::uint64_t)
+ );
+ std::uint8_t* pos = buffer->data();
+
+ std::copy(kWinscopeMagicString.cbegin(), kWinscopeMagicString.cend(), pos);
+ pos += kWinscopeMagicString.size();
+
+ writeValueLE(metadataVersion, pos);
+ pos += sizeof(decltype(metadataVersion));
+
+ writeValueLE(realToMonotonicTimeOffsetNs, pos);
+ pos += sizeof(decltype(realToMonotonicTimeOffsetNs));
+
+ writeValueLE(framesCount, pos);
+ pos += sizeof(decltype(framesCount));
+
+ for (const auto timestampMonotonicUs : timestampsMonotonicUs) {
+ writeValueLE<std::uint64_t>(timestampMonotonicUs * 1000, pos);
+ pos += sizeof(std::uint64_t);
+ }
+
+ AMediaCodecBufferInfo bufferInfo = {
+ 0 /* offset */,
+ static_cast<std::int32_t>(buffer->size()),
+ timestampsMonotonicUs[0] /* presentationTimeUs */,
+ 0 /* flags */
};
return AMediaMuxer_writeSampleData(muxer, metaTrackIdx, buffer->data(), &bufferInfo);
}
@@ -418,11 +489,12 @@
static int kTimeout = 250000; // be responsive on signal
status_t err;
ssize_t trackIdx = -1;
+ ssize_t metaLegacyTrackIdx = -1;
ssize_t metaTrackIdx = -1;
uint32_t debugNumFrames = 0;
int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
- Vector<int64_t> timestamps;
+ Vector<int64_t> timestampsMonotonicUs;
bool firstFrame = true;
assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
@@ -520,9 +592,9 @@
sp<ABuffer> buffer = new ABuffer(
buffers[bufIndex]->data(), buffers[bufIndex]->size());
AMediaCodecBufferInfo bufferInfo = {
- 0,
+ 0 /* offset */,
static_cast<int32_t>(buffer->size()),
- ptsUsec,
+ ptsUsec /* presentationTimeUs */,
flags
};
err = AMediaMuxer_writeSampleData(muxer, trackIdx, buffer->data(), &bufferInfo);
@@ -532,7 +604,7 @@
return err;
}
if (gOutputFormat == FORMAT_MP4) {
- timestamps.add(ptsUsec);
+ timestampsMonotonicUs.add(ptsUsec);
}
}
debugNumFrames++;
@@ -565,6 +637,7 @@
if (gOutputFormat == FORMAT_MP4) {
AMediaFormat *metaFormat = AMediaFormat_new();
AMediaFormat_setString(metaFormat, AMEDIAFORMAT_KEY_MIME, kMimeTypeApplicationOctetstream);
+ metaLegacyTrackIdx = AMediaMuxer_addTrack(muxer, metaFormat);
metaTrackIdx = AMediaMuxer_addTrack(muxer, metaFormat);
AMediaFormat_delete(metaFormat);
}
@@ -604,10 +677,16 @@
systemTime(CLOCK_MONOTONIC) - startWhenNsec));
fflush(stdout);
}
- if (metaTrackIdx >= 0 && !timestamps.isEmpty()) {
- err = writeWinscopeMetadata(timestamps, metaTrackIdx, muxer);
+ if (metaLegacyTrackIdx >= 0 && metaTrackIdx >= 0 && !timestampsMonotonicUs.isEmpty()) {
+ err = writeWinscopeMetadataLegacy(timestampsMonotonicUs, metaLegacyTrackIdx, muxer);
if (err != NO_ERROR) {
- fprintf(stderr, "Failed writing metadata to muxer (err=%d)\n", err);
+ fprintf(stderr, "Failed writing legacy winscope metadata to muxer (err=%d)\n", err);
+ return err;
+ }
+
+ err = writeWinscopeMetadata(timestampsMonotonicUs, metaTrackIdx, muxer);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed writing winscope metadata to muxer (err=%d)\n", err);
return err;
}
}
@@ -656,6 +735,23 @@
return num & ~1;
}
+struct RecordingData {
+ sp<MediaCodec> encoder;
+ // Configure virtual display.
+ sp<IBinder> dpy;
+
+ sp<Overlay> overlay;
+
+ ~RecordingData() {
+ if (dpy != nullptr) SurfaceComposerClient::destroyDisplay(dpy);
+ if (overlay != nullptr) overlay->stop();
+ if (encoder != nullptr) {
+ encoder->stop();
+ encoder->release();
+ }
+ }
+};
+
/*
* Main "do work" start point.
*
@@ -713,12 +809,12 @@
gVideoHeight = floorToEven(layerStackSpaceRect.getHeight());
}
+ RecordingData recordingData = RecordingData();
// Configure and start the encoder.
- sp<MediaCodec> encoder;
sp<FrameOutput> frameOutput;
sp<IGraphicBufferProducer> encoderInputSurface;
if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
- err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface);
+ err = prepareEncoder(displayMode.refreshRate, &recordingData.encoder, &encoderInputSurface);
if (err != NO_ERROR && !gSizeSpecified) {
// fallback is defined for landscape; swap if we're in portrait
@@ -731,7 +827,8 @@
gVideoWidth, gVideoHeight, newWidth, newHeight);
gVideoWidth = newWidth;
gVideoHeight = newHeight;
- err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface);
+ err = prepareEncoder(displayMode.refreshRate, &recordingData.encoder,
+ &encoderInputSurface);
}
}
if (err != NO_ERROR) return err;
@@ -758,13 +855,11 @@
// Configure optional overlay.
sp<IGraphicBufferProducer> bufferProducer;
- sp<Overlay> overlay;
if (gWantFrameTime) {
// Send virtual display frames to an external texture.
- overlay = new Overlay(gMonotonicTime);
- err = overlay->start(encoderInputSurface, &bufferProducer);
+ recordingData.overlay = new Overlay(gMonotonicTime);
+ err = recordingData.overlay->start(encoderInputSurface, &bufferProducer);
if (err != NO_ERROR) {
- if (encoder != NULL) encoder->release();
return err;
}
if (gVerbose) {
@@ -776,11 +871,13 @@
bufferProducer = encoderInputSurface;
}
+ // We need to hold a reference to mirrorRoot during the entire recording to ensure it's not
+ // cleaned up by SurfaceFlinger. When the reference is dropped, SurfaceFlinger will delete
+ // the resource.
+ sp<SurfaceControl> mirrorRoot;
// Configure virtual display.
- sp<IBinder> dpy;
- err = prepareVirtualDisplay(displayState, bufferProducer, &dpy);
+ err = prepareVirtualDisplay(displayState, bufferProducer, &recordingData.dpy, &mirrorRoot);
if (err != NO_ERROR) {
- if (encoder != NULL) encoder->release();
return err;
}
@@ -820,7 +917,6 @@
case FORMAT_RAW_FRAMES: {
rawFp = prepareRawOutput(fileName);
if (rawFp == NULL) {
- if (encoder != NULL) encoder->release();
return -1;
}
break;
@@ -861,7 +957,8 @@
}
} else {
// Main encoder loop.
- err = runEncoder(encoder, muxer, rawFp, display, dpy, displayState.orientation);
+ err = runEncoder(recordingData.encoder, muxer, rawFp, display, recordingData.dpy,
+ displayState.orientation);
if (err != NO_ERROR) {
fprintf(stderr, "Encoder failed (err=%d)\n", err);
// fall through to cleanup
@@ -875,9 +972,6 @@
// Shut everything down, starting with the producer side.
encoderInputSurface = NULL;
- SurfaceComposerClient::destroyDisplay(dpy);
- if (overlay != NULL) overlay->stop();
- if (encoder != NULL) encoder->stop();
if (muxer != NULL) {
// If we don't stop muxer explicitly, i.e. let the destructor run,
// it may hang (b/11050628).
@@ -885,7 +979,6 @@
} else if (rawFp != stdout) {
fclose(rawFp);
}
- if (encoder != NULL) encoder->release();
return err;
}
@@ -1026,7 +1119,8 @@
" Add additional information, such as a timestamp overlay, that is helpful\n"
" in videos captured to illustrate bugs.\n"
"--time-limit TIME\n"
- " Set the maximum recording time, in seconds. Default / maximum is %d.\n"
+ " Set the maximum recording time, in seconds. Default is %d. Set to 0\n"
+ " to remove the time limit.\n"
"--display-id ID\n"
" specify the physical display ID to record. Default is the primary display.\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
@@ -1113,14 +1207,27 @@
}
break;
case 't':
- gTimeLimitSec = atoi(optarg);
- if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
- fprintf(stderr,
- "Time limit %ds outside acceptable range [1,%d]\n",
- gTimeLimitSec, kMaxTimeLimitSec);
+ {
+ char *next;
+ const int64_t timeLimitSec = strtol(optarg, &next, 10);
+ if (next == optarg || (*next != '\0' && *next != ' ')) {
+ fprintf(stderr, "Error parsing time limit argument\n");
return 2;
}
+ if (timeLimitSec > std::numeric_limits<uint32_t>::max() || timeLimitSec < 0) {
+ fprintf(stderr,
+ "Time limit %" PRIi64 "s outside acceptable range [0,%u] seconds\n",
+ timeLimitSec, std::numeric_limits<uint32_t>::max());
+ return 2;
+ }
+ gTimeLimitSec = (timeLimitSec == 0) ?
+ std::numeric_limits<uint32_t>::max() : timeLimitSec;
+ if (gVerbose) {
+ printf("Time limit set to %u seconds\n", gTimeLimitSec);
+ fflush(stdout);
+ }
break;
+ }
case 'u':
gWantInfoScreen = true;
gWantFrameTime = true;
diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp
index c38dbef..055f20a 100644
--- a/drm/libmediadrm/DrmHalHidl.cpp
+++ b/drm/libmediadrm/DrmHalHidl.cpp
@@ -309,7 +309,7 @@
closeOpenSessions();
Mutex::Autolock autoLock(mLock);
- reportFrameworkMetrics(reportPluginMetrics());
+ if (mInitCheck == OK) reportFrameworkMetrics(reportPluginMetrics());
setListener(NULL);
mInitCheck = NO_INIT;
diff --git a/drm/libmediadrm/DrmMetricsConsumer.cpp b/drm/libmediadrm/DrmMetricsConsumer.cpp
index c06f09b..fd095b7 100644
--- a/drm/libmediadrm/DrmMetricsConsumer.cpp
+++ b/drm/libmediadrm/DrmMetricsConsumer.cpp
@@ -42,7 +42,7 @@
}
return type_names[attribute];
}
-
+
static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
"KEY_EXPIRED", "VENDOR_DEFINED",
"SESSION_RECLAIMED"};
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
index 2d1f741..2732aa7 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -69,3 +69,58 @@
"android.hardware.drm-service.clearkey",
],
}
+
+cc_defaults {
+ name: "fuzz_aidl_clearkey_service_defaults",
+
+ srcs: [
+ "CreatePluginFactories.cpp",
+ "CryptoPlugin.cpp",
+ "DrmFactory.cpp",
+ "DrmPlugin.cpp",
+ ],
+
+ relative_install_path: "hw",
+
+ cflags: ["-Wall", "-Werror", "-Wthread-safety"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ "android.hardware.drm-V1-ndk",
+ ],
+
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "libclearkeybase_fuzz",
+ ],
+
+ local_include_dirs: ["include"],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+
+cc_fuzz {
+ name: "android.hardware.drm-service.clearkey.aidl_fuzzer",
+ defaults: [
+ "fuzz_aidl_clearkey_service_defaults",
+ "service_fuzzer_defaults",
+ ],
+ static_libs: [
+ "liblog",
+ ],
+ srcs: ["fuzzer.cpp"],
+ fuzz_config: {
+ cc: [
+ "hamzeh@google.com",
+ ],
+ },
+}
\ No newline at end of file
diff --git a/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp
index ea51e9d..12aa4ea 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp
@@ -474,6 +474,7 @@
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
+ Mutex::Autolock lock(mSecurityLevelLock);
std::map<std::vector<uint8_t>, ::aidl::android::hardware::drm::SecurityLevel>::iterator itr =
mSecurityLevel.find(sid);
if (itr == mSecurityLevel.end()) {
@@ -1009,6 +1010,7 @@
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
+ Mutex::Autolock lock(mSecurityLevelLock);
std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = mSecurityLevel.find(sid);
if (itr != mSecurityLevel.end()) {
mSecurityLevel[sid] = level;
diff --git a/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp b/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp
new file mode 100644
index 0000000..9ef331f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "CreatePluginFactories.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::drm::clearkey::createDrmFactory;
+using ::aidl::android::hardware::drm::clearkey::DrmFactory;
+
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::shared_ptr<DrmFactory> drmFactory = createDrmFactory();
+ fuzzService(drmFactory->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h
index 25c05f0..7acc1b6 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h
@@ -182,7 +182,8 @@
std::map<std::string, std::vector<uint8_t>> mByteArrayProperties;
std::map<std::string, std::vector<uint8_t>> mReleaseKeysMap;
std::map<std::vector<uint8_t>, std::string> mPlaybackId;
- std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel;
+ std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel
+ GUARDED_BY(mSecurityLevelLock);
::std::shared_ptr<IDrmPluginListener> mListener;
SessionLibrary* mSessionLibrary;
int64_t mOpenSessionOkCount;
@@ -201,6 +202,7 @@
DeviceFiles mFileHandle;
::android::Mutex mSecureStopLock;
+ ::android::Mutex mSecurityLevelLock;
CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
};
diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp
index a6a5b28..6913df4 100644
--- a/drm/mediadrm/plugins/clearkey/common/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/common/Android.bp
@@ -97,3 +97,54 @@
integer_overflow: true,
},
}
+
+cc_library_static {
+ name: "libclearkeydevicefiles-protos.common_fuzz",
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["protos/DeviceFiles.proto"],
+}
+
+cc_library_static {
+ name: "libclearkeybase_fuzz",
+
+ srcs: [
+ "AesCtrDecryptor.cpp",
+ "Base64.cpp",
+ "Buffer.cpp",
+ "ClearKeyUUID.cpp",
+ "DeviceFiles.cpp",
+ "InitDataParser.cpp",
+ "JsonWebKey.cpp",
+ "MemoryFileSystem.cpp",
+ "Session.cpp",
+ "SessionLibrary.cpp",
+ "Utils.cpp",
+ ],
+
+ cflags: ["-Wall", "-Werror"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libutils",
+ "libcrypto",
+ ],
+
+ whole_static_libs: [
+ "libjsmn",
+ "libclearkeydevicefiles-protos.common_fuzz",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "include/clearkeydrm",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 1019520..274a89a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -414,8 +414,6 @@
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
std::map<std::string, std::vector<uint8_t> > mReleaseKeysMap;
std::map<std::vector<uint8_t>, std::string> mPlaybackId;
- std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel
- GUARDED_BY(mSecurityLevelLock);
sp<IDrmPluginListener> mListener;
sp<IDrmPluginListener_V1_2> mListenerV1_2;
SessionLibrary *mSessionLibrary;
@@ -436,6 +434,8 @@
DeviceFiles mFileHandle;
Mutex mSecureStopLock;
Mutex mSecurityLevelLock;
+ std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel
+ GUARDED_BY(mSecurityLevelLock);
CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
};
diff --git a/include/media/MmapStreamCallback.h b/include/media/MmapStreamCallback.h
index 31b8eb5..76ee6d7 100644
--- a/include/media/MmapStreamCallback.h
+++ b/include/media/MmapStreamCallback.h
@@ -37,12 +37,9 @@
/**
* The volume to be applied to the use case specified when opening the stream has changed
- * \param[in] channels a channel mask containing all channels the volume should be applied to.
- * \param[in] values the volume values to be applied to each channel. The size of the vector
- * should correspond to the channel count retrieved with
- * audio_channel_count_from_in_mask() or audio_channel_count_from_out_mask()
+ * \param[in] volume the new target volume
*/
- virtual void onVolumeChanged(audio_channel_mask_t channels, Vector<float> values) = 0;
+ virtual void onVolumeChanged(float volume) = 0;
/**
* The device the stream is routed to/from has changed
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index d4025e5..97e0b1d 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -85,8 +85,6 @@
uint32_t mSize; // Number of bytes of frame data
uint32_t mIccSize; // Number of bytes of ICC data
uint32_t mBitDepth; // number of bits per R / G / B channel
-
- // Adding new items must be 64-bit aligned.
};
}; // namespace android
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index a4c03ba..48a060b 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -1,7 +1,24 @@
// for frameworks/av/media
{
- // TODO (b/229286407) Add EncodeDecodeTest and DecodeEditEncodeTest to
- // presubmit-large once issues in cuttlefish are fixed
+ "presubmit-large": [
+ // runs whenever we change something in this tree
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.DecodeEditEncodeTest"
+ }
+ ]
+ }
+ ],
"presubmit": [
{
"name": "GtsMediaTestCases",
@@ -15,7 +32,8 @@
{
"include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
}
- ]
+ ],
+ "file_patterns": ["(?i)drm|crypto"]
}
],
@@ -23,6 +41,51 @@
{
"path": "frameworks/av/drm/mediadrm/plugins"
}
+ ],
+
+ "platinum-postsubmit": [
+ // runs regularly, independent of changes in this tree.
+ // signals if changes elsewhere break media functionality
+ // @FlakyTest: in staged-postsubmit, but not postsubmit
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.DecodeEditEncodeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+
+ "staged-platinum-postsubmit": [
+ // runs every four hours
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.DecodeEditEncodeTest"
+ }
+ ]
+ }
]
// TODO (b/229286407) Add EncodeDecodeTest and DecodeEditEncodeTest to
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index c4a6601..d62dd91 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -7,11 +7,10 @@
ioprio rt 4
task_profiles ProcessCapacityHigh HighPerformance
onrestart restart vendor.audio-hal
+ onrestart restart vendor.audio-hal-aidl
+ onrestart restart vendor.audio-effect-hal-aidl
onrestart restart vendor.audio-hal-4-0-msd
onrestart restart audio_proxy_service
- # Keep the original service names for backward compatibility
- onrestart restart vendor.audio-hal-2-0
- onrestart restart audio-hal-2-0
on property:vts.native_server.on=1
stop audioserver
@@ -20,42 +19,39 @@
on property:init.svc.audioserver=stopped
stop vendor.audio-hal
+ stop vendor.audio-hal-aidl
+ stop vendor.audio-effect-hal-aidl
stop vendor.audio-hal-4-0-msd
stop audio_proxy_service
- # Keep the original service names for backward compatibility
- stop vendor.audio-hal-2-0
- stop audio-hal-2-0
# See b/155364397. Need to have HAL service running for VTS.
# Can't use 'restart' because then HAL service would restart
# audioserver bringing it back into running state.
start vendor.audio-hal
+ start vendor.audio-hal-aidl
+ start vendor.audio-effect-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- # Keep the original service names for backward compatibility
- start vendor.audio-hal-2-0
- start audio-hal-2-0
on property:init.svc.audioserver=running
start vendor.audio-hal
+ start vendor.audio-hal-aidl
+ start vendor.audio-effect-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- # Keep the original service names for backward compatibility
- start vendor.audio-hal-2-0
- start audio-hal-2-0
on property:sys.audio.restart.hal=1
# See b/159966243. Avoid restart loop between audioserver and HAL.
# Keep the original service names for backward compatibility
stop vendor.audio-hal
+ stop vendor.audio-hal-aidl
+ stop vendor.audio-effect-hal-aidl
stop vendor.audio-hal-4-0-msd
stop audio_proxy_service
- stop vendor.audio-hal-2-0
- stop audio-hal-2-0
start vendor.audio-hal
+ start vendor.audio-hal-aidl
+ start vendor.audio-effect-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- start vendor.audio-hal-2-0
- start audio-hal-2-0
# reset the property
setprop sys.audio.restart.hal 0
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 8b46d3f..28cceca 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -392,9 +392,9 @@
static C2R PictureQuantizationSetter(bool mayBlock,
C2P<C2StreamPictureQuantizationTuning::output> &me) {
(void)mayBlock;
- (void)me;
- // TODO: refactor with same algorithm in the SetQp()
+ // these are the ones we're going to set, so want them to default
+ // to the DEFAULT values for the codec
int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
@@ -419,13 +419,14 @@
ALOGV("PictureQuantizationSetter(entry): i %d-%d p %d-%d b %d-%d",
iMin, iMax, pMin, pMax, bMin, bMax);
- // ensure we have legal values
- iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX);
- iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX);
- pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX);
- pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX);
- bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX);
- bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX);
+ // min is clamped to [AVC_QP_MIN, max] to avoid error
+ // cases where layer.min > layer.max
+ iMax = std::clamp(iMax, AVC_QP_MIN, AVC_QP_MAX);
+ iMin = std::clamp(iMin, AVC_QP_MIN, iMax);
+ pMax = std::clamp(pMax, AVC_QP_MIN, AVC_QP_MAX);
+ pMin = std::clamp(pMin, AVC_QP_MIN, pMax);
+ bMax = std::clamp(bMax, AVC_QP_MIN, AVC_QP_MAX);
+ bMin = std::clamp(bMin, AVC_QP_MIN, bMax);
// put them back into the structure
for (size_t i = 0; i < me.v.flexCount(); ++i) {
@@ -819,7 +820,8 @@
s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
- // TODO: refactor with same algorithm in the PictureQuantizationSetter()
+ // we resolved out-of-bound and unspecified values in PictureQuantizationSetter()
+ // so we can start with defaults that are overridden as needed.
int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 293867d..cde6604 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -103,8 +103,8 @@
/** limits as specified by h264
* (QP_MIN==4 is actually a limitation of this SW codec, not the H.264 standard)
**/
-#define CODEC_QP_MIN 4
-#define CODEC_QP_MAX 51
+#define AVC_QP_MIN 4
+#define AVC_QP_MAX 51
#define MIN(a, b) ((a) < (b))? (a) : (b)
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index 4dec57f..d234f21 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -102,6 +102,29 @@
.withSetter(Hdr10PlusInfoOutputSetter)
.build());
+ // default static info
+ C2HdrStaticMetadataStruct defaultStaticInfo{};
+ helper->addStructDescriptors<C2MasteringDisplayColorVolumeStruct, C2ColorXyStruct>();
+ addParameter(
+ DefineParam(mHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withDefault(new C2StreamHdrStaticInfo::output(0u, defaultStaticInfo))
+ .withFields({
+ C2F(mHdrStaticInfo, mastering.red.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.red.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.green.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.green.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.blue.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.blue.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.maxLuminance).inRange(0, 65535),
+ C2F(mHdrStaticInfo, mastering.minLuminance).inRange(0, 6.5535),
+ C2F(mHdrStaticInfo, maxCll).inRange(0, 0XFFFF),
+ C2F(mHdrStaticInfo, maxFall).inRange(0, 0XFFFF)
+ })
+ .withSetter(HdrStaticInfoSetter)
+ .build());
+
addParameter(
DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
.withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
@@ -331,6 +354,47 @@
// unsafe getters
std::shared_ptr<C2StreamPixelFormatInfo::output> getPixelFormat_l() const { return mPixelFormat; }
+ static C2R HdrStaticInfoSetter(bool mayBlock, C2P<C2StreamHdrStaticInfo::output> &me) {
+ (void)mayBlock;
+ if (me.v.mastering.red.x > 1) {
+ me.set().mastering.red.x = 1;
+ }
+ if (me.v.mastering.red.y > 1) {
+ me.set().mastering.red.y = 1;
+ }
+ if (me.v.mastering.green.x > 1) {
+ me.set().mastering.green.x = 1;
+ }
+ if (me.v.mastering.green.y > 1) {
+ me.set().mastering.green.y = 1;
+ }
+ if (me.v.mastering.blue.x > 1) {
+ me.set().mastering.blue.x = 1;
+ }
+ if (me.v.mastering.blue.y > 1) {
+ me.set().mastering.blue.y = 1;
+ }
+ if (me.v.mastering.white.x > 1) {
+ me.set().mastering.white.x = 1;
+ }
+ if (me.v.mastering.white.y > 1) {
+ me.set().mastering.white.y = 1;
+ }
+ if (me.v.mastering.maxLuminance > 65535.0) {
+ me.set().mastering.maxLuminance = 65535.0;
+ }
+ if (me.v.mastering.minLuminance > 6.5535) {
+ me.set().mastering.minLuminance = 6.5535;
+ }
+ if (me.v.maxCll > 65535.0) {
+ me.set().maxCll = 65535.0;
+ }
+ if (me.v.maxFall > 65535.0) {
+ me.set().maxFall = 65535.0;
+ }
+ return C2R::Ok();
+ }
+
private:
std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
@@ -343,6 +407,7 @@
std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
+ std::shared_ptr<C2StreamHdrStaticInfo::output> mHdrStaticInfo;
};
C2SoftGav1Dec::C2SoftGav1Dec(const char *name, c2_node_id_t id,
@@ -558,6 +623,76 @@
}
}
+void C2SoftGav1Dec::getHDRStaticParams(const libgav1::DecoderBuffer *buffer,
+ const std::unique_ptr<C2Work> &work) {
+ C2StreamHdrStaticMetadataInfo::output hdrStaticMetadataInfo{};
+ bool infoPresent = false;
+ if (buffer->has_hdr_mdcv) {
+ // hdr_mdcv.primary_chromaticity_* values are in 0.16 fixed-point format.
+ hdrStaticMetadataInfo.mastering.red.x = buffer->hdr_mdcv.primary_chromaticity_x[0] / 65536.0;
+ hdrStaticMetadataInfo.mastering.red.y = buffer->hdr_mdcv.primary_chromaticity_y[0] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.green.x = buffer->hdr_mdcv.primary_chromaticity_x[1] / 65536.0;
+ hdrStaticMetadataInfo.mastering.green.y = buffer->hdr_mdcv.primary_chromaticity_y[1] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.blue.x = buffer->hdr_mdcv.primary_chromaticity_x[2] / 65536.0;
+ hdrStaticMetadataInfo.mastering.blue.y = buffer->hdr_mdcv.primary_chromaticity_y[2] / 65536.0;
+
+ // hdr_mdcv.white_point_chromaticity_* values are in 0.16 fixed-point format.
+ hdrStaticMetadataInfo.mastering.white.x = buffer->hdr_mdcv.white_point_chromaticity_x / 65536.0;
+ hdrStaticMetadataInfo.mastering.white.y = buffer->hdr_mdcv.white_point_chromaticity_y / 65536.0;
+
+ // hdr_mdcv.luminance_max is in 24.8 fixed-point format.
+ hdrStaticMetadataInfo.mastering.maxLuminance = buffer->hdr_mdcv.luminance_max / 256.0;
+ // hdr_mdcv.luminance_min is in 18.14 format.
+ hdrStaticMetadataInfo.mastering.minLuminance = buffer->hdr_mdcv.luminance_min / 16384.0;
+ infoPresent = true;
+ }
+
+ if (buffer->has_hdr_cll) {
+ hdrStaticMetadataInfo.maxCll = buffer->hdr_cll.max_cll;
+ hdrStaticMetadataInfo.maxFall = buffer->hdr_cll.max_fall;
+ infoPresent = true;
+ }
+ // config if static info has changed
+ if (infoPresent && !(hdrStaticMetadataInfo == mHdrStaticMetadataInfo)) {
+ mHdrStaticMetadataInfo = hdrStaticMetadataInfo;
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(mHdrStaticMetadataInfo));
+ }
+}
+
+void C2SoftGav1Dec::getHDR10PlusInfoData(const libgav1::DecoderBuffer *buffer,
+ const std::unique_ptr<C2Work> &work) {
+ if (buffer->has_itut_t35) {
+ std::vector<uint8_t> payload;
+ size_t payloadSize = buffer->itut_t35.payload_size;
+ if (payloadSize > 0) {
+ payload.push_back(buffer->itut_t35.country_code);
+ if (buffer->itut_t35.country_code == 0xFF) {
+ payload.push_back(buffer->itut_t35.country_code_extension_byte);
+ }
+ payload.insert(payload.end(), buffer->itut_t35.payload_bytes,
+ buffer->itut_t35.payload_bytes + buffer->itut_t35.payload_size);
+ }
+
+ std::unique_ptr<C2StreamHdr10PlusInfo::output> hdr10PlusInfo =
+ C2StreamHdr10PlusInfo::output::AllocUnique(payload.size());
+ if (!hdr10PlusInfo) {
+ ALOGE("Hdr10PlusInfo allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ memcpy(hdr10PlusInfo->m.value, payload.data(), payload.size());
+
+ // config if hdr10Plus info has changed
+ if (nullptr == mHdr10PlusInfo || !(*hdr10PlusInfo == *mHdr10PlusInfo)) {
+ mHdr10PlusInfo = std::move(hdr10PlusInfo);
+ work->worklets.front()->output.configUpdate.push_back(std::move(mHdr10PlusInfo));
+ }
+ }
+}
+
void C2SoftGav1Dec::getVuiParams(const libgav1::DecoderBuffer *buffer) {
VuiColorAspects vuiColorAspects;
vuiColorAspects.primaries = buffer->color_primary;
@@ -633,6 +768,9 @@
}
getVuiParams(buffer);
+ getHDRStaticParams(buffer, work);
+ getHDR10PlusInfoData(buffer, work);
+
if (!(buffer->image_format == libgav1::kImageFormatYuv420 ||
buffer->image_format == libgav1::kImageFormatMonochrome400)) {
ALOGE("image_format %d not supported", buffer->image_format);
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index 3d4db55..e51c511 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -61,6 +61,9 @@
bool mSignalledOutputEos;
bool mSignalledError;
+ C2StreamHdrStaticMetadataInfo::output mHdrStaticMetadataInfo;
+ std::unique_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfo = nullptr;
+
// Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
// converting them to C2 values for each frame
struct VuiColorAspects {
@@ -86,6 +89,10 @@
nsecs_t mTimeEnd = 0; // Time at the end of decode()
bool initDecoder();
+ void getHDRStaticParams(const libgav1::DecoderBuffer *buffer,
+ const std::unique_ptr<C2Work> &work);
+ void getHDR10PlusInfoData(const libgav1::DecoderBuffer *buffer,
+ const std::unique_ptr<C2Work> &work);
void getVuiParams(const libgav1::DecoderBuffer *buffer);
void destroyDecoder();
void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
diff --git a/media/codec2/fuzzer/C2Fuzzer.cpp b/media/codec2/fuzzer/C2Fuzzer.cpp
index e469d8b..d6793e0 100644
--- a/media/codec2/fuzzer/C2Fuzzer.cpp
+++ b/media/codec2/fuzzer/C2Fuzzer.cpp
@@ -246,7 +246,8 @@
bufferSource->parse();
c2_status_t status = C2_OK;
size_t numFrames = 0;
- while (!bufferSource->isEos()) {
+ int32_t iterationCount = 0;
+ while (!bufferSource->isEos() && ++iterationCount <= kMaxIterations) {
uint8_t* frame = nullptr;
size_t frameSize = 0;
FrameData frameData = bufferSource->getFrame();
diff --git a/media/codec2/fuzzer/C2Fuzzer.h b/media/codec2/fuzzer/C2Fuzzer.h
index da76885..4e3e3ea 100644
--- a/media/codec2/fuzzer/C2Fuzzer.h
+++ b/media/codec2/fuzzer/C2Fuzzer.h
@@ -37,6 +37,7 @@
#define C2FUZZER_ALIGN(_sz, _align) (((_sz) + ((_align)-1)) & ~((_align)-1))
constexpr std::chrono::milliseconds kC2FuzzerTimeOut = 5000ms;
+constexpr int32_t kMaxIterations = 100;
constexpr int32_t kNumberOfC2WorkItems = 8;
constexpr uint32_t kWidthOfVideo = 3840;
constexpr uint32_t kHeightOfVideo = 2160;
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index bd7ec0d..327717b 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -372,9 +372,8 @@
ULock l(queueLock);
flushedIndices.emplace_back(frameID);
}
- char* data = (char*)malloc(bytesCount);
- ASSERT_NE(data, nullptr);
- eleStream.read(data, bytesCount);
+ std::vector<char> eleData(bytesCount);
+ eleStream.read(eleData.data(), bytesCount);
// if we have reached at the end of input stream, signal eos
if (eleStream.gcount() < bytesCount) {
bytesCount = eleStream.gcount();
@@ -396,12 +395,11 @@
ASSERT_EQ(0u, view.offset());
ASSERT_EQ((size_t)bytesCount, view.size());
- memcpy(view.base(), data, bytesCount);
+ memcpy(view.base(), eleData.data(), bytesCount);
work->input.buffers.clear();
work->input.buffers.emplace_back(new LinearBuffer(block));
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- free(data);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 67873fa..117d9ca 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -214,10 +214,8 @@
calc_md5_cksum(uPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_u_chksum);
calc_md5_cksum(vPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_v_chksum);
} else if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 2) {
- uint8_t* cbPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
- uint8_t* crPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
- ASSERT_NE(cbPlane, nullptr);
- ASSERT_NE(crPlane, nullptr);
+ std::vector<uint8_t> cbPlane(cropWidth * cropHeight / 4);
+ std::vector<uint8_t> crPlane(cropWidth * cropHeight / 4);
size_t count = 0;
for (size_t k = 0; k < (cropHeight / 2); k++) {
for (size_t l = 0; l < (cropWidth); l = l + 2) {
@@ -227,10 +225,10 @@
}
}
calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
- calc_md5_cksum(cbPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_u_chksum);
- calc_md5_cksum(crPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_v_chksum);
- free(cbPlane);
- free(crPlane);
+ calc_md5_cksum(cbPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+ au1_u_chksum);
+ calc_md5_cksum(crPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+ au1_v_chksum);
} else {
mMd5Enable = false;
ALOGV("Disabling MD5 chksm flag");
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
index 5c13b0e..c77eb22 100644
--- a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -417,6 +417,7 @@
}
std::unique_lock lock(mQueueMutex);
mQueue.splice(mQueue.end(), *items);
+ mQueueCondition.notify_all();
return C2_OK;
}
@@ -665,6 +666,7 @@
grallocHandle, GraphicBuffer::CLONE_HANDLE,
width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
std::shared_ptr<C2GraphicBlock> dstBlock;
C2BlockPool::local_id_t poolId = mIntf->getPoolId();
std::shared_ptr<C2BlockPool> pool;
@@ -683,6 +685,7 @@
grallocHandle, GraphicBuffer::CLONE_HANDLE,
width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
Rect sourceCrop(0, 0, width, height);
renderengine::DisplaySettings clientCompositionDisplay;
@@ -807,8 +810,6 @@
// affectedParams
{
C2StreamHdrStaticInfo::output::PARAM_TYPE,
- C2StreamHdr10PlusInfo::output::PARAM_TYPE, // will be deprecated
- C2StreamHdrDynamicMetadataInfo::output::PARAM_TYPE,
C2StreamColorAspectsInfo::output::PARAM_TYPE,
},
};
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 5a652a3..ecd5463 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -44,7 +44,7 @@
],
static_libs: [
- "SurfaceFlingerProperties",
+ "libSurfaceFlingerProperties",
],
shared_libs: [
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 296d7ed..124a893 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1899,6 +1899,7 @@
comp = state->comp;
}
status_t err = comp->stop();
+ mChannel->stopUseOutputSurface();
if (err != C2_OK) {
// TODO: convert err into status_t
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
@@ -1972,6 +1973,7 @@
}
mChannel->reset();
+ mChannel->stopUseOutputSurface();
// thiz holds strong ref to this while the thread is running.
sp<CCodec> thiz(this);
std::thread([thiz, sendCallback] { thiz->release(sendCallback); }).detach();
@@ -2139,7 +2141,8 @@
std::map<size_t, sp<MediaCodecBuffer>> clientInputBuffers;
status_t err = mChannel->prepareInitialInputBuffers(&clientInputBuffers);
- if (err != OK) {
+ // FIXME(b/237656746)
+ if (err != OK && err != NO_MEMORY) {
ALOGE("Resume request for Input Buffers failed");
mCallback->onError(err, ACTION_CODE_FATAL);
return;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 5ecb130..4bf8dce 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -960,9 +960,9 @@
hdrDynamicInfo->m.data + hdrDynamicInfo->flexCount());
}
qbi.setHdrMetadata(hdr);
-
- SetHdrMetadataToGralloc4Handle(hdrStaticInfo, hdrDynamicInfo, block.handle());
}
+ SetMetadataToGralloc4Handle(dataSpace, hdrStaticInfo, hdrDynamicInfo, block.handle());
+
// we don't have dirty regions
qbi.setSurfaceDamage(Region::INVALID_REGION);
android::IGraphicBufferProducer::QueueBufferOutput qbo;
@@ -1578,6 +1578,17 @@
mFirstValidFrameIndex = mFrameIndex.load(std::memory_order_relaxed);
}
+void CCodecBufferChannel::stopUseOutputSurface() {
+ if (mOutputSurface.lock()->surface) {
+ C2BlockPool::local_id_t outputPoolId;
+ {
+ Mutexed<BlockPools>::Locked pools(mBlockPools);
+ outputPoolId = pools->outputPoolId;
+ }
+ if (mComponent) mComponent->stopUsingOutputSurface(outputPoolId);
+ }
+}
+
void CCodecBufferChannel::reset() {
stop();
if (mInputSurface != nullptr) {
@@ -1593,14 +1604,6 @@
Mutexed<Output>::Locked output(mOutput);
output->buffers.reset();
}
- if (mOutputSurface.lock()->surface) {
- C2BlockPool::local_id_t outputPoolId;
- {
- Mutexed<BlockPools>::Locked pools(mBlockPools);
- outputPoolId = pools->outputPoolId;
- }
- mComponent->stopUsingOutputSurface(outputPoolId);
- }
}
void CCodecBufferChannel::release() {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index f29a225..61fb06f 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -149,6 +149,12 @@
std::map<size_t, sp<MediaCodecBuffer>> &&clientInputBuffers);
/**
+ * Stop using buffers of the current output surface for other Codec
+ * instances to use the surface safely.
+ */
+ void stopUseOutputSurface();
+
+ /**
* Stop queueing buffers to the component. This object should never queue
* buffers after this call, until start() is called.
*/
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 5208be6..cfadc95 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -389,7 +389,7 @@
// read back always as int
float value;
if (v.get(&value)) {
- return (int32_t)value;
+ return (int32_t) (value + 0.5);
}
return C2Value();
}));
@@ -955,7 +955,7 @@
.limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
add(ConfigMapper(KEY_FLAC_COMPRESSION_LEVEL, C2_PARAMKEY_COMPLEXITY, "value")
.limitTo(D::AUDIO & D::ENCODER));
- add(ConfigMapper("complexity", C2_PARAMKEY_COMPLEXITY, "value")
+ add(ConfigMapper(KEY_COMPLEXITY, C2_PARAMKEY_COMPLEXITY, "value")
.limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
add(ConfigMapper(KEY_GRID_COLUMNS, C2_PARAMKEY_TILE_LAYOUT, "columns")
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index cde4c72..876c96d 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -843,6 +843,10 @@
}
}
sp<ABuffer> aBuffer(alloc(align(width, 16) * align(height, 16) * bpp / 8));
+ if (aBuffer == nullptr) {
+ ALOGD("%s: failed to allocate buffer", __func__);
+ return nullptr;
+ }
return new ConstGraphicBlockBuffer(
format,
aBuffer,
@@ -998,9 +1002,11 @@
}
using ::aidl::android::hardware::graphics::common::Cta861_3;
+using ::aidl::android::hardware::graphics::common::Dataspace;
using ::aidl::android::hardware::graphics::common::Smpte2086;
using ::android::gralloc4::MetadataType_Cta861_3;
+using ::android::gralloc4::MetadataType_Dataspace;
using ::android::gralloc4::MetadataType_Smpte2086;
using ::android::gralloc4::MetadataType_Smpte2094_40;
@@ -1156,7 +1162,8 @@
return err;
}
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+ android_dataspace_t dataSpace,
const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
const C2Handle *const handle) {
@@ -1167,6 +1174,17 @@
// Gralloc4 not supported; nothing to do
return err;
}
+ {
+ hidl_vec<uint8_t> metadata;
+ if (gralloc4::encodeDataspace(static_cast<Dataspace>(dataSpace), &metadata) == OK) {
+ Return<Error4> ret = mapper->set(buffer.get(), MetadataType_Dataspace, metadata);
+ if (!ret.isOk()) {
+ err = C2_REFUSED;
+ } else if (ret != Error4::NONE) {
+ err = C2_CORRUPTED;
+ }
+ }
+ }
if (staticInfo && *staticInfo) {
ALOGV("Setting static HDR info as gralloc4 metadata");
std::optional<Smpte2086> smpte2086 = Smpte2086{
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index b02b042..b73acab 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -410,14 +410,16 @@
std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> *dynamicInfo);
/**
- * Set HDR metadata to Gralloc4 handle.
+ * Set metadata to Gralloc4 handle.
*
+ * \param[in] dataSpace Dataspace to set.
* \param[in] staticInfo HDR static info to set. Ignored if null or invalid.
* \param[in] dynamicInfo HDR dynamic info to set. Ignored if null or invalid.
* \param[out] handle handle of the allocation.
* \return C2_OK if successful
*/
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+ const android_dataspace_t dataSpace,
const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
const C2Handle *const handle);
diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp
index 9c3ba4d..2217235 100644
--- a/media/codec2/tests/Android.bp
+++ b/media/codec2/tests/Android.bp
@@ -36,6 +36,8 @@
cc_test {
name: "codec2_vndk_test",
test_suites: ["device-tests"],
+ // This test doesn't seem to support isolated with current assumption
+ isolated: false,
srcs: [
"C2_test.cpp",
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index a4a9b3a..f272499 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -706,6 +706,14 @@
}
case static_cast<uint32_t>(PixelFormat4::YCBCR_P010): {
+ // In Android T, P010 is relaxed to allow arbitrary stride for the Y and UV planes,
+ // try locking with the gralloc4 mapper first.
+ c2_status_t status = Gralloc4Mapper_lock(
+ const_cast<native_handle_t*>(mBuffer), grallocUsage, rect, layout, addr);
+ if (status == C2_OK) {
+ break;
+ }
+
void *pointer = nullptr;
status_t err = GraphicBufferMapper::get().lock(
const_cast<native_handle_t *>(mBuffer), grallocUsage, rect, &pointer);
diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp
index 0b556aa..aa908a8 100644
--- a/media/codec2/vndk/C2Fence.cpp
+++ b/media/codec2/vndk/C2Fence.cpp
@@ -186,7 +186,7 @@
class _C2FenceFactory::SyncFenceImpl : public C2Fence::Impl {
public:
virtual c2_status_t wait(c2_nsecs_t timeoutNs) {
- c2_nsecs_t timeoutMs = timeoutNs / 1000;
+ int64_t timeoutMs = timeoutNs / 1000000;
if (timeoutMs > INT_MAX) {
timeoutMs = INT_MAX;
}
diff --git a/media/janitors/media_solutions_OWNERS b/media/janitors/media_solutions_OWNERS
index 8dc1c7b..69e3a5e 100644
--- a/media/janitors/media_solutions_OWNERS
+++ b/media/janitors/media_solutions_OWNERS
@@ -2,9 +2,9 @@
# go/android-fwk-media-solutions for info on areas of ownership.
# Main owners:
+adadukin@google.com
aquilescanta@google.com
-krocard@google.com
+ivanbuper@google.com
# In case of emergency:
andrewlewis@google.com #{LAST_RESORT_SUGGESTION}
-olly@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/media/libaaudio/TEST_MAPPING b/media/libaaudio/TEST_MAPPING
index 3de5a9f..9aff137 100644
--- a/media/libaaudio/TEST_MAPPING
+++ b/media/libaaudio/TEST_MAPPING
@@ -1,10 +1,19 @@
{
- "presubmit": [
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index 2a12191..f5d2939 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -25,6 +25,9 @@
cc_fuzz {
name: "libaaudio_fuzzer",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
srcs: [
"libaaudio_fuzzer.cpp",
],
@@ -39,7 +42,6 @@
"libutils",
],
static_libs: [
- "android.media.audio.common.types-V1-cpp",
"liblog",
"libcutils",
"libaaudio",
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 2ff9f5a..9ca24aa 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -40,7 +40,7 @@
/**
* This is used to represent a value that has not been specified.
* For example, an application could use {@link #AAUDIO_UNSPECIFIED} to indicate
- * that is did not not care what the specific value of a parameter was
+ * that it did not care what the specific value of a parameter was
* and would accept whatever it was given.
*/
#define AAUDIO_UNSPECIFIED 0
@@ -103,7 +103,23 @@
*
* Available since API level 31.
*/
- AAUDIO_FORMAT_PCM_I32
+ AAUDIO_FORMAT_PCM_I32,
+
+ /**
+ * This format is used for compressed audio wrapped in IEC61937 for HDMI
+ * or S/PDIF passthrough.
+ *
+ * Unlike PCM playback, the Android framework is not able to do format
+ * conversion for IEC61937. In that case, when IEC61937 is requested, sampling
+ * rate and channel count or channel mask must be specified. Otherwise, it may
+ * fail when opening the stream. Apps are able to get the correct configuration
+ * for the playback by calling
+ * <a href="/reference/android/media/AudioManager#getDevices(int)">
+ * AudioManager#getDevices(int)</a>.
+ *
+ * Available since API level 34.
+ */
+ AAUDIO_FORMAT_IEC61937
};
typedef int32_t aaudio_format_t;
@@ -1157,7 +1173,10 @@
* in the streams current data format to the audioData buffer.
*
* For an input stream, this function should read and process numFrames of data
- * from the audioData buffer.
+ * from the audioData buffer. The data in the audioData buffer must not be modified
+ * directly. Instead, it should be copied to another buffer before doing any modification.
+ * In many cases, writing to the audioData buffer of an input stream will result in a
+ * native exception.
*
* The audio data is passed through the buffer. So do NOT call AAudioStream_read() or
* AAudioStream_write() on the stream that is making the callback.
@@ -1441,7 +1460,10 @@
/**
* Asynchronous request for the stream to flush.
* Flushing will discard any pending data.
- * This call only works if the stream is pausing or paused. TODO review
+ * This call only works if the stream is OPEN, PAUSED, STOPPED, or FLUSHED.
+ * Calling this function when in other states,
+ * or calling from an AAudio callback function,
+ * will have no effect and an error will be returned.
* Frame counters are not reset by a flush. They may be advanced.
* After this call the state will be in {@link #AAUDIO_STREAM_STATE_FLUSHING} or
* {@link #AAUDIO_STREAM_STATE_FLUSHED}.
diff --git a/media/libaaudio/include/aaudio/AAudioTesting.h b/media/libaaudio/include/aaudio/AAudioTesting.h
index 0f2d7a2..01d97b6 100644
--- a/media/libaaudio/include/aaudio/AAudioTesting.h
+++ b/media/libaaudio/include/aaudio/AAudioTesting.h
@@ -87,7 +87,7 @@
* @note This is only for testing. Do not use this in an application.
* It may change or be removed at any time.
*
- * @return true if the stream uses ther MMAP data path
+ * @return true if the stream uses the MMAP data path
*/
AAUDIO_API bool AAudioStream_isMMapUsed(AAudioStream* stream);
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 363d219..30f451a 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -134,6 +134,10 @@
cc_library {
name: "libaaudio_internal",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
local_include_dirs: [
"binding",
"client",
@@ -167,7 +171,6 @@
"libbinder",
"framework-permission-aidl-cpp",
"aaudio-aidl-cpp",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudioclient_aidl_conversion",
],
@@ -209,6 +212,7 @@
"flowgraph/ChannelCountConverter.cpp",
"flowgraph/ClipToRange.cpp",
"flowgraph/FlowGraphNode.cpp",
+ "flowgraph/Limiter.cpp",
"flowgraph/ManyToMultiConverter.cpp",
"flowgraph/MonoBlend.cpp",
"flowgraph/MonoToMultiConverter.cpp",
@@ -260,7 +264,7 @@
"binding/aidl/aaudio/IAAudioService.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"audioclient-types-aidl",
"shared-file-region-aidl",
"framework-permission-aidl",
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 2ed3e3c..5444565 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -20,7 +20,7 @@
#include "AAudioFlowGraph.h"
-#include <flowgraph/ClipToRange.h>
+#include <flowgraph/Limiter.h>
#include <flowgraph/ManyToMultiConverter.h>
#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
@@ -78,11 +78,11 @@
}
// For a pure float graph, there is chance that the data range may be very large.
- // So we should clip to a reasonable value that allows a little headroom.
+ // So we should limit to a reasonable value that allows a little headroom.
if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) {
- mClipper = std::make_unique<ClipToRange>(sourceChannelCount);
- lastOutput->connect(&mClipper->input);
- lastOutput = &mClipper->output;
+ mLimiter = std::make_unique<Limiter>(sourceChannelCount);
+ lastOutput->connect(&mLimiter->input);
+ lastOutput = &mLimiter->output;
}
// Expand the number of channels if required.
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index 602c17f..35fef37 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -24,7 +24,7 @@
#include <aaudio/AAudio.h>
#include <audio_utils/Balance.h>
-#include <flowgraph/ClipToRange.h>
+#include <flowgraph/Limiter.h>
#include <flowgraph/ManyToMultiConverter.h>
#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
@@ -74,7 +74,7 @@
private:
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::FlowGraphSourceBuffered> mSource;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::MonoBlend> mMonoBlend;
- std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::ClipToRange> mClipper;
+ std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::Limiter> mLimiter;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::MonoToMultiConverter> mChannelConverter;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::ManyToMultiConverter>
mManyToMultiConverter;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 9f0564f..07a96b7 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -605,13 +605,6 @@
return AAUDIO_ERROR_INVALID_STATE;
}
-aaudio_result_t AudioStreamInternal::updateStateMachine() {
- if (isDataCallbackActive()) {
- return AAUDIO_OK; // state is getting updated by the callback thread read/write call
- }
- return processCommands();
-}
-
void AudioStreamInternal::logTimestamp(AAudioServiceMessage &command) {
static int64_t oldPosition = 0;
static int64_t oldTime = 0;
@@ -654,6 +647,8 @@
if (getState() == AAUDIO_STREAM_STATE_STARTING) {
setState(AAUDIO_STREAM_STATE_STARTED);
}
+ mPlayerBase->triggerPortIdUpdate(static_cast<audio_port_handle_t>(
+ message->event.dataLong));
break;
case AAUDIO_SERVICE_EVENT_PAUSED:
ALOGD("%s - got AAUDIO_SERVICE_EVENT_PAUSED", __func__);
@@ -814,7 +809,6 @@
if (wakeTimeNanos > deadlineNanos) {
// If we time out, just return the framesWritten so far.
- // TODO remove after we fix the deadline bug
ALOGW("processData(): entered at %lld nanos, currently %lld",
(long long) entryTimeNanos, (long long) currentTimeNanos);
ALOGW("processData(): TIMEOUT after %lld nanos",
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 2367572..4ea61d2 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -48,7 +48,7 @@
int64_t *framePosition,
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t updateStateMachine() override;
+ virtual aaudio_result_t processCommands() override;
aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -110,8 +110,6 @@
aaudio_result_t drainTimestampsFromService();
- aaudio_result_t processCommands();
-
aaudio_result_t stopCallback_l();
virtual void prepareBuffersForStart() {}
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 1efccb1..f5cc2be 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -25,6 +25,7 @@
#include "client/AudioStreamInternalCapture.h"
#include "utility/AudioClock.h"
+#undef ATRACE_TAG
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include <utils/Trace.h>
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 90ff4a5..938079b 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -566,9 +566,7 @@
int64_t *timeNanoseconds)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- if (framePosition == nullptr) {
- return AAUDIO_ERROR_NULL;
- } else if (timeNanoseconds == nullptr) {
+ if (framePosition == nullptr || timeNanoseconds == nullptr) {
return AAUDIO_ERROR_NULL;
} else if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_BOOTTIME) {
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 8b7b75e..e6b00d7 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -58,6 +58,7 @@
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_IEC61937:
break; // valid
default:
ALOGD("audioFormat not valid, audio_format_t = 0x%08x", format);
@@ -83,7 +84,6 @@
switch (mSessionId) {
case AAUDIO_SESSION_ID_NONE:
case AAUDIO_SESSION_ID_ALLOCATE:
- break;
default:
break;
}
diff --git a/media/libaaudio/src/core/AudioGlobal.cpp b/media/libaaudio/src/core/AudioGlobal.cpp
index 0e5b8be..30f9677 100644
--- a/media/libaaudio/src/core/AudioGlobal.cpp
+++ b/media/libaaudio/src/core/AudioGlobal.cpp
@@ -82,6 +82,7 @@
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_FLOAT);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I24_PACKED);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I32);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_IEC61937);
}
return "Unrecognized";
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 06f05b0..8a5186a 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -21,7 +21,9 @@
#include <atomic>
#include <stdint.h>
+#include <linux/futex.h>
#include <media/MediaMetricsItem.h>
+#include <sys/syscall.h>
#include <aaudio/AAudio.h>
@@ -362,34 +364,37 @@
}
void AudioStream::setState(aaudio_stream_state_t state) {
- ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
- if (state == mState) {
+ aaudio_stream_state_t oldState = mState.load();
+ ALOGD("%s(s#%d) from %d to %d", __func__, getId(), oldState, state);
+ if (state == oldState) {
return; // no change
}
// Track transition to DISCONNECTED state.
if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
android::mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
- .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(oldState))
.record();
}
// CLOSED is a final state
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
// Once CLOSING, we can only move to CLOSED state.
- } else if (mState == AAUDIO_STREAM_STATE_CLOSING
+ } else if (oldState == AAUDIO_STREAM_STATE_CLOSING
&& state != AAUDIO_STREAM_STATE_CLOSED) {
ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
// Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
- } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
+ } else if (oldState == AAUDIO_STREAM_STATE_DISCONNECTED
&& !(state == AAUDIO_STREAM_STATE_CLOSING
|| state == AAUDIO_STREAM_STATE_CLOSED)) {
ALOGW("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);
} else {
- mState = state;
+ mState.store(state);
+ // Wake up a wakeForStateChange thread if it exists.
+ syscall(SYS_futex, &mState, FUTEX_WAKE_PRIVATE, INT_MAX, NULL, NULL, 0);
}
}
@@ -408,9 +413,15 @@
if (durationNanos > timeoutNanoseconds) {
durationNanos = timeoutNanoseconds;
}
- AudioClock::sleepForNanos(durationNanos);
- timeoutNanoseconds -= durationNanos;
+ struct timespec time;
+ time.tv_sec = durationNanos / AAUDIO_NANOS_PER_SECOND;
+ // Add the fractional nanoseconds.
+ time.tv_nsec = durationNanos - (time.tv_sec * AAUDIO_NANOS_PER_SECOND);
+ // Sleep for durationNanos. If mState changes from the callback
+ // thread, this thread will wake up earlier.
+ syscall(SYS_futex, &mState, FUTEX_WAIT_PRIVATE, currentState, &time, NULL, 0);
+ timeoutNanoseconds -= durationNanos;
aaudio_result_t result = updateStateMachine();
if (result != AAUDIO_OK) {
return result;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 5fb4528..e36928d 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -100,10 +100,17 @@
int64_t *timeNanoseconds) = 0;
/**
- * Update state machine.()
- * @return
+ * Update state machine.
+ * @return result of the operation.
*/
- virtual aaudio_result_t updateStateMachine() = 0;
+ aaudio_result_t updateStateMachine() {
+ if (isDataCallbackActive()) {
+ return AAUDIO_OK; // state is getting updated by the callback thread read/write call
+ }
+ return processCommands();
+ };
+
+ virtual aaudio_result_t processCommands() = 0;
// =========== End ABSTRACT methods ===========================
@@ -184,7 +191,7 @@
// ============== Queries ===========================
aaudio_stream_state_t getState() const {
- return mState;
+ return mState.load();
}
virtual int32_t getBufferSize() const {
@@ -403,7 +410,7 @@
* This should only be called for client streams and not for streams
* that run in the service.
*/
- void registerPlayerBase() {
+ virtual void registerPlayerBase() {
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
mPlayerBase->registerWithAudioManager(this);
}
@@ -657,6 +664,8 @@
std::mutex mStreamLock;
+ const android::sp<MyPlayerBase> mPlayerBase;
+
private:
aaudio_result_t safeStop_l() REQUIRES(mStreamLock);
@@ -672,7 +681,7 @@
close_l();
}
- const android::sp<MyPlayerBase> mPlayerBase;
+ std::atomic<aaudio_stream_state_t> mState{AAUDIO_STREAM_STATE_UNINITIALIZED};
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
@@ -682,7 +691,6 @@
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
bool mSharingModeMatchRequired = false; // must match sharing mode requested
audio_format_t mFormat = AUDIO_FORMAT_DEFAULT;
- aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
int32_t mFramesPerBurst = 0;
int32_t mBufferCapacity = 0;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index a100aa9..04b4325 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -192,6 +192,11 @@
allowMMap = false;
}
+ if (getFormat() == AUDIO_FORMAT_IEC61937) {
+ ALOGD("%s IEC61937 format is selected, do not allow MMAP in this case.", __func__);
+ allowMMap = false;
+ }
+
if (!allowMMap && !allowLegacy) {
ALOGE("%s() no backend available: neither MMAP nor legacy path are allowed", __func__);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
diff --git a/media/libaaudio/src/flowgraph/Limiter.cpp b/media/libaaudio/src/flowgraph/Limiter.cpp
new file mode 100644
index 0000000..def905a
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/Limiter.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include <algorithm>
+#include <math.h>
+#include <unistd.h>
+#include "FlowGraphNode.h"
+#include "Limiter.h"
+
+using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+
+Limiter::Limiter(int32_t channelCount)
+ : FlowGraphFilter(channelCount) {
+}
+
+int32_t Limiter::onProcess(int32_t numFrames) {
+ const float *inputBuffer = input.getBuffer();
+ float *outputBuffer = output.getBuffer();
+
+ int32_t numSamples = numFrames * output.getSamplesPerFrame();
+
+ // Cache the last valid output to reduce memory read/write
+ float lastValidOutput = mLastValidOutput;
+
+ for (int32_t i = 0; i < numSamples; i++) {
+ // Use the previous output if the input is NaN
+ if (!isnan(*inputBuffer)) {
+ lastValidOutput = processFloat(*inputBuffer);
+ }
+ inputBuffer++;
+ *outputBuffer++ = lastValidOutput;
+ }
+ mLastValidOutput = lastValidOutput;
+
+ return numFrames;
+}
+
+float Limiter::processFloat(float in)
+{
+ float in_abs = fabsf(in);
+ if (in_abs <= 1) {
+ return in;
+ }
+ float out;
+ if (in_abs < kXWhenYis3Decibels) {
+ out = (kPolynomialSplineA * in_abs + kPolynomialSplineB) * in_abs + kPolynomialSplineC;
+ } else {
+ out = M_SQRT2;
+ }
+ if (in < 0) {
+ out = -out;
+ }
+ return out;
+}
diff --git a/media/libaaudio/src/flowgraph/Limiter.h b/media/libaaudio/src/flowgraph/Limiter.h
new file mode 100644
index 0000000..393a7bf
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/Limiter.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 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 FLOWGRAPH_LIMITER_H
+#define FLOWGRAPH_LIMITER_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "FlowGraphNode.h"
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+class Limiter : public FlowGraphFilter {
+public:
+ explicit Limiter(int32_t channelCount);
+
+ int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "Limiter";
+ }
+
+private:
+ // These numbers are based on a polynomial spline for a quadratic solution Ax^2 + Bx + C
+ // The range is up to 3 dB, (10^(3/20)), to match AudioTrack for float data.
+ static constexpr float kPolynomialSplineA = -0.6035533905; // -(1+sqrt(2))/4
+ static constexpr float kPolynomialSplineB = 2.2071067811; // (3+sqrt(2))/2
+ static constexpr float kPolynomialSplineC = -0.6035533905; // -(1+sqrt(2))/4
+ static constexpr float kXWhenYis3Decibels = 1.8284271247; // -1+2sqrt(2)
+
+ /**
+ * Process an input based on the following:
+ * If between -1 and 1, return the input value.
+ * If above kXWhenYis3Decibels, return sqrt(2).
+ * If below -kXWhenYis3Decibels, return -sqrt(2).
+ * If between 1 and kXWhenYis3Decibels, use a quadratic spline (Ax^2 + Bx + C).
+ * If between -kXWhenYis3Decibels and -1, use the absolute value for the spline and flip it.
+ * The derivative of the spline is 1 at 1 and 0 at kXWhenYis3Decibels.
+ * This way, the graph is both continuous and differentiable.
+ */
+ float processFloat(float in);
+
+ // Use the previous valid output for NaN inputs
+ float mLastValidOutput = 0.0f;
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif //FLOWGRAPH_LIMITER_H
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index dd11169..f32ef65 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -127,7 +127,7 @@
mCallbackEnabled.store(false);
}
- if (updateStateMachine() != AAUDIO_OK) {
+ if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
@@ -192,7 +192,7 @@
mCallbackEnabled.store(false);
}
- if (updateStateMachine() != AAUDIO_OK) {
+ if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 1e39e0f..9a136a7 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -364,8 +364,7 @@
return checkForDisconnectRequest(false);
}
-aaudio_result_t AudioStreamRecord::updateStateMachine()
-{
+aaudio_result_t AudioStreamRecord::processCommands() {
aaudio_result_t result = AAUDIO_OK;
aaudio_wrapping_frames_t position;
status_t err;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 5ce73f9..252ff3c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -58,7 +58,7 @@
int64_t getFramesWritten() override;
- aaudio_result_t updateStateMachine() override;
+ aaudio_result_t processCommands() override;
aaudio_direction_t getDirection() const override {
return AAUDIO_DIRECTION_INPUT;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 6f1dc92..fb3fcc1 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -378,8 +378,7 @@
return checkForDisconnectRequest(false);;
}
-aaudio_result_t AudioStreamTrack::updateStateMachine()
-{
+aaudio_result_t AudioStreamTrack::processCommands() {
status_t err;
aaudio_wrapping_frames_t position;
switch (getState()) {
@@ -407,7 +406,6 @@
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
} else if (position == 0) {
- // TODO Advance frames read to match written.
setState(AAUDIO_STREAM_STATE_FLUSHED);
}
}
@@ -553,6 +551,16 @@
return status;
}
+void AudioStreamTrack::registerPlayerBase() {
+ AudioStream::registerPlayerBase();
+
+ if (mAudioTrack == nullptr) {
+ ALOGW("%s: cannot set piid, AudioTrack is null", __func__);
+ return;
+ }
+ mAudioTrack->setPlayerIId(mPlayerBase->getPlayerIId());
+}
+
#if AAUDIO_USE_VOLUME_SHAPER
using namespace android::media::VolumeShaper;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 0f4d72b..05609c4 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -79,7 +79,7 @@
return AAUDIO_DIRECTION_OUTPUT;
}
- aaudio_result_t updateStateMachine() override;
+ aaudio_result_t processCommands() override;
int64_t incrementClientFrameCounter(int32_t frames) override {
return incrementFramesWritten(frames);
@@ -87,6 +87,8 @@
android::status_t doSetVolume() override;
+ void registerPlayerBase() override;
+
#if AAUDIO_USE_VOLUME_SHAPER
virtual android::binder::Status applyVolumeShaper(
const android::media::VolumeShaper::Configuration& configuration,
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 872faca..a197ced 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -140,6 +140,9 @@
case AAUDIO_FORMAT_PCM_I32:
androidFormat = AUDIO_FORMAT_PCM_32_BIT;
break;
+ case AAUDIO_FORMAT_IEC61937:
+ androidFormat = AUDIO_FORMAT_IEC61937;
+ break;
default:
androidFormat = AUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
@@ -166,6 +169,9 @@
case AUDIO_FORMAT_PCM_32_BIT:
aaudioFormat = AAUDIO_FORMAT_PCM_I32;
break;
+ case AUDIO_FORMAT_IEC61937:
+ aaudioFormat = AAUDIO_FORMAT_IEC61937;
+ break;
default:
aaudioFormat = AAUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 66b77eb..6f75f5a 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -26,6 +26,7 @@
#include <gtest/gtest.h>
#include "flowgraph/ClipToRange.h"
+#include "flowgraph/Limiter.h"
#include "flowgraph/MonoBlend.h"
#include "flowgraph/MonoToMultiConverter.h"
#include "flowgraph/SourceFloat.h"
@@ -319,3 +320,77 @@
}
}
+TEST(test_flowgraph, module_limiter) {
+ constexpr int kNumSamples = 101;
+ constexpr float kLastSample = 3.0f;
+ constexpr float kFirstSample = -kLastSample;
+ constexpr float kDeltaBetweenSamples = (kLastSample - kFirstSample) / (kNumSamples - 1);
+ constexpr float kTolerance = 0.00001f;
+
+ float input[kNumSamples];
+ float output[kNumSamples];
+ SourceFloat sourceFloat{1};
+ Limiter limiter{1};
+ SinkFloat sinkFloat{1};
+
+ for (int i = 0; i < kNumSamples; i++) {
+ input[i] = kFirstSample + i * kDeltaBetweenSamples;
+ }
+
+ const int numInputFrames = std::size(input);
+ sourceFloat.setData(input, numInputFrames);
+
+ sourceFloat.output.connect(&limiter.input);
+ limiter.output.connect(&sinkFloat.input);
+
+ const int numOutputFrames = std::size(output);
+ int32_t numRead = sinkFloat.read(output, numOutputFrames);
+ ASSERT_EQ(numInputFrames, numRead);
+
+ for (int i = 0; i < numRead; i++) {
+ // limiter must be symmetric wrt 0.
+ EXPECT_NEAR(output[i], -output[kNumSamples - i - 1], kTolerance);
+ if (i > 0) {
+ EXPECT_GE(output[i], output[i - 1]); // limiter must be monotonic
+ }
+ if (input[i] == 0.f) {
+ EXPECT_EQ(0.f, output[i]);
+ } else if (input[i] > 0.0f) {
+ EXPECT_GE(output[i], 0.0f);
+ EXPECT_LE(output[i], M_SQRT2); // limiter actually limits
+ EXPECT_LE(output[i], input[i]); // a limiter, gain <= 1
+ } else {
+ EXPECT_LE(output[i], 0.0f);
+ EXPECT_GE(output[i], -M_SQRT2); // limiter actually limits
+ EXPECT_GE(output[i], input[i]); // a limiter, gain <= 1
+ }
+ if (-1.f <= input[i] && input[i] <= 1.f) {
+ EXPECT_EQ(input[i], output[i]);
+ }
+ }
+}
+
+TEST(test_flowgraph, module_limiter_nan) {
+ constexpr int kArbitraryOutputSize = 100;
+ static const float input[] = {NAN, 0.5f, NAN, NAN, -10.0f, NAN};
+ static const float expected[] = {0.0f, 0.5f, 0.5f, 0.5f, -M_SQRT2, -M_SQRT2};
+ constexpr float tolerance = 0.00001f;
+ float output[kArbitraryOutputSize];
+ SourceFloat sourceFloat{1};
+ Limiter limiter{1};
+ SinkFloat sinkFloat{1};
+
+ const int numInputFrames = std::size(input);
+ sourceFloat.setData(input, numInputFrames);
+
+ sourceFloat.output.connect(&limiter.input);
+ limiter.output.connect(&sinkFloat.input);
+
+ const int numOutputFrames = std::size(output);
+ int32_t numRead = sinkFloat.read(output, numOutputFrames);
+ ASSERT_EQ(numInputFrames, numRead);
+
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_NEAR(expected[i], output[i], tolerance);
+ }
+}
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 11724e0..1521882 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -1932,7 +1932,7 @@
case media::AudioPortType::SESSION:
legacy.session = VALUE_OR_RETURN(
aidl2legacy_int32_t_audio_port_config_session_ext(
- VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ VALUE_OR_RETURN(UNION_GET(aidlSys, session))));
return legacy;
}
@@ -1966,9 +1966,9 @@
return OK;
}
case AUDIO_PORT_TYPE_SESSION:
- UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS(
+ UNION_SET(*aidl, unspecified, false);
+ UNION_SET(*aidlSys, session, VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_port_config_session_ext_int32_t(legacy.session)));
- UNION_SET(*aidlSys, unspecified, false);
return OK;
}
LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
@@ -2816,7 +2816,7 @@
case media::AudioPortType::SESSION:
legacy.session = VALUE_OR_RETURN(
aidl2legacy_int32_t_audio_port_session_ext(
- VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ VALUE_OR_RETURN(UNION_GET(aidlSys, session))));
return legacy;
}
@@ -2852,9 +2852,9 @@
return OK;
}
case AUDIO_PORT_TYPE_SESSION:
- UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS(
+ UNION_SET(*aidl, unspecified, false);
+ UNION_SET(*aidlSys, session, VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_port_session_ext_int32_t(legacy.session)));
- UNION_SET(*aidlSys, unspecified, false);
return OK;
}
LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
@@ -3388,4 +3388,24 @@
enumToMask_index<int32_t, media::AudioDirectMode>);
}
+ConversionResult<audio_latency_mode_t>
+aidl2legacy_LatencyMode_audio_latency_mode_t(media::LatencyMode aidl) {
+ switch (aidl) {
+ case media::LatencyMode::FREE:
+ return AUDIO_LATENCY_MODE_FREE;
+ case media::LatencyMode::LOW:
+ return AUDIO_LATENCY_MODE_LOW;
+ }
+ return unexpected(BAD_VALUE);
+}
+ConversionResult<media::LatencyMode>
+legacy2aidl_audio_latency_mode_t_LatencyMode(audio_latency_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_LATENCY_MODE_FREE:
+ return media::LatencyMode::FREE;
+ case AUDIO_LATENCY_MODE_LOW:
+ return media::LatencyMode::LOW;
+ }
+ return unexpected(BAD_VALUE);
+}
} // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 69a9c68..b6ddf56 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -54,8 +54,10 @@
"AudioVolumeGroup.cpp",
"PolicyAidlConversion.cpp"
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_export_shared",
+ ],
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
@@ -76,7 +78,6 @@
include_dirs: ["system/media/audio_utils/include"],
export_include_dirs: ["include"],
export_shared_lib_headers: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
@@ -116,8 +117,10 @@
"RecordingActivityTracker.cpp",
"TrackPlayerBase.cpp",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
@@ -211,7 +214,7 @@
],
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
@@ -237,8 +240,10 @@
export_header_lib_headers: [
"libaudioclient_aidl_conversion_util",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_export_shared",
+ ],
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libbase",
"libbinder",
@@ -250,7 +255,6 @@
"framework-permission-aidl-cpp",
],
export_shared_lib_headers: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libbase",
"shared-file-region-aidl-cpp",
@@ -302,12 +306,26 @@
double_loadable: true,
vendor_available: true,
srcs: [
+ "aidl/android/media/EffectConfig.aidl",
"aidl/android/media/IEffect.aidl",
"aidl/android/media/IEffectClient.aidl",
],
imports: [
+ "android.media.audio.common.types-V2",
"shared-file-region-aidl",
],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ },
}
aidl_interface {
@@ -342,10 +360,11 @@
"aidl/android/media/AudioUniqueIdUse.aidl",
"aidl/android/media/AudioVibratorInfo.aidl",
"aidl/android/media/EffectDescriptor.aidl",
+ "aidl/android/media/LatencyMode.aidl",
"aidl/android/media/TrackSecondaryOutputInfo.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"framework-permission-aidl",
],
backend: {
@@ -389,7 +408,7 @@
"aidl/android/media/SpatializerHeadTrackingMode.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"audioclient-types-aidl",
],
backend: {
@@ -432,7 +451,7 @@
"aidl/android/media/IAudioTrackCallback.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"audioclient-types-aidl",
"av-types-aidl",
"effect-aidl",
@@ -469,7 +488,7 @@
"aidl/android/media/IAudioPolicyServiceClient.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"audioclient-types-aidl",
"audiopolicy-types-aidl",
"capture_state_listener-aidl",
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 7b273ec..2870c4c 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -89,7 +89,7 @@
return NO_INIT;
}
- if (type == NULL && uuid == NULL) {
+ if (type == nullptr && uuid == nullptr) {
ALOGW("Must specify at least type or uuid");
return BAD_VALUE;
}
@@ -99,8 +99,8 @@
mCallback = callback;
memset(&mDescriptor, 0, sizeof(effect_descriptor_t));
- mDescriptor.type = *(type != NULL ? type : EFFECT_UUID_NULL);
- mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL);
+ mDescriptor.type = *(type != nullptr ? type : EFFECT_UUID_NULL);
+ mDescriptor.uuid = *(uuid != nullptr ? uuid : EFFECT_UUID_NULL);
// TODO b/182392769: use attribution source util
mIEffectClient = new EffectClient(this);
@@ -292,7 +292,7 @@
AudioSystem::releaseAudioSessionId(mSessionId,
VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid)));
}
- if (mIEffect != NULL) {
+ if (mIEffect != nullptr) {
mIEffect->disconnect();
IInterface::asBinder(mIEffect)->unlinkToDeath(mIEffectClient);
}
@@ -370,7 +370,7 @@
if (mEnabled == (cmdCode == EFFECT_CMD_ENABLE)) {
return NO_ERROR;
}
- if (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL) {
+ if (replySize == nullptr || *replySize != sizeof(status_t) || replyData == nullptr) {
return BAD_VALUE;
}
mLock.lock();
@@ -413,7 +413,7 @@
return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
- if (param == NULL || param->psize == 0 || param->vsize == 0) {
+ if (param == nullptr || param->psize == 0 || param->vsize == 0) {
return BAD_VALUE;
}
@@ -448,8 +448,7 @@
if (mStatus != NO_ERROR) {
return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
-
- if (param == NULL || param->psize == 0 || param->vsize == 0) {
+ if (param == nullptr || param->psize == 0 || param->vsize == 0) {
return BAD_VALUE;
}
@@ -504,8 +503,7 @@
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
return mStatus;
}
-
- if (param == NULL || param->psize == 0 || param->vsize == 0) {
+ if (param == nullptr || param->psize == 0 || param->vsize == 0) {
return BAD_VALUE;
}
@@ -529,6 +527,36 @@
return status;
}
+status_t AudioEffect::getConfigs(
+ audio_config_base_t *inputCfg, audio_config_base_t *outputCfg)
+{
+ if (mProbe) {
+ return INVALID_OPERATION;
+ }
+ if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
+ return mStatus;
+ }
+ if (inputCfg == NULL || outputCfg == NULL) {
+ return BAD_VALUE;
+ }
+ status_t status;
+ media::EffectConfig cfg;
+ Status bs = mIEffect->getConfig(&cfg, &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ ALOGW("%s received status %d from binder transaction", __func__, status);
+ return status;
+ }
+ if (status == NO_ERROR) {
+ *inputCfg = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioConfigBase_audio_config_base_t(
+ cfg.inputCfg, cfg.isOnInputStream));
+ *outputCfg = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioConfigBase_audio_config_base_t(
+ cfg.outputCfg, cfg.isOnInputStream));
+ } else {
+ ALOGW("%s received status %d from the effect", __func__, status);
+ }
+ return status;
+}
// -------------------------------------------------------------------------
@@ -540,7 +568,6 @@
if (cb != nullptr) {
cb->onError(mStatus);
}
- mIEffect.clear();
}
// -------------------------------------------------------------------------
@@ -603,6 +630,9 @@
status_t AudioEffect::queryNumberEffects(uint32_t *numEffects)
{
+ if (numEffects == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->queryNumberEffects(numEffects);
@@ -610,6 +640,9 @@
status_t AudioEffect::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
{
+ if (descriptor == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->queryEffect(index, descriptor);
@@ -620,6 +653,9 @@
uint32_t preferredTypeFlag,
effect_descriptor_t *descriptor)
{
+ if (uuid == nullptr || type == nullptr || descriptor == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->getEffectDescriptor(uuid, type, preferredTypeFlag, descriptor);
@@ -650,6 +686,9 @@
status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id)
{
+ if (id == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*id = af->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
@@ -663,14 +702,15 @@
audio_source_t source,
audio_unique_id_t *id)
{
+ if ((typeStr == nullptr && uuidStr == nullptr) || id == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE;
-
// Convert type & uuid from string to effect_uuid_t.
effect_uuid_t type;
- if (typeStr != NULL) {
+ if (typeStr != nullptr) {
status_t res = stringToGuid(typeStr, &type);
if (res != OK) return res;
} else {
@@ -678,7 +718,7 @@
}
effect_uuid_t uuid;
- if (uuidStr != NULL) {
+ if (uuidStr != nullptr) {
status_t res = stringToGuid(uuidStr, &uuid);
if (res != OK) return res;
} else {
@@ -706,14 +746,15 @@
audio_usage_t usage,
audio_unique_id_t *id)
{
+ if ((typeStr == nullptr && uuidStr == nullptr) || id == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE;
-
// Convert type & uuid from string to effect_uuid_t.
effect_uuid_t type;
- if (typeStr != NULL) {
+ if (typeStr != nullptr) {
status_t res = stringToGuid(typeStr, &type);
if (res != OK) return res;
} else {
@@ -721,7 +762,7 @@
}
effect_uuid_t uuid;
- if (uuidStr != NULL) {
+ if (uuidStr != nullptr) {
status_t res = stringToGuid(uuidStr, &uuid);
if (res != OK) return res;
} else {
@@ -764,7 +805,7 @@
status_t AudioEffect::stringToGuid(const char *str, effect_uuid_t *guid)
{
- if (str == NULL || guid == NULL) {
+ if (str == nullptr || guid == nullptr) {
return BAD_VALUE;
}
@@ -790,7 +831,7 @@
status_t AudioEffect::guidToString(const effect_uuid_t *guid, char *str, size_t maxLen)
{
- if (guid == NULL || str == NULL) {
+ if (guid == nullptr || str == nullptr) {
return BAD_VALUE;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 15203d6..69d73ad 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -146,39 +146,6 @@
audio_channel_mask_t channelMask,
const AttributionSourceState& client,
size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- audio_input_flags_t flags,
- const audio_attributes_t* pAttributes,
- audio_port_handle_t selectedDeviceId,
- audio_microphone_direction_t selectedMicDirection,
- float microphoneFieldDimension)
- : mActive(false),
- mStatus(NO_INIT),
- mClientAttributionSource(client),
- mSessionId(AUDIO_SESSION_ALLOCATE),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
-{
- uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid));
- pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid));
- (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback, user,
- notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
- uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
- microphoneFieldDimension);
-}
-
-AudioRecord::AudioRecord(
- audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const AttributionSourceState& client,
- size_t frameCount,
const wp<IAudioRecordCallback>& callback,
uint32_t notificationFrames,
audio_session_t sessionId,
@@ -255,37 +222,6 @@
mDeviceCallback.clear();
}
}
-namespace {
-class LegacyCallbackWrapper : public AudioRecord::IAudioRecordCallback {
- const AudioRecord::legacy_callback_t mCallback;
- void* const mData;
-
- public:
- LegacyCallbackWrapper(AudioRecord::legacy_callback_t callback, void* user)
- : mCallback(callback), mData(user) {}
-
- size_t onMoreData(const AudioRecord::Buffer& buffer) override {
- AudioRecord::Buffer copy = buffer;
- mCallback(AudioRecord::EVENT_MORE_DATA, mData, ©);
- return copy.size();
- }
-
- void onOverrun() override { mCallback(AudioRecord::EVENT_OVERRUN, mData, nullptr); }
-
- void onMarker(uint32_t markerPosition) override {
- mCallback(AudioRecord::EVENT_MARKER, mData, &markerPosition);
- }
-
- void onNewPos(uint32_t newPos) override {
- mCallback(AudioRecord::EVENT_NEW_POS, mData, &newPos);
- }
-
- void onNewIAudioRecord() override {
- mCallback(AudioRecord::EVENT_NEW_IAUDIORECORD, mData, nullptr);
- }
-};
-} // namespace
-
status_t AudioRecord::set(
audio_source_t inputSource,
uint32_t sampleRate,
@@ -479,37 +415,6 @@
return status;
}
-status_t AudioRecord::set(
- audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames,
- bool threadCanCallJava,
- audio_session_t sessionId,
- transfer_type transferType,
- audio_input_flags_t flags,
- uid_t uid,
- pid_t pid,
- const audio_attributes_t* pAttributes,
- audio_port_handle_t selectedDeviceId,
- audio_microphone_direction_t selectedMicDirection,
- float microphoneFieldDimension,
- int32_t maxSharedAudioHistoryMs)
-{
- if (callback != nullptr) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- return set(inputSource, sampleRate, format, channelMask, frameCount, mLegacyCallbackWrapper,
- notificationFrames, threadCanCallJava, sessionId, transferType, flags, uid, pid,
- pAttributes, selectedDeviceId, selectedMicDirection, microphoneFieldDimension,
- maxSharedAudioHistoryMs);
-}
// -------------------------------------------------------------------------
status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession)
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index de8c298..05f27b0 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -670,6 +670,30 @@
return Status::ok();
}
+Status AudioSystem::AudioFlingerClient::onSupportedLatencyModesChanged(
+ int output, const std::vector<media::LatencyMode>& latencyModes) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ std::vector<audio_latency_mode_t> modesLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<audio_latency_mode_t>>(
+ latencyModes, aidl2legacy_LatencyMode_audio_latency_mode_t));
+
+ std::vector<sp<SupportedLatencyModesCallback>> callbacks;
+ {
+ Mutex::Autolock _l(mLock);
+ for (auto callback : mSupportedLatencyModesCallbacks) {
+ if (auto ref = callback.promote(); ref != nullptr) {
+ callbacks.push_back(ref);
+ }
+ }
+ }
+ for (const auto& callback : callbacks) {
+ callback->onSupportedLatencyModesChanged(outputLegacy, modesLegacy);
+ }
+
+ return Status::ok();
+}
+
status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize) {
@@ -749,6 +773,31 @@
return NO_ERROR;
}
+status_t AudioSystem::AudioFlingerClient::addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ Mutex::Autolock _l(mLock);
+ if (std::find(mSupportedLatencyModesCallbacks.begin(),
+ mSupportedLatencyModesCallbacks.end(),
+ callback) != mSupportedLatencyModesCallbacks.end()) {
+ return INVALID_OPERATION;
+ }
+ mSupportedLatencyModesCallbacks.push_back(callback);
+ return NO_ERROR;
+}
+
+status_t AudioSystem::AudioFlingerClient::removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ Mutex::Autolock _l(mLock);
+ auto it = std::find(mSupportedLatencyModesCallbacks.begin(),
+ mSupportedLatencyModesCallbacks.end(),
+ callback);
+ if (it == mSupportedLatencyModesCallbacks.end()) {
+ return INVALID_OPERATION;
+ }
+ mSupportedLatencyModesCallbacks.erase(it);
+ return NO_ERROR;
+}
+
/* static */ uintptr_t AudioSystem::addErrorCallback(audio_error_callback cb) {
Mutex::Autolock _l(gLockErrorCallbacks);
gAudioErrorCallbacks.insert(cb);
@@ -966,7 +1015,7 @@
audio_session_t session,
audio_stream_type_t* stream,
const AttributionSourceState& attributionSource,
- const audio_config_t* config,
+ audio_config_t* config,
audio_output_flags_t flags,
audio_port_handle_t* selectedDeviceId,
audio_port_handle_t* portId,
@@ -1008,9 +1057,18 @@
media::GetOutputForAttrResponse responseAidl;
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ status_t status = statusTFromBinderStatus(
aps->getOutputForAttr(attrAidl, sessionAidl, attributionSource, configAidl, flagsAidl,
- selectedDeviceIdAidl, &responseAidl)));
+ selectedDeviceIdAidl, &responseAidl));
+ if (status != NO_ERROR) {
+ config->format = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioFormatDescription_audio_format_t(responseAidl.configBase.format));
+ config->channel_mask = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ responseAidl.configBase.channelMask, false /*isInput*/));
+ config->sample_rate = responseAidl.configBase.sampleRate;
+ return status;
+ }
*output = VALUE_OR_RETURN_STATUS(
aidl2legacy_int32_t_audio_io_handle_t(responseAidl.output));
@@ -1065,7 +1123,7 @@
audio_unique_id_t riid,
audio_session_t session,
const AttributionSourceState &attributionSource,
- const audio_config_base_t* config,
+ audio_config_base_t* config,
audio_input_flags_t flags,
audio_port_handle_t* selectedDeviceId,
audio_port_handle_t* portId) {
@@ -1102,9 +1160,14 @@
media::GetInputForAttrResponse response;
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ status_t status = statusTFromBinderStatus(
aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl, attributionSource,
- configAidl, flagsAidl, selectedDeviceIdAidl, &response)));
+ configAidl, flagsAidl, selectedDeviceIdAidl, &response));
+ if (status != NO_ERROR) {
+ *config = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(response.config, true /*isInput*/));
+ return status;
+ }
*input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.input));
*selectedDeviceId = VALUE_OR_RETURN_STATUS(
@@ -1662,6 +1725,24 @@
return afc->removeAudioDeviceCallback(callback, audioIo, portId);
}
+status_t AudioSystem::addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->addSupportedLatencyModesCallback(callback);
+}
+
+status_t AudioSystem::removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->removeSupportedLatencyModesCallback(callback);
+}
+
audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -2272,6 +2353,9 @@
const AudioDeviceTypeAddrVector &devices,
bool *canBeSpatialized) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (canBeSpatialized == nullptr) {
+ return BAD_VALUE;
+ }
if (aps == 0) {
return PERMISSION_DENIED;
}
@@ -2317,7 +2401,7 @@
status_t AudioSystem::getDirectProfilesForAttributes(const audio_attributes_t* attr,
std::vector<audio_profile>* audioProfiles) {
- if (attr == nullptr) {
+ if (attr == nullptr || audioProfiles == nullptr) {
return BAD_VALUE;
}
@@ -2338,6 +2422,24 @@
return NO_ERROR;
}
+status_t AudioSystem::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->setRequestedLatencyMode(output, mode);
+}
+
+status_t AudioSystem::getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->getSupportedLatencyModes(output, modes);
+}
+
class CaptureStateListenerImpl : public media::BnCaptureStateListener,
public IBinder::DeathRecipient {
public:
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 6ab8339..b18a569 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -29,6 +29,7 @@
#include <audio_utils/clock.h>
#include <audio_utils/primitives.h>
#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <media/AudioTrack.h>
#include <utils/Log.h>
#include <private/media/AudioTrackShared.h>
@@ -42,7 +43,9 @@
#define WAIT_PERIOD_MS 10
#define WAIT_STREAM_END_TIMEOUT_SEC 120
+
static const int kMaxLoopCountNotifications = 32;
+static constexpr char kAudioServiceName[] = "audio";
using ::android::aidl_utils::statusTFromBinderStatus;
using ::android::base::StringPrintf;
@@ -325,45 +328,6 @@
}
};
}
-
-AudioTrack::AudioTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void* user,
- int32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed,
- audio_port_handle_t selectedDeviceId)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mAudioTrackCallback(new AudioTrackCallback())
-{
- mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
- if (callback != nullptr) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- mSetParams = std::unique_ptr<SetParams>{new SetParams{
- streamType, sampleRate, format, channelMask, frameCount, flags, mLegacyCallbackWrapper,
- notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId,
- transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect,
- maxRequiredSpeed, selectedDeviceId}};
-}
-
AudioTrack::AudioTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
@@ -397,44 +361,6 @@
doNotReconnect, maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}};
}
-AudioTrack::AudioTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const sp<IMemory>& sharedBuffer,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void* user,
- int32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mAudioTrackCallback(new AudioTrackCallback())
-{
- mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
- if (callback) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- mSetParams = std::unique_ptr<SetParams>{new SetParams{
- streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags,
- mLegacyCallbackWrapper, notificationFrames, sharedBuffer, false /*threadCanCallJava*/,
- sessionId, transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect,
- maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}};
-}
-
void AudioTrack::onFirstRef() {
if (mSetParams) {
set(*mSetParams);
@@ -496,38 +422,6 @@
mDeviceCallback.clear();
}
}
-
-status_t AudioTrack::set(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void * user,
- int32_t notificationFrames,
- const sp<IMemory>& sharedBuffer,
- bool threadCanCallJava,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed,
- audio_port_handle_t selectedDeviceId)
-{
- if (callback) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- return set(streamType, sampleRate,format, channelMask, frameCount, flags,
- mLegacyCallbackWrapper, notificationFrames, sharedBuffer, threadCanCallJava,
- sessionId, transferType, offloadInfo, attributionSource, pAttributes,
- doNotReconnect, maxRequiredSpeed, selectedDeviceId);
-}
status_t AudioTrack::set(
audio_stream_type_t streamType,
uint32_t sampleRate,
@@ -559,7 +453,7 @@
std::string errorMessage;
// Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
+ "flags %#x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
__func__,
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
sessionId, transferType, attributionSource.uid, attributionSource.pid);
@@ -1398,6 +1292,10 @@
legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
if (status == NO_ERROR) {
mPlaybackRate = playbackRate;
+ } else if (status == INVALID_OPERATION
+ && playbackRate.mSpeed == 1.0f && mPlaybackRate.mPitch == 1.0f) {
+ mPlaybackRate = playbackRate;
+ return NO_ERROR;
}
return status;
}
@@ -2054,6 +1952,9 @@
}
mPortId = output.portId;
+ // notify the upper layers about the new portId
+ triggerPortIdUpdate_l();
+
// We retain a copy of the I/O handle, but don't own the reference
mOutput = output.outputId;
mRefreshRemaining = true;
@@ -3616,12 +3517,34 @@
if (mPlayerIId == playerIId) return;
mPlayerIId = playerIId;
+ triggerPortIdUpdate_l();
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID)
.set(AMEDIAMETRICS_PROP_PLAYERIID, playerIId)
.record();
}
+void AudioTrack::triggerPortIdUpdate_l() {
+ if (mAudioManager == nullptr) {
+ // use checkService() to avoid blocking if audio service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16(kAudioServiceName));
+ if (binder == nullptr) {
+ ALOGE("%s(%d): binding to audio service failed.",
+ __func__,
+ mPlayerIId);
+ return;
+ }
+
+ mAudioManager = interface_cast<IAudioManager>(binder);
+ }
+
+ // first time when the track is created we do not have a valid piid
+ if (mPlayerIId != PLAYER_PIID_INVALID) {
+ mAudioManager->playerEvent(mPlayerIId, PLAYER_UPDATE_PORT_ID, mPortId);
+ }
+}
+
status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
{
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 292d92f..6ad97d1 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -810,6 +810,33 @@
return statusTFromBinderStatus(mDelegate->setDeviceConnectedState(aidlPort, connected));
}
+status_t AudioFlingerClientAdapter::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ media::LatencyMode modeAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_latency_mode_t_LatencyMode(mode));
+ return statusTFromBinderStatus(mDelegate->setRequestedLatencyMode(outputAidl, modeAidl));
+}
+
+status_t AudioFlingerClientAdapter::getSupportedLatencyModes(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>* modes) {
+ if (modes == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ std::vector<media::LatencyMode> modesAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getSupportedLatencyModes(outputAidl, &modesAidl)));
+
+ *modes = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<audio_latency_mode_t>>(modesAidl,
+ aidl2legacy_LatencyMode_audio_latency_mode_t));
+
+ return NO_ERROR;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// AudioFlingerServerAdapter
AudioFlingerServerAdapter::AudioFlingerServerAdapter(
@@ -1304,4 +1331,28 @@
return Status::fromStatusT(mDelegate->setDeviceConnectedState(&portLegacy, connected));
}
+Status AudioFlingerServerAdapter::setRequestedLatencyMode(
+ int32_t output, media::LatencyMode modeAidl) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ audio_latency_mode_t modeLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_LatencyMode_audio_latency_mode_t(modeAidl));
+ return Status::fromStatusT(mDelegate->setRequestedLatencyMode(
+ outputLegacy, modeLegacy));
+}
+
+Status AudioFlingerServerAdapter::getSupportedLatencyModes(
+ int output, std::vector<media::LatencyMode>* _aidl_return) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ std::vector<audio_latency_mode_t> modesLegacy;
+
+ RETURN_BINDER_IF_ERROR(mDelegate->getSupportedLatencyModes(outputLegacy, &modesLegacy));
+
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertContainer<std::vector<media::LatencyMode>>(
+ modesLegacy, legacy2aidl_audio_latency_mode_t_LatencyMode));
+ return Status::ok();
+}
+
} // namespace android
diff --git a/media/libaudioclient/PlayerBase.cpp b/media/libaudioclient/PlayerBase.cpp
index 446a58c..651255a 100644
--- a/media/libaudioclient/PlayerBase.cpp
+++ b/media/libaudioclient/PlayerBase.cpp
@@ -58,6 +58,20 @@
}
}
+void PlayerBase::triggerPortIdUpdate(audio_port_handle_t portId) const {
+ if (mAudioManager == nullptr) {
+ ALOGE("%s: no audio service, player %d will not update portId %d",
+ __func__,
+ mPIId,
+ portId);
+ return;
+ }
+
+ if (mPIId != PLAYER_PIID_INVALID && portId != AUDIO_PORT_HANDLE_NONE) {
+ mAudioManager->playerEvent(mPIId, android::PLAYER_UPDATE_PORT_ID, portId);
+ }
+}
+
void PlayerBase::baseDestroy() {
serviceReleasePlayer();
if (mAudioManager != 0) {
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 3751f80..1aecfc6 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -4,10 +4,42 @@
"name": "audio_aidl_conversion_tests"
},
{
+ "name": "audio_aidl_status_tests"
+ },
+ {
+ "name": "audiorecord_tests"
+ },
+ {
+ "name": "audioeffect_tests"
+ },
+ {
+ "name": "audiorouting_tests"
+ },
+ {
+ "name": "audioclient_serialization_tests"
+ },
+ {
+ "name": "trackplayerbase_tests"
+ },
+ {
+ "name": "audiosystem_tests"
+ }
+ ],
+ "postsubmit": [
+ {
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
index 2cdf4f6..d9c6df4 100644
--- a/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
@@ -31,4 +31,6 @@
AudioPortDeviceExtSys device;
/** System-only parameters when the port is an audio mix. */
AudioPortMixExtSys mix;
+ /** Framework audio session identifier. */
+ int session;
}
diff --git a/media/libaudioclient/aidl/android/media/EffectConfig.aidl b/media/libaudioclient/aidl/android/media/EffectConfig.aidl
new file mode 100644
index 0000000..5f62b73
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/EffectConfig.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.media;
+
+import android.media.audio.common.AudioConfigBase;
+
+/**
+ * Describes configuration of an audio effect. Input and output
+ * audio configurations are described separately because the effect
+ * can perform transformations on channel layouts, for example.
+ *
+ * {@hide}
+ */
+parcelable EffectConfig {
+ /** Configuration of the audio input of the effect. */
+ AudioConfigBase inputCfg;
+ /** Configuration of the audio output of the effect. */
+ AudioConfigBase outputCfg;
+ /**
+ * Specifies whether the effect is instantiated on an input stream,
+ * e.g. on the input from a microphone.
+ */
+ boolean isOnInputStream;
+}
diff --git a/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl
index 9696124..347bf79 100644
--- a/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl
@@ -16,6 +16,8 @@
package android.media;
+import android.media.audio.common.AudioConfigBase;
+
/**
* {@hide}
*/
@@ -26,4 +28,6 @@
int selectedDeviceId;
/** Interpreted as audio_port_handle_t. */
int portId;
+ /** The suggested config if fails to get an input. **/
+ AudioConfigBase config;
}
diff --git a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
index f1848b6..5b25d79 100644
--- a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.media.audio.common.AudioConfigBase;
import android.media.audio.common.AudioStreamType;
/**
@@ -33,4 +34,6 @@
int[] secondaryOutputs;
/** True if the track is connected to a spatializer mixer and actually spatialized */
boolean isSpatialized;
+ /** The suggested audio config if fails to get an output. **/
+ AudioConfigBase configBase;
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
index 421c31c..a2bb024 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
@@ -18,6 +18,7 @@
import android.media.AudioIoConfigEvent;
import android.media.AudioIoDescriptor;
+import android.media.LatencyMode;
/**
* A callback interface for AudioFlinger.
@@ -27,4 +28,10 @@
interface IAudioFlingerClient {
oneway void ioConfigChanged(AudioIoConfigEvent event,
in AudioIoDescriptor ioDesc);
+ /**
+ * Called when the latency modes supported on a given output stream change.
+ * output is the I/O handle of the output stream for which the change is signalled.
+ * latencyModes is the new list of supported latency modes (See LatencyMode.aidl).
+ */
+ oneway void onSupportedLatencyModesChanged(int output, in LatencyMode[] latencyModes);
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index 10da028..9b8a843 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -35,6 +35,7 @@
import android.media.IAudioFlingerClient;
import android.media.IAudioRecord;
import android.media.IAudioTrack;
+import android.media.LatencyMode;
import android.media.MicrophoneInfoData;
import android.media.RenderPosition;
import android.media.TrackSecondaryOutputInfo;
@@ -228,6 +229,23 @@
void setDeviceConnectedState(in AudioPort devicePort, boolean connected);
+ /**
+ * Requests a given latency mode (See LatencyMode.aidl) on an output stream.
+ * This can be used when some use case on a given mixer/stream can only be enabled
+ * if a specific latency mode is selected on the audio path below the HAL.
+ * For instance spatial audio with head tracking.
+ * output is the I/O handle of the output stream for which the request is made.
+ * latencyMode is the requested latency mode.
+ */
+ void setRequestedLatencyMode(int output, LatencyMode latencyMode);
+
+ /**
+ * Queries the list of latency modes (See LatencyMode.aidl) supported by an output stream.
+ * output is the I/O handle of the output stream to which the query applies.
+ * returns the list of supported latency modes.
+ */
+ LatencyMode[] getSupportedLatencyModes(int output);
+
// When adding a new method, please review and update
// IAudioFlinger.h AudioFlingerServerAdapter::Delegate::TransactionCode
// AudioFlinger.cpp AudioFlinger::onTransactWrapper()
diff --git a/media/libaudioclient/aidl/android/media/IEffect.aidl b/media/libaudioclient/aidl/android/media/IEffect.aidl
index 6ec0405..b7f70a6 100644
--- a/media/libaudioclient/aidl/android/media/IEffect.aidl
+++ b/media/libaudioclient/aidl/android/media/IEffect.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.media.EffectConfig;
import android.media.SharedFileRegion;
/**
@@ -63,6 +64,14 @@
*/
SharedFileRegion getCblk();
+ /**
+ * Provides audio configurations for effect's input and output,
+ * see EffectConfig parcelable for more details.
+ *
+ * @return a status_t code.
+ */
+ int getConfig(out EffectConfig config);
+
// When adding a new method, please review and update
// Effects.cpp AudioFlinger::EffectHandle::onTransact()
// Effects.cpp IEFFECT_BINDER_METHOD_MACRO_LIST
diff --git a/media/libaudioclient/aidl/android/media/LatencyMode.aidl b/media/libaudioclient/aidl/android/media/LatencyMode.aidl
new file mode 100644
index 0000000..0b2a72b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/LatencyMode.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 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.
+ */
+
+package android.media;
+
+/**
+ * The latency mode currently used by the spatializer mixer.
+ * {@hide}
+ */
+@Backing(type="byte")
+enum LatencyMode {
+ /** No specific constraint on the latency */
+ FREE = 0,
+ /** A relatively low latency compatible with head tracking operation (e.g less than 100ms) */
+ LOW = 1,
+}
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index 969e3e6..dd3e3ba 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -25,6 +25,9 @@
cc_fuzz {
name: "audioflinger_fuzzer",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
srcs: [
"audioflinger_fuzzer.cpp",
],
@@ -46,7 +49,6 @@
],
shared_libs: [
"android.hardware.audio.common-util",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index 036e72e..5536bcb 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -84,13 +84,15 @@
};
template <typename T, typename X, typename FUNC>
-std::vector<T> getFlags(const xsdc_enum_range<X> &range, const FUNC &func,
- const std::string &findString = {}) {
+std::vector<T> getFlags(const xsdc_enum_range<X>& range, const FUNC& func,
+ const std::string& findString = {},
+ const std::set<X>& excludedValues = {}) {
std::vector<T> vec;
for (const auto &xsdEnumVal : range) {
T enumVal;
std::string enumString = toString(xsdEnumVal);
if (enumString.find(findString) != std::string::npos &&
+ (excludedValues.find(xsdEnumVal) == excludedValues.end()) &&
func(enumString.c_str(), &enumVal)) {
vec.push_back(enumVal);
}
@@ -102,13 +104,29 @@
getFlags<audio_stream_type_t, xsd::AudioStreamType, decltype(audio_stream_type_from_string)>(
xsdc_enum_range<xsd::AudioStreamType>{}, audio_stream_type_from_string);
+/**
+ * AudioFormat - AUDIO_FORMAT_HE_AAC_V1 and AUDIO_FORMAT_HE_AAC_V2
+ * are excluded from kFormats[] in order to avoid the abort triggered
+ * for these two types of AudioFormat in
+ * AidlConversion::legacy2aidl_audio_format_t_AudioFormatDescription()
+ */
static const std::vector<audio_format_t> kFormats =
- getFlags<audio_format_t, xsd::AudioFormat, decltype(audio_format_from_string)>(
- xsdc_enum_range<xsd::AudioFormat>{}, audio_format_from_string);
+ getFlags<audio_format_t, xsd::AudioFormat, decltype(audio_format_from_string)>(
+ xsdc_enum_range<xsd::AudioFormat>{}, audio_format_from_string, {},
+ {xsd::AudioFormat::AUDIO_FORMAT_HE_AAC_V1,
+ xsd::AudioFormat::AUDIO_FORMAT_HE_AAC_V2});
+/**
+ * AudioChannelMask - AUDIO_CHANNEL_IN_6
+ * is excluded from kChannelMasks[] in order to avoid the abort triggered
+ * for this type of AudioChannelMask in
+ * AidlConversion::legacy2aidl_audio_channel_mask_t_AudioChannelLayout()
+ */
static const std::vector<audio_channel_mask_t> kChannelMasks =
- getFlags<audio_channel_mask_t, xsd::AudioChannelMask, decltype(audio_channel_mask_from_string)>(
- xsdc_enum_range<xsd::AudioChannelMask>{}, audio_channel_mask_from_string);
+ getFlags<audio_channel_mask_t, xsd::AudioChannelMask,
+ decltype(audio_channel_mask_from_string)>(
+ xsdc_enum_range<xsd::AudioChannelMask>{}, audio_channel_mask_from_string, {},
+ {xsd::AudioChannelMask::AUDIO_CHANNEL_IN_6});
static const std::vector<audio_usage_t> kUsages =
getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
@@ -126,9 +144,17 @@
getFlags<audio_gain_mode_t, xsd::AudioGainMode, decltype(audio_gain_mode_from_string)>(
xsdc_enum_range<xsd::AudioGainMode>{}, audio_gain_mode_from_string);
+/**
+ * AudioDevice - AUDIO_DEVICE_IN_AMBIENT and AUDIO_DEVICE_IN_COMMUNICATION
+ * are excluded from kDevices[] in order to avoid the abort triggered
+ * for these two types of AudioDevice in
+ * AidlConversion::aidl2legacy_AudioDeviceDescription_audio_devices_t()
+ */
static const std::vector<audio_devices_t> kDevices =
- getFlags<audio_devices_t, xsd::AudioDevice, decltype(audio_device_from_string)>(
- xsdc_enum_range<xsd::AudioDevice>{}, audio_device_from_string);
+ getFlags<audio_devices_t, xsd::AudioDevice, decltype(audio_device_from_string)>(
+ xsdc_enum_range<xsd::AudioDevice>{}, audio_device_from_string, {},
+ {xsd::AudioDevice::AUDIO_DEVICE_IN_AMBIENT,
+ xsd::AudioDevice::AUDIO_DEVICE_IN_COMMUNICATION});
static const std::vector<audio_input_flags_t> kInputFlags =
getFlags<audio_input_flags_t, xsd::AudioInOutFlag, decltype(audio_input_flag_from_string)>(
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index e769303..1e66164 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -35,6 +35,7 @@
#include <android/media/AudioTimestampInternal.h>
#include <android/media/AudioUniqueIdUse.h>
#include <android/media/EffectDescriptor.h>
+#include <android/media/LatencyMode.h>
#include <android/media/TrackSecondaryOutputInfo.h>
#include <android/media/audio/common/AudioChannelLayout.h>
#include <android/media/audio/common/AudioConfig.h>
@@ -454,5 +455,9 @@
ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl);
ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy);
+ConversionResult<audio_latency_mode_t>
+aidl2legacy_LatencyMode_audio_latency_mode_t(media::LatencyMode aidl);
+ConversionResult<media::LatencyMode>
+legacy2aidl_audio_latency_mode_t_LatencyMode(audio_latency_mode_t legacy);
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index 56884a3..72c050b 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -136,7 +136,7 @@
* indicated by count.
* PERMISSION_DENIED could not get AudioFlinger interface
* NO_INIT effect library failed to initialize
- * BAD_VALUE invalid audio session or descriptor pointers
+ * BAD_VALUE invalid audio session, or invalid descriptor or count pointers
*
* Returned value
* *descriptor updated with descriptors of pre processings enabled by default
@@ -160,6 +160,7 @@
* NO_ERROR successful operation.
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
+ * BAD_VALUE invalid pointer to id
* Returned value
* *id: The new unique system-wide effect id.
*/
@@ -194,7 +195,7 @@
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
* NO_INIT effect library failed to initialize.
- * BAD_VALUE invalid source, type uuid or implementation uuid.
+ * BAD_VALUE invalid source, type uuid or implementation uuid, or id pointer
* NAME_NOT_FOUND no effect with this uuid or type found.
*
* Returned value
@@ -233,7 +234,7 @@
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
* NO_INIT effect library failed to initialize.
- * BAD_VALUE invalid type uuid or implementation uuid.
+ * BAD_VALUE invalid type uuid or implementation uuid, or id pointer
* NAME_NOT_FOUND no effect with this uuid or type found.
*
* Returned value
@@ -517,7 +518,7 @@
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation.
* - INVALID_OPERATION: the application does not have control of the effect engine.
- * - BAD_VALUE: invalid parameter identifier or value.
+ * - BAD_VALUE: invalid parameter structure pointer, or invalid identifier or value.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
virtual status_t setParameter(effect_param_t *param);
@@ -562,7 +563,7 @@
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation.
* - INVALID_OPERATION: the AudioEffect was not successfully initialized.
- * - BAD_VALUE: invalid parameter identifier.
+ * - BAD_VALUE: invalid parameter structure pointer, or invalid parameter identifier.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
virtual status_t getParameter(effect_param_t *param);
@@ -577,6 +578,25 @@
uint32_t *replySize,
void *replyData);
+ /* Retrieves the configuration of the effect.
+ *
+ * Parameters:
+ * inputCfg: pointer to audio_config_base_t structure receiving input
+ * configuration of the effect
+ * outputCfg: pointer to audio_config_base_t structure receiving output
+ * configuration of the effect
+ *
+ * Channel masks of the returned configs are "input" or "output" depending
+ * on the direction of the stream that the effect is attached to.
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR: successful operation.
+ * - INVALID_OPERATION: the AudioEffect was not successfully initialized.
+ * - BAD_VALUE: null config pointers
+ * - DEAD_OBJECT: the effect engine has been deleted.
+ */
+ virtual status_t getConfigs(audio_config_base_t *inputCfg,
+ audio_config_base_t *outputCfg);
/*
* Utility functions.
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index cb05dd9..5a1ff65 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -46,27 +46,6 @@
{
public:
- /* Events used by AudioRecord callback function (legacy_callback_t).
- * Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
- */
- enum event_type {
- EVENT_MORE_DATA = 0, // Request to read available data from buffer.
- // If this event is delivered but the callback handler
- // does not want to read the available data, the handler must
- // explicitly ignore the event by setting frameCount to zero.
- EVENT_OVERRUN = 1, // Buffer overrun occurred.
- EVENT_MARKER = 2, // Record head is at the specified marker position
- // (See setMarkerPosition()).
- EVENT_NEW_POS = 3, // Record head is at a new position
- // (See setPositionUpdatePeriod()).
- EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and
- // voluntary invalidation by mediaserver, or mediaserver crash.
- };
-
- /* Client should declare a Buffer and pass address to obtainBuffer()
- * and releaseBuffer(). See also legacy_callback_t for EVENT_MORE_DATA.
- */
-
class Buffer
{
friend AudioRecord;
@@ -122,7 +101,6 @@
* - EVENT_NEW_IAUDIORECORD: unused.
*/
- typedef void (*legacy_callback_t)(int event, void* user, void *info);
class IAudioRecordCallback : public virtual RefBase {
friend AudioRecord;
@@ -226,24 +204,6 @@
float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);
- AudioRecord(audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const android::content::AttributionSourceState& client,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
- const audio_attributes_t* pAttributes = nullptr,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- audio_microphone_direction_t
- selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);
-
/* Terminates the AudioRecord and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioRecord.
*/
@@ -286,27 +246,6 @@
float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
int32_t maxSharedAudioHistoryMs = 0);
- status_t set(audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames = 0,
- bool threadCanCallJava = false,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
- const audio_attributes_t* pAttributes = nullptr,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- audio_microphone_direction_t
- selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
- int32_t maxSharedAudioHistoryMs = 0);
-
/* Result of constructing the AudioRecord. This must be checked for successful initialization
* before using any AudioRecord API (except for set()), because using
* an uninitialized AudioRecord produces undefined results.
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 360b83d..1c414ec 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -26,8 +26,11 @@
#include <android/media/AudioVibratorInfo.h>
#include <android/media/BnAudioFlingerClient.h>
#include <android/media/BnAudioPolicyServiceClient.h>
+#include <android/media/EffectDescriptor.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
+#include <android/media/RecordClientInfo.h>
+#include <android/media/audio/common/AudioConfigBase.h>
#include <android/media/audio/common/AudioMMapPolicyInfo.h>
#include <android/media/audio/common/AudioMMapPolicyType.h>
#include <android/media/audio/common/AudioPort.h>
@@ -279,12 +282,31 @@
static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
+ /**
+ * Get output stream for given parameters.
+ *
+ * @param[in] attr the requested audio attributes
+ * @param[in|out] output the io handle of the output for the playback. It is specified when
+ * starting mmap thread.
+ * @param[in] session the session id for the client
+ * @param[in|out] stream the stream type used for the playback
+ * @param[in] attributionSource a source to which access to permission protected data
+ * @param[in|out] config the requested configuration client, the suggested configuration will
+ * be returned if no proper output is found for requested configuration
+ * @param[in] flags the requested output flag from client
+ * @param[in|out] selectedDeviceId the requested device id for playback, the actual device id
+ * for playback will be returned
+ * @param[out] portId the generated port id to identify the client
+ * @param[out] secondaryOutputs collection of io handle for secondary outputs
+ * @param[out] isSpatialized true if the playback will be spatialized
+ * @return if the call is successful or not
+ */
static status_t getOutputForAttr(audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
const AttributionSourceState& attributionSource,
- const audio_config_t *config,
+ audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
@@ -294,14 +316,31 @@
static status_t stopOutput(audio_port_handle_t portId);
static void releaseOutput(audio_port_handle_t portId);
- // Client must successfully hand off the handle reference to AudioFlinger via createRecord(),
- // or release it with releaseInput().
+ /**
+ * Get input stream for given parameters.
+ * Client must successfully hand off the handle reference to AudioFlinger via createRecord(),
+ * or release it with releaseInput().
+ *
+ * @param[in] attr the requested audio attributes
+ * @param[in|out] input the io handle of the input for the capture. It is specified when
+ * starting mmap thread.
+ * @param[in] riid an unique id to identify the record client
+ * @param[in] session the session id for the client
+ * @param[in] attributionSource a source to which access to permission protected data
+ * @param[in|out] config the requested configuration client, the suggested configuration will
+ * be returned if no proper input is found for requested configuration
+ * @param[in] flags the requested input flag from client
+ * @param[in|out] selectedDeviceId the requested device id for playback, the actual device id
+ * for playback will be returned
+ * @param[out] portId the generated port id to identify the client
+ * @return if the call is successful or not
+ */
static status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_unique_id_t riid,
audio_session_t session,
- const AttributionSourceState& attributionSource,
- const audio_config_base_t *config,
+ const AttributionSourceState& attributionSource,
+ audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
@@ -569,6 +608,12 @@
static status_t getDirectProfilesForAttributes(const audio_attributes_t* attr,
std::vector<audio_profile>* audioProfiles);
+ static status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode);
+
+ static status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes);
+
// A listener for capture state changes.
class CaptureStateListener : public virtual RefBase {
public:
@@ -644,6 +689,22 @@
audio_io_handle_t audioIo,
audio_port_handle_t portId);
+ class SupportedLatencyModesCallback : public virtual RefBase
+ {
+ public:
+
+ SupportedLatencyModesCallback() = default;
+ virtual ~SupportedLatencyModesCallback() = default;
+
+ virtual void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) = 0;
+ };
+
+ static status_t addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+ static status_t removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+
static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
static status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
@@ -682,6 +743,9 @@
media::AudioIoConfigEvent event,
const media::AudioIoDescriptor& ioDesc) override;
+ binder::Status onSupportedLatencyModesChanged(
+ int output, const std::vector<media::LatencyMode>& latencyModes) override;
+
status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo,
audio_port_handle_t portId);
@@ -689,6 +753,11 @@
audio_io_handle_t audioIo,
audio_port_handle_t portId);
+ status_t addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+ status_t removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+
audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
private:
@@ -697,6 +766,10 @@
std::map<audio_io_handle_t, std::map<audio_port_handle_t, wp<AudioDeviceCallback>>>
mAudioDeviceCallbacks;
+
+ std::vector<wp<SupportedLatencyModesCallback>>
+ mSupportedLatencyModesCallbacks GUARDED_BY(mLock);
+
// cached values for recording getInputBufferSize() queries
size_t mInBuffSize; // zero indicates cache is invalid
uint32_t mInSamplingRate;
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 9f540e6..e873607 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOTRACK_H
#define ANDROID_AUDIOTRACK_H
+#include <audiomanager/IAudioManager.h>
#include <binder/IMemory.h>
#include <cutils/sched_policy.h>
#include <media/AudioSystem.h>
@@ -148,7 +149,6 @@
* - EVENT_NEW_TIMESTAMP: pointer to const AudioTimestamp.
*/
- typedef void (*legacy_callback_t)(int event, void* user, void* info);
class IAudioTrackCallback : public virtual RefBase {
friend AudioTrack;
protected:
@@ -343,26 +343,6 @@
float maxRequiredSpeed = 1.0f,
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
- AudioTrack( audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t cbf,
- void* user = nullptr,
- int32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
* Data to be rendered is passed in a shared memory buffer
@@ -391,25 +371,6 @@
bool doNotReconnect = false,
float maxRequiredSpeed = 1.0f);
-
- AudioTrack( audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const sp<IMemory>& sharedBuffer,
- audio_output_flags_t flags,
- legacy_callback_t cbf,
- void* user = nullptr,
- int32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f);
-
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
*/
@@ -490,28 +451,8 @@
}
void onFirstRef() override;
public:
- status_t set(audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void * user = nullptr,
- int32_t notificationFrames = 0,
- const sp<IMemory>& sharedBuffer = 0,
- bool threadCanCallJava = false,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
- // FIXME(b/169889714): Vendor code depends on the old method signature at link time
+ typedef void (*legacy_callback_t)(int event, void* user, void* info);
+ // FIXME(b/169889714): Vendor code depends on the old method signature at link time
status_t set(audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
@@ -1205,6 +1146,8 @@
void setAudioTrackCallback(const sp<media::IAudioTrackCallback>& callback) {
mAudioTrackCallback->setAudioTrackCallback(callback);
}
+ private:
+ void triggerPortIdUpdate_l();
protected:
/* copying audio tracks is not allowed */
@@ -1471,7 +1414,7 @@
audio_session_t mSessionId;
int mAuxEffectId;
- audio_port_handle_t mPortId; // Id from Audio Policy Manager
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE; // Id from Audio Policy Manager
/**
* mPlayerIId is the player id of the AudioTrack used by AudioManager.
@@ -1479,6 +1422,9 @@
*/
int mPlayerIId = -1; // AudioManager.h PLAYER_PIID_INVALID
+ /** Interface for interacting with the AudioService. */
+ sp<IAudioManager> mAudioManager;
+
/**
* mLogSessionId is a string identifying this AudioTrack for the metrics service.
* It may be unique or shared with other objects. An empty string means the
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 3c3715d..c891ae6 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -360,6 +360,13 @@
virtual int32_t getAAudioHardwareBurstMinUsec() = 0;
virtual status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) = 0;
+
+ virtual status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) = 0;
+
+ virtual status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) = 0;
+
};
/**
@@ -462,6 +469,10 @@
int32_t getAAudioMixerBurstCount() override;
int32_t getAAudioHardwareBurstMinUsec() override;
status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) override;
+ status_t setRequestedLatencyMode(audio_io_handle_t output,
+ audio_latency_mode_t mode) override;
+ status_t getSupportedLatencyModes(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>* modes) override;
private:
const sp<media::IAudioFlingerService> mDelegate;
@@ -551,6 +562,8 @@
GET_AAUDIO_MIXER_BURST_COUNT = media::BnAudioFlingerService::TRANSACTION_getAAudioMixerBurstCount,
GET_AAUDIO_HARDWARE_BURST_MIN_USEC = media::BnAudioFlingerService::TRANSACTION_getAAudioHardwareBurstMinUsec,
SET_DEVICE_CONNECTED_STATE = media::BnAudioFlingerService::TRANSACTION_setDeviceConnectedState,
+ SET_REQUESTED_LATENCY_MODE = media::BnAudioFlingerService::TRANSACTION_setRequestedLatencyMode,
+ GET_SUPPORTED_LATENCY_MODES = media::BnAudioFlingerService::TRANSACTION_getSupportedLatencyModes,
};
protected:
@@ -672,7 +685,9 @@
Status getAAudioMixerBurstCount(int32_t* _aidl_return) override;
Status getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
Status setDeviceConnectedState(const media::AudioPort& port, bool connected) override;
-
+ Status setRequestedLatencyMode(int output, media::LatencyMode mode) override;
+ Status getSupportedLatencyModes(int output,
+ std::vector<media::LatencyMode>* _aidl_return) override;
private:
const sp<AudioFlingerServerAdapter::Delegate> mDelegate;
};
diff --git a/media/libaudioclient/include/media/PlayerBase.h b/media/libaudioclient/include/media/PlayerBase.h
index 23b6bfd..5475f76 100644
--- a/media/libaudioclient/include/media/PlayerBase.h
+++ b/media/libaudioclient/include/media/PlayerBase.h
@@ -53,6 +53,10 @@
void baseUpdateDeviceId(audio_port_handle_t deviceId);
+ /**
+ * Updates the mapping in the AudioService between portId and piid
+ */
+ void triggerPortIdUpdate(audio_port_handle_t portId) const;
protected:
void init(player_type_t playerType, audio_usage_t usage, audio_session_t sessionId);
@@ -74,7 +78,6 @@
// player interface ID, uniquely identifies the player in the system
// effectively const after PlayerBase::init().
audio_unique_id_t mPIId;
-
private:
// report events to AudioService
void servicePlayerEvent(player_state_t event, audio_port_handle_t deviceId);
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 891293e..1b09173 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -24,7 +24,10 @@
cc_test {
name: "audio_aidl_conversion_tests",
- defaults: ["libaudioclient_tests_defaults"],
+ defaults: [
+ "libaudioclient_tests_defaults",
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
srcs: ["audio_aidl_legacy_conversion_tests.cpp"],
shared_libs: [
"libbinder",
@@ -33,7 +36,6 @@
"libutils",
],
static_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudioclient_aidl_conversion",
"libstagefright_foundation",
@@ -93,3 +95,115 @@
],
data: ["record_test_input_*.txt"],
}
+
+cc_defaults {
+ name: "libaudioclient_gtests_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
+ shared_libs: [
+ "capture_state_listener-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ "libbase",
+ "libbinder",
+ "libcgrouprc",
+ "libcutils",
+ "libdl",
+ "liblog",
+ "libmedia",
+ "libmediametrics",
+ "libmediautils",
+ "libmedia_helper",
+ "libnblog",
+ "libprocessgroup",
+ "libshmemcompat",
+ "libstagefright_foundation",
+ "libutils",
+ "libxml2",
+ "mediametricsservice-aidl-cpp",
+ "packagemanager_aidl-cpp",
+ "shared-file-region-aidl-cpp",
+ ],
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "effect-aidl-cpp",
+ "libaudioclient",
+ "libaudioclient_aidl_conversion",
+ "libaudiofoundation",
+ "libaudiomanager",
+ "libaudiopolicy",
+ "libaudioutils",
+ ],
+ data: ["bbb*.raw"],
+ test_config_template: "audio_test_template.xml",
+ test_suites: ["device-tests"],
+}
+
+cc_test {
+ name: "audiorecord_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiorecord_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audiotrack_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiotrack_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audioeffect_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audioeffect_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audiorouting_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiorouting_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audioclient_serialization_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audioclient_serialization_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "trackplayerbase_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: ["trackplayerbase_tests.cpp"],
+}
+
+cc_test {
+ name: "audiosystem_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiosystem_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
diff --git a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
index 997f62a..9e663bc 100644
--- a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
@@ -16,22 +16,29 @@
#include <gtest/gtest.h>
-#include <media/AudioCommonTypes.h>
#include <media/AidlConversion.h>
+#include <media/AudioCommonTypes.h>
using namespace android;
using namespace android::aidl_utils;
+using android::media::AudioDirectMode;
using media::audio::common::AudioChannelLayout;
using media::audio::common::AudioDeviceDescription;
using media::audio::common::AudioDeviceType;
+using media::audio::common::AudioEncapsulationMetadataType;
+using media::audio::common::AudioEncapsulationType;
using media::audio::common::AudioFormatDescription;
using media::audio::common::AudioFormatType;
+using media::audio::common::AudioGainMode;
+using media::audio::common::AudioStandard;
+using media::audio::common::ExtraAudioDescriptor;
using media::audio::common::PcmType;
namespace {
-template<typename T> size_t hash(const T& t) {
+template <typename T>
+size_t hash(const T& t) {
return std::hash<T>{}(t);
}
@@ -52,10 +59,8 @@
return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
// Use channels that exist both for input and output,
// but doesn't form a known layout mask.
- AudioChannelLayout::CHANNEL_FRONT_LEFT |
- AudioChannelLayout::CHANNEL_FRONT_RIGHT |
- AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT |
- AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT);
+ AudioChannelLayout::CHANNEL_FRONT_LEFT | AudioChannelLayout::CHANNEL_FRONT_RIGHT |
+ AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT | AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT);
}
AudioChannelLayout make_ACL_ChannelIndex2() {
@@ -74,7 +79,7 @@
}
AudioDeviceDescription make_AudioDeviceDescription(AudioDeviceType type,
- const std::string& connection = "") {
+ const std::string& connection = "") {
AudioDeviceDescription result;
result.type = type;
result.connection = connection;
@@ -95,12 +100,12 @@
AudioDeviceDescription make_ADD_WiredHeadset() {
return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET,
- AudioDeviceDescription::CONNECTION_ANALOG());
+ AudioDeviceDescription::CONNECTION_ANALOG());
}
AudioDeviceDescription make_ADD_BtScoHeadset() {
return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET,
- AudioDeviceDescription::CONNECTION_BT_SCO());
+ AudioDeviceDescription::CONNECTION_BT_SCO());
}
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
@@ -121,8 +126,7 @@
return result;
}
-AudioFormatDescription make_AudioFormatDescription(PcmType transport,
- const std::string& encoding) {
+AudioFormatDescription make_AudioFormatDescription(PcmType transport, const std::string& encoding) {
auto result = make_AudioFormatDescription(encoding);
result.pcm = transport;
return result;
@@ -154,6 +158,22 @@
return afd;
}
+android::media::TrackSecondaryOutputInfo make_TrackSecondaryOutputInfo() {
+ android::media::TrackSecondaryOutputInfo result;
+ result.portId = 1;
+ result.secondaryOutputIds = {0, 5, 7};
+ return result;
+}
+
+ExtraAudioDescriptor make_ExtraAudioDescriptor(AudioStandard audioStandard,
+ AudioEncapsulationType audioEncapsulationType) {
+ ExtraAudioDescriptor result;
+ result.standard = audioStandard;
+ result.audioDescriptor = {0xb4, 0xaf, 0x98, 0x1a};
+ result.encapsulationType = audioEncapsulationType;
+ return result;
+}
+
} // namespace
// Verify that two independently constructed ADDs/AFDs have the same hash.
@@ -163,7 +183,8 @@
// is identical to the same format description constructed by the framework.
class HashIdentityTest : public ::testing::Test {
public:
- template<typename T> void verifyHashIdentity(const std::vector<std::function<T()>>& valueGens) {
+ template <typename T>
+ void verifyHashIdentity(const std::vector<std::function<T()>>& valueGens) {
for (size_t i = 0; i < valueGens.size(); ++i) {
for (size_t j = 0; j < valueGens.size(); ++j) {
if (i == j) {
@@ -177,27 +198,25 @@
};
TEST_F(HashIdentityTest, AudioChannelLayoutHashIdentity) {
- verifyHashIdentity<AudioChannelLayout>({
- make_ACL_None, make_ACL_Invalid, make_ACL_Stereo,
- make_ACL_LayoutArbitrary, make_ACL_ChannelIndex2,
- make_ACL_ChannelIndexArbitrary, make_ACL_VoiceCall});
+ verifyHashIdentity<AudioChannelLayout>({make_ACL_None, make_ACL_Invalid, make_ACL_Stereo,
+ make_ACL_LayoutArbitrary, make_ACL_ChannelIndex2,
+ make_ACL_ChannelIndexArbitrary, make_ACL_VoiceCall});
}
TEST_F(HashIdentityTest, AudioDeviceDescriptionHashIdentity) {
- verifyHashIdentity<AudioDeviceDescription>({
- make_ADD_None, make_ADD_DefaultIn, make_ADD_DefaultOut, make_ADD_WiredHeadset,
- make_ADD_BtScoHeadset});
+ verifyHashIdentity<AudioDeviceDescription>({make_ADD_None, make_ADD_DefaultIn,
+ make_ADD_DefaultOut, make_ADD_WiredHeadset,
+ make_ADD_BtScoHeadset});
}
TEST_F(HashIdentityTest, AudioFormatDescriptionHashIdentity) {
- verifyHashIdentity<AudioFormatDescription>({
- make_AFD_Default, make_AFD_Invalid, make_AFD_Pcm16Bit, make_AFD_Bitstream,
- make_AFD_Encap, make_AFD_Encap_with_Enc});
+ verifyHashIdentity<AudioFormatDescription>({make_AFD_Default, make_AFD_Invalid,
+ make_AFD_Pcm16Bit, make_AFD_Bitstream,
+ make_AFD_Encap, make_AFD_Encap_with_Enc});
}
using ChannelLayoutParam = std::tuple<AudioChannelLayout, bool /*isInput*/>;
-class AudioChannelLayoutRoundTripTest :
- public testing::TestWithParam<ChannelLayoutParam> {};
+class AudioChannelLayoutRoundTripTest : public testing::TestWithParam<ChannelLayoutParam> {};
TEST_P(AudioChannelLayoutRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = std::get<0>(GetParam());
const bool isInput = std::get<1>(GetParam());
@@ -207,21 +226,82 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutRoundTrip,
- AudioChannelLayoutRoundTripTest,
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioChannelLayoutRoundTrip, AudioChannelLayoutRoundTripTest,
testing::Combine(
testing::Values(AudioChannelLayout{}, make_ACL_Invalid(), make_ACL_Stereo(),
- make_ACL_LayoutArbitrary(), make_ACL_ChannelIndex2(),
- make_ACL_ChannelIndexArbitrary()),
+ make_ACL_LayoutArbitrary(), make_ACL_ChannelIndex2(),
+ make_ACL_ChannelIndexArbitrary(),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_LOW_FREQUENCY),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT)),
testing::Values(false, true)));
-INSTANTIATE_TEST_SUITE_P(AudioChannelVoiceRoundTrip,
- AudioChannelLayoutRoundTripTest,
- // In legacy constants the voice call is only defined for input.
- testing::Combine(testing::Values(make_ACL_VoiceCall()), testing::Values(true)));
+INSTANTIATE_TEST_SUITE_P(AudioChannelVoiceRoundTrip, AudioChannelLayoutRoundTripTest,
+ // In legacy constants the voice call is only defined for input.
+ testing::Combine(testing::Values(make_ACL_VoiceCall()),
+ testing::Values(true)));
+
+INSTANTIATE_TEST_SUITE_P(
+ OutAudioChannelLayoutLayoutRoundTrip, AudioChannelLayoutRoundTripTest,
+ testing::Combine(
+ testing::Values(AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_SIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_SIDE_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_HAPTIC_A),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_HAPTIC_B)),
+ testing::Values(false)));
using ChannelLayoutEdgeCaseParam = std::tuple<int /*legacy*/, bool /*isInput*/, bool /*isValid*/>;
-class AudioChannelLayoutEdgeCaseTest :
- public testing::TestWithParam<ChannelLayoutEdgeCaseParam> {};
+class AudioChannelLayoutEdgeCaseTest : public testing::TestWithParam<ChannelLayoutEdgeCaseParam> {};
TEST_P(AudioChannelLayoutEdgeCaseTest, Legacy2Aidl) {
const audio_channel_mask_t legacy = static_cast<audio_channel_mask_t>(std::get<0>(GetParam()));
const bool isInput = std::get<1>(GetParam());
@@ -229,8 +309,8 @@
auto conv = legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy, isInput);
EXPECT_EQ(isValid, conv.ok());
}
-INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutEdgeCase,
- AudioChannelLayoutEdgeCaseTest,
+INSTANTIATE_TEST_SUITE_P(
+ AudioChannelLayoutEdgeCase, AudioChannelLayoutEdgeCaseTest,
testing::Values(
// Valid legacy input masks.
std::make_tuple(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO, true, true),
@@ -240,25 +320,26 @@
std::make_tuple(
// This has the same numerical representation as Mask 'A' below
AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT, false, true),
+ AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT,
+ false, true),
std::make_tuple(
// This has the same numerical representation as Mask 'B' below
AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_TOP_BACK_LEFT, false, true),
+ AUDIO_CHANNEL_OUT_TOP_BACK_LEFT,
+ false, true),
// Invalid legacy input masks.
std::make_tuple(AUDIO_CHANNEL_IN_6, true, false),
- std::make_tuple(
- AUDIO_CHANNEL_IN_6 | AUDIO_CHANNEL_IN_FRONT_PROCESSED, true, false),
- std::make_tuple(
- AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS |
- AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS, true, false),
+ std::make_tuple(AUDIO_CHANNEL_IN_6 | AUDIO_CHANNEL_IN_FRONT_PROCESSED, true, false),
+ std::make_tuple(AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS |
+ AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS,
+ true, false),
std::make_tuple( // Mask 'A'
AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_UPLINK, true, false),
std::make_tuple( // Mask 'B'
AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_DNLINK, true, false)));
-class AudioDeviceDescriptionRoundTripTest :
- public testing::TestWithParam<AudioDeviceDescription> {};
+class AudioDeviceDescriptionRoundTripTest : public testing::TestWithParam<AudioDeviceDescription> {
+};
TEST_P(AudioDeviceDescriptionRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = GetParam();
auto conv = aidl2legacy_AudioDeviceDescription_audio_devices_t(initial);
@@ -267,13 +348,13 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioDeviceDescriptionRoundTrip,
- AudioDeviceDescriptionRoundTripTest,
- testing::Values(AudioDeviceDescription{}, make_ADD_DefaultIn(),
- make_ADD_DefaultOut(), make_ADD_WiredHeadset(), make_ADD_BtScoHeadset()));
+INSTANTIATE_TEST_SUITE_P(AudioDeviceDescriptionRoundTrip, AudioDeviceDescriptionRoundTripTest,
+ testing::Values(AudioDeviceDescription{}, make_ADD_DefaultIn(),
+ make_ADD_DefaultOut(), make_ADD_WiredHeadset(),
+ make_ADD_BtScoHeadset()));
-class AudioFormatDescriptionRoundTripTest :
- public testing::TestWithParam<AudioFormatDescription> {};
+class AudioFormatDescriptionRoundTripTest : public testing::TestWithParam<AudioFormatDescription> {
+};
TEST_P(AudioFormatDescriptionRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = GetParam();
auto conv = aidl2legacy_AudioFormatDescription_audio_format_t(initial);
@@ -282,6 +363,140 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioFormatDescriptionRoundTrip,
- AudioFormatDescriptionRoundTripTest,
- testing::Values(make_AFD_Invalid(), AudioFormatDescription{}, make_AFD_Pcm16Bit()));
+INSTANTIATE_TEST_SUITE_P(AudioFormatDescriptionRoundTrip, AudioFormatDescriptionRoundTripTest,
+ testing::Values(make_AFD_Invalid(), AudioFormatDescription{},
+ make_AFD_Pcm16Bit()));
+
+class AudioDirectModeRoundTripTest : public testing::TestWithParam<AudioDirectMode> {};
+TEST_P(AudioDirectModeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioDirectMode_audio_direct_mode_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_direct_mode_t_AudioDirectMode(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioDirectMode, AudioDirectModeRoundTripTest,
+ testing::Values(AudioDirectMode::NONE, AudioDirectMode::OFFLOAD,
+ AudioDirectMode::OFFLOAD_GAPLESS,
+ AudioDirectMode::BITSTREAM));
+
+class AudioStandardRoundTripTest : public testing::TestWithParam<AudioStandard> {};
+TEST_P(AudioStandardRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioStandard_audio_standard_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_standard_t_AudioStandard(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioStandard, AudioStandardRoundTripTest,
+ testing::Values(AudioStandard::NONE, AudioStandard::EDID));
+
+class AudioEncapsulationMetadataTypeRoundTripTest
+ : public testing::TestWithParam<AudioEncapsulationMetadataType> {};
+TEST_P(AudioEncapsulationMetadataTypeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv =
+ aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType(
+ conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioEncapsulationMetadataType,
+ AudioEncapsulationMetadataTypeRoundTripTest,
+ testing::Values(AudioEncapsulationMetadataType::NONE,
+ AudioEncapsulationMetadataType::FRAMEWORK_TUNER,
+ AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR));
+
+class AudioGainModeRoundTripTest : public testing::TestWithParam<AudioGainMode> {};
+TEST_P(AudioGainModeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioGainMode_audio_gain_mode_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_gain_mode_t_AudioGainMode(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioGainMode, AudioGainModeRoundTripTest,
+ testing::Values(AudioGainMode::JOINT, AudioGainMode::CHANNELS,
+ AudioGainMode::RAMP));
+
+TEST(AudioTrackSecondaryOutputInfoRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = make_TrackSecondaryOutputInfo();
+ auto conv = aidl2legacy_TrackSecondaryOutputInfo_TrackSecondaryOutputInfoPair(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+using ExtraAudioDescriptorParam = std::tuple<AudioStandard, AudioEncapsulationType>;
+class ExtraAudioDescriptorRoundTripTest : public testing::TestWithParam<ExtraAudioDescriptorParam> {
+};
+TEST_P(ExtraAudioDescriptorRoundTripTest, Aidl2Legacy2Aidl) {
+ ExtraAudioDescriptor initial =
+ make_ExtraAudioDescriptor(std::get<0>(GetParam()), std::get<1>(GetParam()));
+ auto conv = aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ExtraAudioDescriptor, ExtraAudioDescriptorRoundTripTest,
+ testing::Values(std::make_tuple(AudioStandard::NONE, AudioEncapsulationType::NONE),
+ std::make_tuple(AudioStandard::EDID, AudioEncapsulationType::NONE),
+ std::make_tuple(AudioStandard::EDID, AudioEncapsulationType::IEC61937)));
+
+TEST(AudioPortSessionExtRoundTripTest, Aidl2Legacy2Aidl) {
+ const int32_t initial = 7;
+ auto conv = aidl2legacy_int32_t_audio_port_session_ext(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_port_session_ext_int32_t(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+class AudioGainTest : public testing::TestWithParam<bool> {};
+TEST_P(AudioGainTest, Legacy2Aidl2Legacy) {
+ audio_port_v7 port;
+ port.num_gains = 2;
+ port.gains[0] = {.mode = AUDIO_GAIN_MODE_JOINT,
+ .channel_mask = AUDIO_CHANNEL_IN_STEREO,
+ .min_value = -3200,
+ .max_value = 600,
+ .default_value = 0,
+ .step_value = 100,
+ .min_ramp_ms = 10,
+ .max_ramp_ms = 20};
+ port.gains[1] = {.mode = AUDIO_GAIN_MODE_JOINT,
+ .channel_mask = AUDIO_CHANNEL_IN_MONO,
+ .min_value = -8800,
+ .max_value = 4000,
+ .default_value = 0,
+ .step_value = 100,
+ .min_ramp_ms = 192,
+ .max_ramp_ms = 224};
+
+ const auto isInput = GetParam();
+ for (int i = 0; i < port.num_gains; i++) {
+ auto initial = port.gains[i];
+ auto conv = legacy2aidl_audio_gain_AudioGain(initial, isInput);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = aidl2legacy_AudioGain_audio_gain(conv.value(), isInput);
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial.mode, convBack.value().mode);
+ EXPECT_EQ(initial.channel_mask, convBack.value().channel_mask);
+ EXPECT_EQ(initial.min_value, convBack.value().min_value);
+ EXPECT_EQ(initial.max_value, convBack.value().max_value);
+ EXPECT_EQ(initial.default_value, convBack.value().default_value);
+ EXPECT_EQ(initial.step_value, convBack.value().step_value);
+ EXPECT_EQ(initial.min_ramp_ms, convBack.value().min_ramp_ms);
+ EXPECT_EQ(initial.max_ramp_ms, convBack.value().max_ramp_ms);
+ }
+}
+INSTANTIATE_TEST_SUITE_P(AudioGain, AudioGainTest, testing::Values(true, false));
diff --git a/media/libaudioclient/tests/audio_aidl_status_tests.cpp b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
index 5517091..8a7e6c1 100644
--- a/media/libaudioclient/tests/audio_aidl_status_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
@@ -37,25 +37,10 @@
// Special status values are preserved on round trip.
TEST(audio_aidl_status_tests, statusRoundTripSpecialValues) {
- for (status_t status : {
- OK,
- UNKNOWN_ERROR,
- NO_MEMORY,
- INVALID_OPERATION,
- BAD_VALUE,
- BAD_TYPE,
- NAME_NOT_FOUND,
- PERMISSION_DENIED,
- NO_INIT,
- ALREADY_EXISTS,
- DEAD_OBJECT,
- FAILED_TRANSACTION,
- BAD_INDEX,
- NOT_ENOUGH_DATA,
- WOULD_BLOCK,
- TIMED_OUT,
- UNKNOWN_TRANSACTION,
- FDS_NOT_ALLOWED}) {
+ for (status_t status :
+ {OK, UNKNOWN_ERROR, NO_MEMORY, INVALID_OPERATION, BAD_VALUE, BAD_TYPE, NAME_NOT_FOUND,
+ PERMISSION_DENIED, NO_INIT, ALREADY_EXISTS, DEAD_OBJECT, FAILED_TRANSACTION, BAD_INDEX,
+ NOT_ENOUGH_DATA, WOULD_BLOCK, TIMED_OUT, UNKNOWN_TRANSACTION, FDS_NOT_ALLOWED}) {
ASSERT_EQ(status, statusTFromBinderStatus(binderStatusFromStatusT(status)));
}
}
@@ -63,47 +48,29 @@
// Binder exceptions show as an error (not fixed at this time); these come fromExceptionCode().
TEST(audio_aidl_status_tests, binderStatusExceptions) {
for (int exceptionCode : {
- //Status::EX_NONE,
- Status::EX_SECURITY,
- Status::EX_BAD_PARCELABLE,
- Status::EX_ILLEGAL_ARGUMENT,
- Status::EX_NULL_POINTER,
- Status::EX_ILLEGAL_STATE,
- Status::EX_NETWORK_MAIN_THREAD,
- Status::EX_UNSUPPORTED_OPERATION,
- //Status::EX_SERVICE_SPECIFIC, -- tested fromServiceSpecificError()
- Status::EX_PARCELABLE,
- // This is special and Java specific; see Parcel.java.
- Status::EX_HAS_REPLY_HEADER,
- // This is special, and indicates to C++ binder proxies that the
- // transaction has failed at a low level.
- //Status::EX_TRANSACTION_FAILED, -- tested fromStatusT().
- }) {
+ // Status::EX_NONE,
+ Status::EX_SECURITY, Status::EX_BAD_PARCELABLE, Status::EX_ILLEGAL_ARGUMENT,
+ Status::EX_NULL_POINTER, Status::EX_ILLEGAL_STATE, Status::EX_NETWORK_MAIN_THREAD,
+ Status::EX_UNSUPPORTED_OPERATION,
+ // Status::EX_SERVICE_SPECIFIC, -- tested fromServiceSpecificError()
+ Status::EX_PARCELABLE,
+ // This is special and Java specific; see Parcel.java.
+ Status::EX_HAS_REPLY_HEADER,
+ // This is special, and indicates to C++ binder proxies that the
+ // transaction has failed at a low level.
+ // Status::EX_TRANSACTION_FAILED, -- tested fromStatusT().
+ }) {
ASSERT_NE(OK, statusTFromBinderStatus(Status::fromExceptionCode(exceptionCode)));
}
}
// Binder transaction errors show exactly in status_t; these come fromStatusT().
TEST(audio_aidl_status_tests, binderStatusTransactionError) {
- for (status_t status : {
- OK, // Note: fromStatusT does check if this is 0, so this is no error.
- UNKNOWN_ERROR,
- NO_MEMORY,
- INVALID_OPERATION,
- BAD_VALUE,
- BAD_TYPE,
- NAME_NOT_FOUND,
- PERMISSION_DENIED,
- NO_INIT,
- ALREADY_EXISTS,
- DEAD_OBJECT,
- FAILED_TRANSACTION,
- BAD_INDEX,
- NOT_ENOUGH_DATA,
- WOULD_BLOCK,
- TIMED_OUT,
- UNKNOWN_TRANSACTION,
- FDS_NOT_ALLOWED}) {
+ for (status_t status :
+ {OK, // Note: fromStatusT does check if this is 0, so this is no error.
+ UNKNOWN_ERROR, NO_MEMORY, INVALID_OPERATION, BAD_VALUE, BAD_TYPE, NAME_NOT_FOUND,
+ PERMISSION_DENIED, NO_INIT, ALREADY_EXISTS, DEAD_OBJECT, FAILED_TRANSACTION, BAD_INDEX,
+ NOT_ENOUGH_DATA, WOULD_BLOCK, TIMED_OUT, UNKNOWN_TRANSACTION, FDS_NOT_ALLOWED}) {
ASSERT_EQ(status, statusTFromBinderStatus(Status::fromStatusT(status)));
}
}
diff --git a/media/libaudioclient/tests/audio_test_template.xml b/media/libaudioclient/tests/audio_test_template.xml
new file mode 100644
index 0000000..ed0cb21
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_template.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Unit test configuration for {MODULE}">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="{MODULE}" value="/data/local/tmp/{MODULE}" />
+
+ <!-- Files used for audio testing -->
+ <option name="push-file" key="bbb_1ch_8kHz_s16le.raw" value="/data/local/tmp/bbb_1ch_8kHz_s16le.raw" />
+ <option name="push-file" key="bbb_2ch_24kHz_s16le.raw" value="/data/local/tmp/bbb_2ch_24kHz_s16le.raw" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="{MODULE}" />
+ </test>
+</configuration>
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
new file mode 100644
index 0000000..44f0f50
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2021 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 "AudioTestUtils"
+
+#include <system/audio_config.h>
+#include <utils/Log.h>
+
+#include "audio_test_utils.h"
+
+template <class T>
+constexpr void (*xmlDeleter)(T* t);
+template <>
+constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
+template <>
+constexpr auto xmlDeleter<xmlChar> = [](xmlChar* s) { xmlFree(s); };
+
+/** @return a unique_ptr with the correct deleter for the libxml2 object. */
+template <class T>
+constexpr auto make_xmlUnique(T* t) {
+ // Wrap deleter in lambda to enable empty base optimization
+ auto deleter = [](T* t) { xmlDeleter<T>(t); };
+ return std::unique_ptr<T, decltype(deleter)>{t, deleter};
+}
+
+// Generates a random string.
+void CreateRandomFile(int& fd) {
+ std::string filename = "/data/local/tmp/record-XXXXXX";
+ fd = mkstemp(filename.data());
+}
+
+void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) {
+ std::unique_lock<std::mutex> lock{mMutex};
+ ALOGD("%s audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
+ mAudioIo = audioIo;
+ mDeviceId = deviceId;
+ mCondition.notify_all();
+}
+
+status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(500));
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE) return TIMED_OUT;
+ }
+ return OK;
+}
+
+AudioPlayback::AudioPlayback(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_output_flags_t flags,
+ audio_session_t sessionId, AudioTrack::transfer_type transferType,
+ audio_attributes_t* attributes, audio_offload_info_t* info)
+ : mSampleRate(sampleRate),
+ mFormat(format),
+ mChannelMask(channelMask),
+ mFlags(flags),
+ mSessionId(sessionId),
+ mTransferType(transferType),
+ mAttributes(attributes),
+ mOffloadInfo(info) {
+ mStopPlaying = false;
+ mBytesUsedSoFar = 0;
+ mState = PLAY_NO_INIT;
+ mMemCapacity = 0;
+ mMemoryDealer = nullptr;
+ mMemory = nullptr;
+}
+
+AudioPlayback::~AudioPlayback() {
+ stop();
+}
+
+status_t AudioPlayback::create() {
+ if (mState != PLAY_NO_INIT) return INVALID_OPERATION;
+ std::string packageName{"AudioPlayback"};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ if (mTransferType == AudioTrack::TRANSFER_OBTAIN) {
+ mTrack = new AudioTrack(attributionSource);
+ mTrack->set(AUDIO_STREAM_MUSIC, mSampleRate, mFormat, mChannelMask, 0 /* frameCount */,
+ mFlags, nullptr /* callback */, 0 /* notificationFrames */,
+ nullptr /* sharedBuffer */, false /*canCallJava */, mSessionId, mTransferType,
+ mOffloadInfo, attributionSource, mAttributes);
+ } else if (mTransferType == AudioTrack::TRANSFER_SHARED) {
+ mTrack = new AudioTrack(AUDIO_STREAM_MUSIC, mSampleRate, mFormat, mChannelMask, mMemory,
+ mFlags, wp<AudioTrack::IAudioTrackCallback>::fromExisting(this), 0,
+ mSessionId, mTransferType, nullptr, attributionSource, mAttributes);
+ } else {
+ ALOGE("Test application is not handling transfer type %s",
+ AudioTrack::convertTransferToText(mTransferType));
+ return INVALID_OPERATION;
+ }
+ mTrack->setCallerName(packageName);
+ status_t status = mTrack->initCheck();
+ if (NO_ERROR == status) mState = PLAY_READY;
+ return status;
+}
+
+status_t AudioPlayback::loadResource(const char* name) {
+ status_t status = OK;
+ FILE* fp = fopen(name, "rbe");
+ struct stat buf {};
+ if (fp && !fstat(fileno(fp), &buf)) {
+ mMemCapacity = buf.st_size;
+ mMemoryDealer = new MemoryDealer(mMemCapacity, "AudioPlayback");
+ if (nullptr == mMemoryDealer.get()) {
+ ALOGE("couldn't get MemoryDealer!");
+ fclose(fp);
+ return NO_MEMORY;
+ }
+ mMemory = mMemoryDealer->allocate(mMemCapacity);
+ if (nullptr == mMemory.get()) {
+ ALOGE("couldn't get IMemory!");
+ fclose(fp);
+ return NO_MEMORY;
+ }
+ uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
+ fread(ipBuffer, sizeof(uint8_t), mMemCapacity, fp);
+ } else {
+ ALOGE("unable to open input file %s", name);
+ status = NAME_NOT_FOUND;
+ }
+ if (fp) fclose(fp);
+ return status;
+}
+
+sp<AudioTrack> AudioPlayback::getAudioTrackHandle() {
+ return (PLAY_NO_INIT != mState) ? mTrack : nullptr;
+}
+
+status_t AudioPlayback::start() {
+ status_t status;
+ if (PLAY_READY != mState) {
+ return INVALID_OPERATION;
+ } else {
+ status = mTrack->start();
+ if (OK == status) {
+ mState = PLAY_STARTED;
+ LOG_FATAL_IF(false != mTrack->stopped());
+ }
+ }
+ return status;
+}
+
+void AudioPlayback::onBufferEnd() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mStopPlaying = true;
+ mCondition.notify_all();
+}
+
+status_t AudioPlayback::fillBuffer() {
+ if (PLAY_STARTED != mState && PLAY_STOPPED != mState) return INVALID_OPERATION;
+ int retry = 25;
+ uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
+ size_t nonContig = 0;
+ size_t bytesAvailable = mMemCapacity - mBytesUsedSoFar;
+ while (bytesAvailable > 0) {
+ AudioTrack::Buffer trackBuffer;
+ trackBuffer.frameCount = mTrack->frameCount() * 2;
+ status_t status = mTrack->obtainBuffer(&trackBuffer, retry, &nonContig);
+ if (OK == status) {
+ size_t bytesToCopy = std::min(bytesAvailable, trackBuffer.size());
+ if (bytesToCopy > 0) {
+ memcpy(trackBuffer.data(), ipBuffer + mBytesUsedSoFar, bytesToCopy);
+ }
+ mTrack->releaseBuffer(&trackBuffer);
+ mBytesUsedSoFar += bytesToCopy;
+ bytesAvailable = mMemCapacity - mBytesUsedSoFar;
+ if (bytesAvailable == 0) {
+ stop();
+ }
+ } else if (WOULD_BLOCK == status) {
+ if (mStopPlaying)
+ return OK;
+ else
+ return TIMED_OUT;
+ }
+ }
+ return OK;
+}
+
+status_t AudioPlayback::waitForConsumption(bool testSeek) {
+ if (PLAY_STARTED != mState) return INVALID_OPERATION;
+ // in static buffer mode, lets not play clips with duration > 30 sec
+ int retry = 30;
+ // Total number of frames in the input file.
+ size_t totalFrameCount = mMemCapacity / mTrack->frameSize();
+ while (!mStopPlaying && retry > 0) {
+ // Get the total numbers of frames played.
+ uint32_t currPosition;
+ mTrack->getPosition(&currPosition);
+ if (testSeek && (currPosition > totalFrameCount * 0.6)) {
+ testSeek = false;
+ if (!mTrack->hasStarted()) return BAD_VALUE;
+ mTrack->pauseAndWait(std::chrono::seconds(2));
+ if (mTrack->hasStarted()) return BAD_VALUE;
+ mTrack->reload();
+ mTrack->getPosition(&currPosition);
+ if (currPosition != 0) return BAD_VALUE;
+ mTrack->start();
+ while (currPosition < totalFrameCount * 0.3) {
+ mTrack->getPosition(&currPosition);
+ }
+ mTrack->pauseAndWait(std::chrono::seconds(2));
+ uint32_t setPosition = totalFrameCount * 0.9;
+ mTrack->setPosition(setPosition);
+ uint32_t bufferPosition;
+ mTrack->getBufferPosition(&bufferPosition);
+ if (bufferPosition != setPosition) return BAD_VALUE;
+ mTrack->start();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(300));
+ retry--;
+ }
+ if (!mStopPlaying) return TIMED_OUT;
+ return OK;
+}
+
+status_t AudioPlayback::onProcess(bool testSeek) {
+ if (mTransferType == AudioTrack::TRANSFER_SHARED)
+ return waitForConsumption(testSeek);
+ else if (mTransferType == AudioTrack::TRANSFER_OBTAIN)
+ return fillBuffer();
+ else
+ return INVALID_OPERATION;
+}
+
+void AudioPlayback::stop() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mStopPlaying = true;
+ if (mState != PLAY_STOPPED) {
+ int32_t msec = 0;
+ (void)mTrack->pendingDuration(&msec);
+ mTrack->stopAndJoinCallbacks();
+ LOG_FATAL_IF(true != mTrack->stopped());
+ mState = PLAY_STOPPED;
+ if (msec > 0) {
+ ALOGD("deleting recycled track, waiting for data drain (%d msec)", msec);
+ usleep(msec * 1000LL);
+ }
+ }
+}
+
+// hold pcm data sent by AudioRecord
+RawBuffer::RawBuffer(int64_t ptsPipeline, int64_t ptsManual, int32_t capacity)
+ : mData(capacity > 0 ? new uint8_t[capacity] : nullptr),
+ mPtsPipeline(ptsPipeline),
+ mPtsManual(ptsManual),
+ mCapacity(capacity) {}
+
+// Simple AudioCapture
+size_t AudioCapture::onMoreData(const AudioRecord::Buffer& buffer) {
+ if (mState != REC_STARTED) {
+ ALOGE("Unexpected Callback from audiorecord, not reading data");
+ return 0;
+ }
+
+ // no more frames to read
+ if (mNumFramesReceived > mNumFramesToRecord || mStopRecording) {
+ mStopRecording = true;
+ return 0;
+ }
+
+ int64_t timeUs = 0, position = 0, timeNs = 0;
+ ExtendedTimestamp ts;
+ ExtendedTimestamp::Location location;
+ const int32_t usPerSec = 1000000;
+
+ if (mRecord->getTimestamp(&ts) == OK &&
+ ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC, &location) ==
+ OK) {
+ // Use audio timestamp.
+ timeUs = timeNs / 1000 -
+ (position - mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
+ } else {
+ // This should not happen in normal case.
+ ALOGW("Failed to get audio timestamp, fallback to use systemclock");
+ timeUs = systemTime() / 1000LL;
+ // Estimate the real sampling time of the 1st sample in this buffer
+ // from AudioRecord's latency. (Apply this adjustment first so that
+ // the start time logic is not affected.)
+ timeUs -= mRecord->latency() * 1000LL;
+ }
+
+ ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
+
+ const size_t frameSize = mRecord->frameSize();
+ uint64_t numLostBytes = (uint64_t)mRecord->getInputFramesLost() * frameSize;
+ if (numLostBytes > 0) {
+ ALOGW("Lost audio record data: %" PRIu64 " bytes", numLostBytes);
+ }
+ std::deque<RawBuffer> tmpQueue;
+ while (numLostBytes > 0) {
+ uint64_t bufferSize = numLostBytes;
+ if (numLostBytes > mMaxBytesPerCallback) {
+ numLostBytes -= mMaxBytesPerCallback;
+ bufferSize = mMaxBytesPerCallback;
+ } else {
+ numLostBytes = 0;
+ }
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer emptyBuffer{timeUs, timestampUs, static_cast<int32_t>(bufferSize)};
+ memset(emptyBuffer.mData.get(), 0, bufferSize);
+ mNumFramesLost += bufferSize / frameSize;
+ mNumFramesReceived += bufferSize / frameSize;
+ tmpQueue.push_back(std::move(emptyBuffer));
+ }
+
+ if (buffer.size() == 0) {
+ ALOGW("Nothing is available from AudioRecord callback buffer");
+ } else {
+ const size_t bufferSize = buffer.size();
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer audioBuffer{timeUs, timestampUs, static_cast<int32_t>(bufferSize)};
+ memcpy(audioBuffer.mData.get(), buffer.data(), bufferSize);
+ mNumFramesReceived += bufferSize / frameSize;
+ tmpQueue.push_back(std::move(audioBuffer));
+ }
+
+ if (tmpQueue.size() > 0) {
+ std::unique_lock<std::mutex> lock{mMutex};
+ for (auto it = tmpQueue.begin(); it != tmpQueue.end(); it++)
+ mBuffersReceived.push_back(std::move(*it));
+ mCondition.notify_all();
+ }
+ return buffer.size();
+}
+
+void AudioCapture::onOverrun() {
+ ALOGV("received event overrun");
+ mBufferOverrun = true;
+}
+
+void AudioCapture::onMarker(uint32_t markerPosition) {
+ ALOGV("received Callback at position %d", markerPosition);
+ mReceivedCbMarkerAtPosition = markerPosition;
+}
+
+void AudioCapture::onNewPos(uint32_t markerPosition) {
+ ALOGV("received Callback at position %d", markerPosition);
+ mReceivedCbMarkerCount++;
+}
+
+void AudioCapture::onNewIAudioRecord() {
+ ALOGV("IAudioRecord is re-created");
+}
+
+AudioCapture::AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_input_flags_t flags,
+ audio_session_t sessionId, AudioRecord::transfer_type transferType)
+ : mInputSource(inputSource),
+ mSampleRate(sampleRate),
+ mFormat(format),
+ mChannelMask(channelMask),
+ mFlags(flags),
+ mSessionId(sessionId),
+ mTransferType(transferType) {
+ mFrameCount = 0;
+ mNotificationFrames = 0;
+ mNumFramesToRecord = 0;
+ mNumFramesReceived = 0;
+ mNumFramesLost = 0;
+ mBufferOverrun = false;
+ mMarkerPosition = 0;
+ mMarkerPeriod = 0;
+ mReceivedCbMarkerAtPosition = -1;
+ mReceivedCbMarkerCount = 0;
+ mState = REC_NO_INIT;
+ mStopRecording = false;
+#if RECORD_TO_FILE
+ CreateRandomFile(mOutFileFd);
+#endif
+}
+
+AudioCapture::~AudioCapture() {
+ if (mOutFileFd > 0) close(mOutFileFd);
+ stop();
+}
+
+status_t AudioCapture::create() {
+ if (mState != REC_NO_INIT) return INVALID_OPERATION;
+ // get Min Frame Count
+ size_t minFrameCount;
+ status_t status =
+ AudioRecord::getMinFrameCount(&minFrameCount, mSampleRate, mFormat, mChannelMask);
+ if (NO_ERROR != status) return status;
+ // Limit notificationFrames basing on client bufferSize
+ const int samplesPerFrame = audio_channel_count_from_in_mask(mChannelMask);
+ const int bytesPerSample = audio_bytes_per_sample(mFormat);
+ mNotificationFrames = mMaxBytesPerCallback / (samplesPerFrame * bytesPerSample);
+ // select frameCount to be at least minFrameCount
+ mFrameCount = 2 * mNotificationFrames;
+ while (mFrameCount < minFrameCount) {
+ mFrameCount += mNotificationFrames;
+ }
+ if (mFlags & AUDIO_INPUT_FLAG_FAST) {
+ ALOGW("Overriding all previous computations");
+ mFrameCount = 0;
+ mNotificationFrames = 0;
+ }
+ mNumFramesToRecord = (mSampleRate * 0.25); // record .25 sec
+ std::string packageName{"AudioCapture"};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ if (mTransferType == AudioRecord::TRANSFER_OBTAIN) {
+ if (mSampleRate == 48000) { // test all available constructors
+ mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
+ attributionSource, mFrameCount, nullptr /* callback */,
+ mNotificationFrames, mSessionId, mTransferType, mFlags);
+ } else {
+ mRecord = new AudioRecord(attributionSource);
+ status = mRecord->set(mInputSource, mSampleRate, mFormat, mChannelMask, mFrameCount,
+ nullptr /* callback */, 0 /* notificationFrames */,
+ false /* canCallJava */, mSessionId, mTransferType, mFlags,
+ attributionSource.uid, attributionSource.pid);
+ }
+ if (NO_ERROR != status) return status;
+ } else if (mTransferType == AudioRecord::TRANSFER_CALLBACK) {
+ mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
+ attributionSource, mFrameCount, this, mNotificationFrames,
+ mSessionId, mTransferType, mFlags);
+ } else {
+ ALOGE("Test application is not handling transfer type %s",
+ AudioRecord::convertTransferToText(mTransferType));
+ return NO_INIT;
+ }
+ mRecord->setCallerName(packageName);
+ status = mRecord->initCheck();
+ if (NO_ERROR == status) mState = REC_READY;
+ if (mFlags & AUDIO_INPUT_FLAG_FAST) {
+ mFrameCount = mRecord->frameCount();
+ mNotificationFrames = mRecord->getNotificationPeriodInFrames();
+ mMaxBytesPerCallback = mNotificationFrames * samplesPerFrame * bytesPerSample;
+ }
+ return status;
+}
+
+sp<AudioRecord> AudioCapture::getAudioRecordHandle() {
+ return (REC_NO_INIT == mState) ? nullptr : mRecord;
+}
+
+status_t AudioCapture::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) {
+ status_t status;
+ if (REC_READY != mState) {
+ return INVALID_OPERATION;
+ } else {
+ status = mRecord->start(event, triggerSession);
+ if (OK == status) {
+ mState = REC_STARTED;
+ LOG_FATAL_IF(false != mRecord->stopped());
+ }
+ }
+ return status;
+}
+
+status_t AudioCapture::stop() {
+ status_t status = OK;
+ mStopRecording = true;
+ if (mState != REC_STOPPED) {
+ if (mInputSource != AUDIO_SOURCE_DEFAULT) {
+ bool state = false;
+ status = AudioSystem::isSourceActive(mInputSource, &state);
+ if (status == OK && !state) status = BAD_VALUE;
+ }
+ mRecord->stopAndJoinCallbacks();
+ mState = REC_STOPPED;
+ LOG_FATAL_IF(true != mRecord->stopped());
+ }
+ return status;
+}
+
+status_t AudioCapture::obtainBuffer(RawBuffer& buffer) {
+ if (REC_STARTED != mState && REC_STOPPED != mState) return INVALID_OPERATION;
+ int retry = 25;
+ AudioRecord::Buffer recordBuffer;
+ recordBuffer.frameCount = mNotificationFrames;
+ size_t nonContig = 0;
+ status_t status = mRecord->obtainBuffer(&recordBuffer, retry, &nonContig);
+ if (OK == status) {
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
+ memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
+ buffer = std::move(buff);
+ mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
+ mRecord->releaseBuffer(&recordBuffer);
+ if (mNumFramesReceived > mNumFramesToRecord) {
+ stop();
+ }
+ } else if (status == WOULD_BLOCK) {
+ if (mStopRecording)
+ return WOULD_BLOCK;
+ else
+ return TIMED_OUT;
+ }
+ return OK;
+}
+
+status_t AudioCapture::obtainBufferCb(RawBuffer& buffer) {
+ if (REC_STARTED != mState) return INVALID_OPERATION;
+ int retry = 10;
+ std::unique_lock<std::mutex> lock{mMutex};
+ while (mBuffersReceived.empty() && !mStopRecording && retry > 0) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(100));
+ retry--;
+ }
+ if (!mBuffersReceived.empty()) {
+ auto it = mBuffersReceived.begin();
+ buffer = std::move(*it);
+ mBuffersReceived.erase(it);
+ } else {
+ if (retry == 0) return TIMED_OUT;
+ if (mStopRecording)
+ return WOULD_BLOCK;
+ else
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t AudioCapture::audioProcess() {
+ RawBuffer buffer;
+ while (true) {
+ status_t status;
+ if (mTransferType == AudioRecord::TRANSFER_CALLBACK)
+ status = obtainBufferCb(buffer);
+ else
+ status = obtainBuffer(buffer);
+ switch (status) {
+ case OK:
+ if (mOutFileFd > 0) {
+ const char* ptr =
+ static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
+ write(mOutFileFd, ptr, buffer.mCapacity);
+ }
+ break;
+ case WOULD_BLOCK:
+ return OK;
+ case TIMED_OUT: // "recorder application timed out from receiving buffers"
+ case NO_INIT: // "recorder not initialized"
+ case INVALID_OPERATION: // "recorder not started"
+ case UNKNOWN_ERROR: // "Unknown error"
+ default:
+ return status;
+ }
+ }
+}
+
+status_t listAudioPorts(std::vector<audio_port_v7>& portsVec) {
+ int attempts = 5;
+ status_t status;
+ unsigned int generation1, generation;
+ unsigned int numPorts = 0;
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
+ nullptr, &generation1);
+ if (status != NO_ERROR) {
+ ALOGE("AudioSystem::listAudioPorts returned error %d", status);
+ break;
+ }
+ portsVec.resize(numPorts);
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
+ portsVec.data(), &generation);
+ } while (generation1 != generation && status == NO_ERROR);
+ if (status != NO_ERROR) {
+ numPorts = 0;
+ portsVec.clear();
+ }
+ return status;
+}
+
+status_t getPortById(const audio_port_handle_t portId, audio_port_v7& port) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ if (status != OK) return status;
+ for (auto i = 0; i < ports.size(); i++) {
+ if (ports[i].id == portId) {
+ port = ports[i];
+ return OK;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
+ audio_devices_t deviceType, audio_port_v7& port) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ if (status != OK) return status;
+ for (auto i = 0; i < ports.size(); i++) {
+ if (ports[i].role == role && ports[i].type == type &&
+ ports[i].ext.device.type == deviceType) {
+ port = ports[i];
+ return OK;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec) {
+ int attempts = 5;
+ status_t status;
+ unsigned int generation1, generation;
+ unsigned int numPatches = 0;
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+ status = AudioSystem::listAudioPatches(&numPatches, nullptr, &generation1);
+ if (status != NO_ERROR) {
+ ALOGE("AudioSystem::listAudioPatches returned error %d", status);
+ break;
+ }
+ patchesVec.resize(numPatches);
+ status = AudioSystem::listAudioPatches(&numPatches, patchesVec.data(), &generation);
+ } while (generation1 != generation && status == NO_ERROR);
+ if (status != NO_ERROR) {
+ numPatches = 0;
+ patchesVec.clear();
+ }
+ return status;
+}
+
+status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ if (status != OK) return status;
+
+ for (auto i = 0; i < patches.size(); i++) {
+ for (auto j = 0; j < patches[i].num_sources; j++) {
+ if (patches[i].sources[j].type == AUDIO_PORT_TYPE_MIX &&
+ patches[i].sources[j].ext.mix.handle == audioIo) {
+ patch = patches[i];
+ return OK;
+ }
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ if (status != OK) return status;
+
+ for (auto i = 0; i < patches.size(); i++) {
+ for (auto j = 0; j < patches[i].num_sinks; j++) {
+ if (patches[i].sinks[j].type == AUDIO_PORT_TYPE_MIX &&
+ patches[i].sinks[j].ext.mix.handle == audioIo) {
+ patch = patches[i];
+ return OK;
+ }
+ }
+ }
+ return BAD_VALUE;
+}
+
+bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch) {
+ for (auto j = 0; j < patch.num_sinks; j++) {
+ if (patch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE && patch.sinks[j].id == deviceId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch) {
+ for (auto j = 0; j < patch.num_sources; j++) {
+ if (patch.sources[j].type == AUDIO_PORT_TYPE_DEVICE && patch.sources[j].id == deviceId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId) {
+ struct audio_patch patch;
+ if (getPatchForOutputMix(audioIo, patch) == OK) {
+ return patchContainsOutputDevice(deviceId, patch);
+ }
+ return false;
+}
+
+bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId) {
+ struct audio_patch patch;
+ if (getPatchForInputMix(audioIo, patch) == OK) {
+ return patchContainsInputDevice(deviceId, patch);
+ }
+ return false;
+}
+
+std::string dumpPortConfig(const audio_port_config& port) {
+ std::ostringstream result;
+ std::string deviceInfo;
+ if (port.type == AUDIO_PORT_TYPE_DEVICE) {
+ if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
+ InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ } else {
+ OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ }
+ deviceInfo += std::string(", address = ") + port.ext.device.address;
+ }
+ result << "audio_port_handle_t = " << port.id << ", "
+ << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
+ << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
+ << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
+ << "config_mask = 0x" << std::hex << port.config_mask << std::dec << ", ";
+ if (port.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ result << "sample rate = " << port.sample_rate << ", ";
+ }
+ if (port.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ result << "channel mask = " << port.channel_mask << ", ";
+ }
+ if (port.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ result << "format = " << port.format << ", ";
+ }
+ result << "input flags = " << port.flags.input << ", ";
+ result << "output flags = " << port.flags.output << ", ";
+ result << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
+ << "\n";
+ return result.str();
+}
+
+std::string dumpPatch(const audio_patch& patch) {
+ std::ostringstream result;
+ result << "----------------- Dumping Patch ------------ \n";
+ result << "Patch Handle: " << patch.id << ", sources: " << patch.num_sources
+ << ", sink: " << patch.num_sinks << "\n";
+ audio_port_v7 port;
+ for (uint32_t i = 0; i < patch.num_sources; i++) {
+ result << "----------------- Dumping Source Port Config @ index " << i
+ << " ------------ \n";
+ result << dumpPortConfig(patch.sources[i]);
+ result << "----------------- Dumping Source Port for id " << patch.sources[i].id
+ << " ------------ \n";
+ getPortById(patch.sources[i].id, port);
+ result << dumpPort(port);
+ }
+ for (uint32_t i = 0; i < patch.num_sinks; i++) {
+ result << "----------------- Dumping Sink Port Config @ index " << i << " ------------ \n";
+ result << dumpPortConfig(patch.sinks[i]);
+ result << "----------------- Dumping Sink Port for id " << patch.sinks[i].id
+ << " ------------ \n";
+ getPortById(patch.sinks[i].id, port);
+ result << dumpPort(port);
+ }
+ return result.str();
+}
+
+std::string dumpPort(const audio_port_v7& port) {
+ std::ostringstream result;
+ std::string deviceInfo;
+ if (port.type == AUDIO_PORT_TYPE_DEVICE) {
+ if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
+ InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ } else {
+ OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ }
+ deviceInfo += std::string(", address = ") + port.ext.device.address;
+ }
+ result << "audio_port_handle_t = " << port.id << ", "
+ << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
+ << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
+ << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
+ << "Name = " << port.name << ", "
+ << "num profiles = " << port.num_audio_profiles << ", "
+ << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
+ << ", ";
+ for (int i = 0; i < port.num_audio_profiles; i++) {
+ result << "AudioProfile = " << i << " {";
+ result << "format = " << port.audio_profiles[i].format << ", ";
+ result << "samplerates = ";
+ for (int j = 0; j < port.audio_profiles[i].num_sample_rates; j++) {
+ result << port.audio_profiles[i].sample_rates[j] << ", ";
+ }
+ result << "channelmasks = ";
+ for (int j = 0; j < port.audio_profiles[i].num_channel_masks; j++) {
+ result << "0x" << std::hex << port.audio_profiles[i].channel_masks[j] << std::dec
+ << ", ";
+ }
+ result << "} ";
+ }
+ result << dumpPortConfig(port.active_config);
+ return result.str();
+}
+
+std::string getXmlAttribute(const xmlNode* cur, const char* attribute) {
+ auto charPtr = make_xmlUnique(xmlGetProp(cur, reinterpret_cast<const xmlChar*>(attribute)));
+ if (charPtr == NULL) {
+ return "";
+ }
+ std::string value(reinterpret_cast<const char*>(charPtr.get()));
+ return value;
+}
+
+status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
+ std::vector<MixPort>& mixPorts,
+ std::vector<Route>& routes) {
+ std::string path = audio_find_readable_configuration_file("audio_policy_configuration.xml");
+ if (path.length() == 0) return UNKNOWN_ERROR;
+ auto doc = make_xmlUnique(xmlParseFile(path.c_str()));
+ if (doc == nullptr) return UNKNOWN_ERROR;
+ xmlNode* root = xmlDocGetRootElement(doc.get());
+ if (root == nullptr) return UNKNOWN_ERROR;
+ if (xmlXIncludeProcess(doc.get()) < 0) return UNKNOWN_ERROR;
+ mixPorts.clear();
+ if (!xmlStrcmp(root->name, reinterpret_cast<const xmlChar*>("audioPolicyConfiguration"))) {
+ std::string raw{getXmlAttribute(root, "version")};
+ for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
+ if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("modules"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
+ if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("module"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("mixPorts"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("mixPort"))) {
+ MixPort mixPort;
+ xmlNode* root = child;
+ mixPort.name = getXmlAttribute(root, "name");
+ mixPort.role = getXmlAttribute(root, "role");
+ mixPort.flags = getXmlAttribute(root, "flags");
+ if (mixPort.role == "source") mixPorts.push_back(mixPort);
+ }
+ }
+ } else if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(
+ "attachedDevices"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("item"))) {
+ auto xmlValue = make_xmlUnique(xmlNodeListGetString(
+ child->doc, child->xmlChildrenNode, 1));
+ if (xmlValue == nullptr) {
+ raw = "";
+ } else {
+ raw = reinterpret_cast<const char*>(xmlValue.get());
+ }
+ std::string& value = raw;
+ attachedDevices.push_back(std::move(value));
+ }
+ }
+ } else if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("routes"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("route"))) {
+ Route route;
+ xmlNode* root = child;
+ route.name = getXmlAttribute(root, "name");
+ route.sources = getXmlAttribute(root, "sources");
+ route.sink = getXmlAttribute(root, "sink");
+ routes.push_back(route);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return OK;
+}
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
new file mode 100644
index 0000000..f35b65d
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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 AUDIO_TEST_UTILS_H_
+#define AUDIO_TEST_UTILS_H_
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <atomic>
+#include <chrono>
+#include <cinttypes>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include <binder/MemoryDealer.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+#include <media/AidlConversion.h>
+#include <media/AudioRecord.h>
+#include <media/AudioTrack.h>
+
+#define RECORD_TO_FILE 0
+
+using namespace android;
+
+struct MixPort {
+ std::string name;
+ std::string role;
+ std::string flags;
+};
+
+struct Route {
+ std::string name;
+ std::string sources;
+ std::string sink;
+};
+
+status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
+ std::vector<MixPort>& mixPorts,
+ std::vector<Route>& routes);
+void CreateRandomFile(int& fd);
+status_t listAudioPorts(std::vector<audio_port_v7>& portsVec);
+status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec);
+status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
+ audio_devices_t deviceType, audio_port_v7& port);
+status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch);
+status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch);
+bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch);
+bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch);
+bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+std::string dumpPort(const audio_port_v7& port);
+std::string dumpPortConfig(const audio_port_config& port);
+std::string dumpPatch(const audio_patch& patch);
+
+class OnAudioDeviceUpdateNotifier : public AudioSystem::AudioDeviceCallback {
+ public:
+ audio_io_handle_t mAudioIo = AUDIO_IO_HANDLE_NONE;
+ audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+
+ void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+ status_t waitForAudioDeviceCb();
+};
+
+// Simple AudioPlayback class.
+class AudioPlayback : public AudioTrack::IAudioTrackCallback {
+ friend sp<AudioPlayback>;
+ AudioPlayback(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_session_t sessionId = AUDIO_SESSION_NONE,
+ AudioTrack::transfer_type transferType = AudioTrack::TRANSFER_SHARED,
+ audio_attributes_t* attributes = nullptr, audio_offload_info_t* info = nullptr);
+
+ public:
+ status_t loadResource(const char* name);
+ status_t create();
+ sp<AudioTrack> getAudioTrackHandle();
+ status_t start();
+ status_t waitForConsumption(bool testSeek = false);
+ status_t fillBuffer();
+ status_t onProcess(bool testSeek = false);
+ virtual void onBufferEnd() override;
+ void stop();
+
+ bool mStopPlaying;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+
+ enum State {
+ PLAY_NO_INIT,
+ PLAY_READY,
+ PLAY_STARTED,
+ PLAY_STOPPED,
+ };
+
+ private:
+ ~AudioPlayback();
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_output_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const AudioTrack::transfer_type mTransferType;
+ const audio_attributes_t* mAttributes;
+ const audio_offload_info_t* mOffloadInfo;
+
+ size_t mBytesUsedSoFar;
+ State mState;
+ size_t mMemCapacity;
+ sp<MemoryDealer> mMemoryDealer;
+ sp<IMemory> mMemory;
+
+ sp<AudioTrack> mTrack;
+};
+
+// hold pcm data sent by AudioRecord
+class RawBuffer {
+ public:
+ RawBuffer(int64_t ptsPipeline = -1, int64_t ptsManual = -1, int32_t capacity = 0);
+
+ std::unique_ptr<uint8_t[]> mData;
+ int64_t mPtsPipeline;
+ int64_t mPtsManual;
+ int32_t mCapacity;
+};
+
+// Simple AudioCapture
+class AudioCapture : public AudioRecord::IAudioRecordCallback {
+ public:
+ AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
+ AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK);
+ ~AudioCapture();
+ size_t onMoreData(const AudioRecord::Buffer& buffer) override;
+ void onOverrun() override;
+ void onMarker(uint32_t markerPosition) override;
+ void onNewPos(uint32_t newPos) override;
+ void onNewIAudioRecord() override;
+ status_t create();
+ sp<AudioRecord> getAudioRecordHandle();
+ status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
+ audio_session_t triggerSession = AUDIO_SESSION_NONE);
+ status_t obtainBufferCb(RawBuffer& buffer);
+ status_t obtainBuffer(RawBuffer& buffer);
+ status_t audioProcess();
+ status_t stop();
+
+ uint32_t mFrameCount;
+ uint32_t mNotificationFrames;
+ int64_t mNumFramesToRecord;
+ int64_t mNumFramesReceived;
+ int64_t mNumFramesLost;
+ uint32_t mMarkerPosition;
+ uint32_t mMarkerPeriod;
+ uint32_t mReceivedCbMarkerAtPosition;
+ uint32_t mReceivedCbMarkerCount;
+ bool mBufferOverrun;
+
+ enum State {
+ REC_NO_INIT,
+ REC_READY,
+ REC_STARTED,
+ REC_STOPPED,
+ };
+
+ private:
+ const audio_source_t mInputSource;
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_input_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const AudioRecord::transfer_type mTransferType;
+
+ size_t mMaxBytesPerCallback = 2048;
+ sp<AudioRecord> mRecord;
+ State mState;
+ bool mStopRecording;
+ int mOutFileFd = -1;
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::deque<RawBuffer> mBuffersReceived;
+};
+
+#endif // AUDIO_TEST_UTILS_H_
diff --git a/media/libaudioclient/tests/audioclient_serialization_tests.cpp b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
new file mode 100644
index 0000000..93baefd6
--- /dev/null
+++ b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2021 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 "AudioClientSerializationUnitTests"
+
+#include <cstdint>
+#include <cstdlib>
+#include <ctime>
+
+#include <gtest/gtest.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <xsdc/XsdcSupport.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+template <typename T, typename X, typename FUNC>
+std::vector<T> getFlags(const xsdc_enum_range<X>& range, const FUNC& func,
+ const std::string& findString = {}) {
+ std::vector<T> vec;
+ for (const auto& xsdEnumVal : range) {
+ T enumVal;
+ std::string enumString = toString(xsdEnumVal);
+ if (enumString.find(findString) != std::string::npos &&
+ func(enumString.c_str(), &enumVal)) {
+ vec.push_back(enumVal);
+ }
+ }
+ return vec;
+}
+
+static const std::vector<audio_usage_t> kUsages =
+ getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
+ xsdc_enum_range<xsd::AudioUsage>{}, audio_usage_from_string);
+
+static const std::vector<audio_content_type_t> kContentType =
+ getFlags<audio_content_type_t, xsd::AudioContentType,
+ decltype(audio_content_type_from_string)>(xsdc_enum_range<xsd::AudioContentType>{},
+ audio_content_type_from_string);
+
+static const std::vector<audio_source_t> kInputSources =
+ getFlags<audio_source_t, xsd::AudioSource, decltype(audio_source_from_string)>(
+ xsdc_enum_range<xsd::AudioSource>{}, audio_source_from_string);
+
+static const std::vector<audio_stream_type_t> kStreamtypes =
+ getFlags<audio_stream_type_t, xsd::AudioStreamType,
+ decltype(audio_stream_type_from_string)>(xsdc_enum_range<xsd::AudioStreamType>{},
+ audio_stream_type_from_string);
+
+static const std::vector<uint32_t> kMixMatchRules = {
+ RULE_MATCH_ATTRIBUTE_USAGE,
+ RULE_EXCLUDE_ATTRIBUTE_USAGE,
+ RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET,
+ RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET,
+ RULE_MATCH_UID,
+ RULE_EXCLUDE_UID,
+ RULE_MATCH_USERID,
+ RULE_EXCLUDE_USERID,
+};
+
+// Generates a random string.
+std::string CreateRandomString(size_t n) {
+ std::string data =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ srand(static_cast<unsigned int>(time(0)));
+ std::string s(n, ' ');
+ for (size_t i = 0; i < n; ++i) {
+ s[i] = data[rand() % data.size()];
+ }
+ return s;
+}
+
+class FillAudioAttributes {
+ public:
+ void fillAudioAttributes(audio_attributes_t& attr);
+
+ unsigned int mSeed;
+};
+
+void FillAudioAttributes::fillAudioAttributes(audio_attributes_t& attr) {
+ attr.content_type = kContentType[rand() % kContentType.size()];
+ attr.usage = kUsages[rand() % kUsages.size()];
+ attr.source = kInputSources[rand() % kInputSources.size()];
+ // attr.flags -> [0, (1 << (CAPTURE_PRIVATE + 1) - 1)]
+ attr.flags = static_cast<audio_flags_mask_t>(rand() & 0x3fff);
+ sprintf(attr.tags, "%s",
+ CreateRandomString((int)rand() % (AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1)).c_str());
+}
+
+class SerializationTest : public FillAudioAttributes, public ::testing::Test {
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+// UNIT TESTS
+TEST_F(SerializationTest, AudioProductStrategyBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string name{"Test APSBinderization for seed::" + std::to_string(mSeed)};
+ std::vector<AudioAttributes> audioattributesvector;
+ for (auto i = 0; i < 16; i++) {
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ AudioAttributes audioattributes{static_cast<volume_group_t>(rand()),
+ kStreamtypes[rand() % kStreamtypes.size()], attributes};
+ audioattributesvector.push_back(audioattributes);
+ }
+ product_strategy_t psId = static_cast<product_strategy_t>(rand());
+ AudioProductStrategy aps{name, audioattributesvector, psId};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, aps.writeToParcel(&p)) << name;
+
+ AudioProductStrategy apsCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, apsCopy.readFromParcel(&p)) << name;
+ EXPECT_EQ(apsCopy.getName(), name) << name;
+ EXPECT_EQ(apsCopy.getId(), psId) << name;
+ auto avec = apsCopy.getAudioAttributes();
+ EXPECT_EQ(avec.size(), audioattributesvector.size()) << name;
+ for (int i = 0; i < audioattributesvector.size(); i++) {
+ EXPECT_EQ(avec[i].getGroupId(), audioattributesvector[i].getGroupId()) << name;
+ EXPECT_EQ(avec[i].getStreamType(), audioattributesvector[i].getStreamType()) << name;
+ EXPECT_TRUE(avec[i].getAttributes() == audioattributesvector[i].getAttributes())
+ << name;
+ }
+ }
+}
+
+TEST_F(SerializationTest, AudioVolumeGroupBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string name{"Test AVGBinderization for seed::" + std::to_string(mSeed)};
+ volume_group_t groupId = static_cast<volume_group_t>(rand());
+ std::vector<audio_attributes_t> attributesvector;
+ for (auto i = 0; i < 16; i++) {
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ attributesvector.push_back(attributes);
+ }
+ std::vector<audio_stream_type_t> streamsvector;
+ for (auto i = 0; i < 8; i++) {
+ streamsvector.push_back(kStreamtypes[rand() % kStreamtypes.size()]);
+ }
+ AudioVolumeGroup avg{name, groupId, attributesvector, streamsvector};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, avg.writeToParcel(&p));
+
+ AudioVolumeGroup avgCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, avgCopy.readFromParcel(&p)) << name;
+ EXPECT_EQ(avgCopy.getName(), name) << name;
+ EXPECT_EQ(avgCopy.getId(), groupId) << name;
+ auto avec = avgCopy.getAudioAttributes();
+ EXPECT_EQ(avec.size(), attributesvector.size()) << name;
+ for (int i = 0; i < avec.size(); i++) {
+ EXPECT_TRUE(avec[i] == attributesvector[i]) << name;
+ }
+ StreamTypeVector svec = avgCopy.getStreamTypes();
+ EXPECT_EQ(svec.size(), streamsvector.size()) << name;
+ for (int i = 0; i < svec.size(); i++) {
+ EXPECT_EQ(svec[i], streamsvector[i]) << name;
+ }
+ }
+}
+
+TEST_F(SerializationTest, AudioMixBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string msg{"Test AMBinderization for seed::" + std::to_string(mSeed)};
+ Vector<AudioMixMatchCriterion> criteria;
+ for (int i = 0; i < 16; i++) {
+ AudioMixMatchCriterion ammc{kUsages[rand() % kUsages.size()],
+ kInputSources[rand() % kInputSources.size()],
+ kMixMatchRules[rand() % kMixMatchRules.size()]};
+ criteria.add(ammc);
+ }
+ audio_config_t config{};
+ config.sample_rate = 48000;
+ config.channel_mask = AUDIO_CHANNEL_IN_MONO;
+ config.format = AUDIO_FORMAT_PCM_16_BIT;
+ config.offload_info = AUDIO_INFO_INITIALIZER;
+ config.frame_count = 4800;
+ AudioMix am{criteria,
+ static_cast<uint32_t>(rand()),
+ config,
+ static_cast<uint32_t>(rand()),
+ String8(msg.c_str()),
+ static_cast<uint32_t>(rand())};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, am.writeToParcel(&p)) << msg;
+
+ AudioMix amCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, amCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(amCopy.mMixType, am.mMixType) << msg;
+ EXPECT_EQ(amCopy.mFormat.sample_rate, am.mFormat.sample_rate) << msg;
+ EXPECT_EQ(amCopy.mFormat.channel_mask, am.mFormat.channel_mask) << msg;
+ EXPECT_EQ(amCopy.mFormat.format, am.mFormat.format) << msg;
+ EXPECT_EQ(amCopy.mRouteFlags, am.mRouteFlags) << msg;
+ EXPECT_EQ(amCopy.mDeviceAddress, am.mDeviceAddress) << msg;
+ EXPECT_EQ(amCopy.mCbFlags, am.mCbFlags) << msg;
+ EXPECT_EQ(amCopy.mCriteria.size(), am.mCriteria.size()) << msg;
+ for (auto i = 0; i < amCopy.mCriteria.size(); i++) {
+ EXPECT_EQ(amCopy.mCriteria[i].mRule, am.mCriteria[i].mRule) << msg;
+ EXPECT_EQ(amCopy.mCriteria[i].mValue.mUserId, am.mCriteria[i].mValue.mUserId) << msg;
+ }
+ }
+}
+
+using MMCTestParams = std::tuple<audio_usage_t, audio_source_t, uint32_t>;
+
+class MMCParameterizedTest : public FillAudioAttributes,
+ public ::testing::TestWithParam<MMCTestParams> {
+ public:
+ MMCParameterizedTest()
+ : mAudioUsage(std::get<0>(GetParam())),
+ mAudioSource(std::get<1>(GetParam())),
+ mAudioMixMatchRules(std::get<2>(GetParam())){};
+
+ const audio_usage_t mAudioUsage;
+ const audio_source_t mAudioSource;
+ const uint32_t mAudioMixMatchRules;
+
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+TEST_P(MMCParameterizedTest, AudioMixMatchCriterionBinderization) {
+ const std::string msg{"Test AMMCBinderization for seed::" + std::to_string(mSeed)};
+ AudioMixMatchCriterion ammc{mAudioUsage, mAudioSource, mAudioMixMatchRules};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, ammc.writeToParcel(&p)) << msg;
+
+ AudioMixMatchCriterion ammcCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, ammcCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(ammcCopy.mRule, ammc.mRule) << msg;
+ EXPECT_EQ(ammcCopy.mValue.mUserId, ammc.mValue.mUserId) << msg;
+}
+
+// audioUsage, audioSource, audioMixMatchRules
+INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, MMCParameterizedTest,
+ ::testing::Combine(testing::ValuesIn(kUsages),
+ testing::ValuesIn(kInputSources),
+ testing::ValuesIn(kMixMatchRules)));
+
+using AudioAttributesTestParams = std::tuple<audio_stream_type_t>;
+
+class AudioAttributesParameterizedTest
+ : public FillAudioAttributes,
+ public ::testing::TestWithParam<AudioAttributesTestParams> {
+ public:
+ AudioAttributesParameterizedTest() : mAudioStream(std::get<0>(GetParam())){};
+
+ const audio_stream_type_t mAudioStream;
+
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+TEST_P(AudioAttributesParameterizedTest, AudioAttributesBinderization) {
+ const std::string msg{"Test AABinderization for seed::" + std::to_string(mSeed)};
+ volume_group_t groupId = static_cast<volume_group_t>(rand());
+ audio_stream_type_t stream = mAudioStream;
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ AudioAttributes audioattributes{groupId, stream, attributes};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, audioattributes.writeToParcel(&p)) << msg;
+
+ AudioAttributes audioattributesCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, audioattributesCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(audioattributesCopy.getGroupId(), audioattributes.getGroupId()) << msg;
+ EXPECT_EQ(audioattributesCopy.getStreamType(), audioattributes.getStreamType()) << msg;
+ EXPECT_TRUE(audioattributesCopy.getAttributes() == attributes) << msg;
+}
+
+// audioStream
+INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest,
+ ::testing::Combine(testing::ValuesIn(kStreamtypes)));
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
new file mode 100644
index 0000000..e6149e4
--- /dev/null
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2021 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 "AudioEffectUnitTests"
+
+#include <gtest/gtest.h>
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
+#include <system/audio_effects/effect_spatializer.h>
+#include <system/audio_effects/effect_visualizer.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+class AudioEffectCallback : public AudioEffect::IAudioEffectCallback {
+ public:
+ bool receivedFramesProcessed = false;
+
+ void onFramesProcessed(int32_t framesProcessed) override {
+ ALOGE("number of frames processed %d", framesProcessed);
+ receivedFramesProcessed = true;
+ }
+};
+
+static constexpr int kDefaultInputEffectPriority = -1;
+static constexpr int kDefaultOutputEffectPriority = 0;
+
+static const char* gPackageName = "AudioEffectTest";
+
+bool doesDeviceSupportLowLatencyMode(std::vector<struct audio_port_v7>& ports) {
+ for (const auto& port : ports) {
+ if (port.role == AUDIO_PORT_ROLE_SOURCE && port.type == AUDIO_PORT_TYPE_MIX) {
+ if ((port.active_config.flags.output & AUDIO_OUTPUT_FLAG_FAST) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+sp<AudioEffect> createEffect(const effect_uuid_t* type, const effect_uuid_t* uuid = nullptr,
+ int priority = 0, audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ const wp<AudioEffectCallback>& callback = nullptr) {
+ std::string packageName{gPackageName};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ sp<AudioEffect> effect = new AudioEffect(attributionSource);
+ effect->set(type, uuid, priority, callback, sessionId, AUDIO_IO_HANDLE_NONE, {}, false,
+ (callback != nullptr));
+ return effect;
+}
+
+status_t isEffectExistsOnAudioSession(const effect_uuid_t* type, const effect_uuid_t* uuid,
+ int priority, audio_session_t sessionId) {
+ sp<AudioEffect> effect = createEffect(type, uuid, priority, sessionId);
+ return effect->initCheck();
+}
+
+bool isEffectDefaultOnRecord(const effect_uuid_t* type, const effect_uuid_t* uuid,
+ const sp<AudioRecord>& audioRecord) {
+ effect_descriptor_t descriptors[AudioEffect::kMaxPreProcessing];
+ uint32_t numEffects = AudioEffect::kMaxPreProcessing;
+ status_t ret = AudioEffect::queryDefaultPreProcessing(audioRecord->getSessionId(), descriptors,
+ &numEffects);
+ if (ret != OK) {
+ return false;
+ }
+ for (int i = 0; i < numEffects; i++) {
+ if ((memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) &&
+ (memcmp(&descriptors[i].uuid, uuid, sizeof(effect_uuid_t)) == 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void listEffectsAvailable(std::vector<effect_descriptor_t>& descriptors) {
+ uint32_t numEffects = 0;
+ ASSERT_EQ(NO_ERROR, AudioEffect::queryNumberEffects(&numEffects));
+ for (auto i = 0; i < numEffects; i++) {
+ effect_descriptor_t des;
+ ASSERT_EQ(NO_ERROR, AudioEffect::queryEffect(i, &des));
+ descriptors.push_back(des);
+ }
+}
+
+bool isPreprocessing(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC);
+}
+
+bool isInsert(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT);
+}
+
+bool isAux(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY);
+}
+
+bool isPostproc(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC);
+}
+
+bool isFastCompatible(effect_descriptor_t& descriptor) {
+ return !(((descriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0) &&
+ ((descriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0));
+}
+
+bool isSpatializer(effect_descriptor_t& descriptor) {
+ return (memcmp(&descriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0);
+}
+
+bool isHapticGenerator(effect_descriptor_t& descriptor) {
+ return (memcmp(&descriptor.type, FX_IID_HAPTICGENERATOR, sizeof(effect_uuid_t)) == 0);
+}
+
+std::tuple<std::string, std::string> typeAndUuidToString(const effect_descriptor_t& desc) {
+ char type[512];
+ AudioEffect::guidToString(&desc.type, type, sizeof(type));
+ char uuid[512];
+ AudioEffect::guidToString(&desc.uuid, uuid, sizeof(uuid));
+ return std::make_tuple(type, uuid);
+}
+
+// UNIT TESTS
+TEST(AudioEffectTest, getEffectDescriptor) {
+ effect_uuid_t randomType = {
+ 0x81781c08, 0x93dd, 0x11ec, 0xb909, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+ effect_uuid_t randomUuid = {
+ 0x653730e1, 0x1be1, 0x438e, 0xa35a, {0xfc, 0x9b, 0xa1, 0x2a, 0x5e, 0xc9}};
+ effect_uuid_t empty = EFFECT_UUID_INITIALIZER;
+
+ effect_descriptor_t descriptor;
+ EXPECT_EQ(NAME_NOT_FOUND, AudioEffect::getEffectDescriptor(&randomUuid, &randomType,
+ EFFECT_FLAG_TYPE_MASK, &descriptor));
+
+ std::vector<effect_descriptor_t> descriptors;
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
+
+ for (auto i = 0; i < descriptors.size(); i++) {
+ EXPECT_EQ(NO_ERROR,
+ AudioEffect::getEffectDescriptor(&descriptors[i].uuid, &descriptors[i].type,
+ EFFECT_FLAG_TYPE_MASK, &descriptor));
+ EXPECT_EQ(0, memcmp(&descriptor, &descriptors[i], sizeof(effect_uuid_t)));
+ }
+ // negative tests
+ if (descriptors.size() > 0) {
+ EXPECT_EQ(BAD_VALUE,
+ AudioEffect::getEffectDescriptor(&descriptors[0].uuid, &descriptors[0].type,
+ EFFECT_FLAG_TYPE_MASK, nullptr));
+ }
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(nullptr, nullptr,
+ EFFECT_FLAG_TYPE_PRE_PROC, &descriptor));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(&empty, &randomType,
+ EFFECT_FLAG_TYPE_MASK, nullptr));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(nullptr, &randomType,
+ EFFECT_FLAG_TYPE_POST_PROC, &descriptor));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(&randomUuid, nullptr,
+ EFFECT_FLAG_TYPE_INSERT, &descriptor));
+}
+
+TEST(AudioEffectTest, DISABLED_GetSetParameterForEffect) {
+ sp<AudioEffect> visualizer = createEffect(SL_IID_VISUALIZATION);
+ status_t status = visualizer->initCheck();
+ ASSERT_TRUE(status == NO_ERROR || status == ALREADY_EXISTS) << "Init check error";
+ ASSERT_EQ(NO_ERROR, visualizer->setEnabled(true)) << "visualizer not enabled";
+
+ uint32_t buf32[3][sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t* vis_none = (effect_param_t*)(buf32[0]);
+ effect_param_t* vis_rms = (effect_param_t*)(buf32[1]);
+ effect_param_t* vis_tmp = (effect_param_t*)(buf32[2]);
+
+ // Visualizer::setMeasurementMode()
+ vis_none->psize = sizeof(uint32_t);
+ vis_none->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_none->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_none->data + 1) = MEASUREMENT_MODE_NONE;
+ EXPECT_EQ(NO_ERROR, visualizer->setParameter(vis_none))
+ << "setMeasurementMode doesn't report success";
+
+ // Visualizer::getMeasurementMode()
+ vis_tmp->psize = sizeof(uint32_t);
+ vis_tmp->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_tmp->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_none->data + 1))
+ << "target mode does not match set mode";
+
+ // Visualizer::setMeasurementModeDeferred()
+ vis_rms->psize = sizeof(uint32_t);
+ vis_rms->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_rms->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_rms->data + 1) = MEASUREMENT_MODE_PEAK_RMS;
+ EXPECT_EQ(NO_ERROR, visualizer->setParameterDeferred(vis_rms))
+ << "setMeasurementModeDeferred doesn't report success";
+
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_none->data + 1))
+ << "target mode does not match set mode";
+
+ // setParameterCommit
+ EXPECT_EQ(NO_ERROR, visualizer->setParameterCommit())
+ << "setMeasurementModeCommit does not report success";
+
+ // validate Params
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_rms->data + 1))
+ << "target mode does not match set mode";
+}
+
+TEST(AudioEffectTest, ManageSourceDefaultEffects) {
+ int32_t selectedEffect = -1;
+
+ const uint32_t sampleRate = 44100;
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_STEREO;
+ sp<AudioCapture> capture = nullptr;
+
+ std::vector<effect_descriptor_t> descriptors;
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (isPreprocessing(descriptors[i])) {
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ if (!isEffectDefaultOnRecord(&descriptors[i].type, &descriptors[i].uuid,
+ capture->getAudioRecordHandle())) {
+ selectedEffect = i;
+ break;
+ }
+ }
+ }
+ if (selectedEffect == -1) GTEST_SKIP() << " expected at least one preprocessing effect";
+
+ effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
+ effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
+ auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
+ << "Effect should not have been default on record. " << type;
+ EXPECT_EQ(NO_ERROR,
+ isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+
+ String16 name{gPackageName};
+ audio_unique_id_t effectId;
+ status_t status = AudioEffect::addSourceDefaultEffect(type.c_str(), name, uuid.c_str(),
+ kDefaultInputEffectPriority,
+ AUDIO_SOURCE_MIC, &effectId);
+ EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;
+
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_TRUE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
+ << "Effect should have been default on record. " << type;
+ EXPECT_EQ(ALREADY_EXISTS,
+ isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+
+ status = AudioEffect::removeSourceDefaultEffect(effectId);
+ EXPECT_EQ(NO_ERROR, status);
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
+ << "Effect should not have been default on record. " << type;
+ EXPECT_EQ(NO_ERROR,
+ isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+}
+
+TEST(AudioEffectTest, AuxEffectSanityTest) {
+ int32_t selectedEffect = -1;
+ std::vector<effect_descriptor_t> descriptors;
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (isAux(descriptors[i])) {
+ selectedEffect = i;
+ break;
+ }
+ }
+ if (selectedEffect == -1) GTEST_SKIP() << "expected at least one aux effect";
+ effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
+ effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
+ auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);
+ String16 name{gPackageName};
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffect> audioEffect = createEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority, sessionId);
+ EXPECT_EQ(NO_INIT, audioEffect->initCheck())
+ << "error, creating auxiliary effect (" << type << ") on session id " << (int)sessionId
+ << " successful ";
+ audio_unique_id_t id;
+ status_t status = AudioEffect::addStreamDefaultEffect(
+ type.c_str(), name, uuid.c_str(), kDefaultOutputEffectPriority, AUDIO_USAGE_MEDIA, &id);
+ if (status == NO_ERROR) {
+ EXPECT_EQ(NO_ERROR, AudioEffect::removeStreamDefaultEffect(id));
+ EXPECT_NE(NO_ERROR, status) << "error, adding auxiliary effect (" << type
+ << ") as stream default effect is successful";
+ }
+}
+
+class AudioPlaybackEffectTest : public ::testing::TestWithParam<bool> {
+ public:
+ AudioPlaybackEffectTest() : mSelectFastMode(GetParam()){};
+
+ const bool mSelectFastMode;
+
+ bool mIsFastCompatibleEffect;
+ effect_uuid_t mType;
+ effect_uuid_t mUuid;
+ std::string mTypeStr;
+ std::string mUuidStr;
+
+ void SetUp() override {
+ if (mSelectFastMode) {
+ std::vector<struct audio_port_v7> ports;
+ ASSERT_EQ(OK, listAudioPorts(ports));
+ if (!doesDeviceSupportLowLatencyMode(ports)) {
+ GTEST_SKIP() << "device does not support low latency mode";
+ }
+ }
+
+ int32_t selectedEffect = -1;
+ std::vector<effect_descriptor_t> descriptors;
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (isSpatializer(descriptors[i])) continue;
+ if (isHapticGenerator(descriptors[i]) && !AudioSystem::isHapticPlaybackSupported())
+ continue;
+ if (!isInsert(descriptors[i])) continue;
+ selectedEffect = i;
+ mIsFastCompatibleEffect = isFastCompatible(descriptors[i]);
+ // in fast mode, pick fast compatible effect if available
+ if (mSelectFastMode == mIsFastCompatibleEffect) break;
+ }
+ if (selectedEffect == -1) {
+ GTEST_SKIP() << "expected at least one valid effect";
+ }
+
+ mType = descriptors[selectedEffect].type;
+ mUuid = descriptors[selectedEffect].uuid;
+ std::tie(mTypeStr, mUuidStr) = typeAndUuidToString(descriptors[selectedEffect]);
+ }
+};
+
+TEST_P(AudioPlaybackEffectTest, StreamDefaultEffectTest) {
+ SCOPED_TRACE(testing::Message()
+ << "\n selected effect type is :: " << mTypeStr
+ << "\n selected effect uuid is :: " << mUuidStr
+ << "\n audiotrack output flag : " << (mSelectFastMode ? "fast" : "default")
+ << "\n audio effect is fast compatible : "
+ << (mIsFastCompatibleEffect ? "yes" : "no"));
+
+ bool compatCheck = !mSelectFastMode || (mSelectFastMode && mIsFastCompatibleEffect);
+
+ // create track
+ audio_attributes_t attributes;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ auto playback = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
+ isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should not have been added. " << mTypeStr;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->stop();
+ playback.clear();
+
+ String16 name{gPackageName};
+ audio_unique_id_t id;
+ status_t status = AudioEffect::addStreamDefaultEffect(mTypeStr.c_str(), name, mUuidStr.c_str(),
+ kDefaultOutputEffectPriority,
+ AUDIO_USAGE_MEDIA, &id);
+ EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << mTypeStr;
+
+ playback = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ // If effect chosen is not compatible with the session, then effect won't be applied
+ EXPECT_EQ(compatCheck ? ALREADY_EXISTS : NO_INIT,
+ isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should have been added. " << mTypeStr;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ if (mSelectFastMode) {
+ EXPECT_EQ(AUDIO_OUTPUT_FLAG_FAST,
+ playback->getAudioTrackHandle()->getFlags() & AUDIO_OUTPUT_FLAG_FAST);
+ }
+ playback->stop();
+ playback.clear();
+
+ status = AudioEffect::removeStreamDefaultEffect(id);
+ EXPECT_EQ(NO_ERROR, status);
+ playback = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
+ isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should not have been added. " << mTypeStr;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->stop();
+ playback.clear();
+}
+
+TEST_P(AudioPlaybackEffectTest, CheckOutputFlagCompatibility) {
+ SCOPED_TRACE(testing::Message()
+ << "\n selected effect type is :: " << mTypeStr
+ << "\n selected effect uuid is :: " << mUuidStr
+ << "\n audiotrack output flag : " << (mSelectFastMode ? "fast" : "default")
+ << "\n audio effect is fast compatible : "
+ << (mIsFastCompatibleEffect ? "yes" : "no"));
+
+ audio_attributes_t attributes;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffectCallback> cb = sp<AudioEffectCallback>::make();
+ sp<AudioEffect> audioEffect =
+ createEffect(&mType, &mUuid, kDefaultOutputEffectPriority, sessionId, cb);
+ ASSERT_EQ(OK, audioEffect->initCheck());
+ ASSERT_EQ(NO_ERROR, audioEffect->setEnabled(true));
+ auto playback = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO,
+ mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, sessionId,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+
+ EXPECT_EQ(ALREADY_EXISTS, isEffectExistsOnAudioSession(
+ &mType, &mUuid, kDefaultOutputEffectPriority - 1, sessionId))
+ << "Effect should have been added. " << mTypeStr;
+ if (mSelectFastMode) {
+ EXPECT_EQ(mIsFastCompatibleEffect ? AUDIO_OUTPUT_FLAG_FAST : 0,
+ playback->getAudioTrackHandle()->getFlags() & AUDIO_OUTPUT_FLAG_FAST);
+ }
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ EXPECT_EQ(NO_ERROR, playback->getAudioTrackHandle()->attachAuxEffect(0));
+ playback->stop();
+ playback.clear();
+ EXPECT_TRUE(cb->receivedFramesProcessed)
+ << "AudioEffect frames processed callback not received";
+}
+
+INSTANTIATE_TEST_SUITE_P(EffectParameterizedTests, AudioPlaybackEffectTest, ::testing::Bool());
+
+TEST(AudioEffectTest, TestHapticEffect) {
+ if (!AudioSystem::isHapticPlaybackSupported())
+ GTEST_SKIP() << "Haptic playback is not supported";
+ int32_t selectedEffect = -1;
+ std::vector<effect_descriptor_t> descriptors;
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (!isHapticGenerator(descriptors[i])) continue;
+ selectedEffect = i;
+ break;
+ }
+ if (selectedEffect == -1) GTEST_SKIP() << "expected at least one valid effect";
+
+ effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
+ effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
+ auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);
+
+ SCOPED_TRACE(testing::Message() << "\n selected effect type is :: " << type
+ << "\n selected effect uuid is :: " << uuid);
+
+ audio_attributes_t attributes;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffectCallback> cb = sp<AudioEffectCallback>::make();
+ sp<AudioEffect> audioEffect = createEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultOutputEffectPriority, sessionId, cb);
+ ASSERT_EQ(OK, audioEffect->initCheck());
+ ASSERT_EQ(NO_ERROR, audioEffect->setEnabled(true));
+ auto playback = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_NONE,
+ sessionId, AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
+ kDefaultOutputEffectPriority - 1, sessionId))
+ << "Effect should have been added. " << type;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->stop();
+ playback.clear();
+ EXPECT_TRUE(cb->receivedFramesProcessed)
+ << "AudioEffect frames processed callback not received";
+}
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
new file mode 100644
index 0000000..8c63a6d
--- /dev/null
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2021 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 "AudioRecordTest"
+
+#include <gtest/gtest.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+class AudioRecordTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mAC = new AudioCapture(AUDIO_SOURCE_DEFAULT, 44100, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_FRONT);
+ ASSERT_NE(nullptr, mAC);
+ ASSERT_EQ(OK, mAC->create()) << "record creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAC) ASSERT_EQ(OK, mAC->stop());
+ }
+
+ sp<AudioCapture> mAC;
+};
+
+class AudioRecordCreateTest
+ : public ::testing::TestWithParam<
+ std::tuple<uint32_t, audio_format_t, audio_channel_mask_t, audio_input_flags_t,
+ audio_session_t, audio_source_t>> {
+ public:
+ AudioRecordCreateTest()
+ : mSampleRate(std::get<0>(GetParam())),
+ mFormat(std::get<1>(GetParam())),
+ mChannelMask(std::get<2>(GetParam())),
+ mFlags(std::get<3>(GetParam())),
+ mSessionId(std::get<4>(GetParam())),
+ mInputSource(std::get<5>(GetParam())){};
+
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_input_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const audio_source_t mInputSource;
+ const AudioRecord::transfer_type mTransferType = AudioRecord::TRANSFER_OBTAIN;
+
+ sp<AudioCapture> mAC;
+
+ virtual void SetUp() override {
+ mAC = new AudioCapture(mInputSource, mSampleRate, mFormat, mChannelMask, mFlags, mSessionId,
+ mTransferType);
+ ASSERT_NE(nullptr, mAC);
+ ASSERT_EQ(OK, mAC->create()) << "record creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAC) ASSERT_EQ(OK, mAC->stop());
+ }
+};
+
+TEST_F(AudioRecordTest, TestSimpleRecord) {
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+TEST_F(AudioRecordTest, TestAudioCbNotifier) {
+ EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->addAudioDeviceCallback(nullptr));
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ sp<OnAudioDeviceUpdateNotifier> cbOld = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, mAC->start()) << "record creation failed";
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
+ EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(nullptr));
+ EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cb));
+ mAC->stop();
+}
+
+TEST_F(AudioRecordTest, TestEventRecordTrackPause) {
+ const auto playback = sp<AudioPlayback>::make(
+ 8000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, playback->create()) << "AudioTrack Creation failed";
+ audio_session_t audioTrackSession = playback->getAudioTrackHandle()->getSessionId();
+ EXPECT_EQ(OK, mAC->start(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE, audioTrackSession))
+ << "record creation failed";
+ EXPECT_EQ(OK, playback->start());
+ RawBuffer buffer;
+ status_t status = mAC->obtainBufferCb(buffer);
+ EXPECT_EQ(status, TIMED_OUT) << "Not expecting any callbacks until track sends Sync event";
+ playback->getAudioTrackHandle()->pause();
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ playback->stop();
+}
+
+TEST_F(AudioRecordTest, TestEventRecordTrackStop) {
+ const auto playback = sp<AudioPlayback>::make(
+ 8000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, playback->create()) << "AudioTrack Creation failed";
+ audio_session_t audioTrackSession = playback->getAudioTrackHandle()->getSessionId();
+ EXPECT_EQ(OK, mAC->start(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE, audioTrackSession))
+ << "record creation failed";
+ EXPECT_EQ(OK, playback->start());
+ RawBuffer buffer;
+ status_t status = mAC->obtainBufferCb(buffer);
+ EXPECT_EQ(status, TIMED_OUT) << "Not expecting any callbacks until track sends Sync event";
+ playback->stop();
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+TEST_F(AudioRecordTest, TestGetSetMarker) {
+ mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition))
+ << "setMarkerPosition() failed";
+ uint32_t marker;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker))
+ << "getMarkerPosition() failed";
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(marker, mAC->mMarkerPosition)
+ << "configured marker and received marker are different";
+ EXPECT_EQ(mAC->mReceivedCbMarkerAtPosition, mAC->mMarkerPosition)
+ << "configured marker and received cb marker are different";
+}
+
+TEST_F(AudioRecordTest, TestGetSetMarkerPeriodical) {
+ mAC->mMarkerPeriod = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPeriod))
+ << "setPositionUpdatePeriod() failed";
+ uint32_t marker;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker))
+ << "getPositionUpdatePeriod() failed";
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(marker, mAC->mMarkerPeriod) << "configured marker and received marker are different";
+ EXPECT_EQ(mAC->mReceivedCbMarkerCount, mAC->mNumFramesToRecord / mAC->mMarkerPeriod)
+ << "configured marker and received cb marker are different";
+}
+
+TEST_F(AudioRecordTest, TestGetPosition) {
+ uint32_t position;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPosition(&position)) << "getPosition() failed";
+ EXPECT_EQ(0, position);
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(OK, mAC->stop());
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPosition(&position)) << "getPosition() failed";
+}
+
+// TODO: Add checkPatchCapture(), verify the information of patch via dumpPort() and dumpPatch()
+TEST_P(AudioRecordCreateTest, TestCreateRecord) {
+ EXPECT_EQ(mFormat, mAC->getAudioRecordHandle()->format());
+ EXPECT_EQ(audio_channel_count_from_in_mask(mChannelMask),
+ mAC->getAudioRecordHandle()->channelCount());
+ if (mAC->mFrameCount != 0)
+ EXPECT_LE(mAC->mFrameCount, mAC->getAudioRecordHandle()->frameCount());
+ EXPECT_EQ(mInputSource, mAC->getAudioRecordHandle()->inputSource());
+ if (mSampleRate != 0) EXPECT_EQ(mSampleRate, mAC->getAudioRecordHandle()->getSampleRate());
+ if (mSessionId != AUDIO_SESSION_NONE)
+ EXPECT_EQ(mSessionId, mAC->getAudioRecordHandle()->getSessionId());
+ if (mTransferType != AudioRecord::TRANSFER_CALLBACK) {
+ uint32_t marker;
+ mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(INVALID_OPERATION,
+ mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker));
+ EXPECT_EQ(INVALID_OPERATION,
+ mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPosition));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker));
+ }
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+// for port primary input
+INSTANTIATE_TEST_SUITE_P(AudioRecordPrimaryInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_8_24_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK),
+ ::testing::Values(AUDIO_INPUT_FLAG_NONE),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+
+// for port fast input
+INSTANTIATE_TEST_SUITE_P(AudioRecordFastInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_8_24_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK),
+ ::testing::Values(AUDIO_INPUT_FLAG_FAST),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+
+// misc
+INSTANTIATE_TEST_SUITE_P(AudioRecordMiscInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_16_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO),
+ ::testing::Values(AUDIO_INPUT_FLAG_NONE),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_MIC,
+ AUDIO_SOURCE_CAMCORDER,
+ AUDIO_SOURCE_VOICE_RECOGNITION,
+ AUDIO_SOURCE_VOICE_COMMUNICATION,
+ AUDIO_SOURCE_UNPROCESSED)));
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
new file mode 100644
index 0000000..445633b
--- /dev/null
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 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
+
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+// UNIT TEST
+TEST(AudioTrackTest, TestPerformanceMode) {
+ std::vector<std::string> attachedDevices;
+ std::vector<MixPort> mixPorts;
+ std::vector<Route> routes;
+ EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
+ std::string output_flags_string[] = {"AUDIO_OUTPUT_FLAG_FAST", "AUDIO_OUTPUT_FLAG_DEEP_BUFFER"};
+ audio_output_flags_t output_flags[] = {AUDIO_OUTPUT_FLAG_FAST, AUDIO_OUTPUT_FLAG_DEEP_BUFFER};
+ audio_flags_mask_t flags[] = {AUDIO_FLAG_LOW_LATENCY, AUDIO_FLAG_DEEP_BUFFER};
+ bool hasFlag = false;
+ for (int i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ hasFlag = false;
+ for (int j = 0; j < mixPorts.size() && !hasFlag; j++) {
+ MixPort port = mixPorts[j];
+ if (port.role == "source" && port.flags.find(output_flags_string[i]) != -1) {
+ for (int k = 0; k < routes.size() && !hasFlag; k++) {
+ if (routes[k].sources.find(port.name) != -1 &&
+ std::find(attachedDevices.begin(), attachedDevices.end(), routes[k].sink) !=
+ attachedDevices.end()) {
+ hasFlag = true;
+ std::cerr << "found port with flag " << output_flags_string[i] << "@ "
+ << " port :: name : " << port.name << " role : " << port.role
+ << " port :: flags : " << port.flags
+ << " connected via route name : " << routes[k].name
+ << " route sources : " << routes[k].sources
+ << " route sink : " << routes[k].sink << std::endl;
+ }
+ }
+ }
+ }
+ if (!hasFlag) continue;
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ attributes.flags = flags[i];
+ sp<AudioPlayback> ap = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN,
+ &attributes);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
+ audio_patch patch;
+ EXPECT_EQ(OK, getPatchForOutputMix(cb->mAudioIo, patch));
+ for (auto j = 0; j < patch.num_sources; j++) {
+ if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
+ patch.sources[j].ext.mix.handle == cb->mAudioIo) {
+ if ((patch.sources[j].flags.output & output_flags[i]) == 0) {
+ ADD_FAILURE() << "expected output flag " << output_flags[i] << " is absent";
+ std::cerr << dumpPortConfig(patch.sources[j]);
+ }
+ }
+ }
+ ap->stop();
+ ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb);
+ }
+}
+
+TEST(AudioTrackTest, TestRemoteSubmix) {
+ std::vector<std::string> attachedDevices;
+ std::vector<MixPort> mixPorts;
+ std::vector<Route> routes;
+ EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
+ bool hasFlag = false;
+ for (int j = 0; j < attachedDevices.size() && !hasFlag; j++) {
+ if (attachedDevices[j].find("Remote Submix") != -1) hasFlag = true;
+ }
+ if (!hasFlag) GTEST_SKIP() << " Device does not have Remote Submix port.";
+ sp<AudioCapture> capture = new AudioCapture(AUDIO_SOURCE_REMOTE_SUBMIX, 48000,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+ ASSERT_NE(nullptr, capture);
+ ASSERT_EQ(OK, capture->create()) << "record creation failed";
+
+ sp<AudioPlayback> playback = sp<AudioPlayback>::make(
+ 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ ASSERT_EQ(OK, playback->create()) << "track creation failed";
+
+ audio_port_v7 port;
+ status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, port);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ EXPECT_EQ(OK, capture->start()) << "start recording failed";
+ EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
+ << "Capture NOT routed on expected port";
+
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, port);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ EXPECT_EQ(OK, playback->start()) << "audio track start failed";
+ EXPECT_EQ(OK, playback->onProcess());
+ ASSERT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+ << "Playback NOT routed on expected port";
+ capture->stop();
+ playback->stop();
+}
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
new file mode 100644
index 0000000..aed847c
--- /dev/null
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2021 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 "AudioSystemTest"
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+#include <media/IAudioFlinger.h>
+#include <utils/Log.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+void anyPatchContainsInputDevice(audio_port_handle_t deviceId, bool& res) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ ASSERT_EQ(OK, status);
+ res = false;
+ for (const auto& patch : patches) {
+ if (patchContainsInputDevice(deviceId, patch)) {
+ res = true;
+ return;
+ }
+ }
+}
+
+class AudioSystemTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ mAF = AudioSystem::get_audio_flinger();
+ ASSERT_NE(mAF, nullptr) << "Permission denied";
+ }
+
+ void TearDown() override {
+ if (mPlayback) {
+ mPlayback->stop();
+ mPlayback->getAudioTrackHandle()->removeAudioDeviceCallback(mCbPlayback);
+ mPlayback.clear();
+ }
+ if (mCapture) {
+ mCapture->stop();
+ mCapture->getAudioRecordHandle()->removeAudioDeviceCallback(mCbRecord);
+ mCapture.clear();
+ }
+ }
+
+ void createPlaybackSession(void);
+ void createRecordSession(void);
+
+ sp<IAudioFlinger> mAF;
+ sp<AudioPlayback> mPlayback;
+ sp<OnAudioDeviceUpdateNotifier> mCbPlayback;
+ sp<AudioCapture> mCapture;
+ sp<OnAudioDeviceUpdateNotifier> mCbRecord;
+};
+
+void AudioSystemTest::createPlaybackSession(void) {
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ mPlayback = sp<AudioPlayback>::make(48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_FAST, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, mPlayback);
+ ASSERT_EQ(NO_ERROR, mPlayback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, mPlayback->create());
+ mCbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mPlayback->getAudioTrackHandle()->addAudioDeviceCallback(mCbPlayback));
+ EXPECT_EQ(NO_ERROR, mPlayback->start());
+ EXPECT_EQ(OK, mPlayback->onProcess());
+ EXPECT_EQ(OK, mCbPlayback->waitForAudioDeviceCb());
+}
+
+void AudioSystemTest::createRecordSession(void) {
+ mCapture = new AudioCapture(AUDIO_SOURCE_DEFAULT, 44100, AUDIO_FORMAT_PCM_8_24_BIT,
+ AUDIO_CHANNEL_IN_MONO, AUDIO_INPUT_FLAG_FAST);
+ ASSERT_NE(nullptr, mCapture);
+ ASSERT_EQ(OK, mCapture->create()) << "record creation failed";
+ mCbRecord = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mCapture->getAudioRecordHandle()->addAudioDeviceCallback(mCbRecord));
+ EXPECT_EQ(OK, mCapture->start()) << "record creation failed";
+ EXPECT_EQ(OK, mCbRecord->waitForAudioDeviceCb());
+}
+
+// UNIT TESTS
+TEST_F(AudioSystemTest, CheckServerSideValues) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ EXPECT_GT(mAF->sampleRate(mCbPlayback->mAudioIo), 0);
+ EXPECT_NE(mAF->format(mCbPlayback->mAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(mCbPlayback->mAudioIo), 0);
+ size_t frameCountHal, frameCountHalCache;
+ frameCountHal = mAF->frameCountHAL(mCbPlayback->mAudioIo);
+ EXPECT_GT(frameCountHal, 0);
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbPlayback->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(frameCountHal, frameCountHalCache);
+ EXPECT_GT(mAF->latency(mCbPlayback->mAudioIo), 0);
+ // client side latency is at least server side latency
+ EXPECT_LE(mAF->latency(mCbPlayback->mAudioIo), mPlayback->getAudioTrackHandle()->latency());
+
+ ASSERT_NO_FATAL_FAILURE(createRecordSession());
+ EXPECT_GT(mAF->sampleRate(mCbRecord->mAudioIo), 0);
+ // EXPECT_NE(mAF->format(mCbRecord->mAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(mCbRecord->mAudioIo), 0);
+ EXPECT_GT(mAF->frameCountHAL(mCbRecord->mAudioIo), 0);
+ frameCountHal = mAF->frameCountHAL(mCbRecord->mAudioIo);
+ EXPECT_GT(frameCountHal, 0);
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbRecord->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(frameCountHal, frameCountHalCache);
+ // EXPECT_GT(mAF->latency(mCbRecord->mAudioIo), 0);
+ // client side latency is at least server side latency
+ // EXPECT_LE(mAF->latency(mCbRecord->mAudioIo), mCapture->getAudioRecordHandle()->latency());
+
+ EXPECT_GT(AudioSystem::getPrimaryOutputSamplingRate(), 0); // first fast mixer sample rate
+ EXPECT_GT(AudioSystem::getPrimaryOutputFrameCount(), 0); // fast mixer frame count
+}
+
+TEST_F(AudioSystemTest, GetSetMasterVolume) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origVol, tstVol;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&origVol));
+ float newVol;
+ if (origVol + 0.2f > 1.0f) {
+ newVol = origVol - 0.2f;
+ } else {
+ newVol = origVol + 0.2f;
+ }
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterVolume(newVol));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&tstVol));
+ EXPECT_EQ(newVol, tstVol);
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterVolume(origVol));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&tstVol));
+ EXPECT_EQ(origVol, tstVol);
+}
+
+TEST_F(AudioSystemTest, GetSetMasterMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState, tstMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterMute(!origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&tstMuteState));
+ EXPECT_EQ(!origMuteState, tstMuteState);
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterMute(origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&tstMuteState));
+ EXPECT_EQ(origMuteState, tstMuteState);
+}
+
+TEST_F(AudioSystemTest, GetSetMicMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState, tstMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::muteMicrophone(!origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&tstMuteState));
+ EXPECT_EQ(!origMuteState, tstMuteState);
+ EXPECT_EQ(NO_ERROR, AudioSystem::muteMicrophone(origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&tstMuteState));
+ EXPECT_EQ(origMuteState, tstMuteState);
+}
+
+TEST_F(AudioSystemTest, GetSetMasterBalance) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origBalance, tstBalance;
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&origBalance));
+ float newBalance;
+ if (origBalance + 0.2f > 1.0f) {
+ newBalance = origBalance - 0.2f;
+ } else {
+ newBalance = origBalance + 0.2f;
+ }
+ EXPECT_EQ(OK, AudioSystem::setMasterBalance(newBalance));
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&tstBalance));
+ EXPECT_EQ(newBalance, tstBalance);
+ EXPECT_EQ(OK, AudioSystem::setMasterBalance(origBalance));
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&tstBalance));
+ EXPECT_EQ(origBalance, tstBalance);
+}
+
+TEST_F(AudioSystemTest, GetStreamVolume) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origStreamVol;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getStreamVolume(AUDIO_STREAM_MUSIC, &origStreamVol,
+ mCbPlayback->mAudioIo));
+}
+
+TEST_F(AudioSystemTest, GetStreamMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getStreamMute(AUDIO_STREAM_MUSIC, &origMuteState));
+}
+
+TEST_F(AudioSystemTest, StartAndStopAudioSource) {
+ std::vector<struct audio_port_v7> ports;
+ audio_port_config sourcePortConfig;
+ audio_attributes_t attributes = AudioSystem::streamTypeToAttributes(AUDIO_STREAM_MUSIC);
+ audio_port_handle_t sourcePortHandle = AUDIO_PORT_HANDLE_NONE;
+
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ if (ports.empty()) {
+ GTEST_SKIP() << "No ports returned by the audio system";
+ }
+
+ for (const auto& port : ports) {
+ if (port.role != AUDIO_PORT_ROLE_SOURCE || port.type != AUDIO_PORT_TYPE_DEVICE) continue;
+ sourcePortConfig = port.active_config;
+
+ bool patchFound;
+
+ // start audio source.
+ status_t ret =
+ AudioSystem::startAudioSource(&sourcePortConfig, &attributes, &sourcePortHandle);
+ EXPECT_EQ(OK, ret) << "AudioSystem::startAudioSource for source " << port.ext.device.address
+ << " failed";
+
+ // verify that patch is established by the source port.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(port.id, patchFound));
+ EXPECT_EQ(true, patchFound);
+ EXPECT_NE(sourcePortHandle, AUDIO_PORT_HANDLE_NONE);
+
+ if (sourcePortHandle != AUDIO_PORT_HANDLE_NONE) {
+ ret = AudioSystem::stopAudioSource(sourcePortHandle);
+ EXPECT_EQ(OK, ret) << "AudioSystem::stopAudioSource for handle failed";
+ }
+
+ // verify that no source port patch exists.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(port.id, patchFound));
+ EXPECT_EQ(false, patchFound);
+ }
+}
+
+TEST_F(AudioSystemTest, CreateAndReleaseAudioPatch) {
+ status_t status;
+ struct audio_patch audioPatch;
+ std::vector<struct audio_port_v7> ports;
+ audio_patch_handle_t audioPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+
+ bool patchFound = false;
+ audio_port_v7 sourcePort{};
+ audio_port_v7 sinkPort{};
+
+ audioPatch.id = 0;
+ audioPatch.num_sources = 1;
+ audioPatch.num_sinks = 1;
+
+ status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ if (ports.empty()) {
+ GTEST_SKIP() << "No output devices returned by the audio system";
+ }
+
+ for (const auto& port : ports) {
+ if (port.role == AUDIO_PORT_ROLE_SOURCE && port.type == AUDIO_PORT_TYPE_DEVICE) {
+ sourcePort = port;
+ }
+ if (port.role == AUDIO_PORT_ROLE_SINK && port.type == AUDIO_PORT_TYPE_DEVICE &&
+ port.ext.device.type == AUDIO_DEVICE_OUT_SPEAKER) {
+ sinkPort = port;
+ }
+ }
+
+ audioPatch.sources[0] = sourcePort.active_config;
+ audioPatch.sinks[0] = sinkPort.active_config;
+
+ status = AudioSystem::createAudioPatch(&audioPatch, &audioPatchHandle);
+ EXPECT_EQ(OK, status) << "AudioSystem::createAudiopatch failed between source "
+ << sourcePort.ext.device.address << " and sink "
+ << sinkPort.ext.device.address;
+
+ // verify that patch is established between source and the sink.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(sourcePort.id, patchFound));
+ EXPECT_EQ(true, patchFound);
+
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, audioPatchHandle);
+ status = AudioSystem::releaseAudioPatch(audioPatchHandle);
+ EXPECT_EQ(OK, status) << "AudioSystem::releaseAudioPatch failed between source "
+ << sourcePort.ext.device.address << " and sink "
+ << sinkPort.ext.device.address;
+
+ // verify that no patch is established between source and the sink after releaseAudioPatch.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(sourcePort.id, patchFound));
+ EXPECT_EQ(false, patchFound);
+}
+
+TEST_F(AudioSystemTest, GetAudioPort) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ for (const auto& port : ports) {
+ audio_port_v7 portTest{.id = port.id};
+ EXPECT_EQ(OK, AudioSystem::getAudioPort(&portTest));
+ EXPECT_TRUE(audio_ports_v7_are_equal(&portTest, &port));
+ }
+}
+
+TEST_F(AudioSystemTest, TestPhoneState) {
+ uid_t uid = getuid();
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_RINGTONE, uid));
+ audio_mode_t state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_RINGTONE, state);
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_IN_COMMUNICATION, uid));
+ state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_IN_COMMUNICATION, state);
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_NORMAL, uid));
+ state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_NORMAL, state);
+}
+
+TEST_F(AudioSystemTest, GetDirectProfilesForAttributes) {
+ std::vector<audio_profile> audioProfiles;
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(nullptr, &audioProfiles));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(&attributes, nullptr));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDirectProfilesForAttributes(&attributes, &audioProfiles));
+}
+
+bool isPublicStrategy(const AudioProductStrategy& strategy) {
+ bool result = true;
+ for (auto& attribute : strategy.getAudioAttributes()) {
+ if (attribute.getAttributes() == AUDIO_ATTRIBUTES_INITIALIZER &&
+ (uint32_t(attribute.getStreamType()) >= AUDIO_STREAM_PUBLIC_CNT)) {
+ result = false;
+ break;
+ }
+ }
+ return result;
+}
+
+TEST_F(AudioSystemTest, DevicesForRoleAndStrategy) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+
+ std::vector<struct audio_port_v7> devicePorts;
+ for (const auto& port : ports) {
+ if (port.type == AUDIO_PORT_TYPE_DEVICE && audio_is_output_device(port.ext.device.type)) {
+ devicePorts.push_back(port);
+ }
+ }
+ if (devicePorts.empty()) {
+ GTEST_SKIP() << "No output devices returned by the audio system";
+ }
+
+ AudioProductStrategyVector strategies;
+ EXPECT_EQ(OK, AudioSystem::listAudioProductStrategies(strategies));
+ if (strategies.empty()) {
+ GTEST_SKIP() << "No strategies returned by the audio system";
+ }
+
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+
+ bool hasStrategyForMedia = false;
+ AudioProductStrategy mediaStrategy;
+ for (const auto& strategy : strategies) {
+ if (!isPublicStrategy(strategy)) continue;
+
+ for (const auto& att : strategy.getAudioAttributes()) {
+ if (strategy.attributesMatches(att.getAttributes(), attributes)) {
+ hasStrategyForMedia = true;
+ mediaStrategy = strategy;
+ break;
+ }
+ }
+ }
+
+ if (!hasStrategyForMedia) {
+ GTEST_SKIP() << "No strategies returned for music media";
+ }
+
+ AudioDeviceTypeAddrVector devices;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndStrategy(PRODUCT_STRATEGY_NONE,
+ DEVICE_ROLE_PREFERRED, devices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_NONE, devices));
+ status = AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(), DEVICE_ROLE_PREFERRED,
+ devices);
+ if (status == NAME_NOT_FOUND) {
+ AudioDeviceTypeAddrVector outputDevices;
+ for (const auto& port : devicePorts) {
+ if (port.ext.device.type == AUDIO_DEVICE_OUT_SPEAKER) {
+ const AudioDeviceTypeAddr outputDevice(port.ext.device.type,
+ port.ext.device.address);
+ outputDevices.push_back(outputDevice);
+ }
+ }
+ EXPECT_EQ(OK, AudioSystem::setDevicesRoleForStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED, outputDevices));
+ EXPECT_EQ(OK, AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED, devices));
+ EXPECT_EQ(devices, outputDevices);
+ EXPECT_EQ(OK, AudioSystem::removeDevicesRoleForStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED));
+ EXPECT_EQ(NAME_NOT_FOUND, AudioSystem::getDevicesForRoleAndStrategy(
+ mediaStrategy.getId(), DEVICE_ROLE_PREFERRED, devices));
+ }
+}
+
+TEST_F(AudioSystemTest, VolumeIndexForAttributes) {
+ AudioVolumeGroupVector groups;
+ EXPECT_EQ(OK, AudioSystem::listAudioVolumeGroups(groups));
+ for (const auto& group : groups) {
+ if (group.getAudioAttributes().empty()) continue;
+ const audio_attributes_t attr = group.getAudioAttributes()[0];
+ if (attr == AUDIO_ATTRIBUTES_INITIALIZER) continue;
+ audio_stream_type_t streamType = AudioSystem::attributesToStreamType(attr);
+ if (streamType >= AUDIO_STREAM_PUBLIC_CNT) continue;
+
+ volume_group_t vg;
+ EXPECT_EQ(OK, AudioSystem::getVolumeGroupFromAudioAttributes(attr, vg));
+ EXPECT_EQ(group.getId(), vg);
+
+ int index;
+ EXPECT_EQ(OK,
+ AudioSystem::getVolumeIndexForAttributes(attr, index, AUDIO_DEVICE_OUT_SPEAKER));
+
+ int indexTest;
+ EXPECT_EQ(OK, AudioSystem::getStreamVolumeIndex(streamType, &indexTest,
+ AUDIO_DEVICE_OUT_SPEAKER));
+ EXPECT_EQ(index, indexTest);
+ }
+}
+
+TEST_F(AudioSystemTest, DevicesRoleForCapturePreset) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+
+ if (ports.empty()) {
+ GTEST_SKIP() << "No ports returned by the audio system";
+ }
+
+ audio_devices_t inDeviceA = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ audio_devices_t inDeviceB = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ for (const auto& port : ports) {
+ if (port.role != AUDIO_PORT_ROLE_SOURCE || port.type != AUDIO_PORT_TYPE_DEVICE) continue;
+ if (port.ext.device.type == inDeviceA) continue;
+ inDeviceB = port.ext.device.type;
+ break;
+ }
+ const audio_source_t audioSource = AUDIO_SOURCE_MIC;
+ const device_role_t role = DEVICE_ROLE_PREFERRED;
+ const AudioDeviceTypeAddr inputDevice(inDeviceA, "");
+ const AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ const AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ const AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+
+ // Test invalid device when setting
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::addDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+
+ // Test invalid role
+ AudioDeviceTypeAddrVector devices;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource,
+ DEVICE_ROLE_NONE, devices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE,
+ inputDevices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::addDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE,
+ inputDevices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::removeDevicesRoleForCapturePreset(
+ audioSource, DEVICE_ROLE_NONE, inputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::clearDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE));
+
+ // Without setting, call get/remove/clear must fail
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_TRUE(devices.empty());
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(NAME_NOT_FOUND, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+
+ // Test set/get devices role
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ ASSERT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices);
+
+ // Test setting will change the previously set devices
+ const AudioDeviceTypeAddr inputDevice2 = AudioDeviceTypeAddr(inDeviceB, "");
+ AudioDeviceTypeAddrVector inputDevices2 = {inputDevice2};
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices2));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices2);
+
+ // Test add devices
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::addDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(2, devices.size());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice) != devices.end());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice2) != devices.end());
+
+ // Test remove devices
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices2);
+
+ // Test remove devices that are not set as the device role
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+
+ // Test clear devices
+ EXPECT_EQ(NO_ERROR, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+ devices.clear();
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+
+ AudioDeviceTypeAddrVector inputDevices3 = {inputDevice, inputDevice2};
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices3));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(2, devices.size());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice) != devices.end());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice2) != devices.end());
+ EXPECT_EQ(NO_ERROR, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+}
+
+TEST_F(AudioSystemTest, UidDeviceAffinities) {
+ uid_t uid = getuid();
+
+ // Test invalid device for example audio_is_input_device
+ AudioDeviceTypeAddr inputDevice(AUDIO_DEVICE_IN_BUILTIN_MIC, "");
+ AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setUidDeviceAffinities(uid, inputDevices));
+
+ // Test valid device for example audio_is_output_device
+ AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+ EXPECT_EQ(NO_ERROR, AudioSystem::setUidDeviceAffinities(uid, outputDevices));
+ EXPECT_EQ(NO_ERROR, AudioSystem::removeUidDeviceAffinities(uid));
+}
+
+TEST_F(AudioSystemTest, UserIdDeviceAffinities) {
+ int userId = 200;
+
+ // Test invalid device for example audio_is_input_device
+ AudioDeviceTypeAddr inputDevice(AUDIO_DEVICE_IN_BUILTIN_MIC, "");
+ AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setUserIdDeviceAffinities(userId, inputDevices));
+
+ // Test valid device for ezample audio_is_output_device
+ AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+ EXPECT_EQ(NO_ERROR, AudioSystem::setUserIdDeviceAffinities(userId, outputDevices));
+ EXPECT_EQ(NO_ERROR, AudioSystem::removeUserIdDeviceAffinities(userId));
+}
diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp
new file mode 100644
index 0000000..8daba0a
--- /dev/null
+++ b/media/libaudioclient/tests/audiotrack_tests.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 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
+
+#include <gtest/gtest.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+TEST(AudioTrackTest, TestPlayTrack) {
+ const auto ap = sp<AudioPlayback>::make(44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_NONE,
+ AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ ap->stop();
+}
+
+TEST(AudioTrackTest, TestSeek) {
+ const auto ap = sp<AudioPlayback>::make(
+ 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess(true));
+ ap->stop();
+}
+
+TEST(AudioTrackTest, OffloadOrDirectPlayback) {
+ audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+ info.sample_rate = 44100;
+ info.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+ info.format = AUDIO_FORMAT_MP3;
+ info.stream_type = AUDIO_STREAM_MUSIC;
+ info.bit_rate = 192;
+ info.duration_us = 120 * 1000000; // 120 sec
+
+ audio_config_base_t config = {/* .sample_rate = */ info.sample_rate,
+ /* .channel_mask = */ info.channel_mask,
+ /* .format = */ AUDIO_FORMAT_PCM_16_BIT};
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.flags = AUDIO_FLAG_NONE;
+
+ if (!AudioTrack::isDirectOutputSupported(config, attributes) &&
+ AUDIO_OFFLOAD_NOT_SUPPORTED == AudioSystem::getOffloadSupport(info)) {
+ GTEST_SKIP() << "offload or direct playback is not supported";
+ }
+ sp<AudioPlayback> ap = nullptr;
+ if (AUDIO_OFFLOAD_NOT_SUPPORTED != AudioSystem::getOffloadSupport(info)) {
+ ap = sp<AudioPlayback>::make(info.sample_rate, info.format, info.channel_mask,
+ AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_OBTAIN, nullptr, &info);
+ } else {
+ ap = sp<AudioPlayback>::make(config.sample_rate, config.format, config.channel_mask,
+ AUDIO_OUTPUT_FLAG_DIRECT, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_OBTAIN);
+ }
+ ASSERT_NE(nullptr, ap);
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ audio_dual_mono_mode_t mode;
+ if (OK != ap->getAudioTrackHandle()->getDualMonoMode(&mode)) {
+ std::cerr << "no dual mono presentation is available" << std::endl;
+ }
+ if (OK != ap->getAudioTrackHandle()->setDualMonoMode(AUDIO_DUAL_MONO_MODE_LR)) {
+ std::cerr << "no dual mono presentation is available" << std::endl;
+ } else {
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->getDualMonoMode(&mode));
+ EXPECT_EQ(AUDIO_DUAL_MONO_MODE_LR, mode);
+ }
+ float leveldB;
+ if (OK != ap->getAudioTrackHandle()->getAudioDescriptionMixLevel(&leveldB)) {
+ std::cerr << "Audio Description mixing is unavailable" << std::endl;
+ }
+ if (OK != ap->getAudioTrackHandle()->setAudioDescriptionMixLevel(3.14f)) {
+ std::cerr << "Audio Description mixing is unavailable" << std::endl;
+ } else {
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->getAudioDescriptionMixLevel(&leveldB));
+ EXPECT_EQ(3.14f, leveldB);
+ }
+ AudioPlaybackRate audioRate;
+ audioRate = ap->getAudioTrackHandle()->getPlaybackRate();
+ std::cerr << "playback speed :: " << audioRate.mSpeed << std::endl
+ << "playback pitch :: " << audioRate.mPitch << std::endl;
+ audioRate.mSpeed = 2.0f;
+ audioRate.mPitch = 2.0f;
+ audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_VOICE;
+ audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_MUTE;
+ EXPECT_TRUE(isAudioPlaybackRateValid(audioRate));
+ if (OK != ap->getAudioTrackHandle()->setPlaybackRate(audioRate)) {
+ std::cerr << "unable to set playback rate parameters" << std::endl;
+ } else {
+ AudioPlaybackRate audioRateLocal;
+ audioRateLocal = ap->getAudioTrackHandle()->getPlaybackRate();
+ EXPECT_TRUE(isAudioPlaybackRateEqual(audioRate, audioRateLocal));
+ }
+ ap->stop();
+}
+
+TEST(AudioTrackTest, TestAudioCbNotifier) {
+ const auto ap = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->addAudioDeviceCallback(nullptr));
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ sp<OnAudioDeviceUpdateNotifier> cbOld = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
+ EXPECT_EQ(cb->mAudioIo, ap->getAudioTrackHandle()->getOutput());
+ EXPECT_EQ(cb->mDeviceId, ap->getAudioTrackHandle()->getRoutedDeviceId());
+ String8 keys;
+ keys = ap->getAudioTrackHandle()->getParameters(keys);
+ if (!keys.isEmpty()) {
+ std::cerr << "track parameters :: " << keys << std::endl;
+ }
+ EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->removeAudioDeviceCallback(nullptr));
+ EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb));
+ ap->stop();
+}
+
+class AudioTrackCreateTest
+ : public ::testing::TestWithParam<std::tuple<uint32_t, audio_format_t, audio_channel_mask_t,
+ audio_output_flags_t, audio_session_t>> {
+ public:
+ AudioTrackCreateTest()
+ : mSampleRate(std::get<0>(GetParam())),
+ mFormat(std::get<1>(GetParam())),
+ mChannelMask(std::get<2>(GetParam())),
+ mFlags(std::get<3>(GetParam())),
+ mSessionId(std::get<4>(GetParam())){};
+
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_output_flags_t mFlags;
+ const audio_session_t mSessionId;
+
+ sp<AudioPlayback> mAP;
+
+ virtual void SetUp() override {
+ mAP = sp<AudioPlayback>::make(mSampleRate, mFormat, mChannelMask, mFlags,
+ mSessionId);
+ ASSERT_NE(nullptr, mAP);
+ ASSERT_EQ(OK, mAP->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ ASSERT_EQ(OK, mAP->create()) << "track creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAP) mAP->stop();
+ }
+};
+
+TEST_P(AudioTrackCreateTest, TestCreateTrack) {
+ EXPECT_EQ(mFormat, mAP->getAudioTrackHandle()->format());
+ EXPECT_EQ(audio_channel_count_from_out_mask(mChannelMask),
+ mAP->getAudioTrackHandle()->channelCount());
+ if (mSampleRate != 0) EXPECT_EQ(mSampleRate, mAP->getAudioTrackHandle()->getSampleRate());
+ if (mSessionId != AUDIO_SESSION_NONE)
+ EXPECT_EQ(mSessionId, mAP->getAudioTrackHandle()->getSessionId());
+ EXPECT_EQ(mSampleRate, mAP->getAudioTrackHandle()->getOriginalSampleRate());
+ EXPECT_EQ(OK, mAP->start()) << "audio track start failed";
+ EXPECT_EQ(OK, mAP->onProcess());
+}
+
+// sampleRate, format, channelMask, flags, sessionId
+INSTANTIATE_TEST_SUITE_P(
+ AudioTrackParameterizedTest, AudioTrackCreateTest,
+ ::testing::Combine(::testing::Values(48000), ::testing::Values(AUDIO_FORMAT_PCM_16_BIT),
+ ::testing::Values(AUDIO_CHANNEL_OUT_STEREO),
+ ::testing::Values(AUDIO_OUTPUT_FLAG_NONE,
+ AUDIO_OUTPUT_FLAG_PRIMARY | AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_OUTPUT_FLAG_RAW | AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
+ ::testing::Values(AUDIO_SESSION_NONE)));
diff --git a/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw b/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw
new file mode 100644
index 0000000..2d1e4bf
--- /dev/null
+++ b/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw
Binary files differ
diff --git a/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw b/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw
new file mode 100644
index 0000000..c8ac5f7
--- /dev/null
+++ b/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw
Binary files differ
diff --git a/media/libaudioclient/tests/test_create_audiorecord.cpp b/media/libaudioclient/tests/test_create_audiorecord.cpp
index 2e0883b..277110b 100644
--- a/media/libaudioclient/tests/test_create_audiorecord.cpp
+++ b/media/libaudioclient/tests/test_create_audiorecord.cpp
@@ -29,14 +29,13 @@
#define NUM_ARGUMENTS 8
#define VERSION_VALUE "1.0"
-#define PACKAGE_NAME "AudioRecord test"
+#define PACKAGE_NAME "AudioRecord test"
namespace android {
using android::content::AttributionSourceState;
-int testRecord(FILE *inputFile, int outputFileFd)
-{
+int testRecord(FILE* inputFile, int outputFileFd) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
uint32_t testCount = 0;
Vector<String16> args;
@@ -47,11 +46,9 @@
attributionSource.token = sp<BBinder>::make();
if (inputFile == nullptr) {
- sp<AudioRecord> record = new AudioRecord(AUDIO_SOURCE_DEFAULT,
- 0 /* sampleRate */,
- AUDIO_FORMAT_DEFAULT,
- AUDIO_CHANNEL_IN_MONO,
- attributionSource);
+ sp<AudioRecord> record =
+ new AudioRecord(AUDIO_SOURCE_DEFAULT, 0 /* sampleRate */, AUDIO_FORMAT_DEFAULT,
+ AUDIO_CHANNEL_IN_MONO, attributionSource);
if (record == 0 || record->initCheck() != NO_ERROR) {
write(outputFileFd, "Error creating AudioRecord\n",
sizeof("Error creating AudioRecord\n"));
@@ -80,11 +77,10 @@
char statusStr[MAX_OUTPUT_FILE_LINE_LENGTH];
bool fast = false;
- if (sscanf(line, " %u %x %x %zu %d %x %u %u",
- &sampleRate, &format, &channelMask,
- &frameCount, ¬ificationFrames,
- &flags, &sessionId, &inputSource) != NUM_ARGUMENTS) {
- fprintf(stderr, "Malformed line for test #%u in input file\n", testCount+1);
+ if (sscanf(line, " %u %x %x %zu %d %x %u %u", &sampleRate, &format, &channelMask,
+ &frameCount, ¬ificationFrames, &flags, &sessionId,
+ &inputSource) != NUM_ARGUMENTS) {
+ fprintf(stderr, "Malformed line for test #%u in input file\n", testCount + 1);
ret = 1;
continue;
}
@@ -100,21 +96,10 @@
sp<AudioRecord> record = new AudioRecord(attributionSource);
const auto emptyCallback = sp<AudioRecord::IAudioRecordCallback>::make();
- record->set(AUDIO_SOURCE_DEFAULT,
- sampleRate,
- format,
- channelMask,
- frameCount,
- fast ? emptyCallback : nullptr,
- notificationFrames,
- false,
- sessionId,
- fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT,
- flags,
- getuid(),
- getpid(),
- &attributes,
- AUDIO_PORT_HANDLE_NONE);
+ record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount,
+ fast ? emptyCallback : nullptr, notificationFrames, false, sessionId,
+ fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT, flags,
+ getuid(), getpid(), &attributes, AUDIO_PORT_HANDLE_NONE);
status = record->initCheck();
sprintf(statusStr, "\n#### Test %u status %d\n", testCount, status);
write(outputFileFd, statusStr, strlen(statusStr));
@@ -126,11 +111,8 @@
return ret;
}
-}; // namespace android
+}; // namespace android
-
-int main(int argc, char **argv)
-{
+int main(int argc, char** argv) {
return android::main(argc, argv, android::testRecord);
}
-
diff --git a/media/libaudioclient/tests/test_create_audiotrack.cpp b/media/libaudioclient/tests/test_create_audiotrack.cpp
index e7231d3..4e09e21 100644
--- a/media/libaudioclient/tests/test_create_audiotrack.cpp
+++ b/media/libaudioclient/tests/test_create_audiotrack.cpp
@@ -32,18 +32,15 @@
namespace android {
-int testTrack(FILE *inputFile, int outputFileFd)
-{
+int testTrack(FILE* inputFile, int outputFileFd) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
uint32_t testCount = 0;
Vector<String16> args;
int ret = 0;
if (inputFile == nullptr) {
- sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_DEFAULT,
- 0 /* sampleRate */,
- AUDIO_FORMAT_DEFAULT,
- AUDIO_CHANNEL_OUT_STEREO);
+ sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_DEFAULT, 0 /* sampleRate */,
+ AUDIO_FORMAT_DEFAULT, AUDIO_CHANNEL_OUT_STEREO);
if (track == 0 || track->initCheck() != NO_ERROR) {
write(outputFileFd, "Error creating AudioTrack\n",
sizeof("Error creating AudioTrack\n"));
@@ -78,11 +75,10 @@
bool offload = false;
bool fast = false;
- if (sscanf(line, " %u %x %x %zu %d %u %x %u %u %u",
- &sampleRate, &format, &channelMask,
- &frameCount, ¬ificationFrames, &useSharedBuffer,
- &flags, &sessionId, &usage, &contentType) != NUM_ARGUMENTS) {
- fprintf(stderr, "Malformed line for test #%u in input file\n", testCount+1);
+ if (sscanf(line, " %u %x %x %zu %d %u %x %u %u %u", &sampleRate, &format, &channelMask,
+ &frameCount, ¬ificationFrames, &useSharedBuffer, &flags, &sessionId, &usage,
+ &contentType) != NUM_ARGUMENTS) {
+ fprintf(stderr, "Malformed line for test #%u in input file\n", testCount + 1);
ret = 1;
continue;
}
@@ -90,7 +86,7 @@
if (useSharedBuffer != 0) {
size_t heapSize = audio_channel_count_from_out_mask(channelMask) *
- audio_bytes_per_sample(format) * frameCount;
+ audio_bytes_per_sample(format) * frameCount;
heap = new MemoryDealer(heapSize, "AudioTrack Heap Base");
sharedBuffer = heap->allocate(heapSize);
frameCount = 0;
@@ -111,25 +107,13 @@
attributes.usage = usage;
sp<AudioTrack> track = new AudioTrack();
const auto emptyCallback = sp<AudioTrack::IAudioTrackCallback>::make();
- track->set(AUDIO_STREAM_DEFAULT,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- (fast || offload) ? emptyCallback : nullptr,
- notificationFrames,
- sharedBuffer,
- false,
- sessionId,
- ((fast && sharedBuffer == 0) || offload) ?
- AudioTrack::TRANSFER_CALLBACK : AudioTrack::TRANSFER_DEFAULT,
- offload ? &offloadInfo : nullptr,
- AttributionSourceState(),
- &attributes,
- false,
- 1.0f,
- AUDIO_PORT_HANDLE_NONE);
+ track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags,
+ (fast || offload) ? emptyCallback : nullptr, notificationFrames, sharedBuffer,
+ false, sessionId,
+ ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK
+ : AudioTrack::TRANSFER_DEFAULT,
+ offload ? &offloadInfo : nullptr, AttributionSourceState(), &attributes, false,
+ 1.0f, AUDIO_PORT_HANDLE_NONE);
status = track->initCheck();
sprintf(statusStr, "\n#### Test %u status %d\n", testCount, status);
write(outputFileFd, statusStr, strlen(statusStr));
@@ -141,11 +125,8 @@
return ret;
}
-}; // namespace android
+}; // namespace android
-
-int main(int argc, char **argv)
-{
+int main(int argc, char** argv) {
return android::main(argc, argv, android::testTrack);
}
-
diff --git a/media/libaudioclient/tests/test_create_utils.cpp b/media/libaudioclient/tests/test_create_utils.cpp
index caf5227..c2c2e8b 100644
--- a/media/libaudioclient/tests/test_create_utils.cpp
+++ b/media/libaudioclient/tests/test_create_utils.cpp
@@ -23,10 +23,10 @@
namespace android {
-int readLine(FILE *inputFile, char *line, int size) {
+int readLine(FILE* inputFile, char* line, int size) {
int ret = 0;
while (true) {
- char *str = fgets(line, size, inputFile);
+ char* str = fgets(line, size, inputFile);
if (str == nullptr) {
ret = -1;
break;
@@ -42,8 +42,7 @@
return ret;
}
-bool checkVersion(FILE *inputFile, const char *version)
-{
+bool checkVersion(FILE* inputFile, const char* version) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
char versionKey[MAX_INPUT_FILE_LINE_LENGTH];
char versionValue[MAX_INPUT_FILE_LINE_LENGTH];
@@ -68,9 +67,8 @@
return true;
}
-int main(int argc, char **argv, test_func_t testFunc)
-{
- FILE *inputFile = nullptr;
+int main(int argc, char** argv, test_func_t testFunc) {
+ FILE* inputFile = nullptr;
int outputFileFd = STDOUT_FILENO;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int ret = 0;
@@ -96,7 +94,7 @@
if (strcmp(*argv, "-o") == 0) {
argv++;
if (*argv) {
- outputFileFd = open(*argv, O_WRONLY|O_CREAT, mode);
+ outputFileFd = open(*argv, O_WRONLY | O_CREAT, mode);
if (outputFileFd < 0) {
ret = 1;
}
@@ -126,5 +124,4 @@
return ret;
}
-}; // namespace android
-
+}; // namespace android
diff --git a/media/libaudioclient/tests/test_create_utils.h b/media/libaudioclient/tests/test_create_utils.h
index 9a6f9fa..110baf7 100644
--- a/media/libaudioclient/tests/test_create_utils.h
+++ b/media/libaudioclient/tests/test_create_utils.h
@@ -27,13 +27,12 @@
namespace android {
-int readLine(FILE *inputFile, char *line, int size);
+int readLine(FILE* inputFile, char* line, int size);
-bool checkVersion(FILE *inputFile, const char *version);
+bool checkVersion(FILE* inputFile, const char* version);
+typedef int (*test_func_t)(FILE* inputFile, int outputFileFd);
-typedef int (*test_func_t)(FILE *inputFile, int outputFileFd);
+int main(int argc, char** argv, test_func_t testFunc);
-int main(int argc, char **argv, test_func_t testFunc);
-
-}; // namespace android
+}; // namespace android
diff --git a/media/libaudioclient/tests/trackplayerbase_tests.cpp b/media/libaudioclient/tests/trackplayerbase_tests.cpp
new file mode 100644
index 0000000..c9b704d
--- /dev/null
+++ b/media/libaudioclient/tests/trackplayerbase_tests.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 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 "TrackPlayerBaseTest"
+
+#include <gtest/gtest.h>
+
+#include <media/TrackPlayerBase.h>
+
+using namespace android;
+using namespace android::media;
+
+class TrackPlayer : public TrackPlayerBase, public AudioTrack::IAudioTrackCallback {
+ public:
+ // methods protected in base class
+ using TrackPlayerBase::playerPause;
+ using TrackPlayerBase::playerSetVolume;
+ using TrackPlayerBase::playerStart;
+ using TrackPlayerBase::playerStop;
+};
+
+class TrackPlayerBaseTest
+ : public ::testing::TestWithParam<std::tuple<double, double, uint32_t, uint32_t>> {
+ public:
+ TrackPlayerBaseTest()
+ : mDuration(std::get<0>(GetParam())),
+ mPulseFreq(std::get<1>(GetParam())),
+ mChannelCount(std::get<2>(GetParam())),
+ mSampleRate(std::get<3>(GetParam())){};
+
+ virtual void SetUp() override {
+ mFrameCount = mDuration * mSampleRate;
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(mChannelCount);
+ sp<AudioTrack> track = new AudioTrack(mStreamType, mSampleRate, mFormat, channelMask,
+ mFrameCount, mFlags, nullptr /* callback */,
+ 0 /* notificationFrames */, AUDIO_SESSION_NONE);
+ ASSERT_EQ(track->initCheck(), NO_ERROR);
+
+ mPlayer = new TrackPlayer();
+ mPlayer->init(track.get(), mPlayer, PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA,
+ AUDIO_SESSION_NONE);
+ sp<AudioTrack> playerTrack = mPlayer->mAudioTrack;
+ ASSERT_EQ(playerTrack->initCheck(), NO_ERROR);
+
+ mBufferSize = mFrameCount * playerTrack->frameSize();
+ mBuffer.resize(mBufferSize, 0);
+
+ // populate buffer
+ ASSERT_NE(mPulseFreq, 0);
+ int32_t nPulseSamples = mSampleRate / mPulseFreq;
+ int32_t pulseSize = nPulseSamples * playerTrack->frameSize();
+
+ int32_t marker = 0;
+ while (marker + pulseSize <= mBufferSize) {
+ memset(mBuffer.data() + marker, 127, pulseSize / 2);
+ marker += pulseSize;
+ }
+ }
+
+ void playBuffer() {
+ bool blocking = true;
+ ssize_t nbytes = mPlayer->mAudioTrack->write(mBuffer.data(), mBufferSize, blocking);
+ EXPECT_EQ(nbytes, mBufferSize) << "Did not write all data in blocking mode";
+ }
+
+ const double mDuration; // seconds
+ sp<TrackPlayer> mPlayer;
+
+ private:
+ const double mPulseFreq;
+ const uint32_t mChannelCount;
+ const uint32_t mSampleRate;
+
+ const audio_format_t mFormat = AUDIO_FORMAT_PCM_16_BIT;
+ const audio_output_flags_t mFlags = AUDIO_OUTPUT_FLAG_NONE;
+ const audio_stream_type_t mStreamType = AUDIO_STREAM_MUSIC;
+
+ int32_t mBufferSize;
+ int32_t mFrameCount;
+ std::vector<uint8_t> mBuffer;
+};
+
+class PlaybackTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(PlaybackTestParam, PlaybackTest) {
+ // no-op implementation
+ EXPECT_TRUE(mPlayer->setStartDelayMs(0).isOk());
+
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PlaybackTestParam,
+ ::testing::Values(std::make_tuple(2.5, 25.0, 2, 48000)));
+
+class ChangeVolumeTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(ChangeVolumeTestParam, ChangeVolumeTest) {
+ float volume = 1.0f;
+ (void)mPlayer->setPlayerVolume(volume / 2, volume);
+
+ ASSERT_TRUE(mPlayer->start().isOk());
+ ASSERT_EQ(mPlayer->playerSetVolume(), NO_ERROR);
+
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ EXPECT_TRUE(mPlayer->stop().isOk());
+
+ std::vector<float> setVol = {0.95f, 0.05f, 0.5f, 0.25f, -1.0f, 1.0f, 1.0f};
+ std::vector<float> setPan = {0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.5f, -0.5f};
+
+ ASSERT_TRUE(mPlayer->start().isOk());
+
+ for (int32_t i = 0; i < setVol.size(); i++) {
+ EXPECT_TRUE(mPlayer->setVolume(setVol[i]).isOk());
+ EXPECT_TRUE(mPlayer->setPan(setPan[i]).isOk());
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ }
+ EXPECT_TRUE(mPlayer->stop().isOk());
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, ChangeVolumeTestParam,
+ ::testing::Values(std::make_tuple(1.0, 100.0, 1, 24000)));
+
+class PauseTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(PauseTestParam, PauseTest) {
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ ASSERT_EQ(mPlayer->playerPause(), NO_ERROR);
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);
+
+ for (int32_t i = 0; i < 5; i++) {
+ ASSERT_TRUE(mPlayer->start().isOk());
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ ASSERT_TRUE(mPlayer->pause().isOk());
+ }
+ EXPECT_TRUE(mPlayer->stop().isOk());
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PauseTestParam,
+ ::testing::Values(std::make_tuple(1.0, 75.0, 2, 24000)));
diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp
index 159f898..a456a2a 100644
--- a/media/libaudiofoundation/Android.bp
+++ b/media/libaudiofoundation/Android.bp
@@ -27,12 +27,13 @@
"libaudio_system_headers",
"libmedia_helper_headers",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_export_static",
+ ],
static_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
],
export_static_lib_headers: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
],
host_supported: true,
@@ -57,8 +58,11 @@
"DeviceDescriptorBase.cpp",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_export_shared",
+ ],
+
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudioclient_aidl_conversion",
"libaudioutils",
@@ -70,7 +74,6 @@
],
export_shared_lib_headers: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudioclient_aidl_conversion",
],
diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index 5ffbffc..91efb96 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -126,7 +126,14 @@
"%*sEncapsulation modes: %u, metadata types: %u\n", spaces, "",
mEncapsulationModes, mEncapsulationMetadataTypes));
- AudioPort::dump(dst, spaces, nullptr, verbose);
+ std::string portStr;
+ AudioPort::dump(&portStr, spaces, nullptr, verbose);
+ if (!portStr.empty()) {
+ if (!mName.empty()) {
+ dst->append(base::StringPrintf("%*s", spaces, ""));
+ }
+ dst->append(portStr);
+ }
}
std::string DeviceDescriptorBase::toString(bool includeSensitiveInfo) const
diff --git a/media/libaudiofoundation/TEST_MAPPING b/media/libaudiofoundation/TEST_MAPPING
index efe8437..dbae9a0 100644
--- a/media/libaudiofoundation/TEST_MAPPING
+++ b/media/libaudiofoundation/TEST_MAPPING
@@ -2,12 +2,23 @@
"presubmit": [
{
"name": "audiofoundation_parcelable_test"
- },
+ }
+ ],
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/media/libaudiofoundation/include/media/AudioPort.h b/media/libaudiofoundation/include/media/AudioPort.h
index d6a098f..b1235f5 100644
--- a/media/libaudiofoundation/include/media/AudioPort.h
+++ b/media/libaudiofoundation/include/media/AudioPort.h
@@ -72,7 +72,7 @@
AudioProfileVector &getAudioProfiles() { return mProfiles; }
void setExtraAudioDescriptors(
- const std::vector<media::audio::common::ExtraAudioDescriptor> extraAudioDescriptors) {
+ const std::vector<media::audio::common::ExtraAudioDescriptor>& extraAudioDescriptors) {
mExtraAudioDescriptors = extraAudioDescriptors;
}
std::vector<media::audio::common::ExtraAudioDescriptor> &getExtraAudioDescriptors() {
diff --git a/media/libaudiofoundation/tests/Android.bp b/media/libaudiofoundation/tests/Android.bp
index 3f1fbea..0e733d3 100644
--- a/media/libaudiofoundation/tests/Android.bp
+++ b/media/libaudiofoundation/tests/Android.bp
@@ -10,6 +10,9 @@
cc_test {
name: "audiofoundation_parcelable_test",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
shared_libs: [
"libbase",
"libbinder",
@@ -18,7 +21,6 @@
],
static_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudioclient_aidl_conversion",
"libaudiofoundation",
diff --git a/media/libaudiohal/TEST_MAPPING b/media/libaudiohal/TEST_MAPPING
index 3de5a9f..9aff137 100644
--- a/media/libaudiohal/TEST_MAPPING
+++ b/media/libaudiohal/TEST_MAPPING
@@ -1,10 +1,19 @@
{
- "presubmit": [
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp
index fdfe225..8743c04 100644
--- a/media/libaudiohal/impl/EffectHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectHalHidl.cpp
@@ -93,13 +93,13 @@
}
status_t EffectHalHidl::process() {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS));
}
status_t EffectHalHidl::processReverse() {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE));
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index d7217fc..3470380 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -17,6 +17,9 @@
#define LOG_TAG "EffectsFactoryHalHidl"
//#define LOG_NDEBUG 0
+#include <optional>
+#include <tuple>
+
#include <cutils/native_handle.h>
#include <UuidUtils.h>
@@ -38,51 +41,71 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
+class EffectDescriptorCache {
+ public:
+ using QueryResult = std::tuple<Return<void>, Result, hidl_vec<EffectDescriptor>>;
+ QueryResult queryAllDescriptors(IEffectsFactory* effectsFactory);
+ private:
+ std::mutex mLock;
+ std::optional<hidl_vec<EffectDescriptor>> mLastDescriptors; // GUARDED_BY(mLock)
+};
+
+EffectDescriptorCache::QueryResult EffectDescriptorCache::queryAllDescriptors(
+ IEffectsFactory* effectsFactory) {
+ {
+ std::lock_guard l(mLock);
+ if (mLastDescriptors.has_value()) {
+ return {::android::hardware::Void(), Result::OK, mLastDescriptors.value()};
+ }
+ }
+ Result retval = Result::NOT_INITIALIZED;
+ hidl_vec<EffectDescriptor> descriptors;
+ Return<void> ret = effectsFactory->getAllDescriptors(
+ [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ descriptors = result;
+ }
+ });
+ if (ret.isOk() && retval == Result::OK) {
+ std::lock_guard l(mLock);
+ mLastDescriptors = descriptors;
+ }
+ return {std::move(ret), retval, std::move(descriptors)};
+}
+
EffectsFactoryHalHidl::EffectsFactoryHalHidl(sp<IEffectsFactory> effectsFactory)
- : EffectConversionHelperHidl("EffectsFactory") {
+ : EffectConversionHelperHidl("EffectsFactory"), mCache(new EffectDescriptorCache) {
ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL");
mEffectsFactory = effectsFactory;
}
-status_t EffectsFactoryHalHidl::queryAllDescriptors() {
- if (mEffectsFactory == 0) return NO_INIT;
- Result retval = Result::NOT_INITIALIZED;
- Return<void> ret = mEffectsFactory->getAllDescriptors(
- [&](Result r, const hidl_vec<EffectDescriptor>& result) {
- retval = r;
- if (retval == Result::OK) {
- mLastDescriptors = result;
- }
- });
- if (ret.isOk()) {
- return retval == Result::OK ? OK : NO_INIT;
- }
- mLastDescriptors.resize(0);
- return processReturn(__FUNCTION__, ret);
-}
-
status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) {
- status_t queryResult = queryAllDescriptors();
- if (queryResult == OK) {
- *pNumEffects = mLastDescriptors.size();
+ if (mEffectsFactory == 0) return NO_INIT;
+ auto [ret, retval, descriptors] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (ret.isOk() && retval == Result::OK) {
+ *pNumEffects = descriptors.size();
+ return OK;
+ } else if (ret.isOk()) {
+ return NO_INIT;
}
- return queryResult;
+ return processReturn(__FUNCTION__, ret);
}
status_t EffectsFactoryHalHidl::getDescriptor(
uint32_t index, effect_descriptor_t *pDescriptor) {
- // TODO: We need somehow to track the changes on the server side
- // or figure out how to convert everybody to query all the descriptors at once.
if (pDescriptor == nullptr) {
return BAD_VALUE;
}
- if (mLastDescriptors.size() == 0) {
- status_t queryResult = queryAllDescriptors();
- if (queryResult != OK) return queryResult;
+ if (mEffectsFactory == 0) return NO_INIT;
+ auto [ret, retval, descriptors] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (ret.isOk() && retval == Result::OK) {
+ if (index >= descriptors.size()) return NAME_NOT_FOUND;
+ EffectUtils::effectDescriptorToHal(descriptors[index], pDescriptor);
+ } else if (ret.isOk()) {
+ return NO_INIT;
}
- if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND;
- EffectUtils::effectDescriptorToHal(mLastDescriptors[index], pDescriptor);
- return OK;
+ return processReturn(__FUNCTION__, ret);
}
status_t EffectsFactoryHalHidl::getDescriptor(
@@ -114,21 +137,15 @@
if (pEffectType == nullptr || descriptors == nullptr) {
return BAD_VALUE;
}
+ if (mEffectsFactory == 0) return NO_INIT;
- uint32_t numEffects = 0;
- status_t status = queryNumberEffects(&numEffects);
- if (status != NO_ERROR) {
- ALOGW("%s error %d from FactoryHal queryNumberEffects", __func__, status);
- return status;
+ auto [ret, retval, hidlDescs] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (!ret.isOk() || retval != Result::OK) {
+ return processReturn(__FUNCTION__, ret, retval);
}
-
- for (uint32_t i = 0; i < numEffects; i++) {
+ for (const auto& hidlDesc : hidlDescs) {
effect_descriptor_t descriptor;
- status = getDescriptor(i, &descriptor);
- if (status != NO_ERROR) {
- ALOGW("%s error %d from FactoryHal getDescriptor", __func__, status);
- continue;
- }
+ EffectUtils::effectDescriptorToHal(hidlDesc, &descriptor);
if (memcmp(&descriptor.type, pEffectType, sizeof(effect_uuid_t)) == 0) {
descriptors->push_back(descriptor);
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index e1882e1..7ccddc6 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
+#include <memory>
+
#include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h)
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -28,6 +30,8 @@
using ::android::hardware::hidl_vec;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
+class EffectDescriptorCache;
+
class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public EffectConversionHelperHidl
{
public:
@@ -63,9 +67,7 @@
private:
sp<IEffectsFactory> mEffectsFactory;
- hidl_vec<EffectDescriptor> mLastDescriptors;
-
- status_t queryAllDescriptors();
+ std::unique_ptr<EffectDescriptorCache> mCache;
};
} // namespace effect
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 021ec51..76f9a60 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -441,7 +441,7 @@
#endif
status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
*written = 0;
@@ -587,7 +587,7 @@
}
status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getRenderPosition(
@@ -668,7 +668,7 @@
}
status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
if (mWriterClient == gettid() && mCommandMQ) {
return callWriterThread(
@@ -1012,7 +1012,7 @@
}
status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
*read = 0;
@@ -1146,7 +1146,7 @@
}
status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
if (mReaderClient == gettid() && mCommandMQ) {
ReadParameters params;
diff --git a/media/libaudioprocessing/TEST_MAPPING b/media/libaudioprocessing/TEST_MAPPING
index 3de5a9f..9aff137 100644
--- a/media/libaudioprocessing/TEST_MAPPING
+++ b/media/libaudioprocessing/TEST_MAPPING
@@ -1,10 +1,19 @@
{
- "presubmit": [
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index 3137e13..d8cf20e 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -22,6 +22,7 @@
#include <algorithm>
#include <memory>
+#include <sstream>
#include <string>
#include <utility>
@@ -94,6 +95,33 @@
return defaultValue;
}
+std::string hapticParamToString(const struct HapticGeneratorParam& param) {
+ std::stringstream ss;
+ ss << "\t\tHapticGenerator Parameters:\n";
+ ss << "\t\t- resonant frequency: " << param.resonantFrequency << '\n';
+ ss << "\t\t- bpf Q: " << param.bpfQ << '\n';
+ ss << "\t\t- slow env normalization power: " << param.slowEnvNormalizationPower << '\n';
+ ss << "\t\t- bsf zero Q: " << param.bsfZeroQ << '\n';
+ ss << "\t\t- bsf pole Q: " << param.bsfPoleQ << '\n';
+ ss << "\t\t- distortion corner frequency: " << param.distortionCornerFrequency << '\n';
+ ss << "\t\t- distortion input gain: " << param.distortionInputGain << '\n';
+ ss << "\t\t- distortion cube threshold: " << param.distortionCubeThreshold << '\n';
+ ss << "\t\t- distortion output gain: " << param.distortionOutputGain << '\n';
+ return ss.str();
+}
+
+std::string hapticSettingToString(const struct HapticGeneratorParam& param) {
+ std::stringstream ss;
+ ss << "\t\tHaptic setting:\n";
+ ss << "\t\t- tracks intensity map:\n";
+ for (const auto&[id, intensity] : param.id2Intensity) {
+ ss << "\t\t\t- id=" << id << ", intensity=" << (int) intensity;
+ }
+ ss << "\t\t- max intensity: " << (int) param.maxHapticIntensity << '\n';
+ ss << "\t\t- max haptic amplitude: " << param.maxHapticAmplitude << '\n';
+ return ss.str();
+}
+
int HapticGenerator_Init(struct HapticGeneratorContext *context) {
context->itfe = &gHapticGeneratorInterface;
@@ -129,7 +157,7 @@
context->param.distortionCubeThreshold = 0.1f;
context->param.distortionOutputGain = getFloatProperty(
"vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
- ALOGD("Using distortion output gain as %f", context->param.distortionOutputGain);
+ ALOGD("%s\n%s", __func__, hapticParamToString(context->param).c_str());
context->state = HAPTICGENERATOR_STATE_INITIALIZED;
return 0;
@@ -289,6 +317,7 @@
}
int id = *(int *) value;
os::HapticScale hapticIntensity = static_cast<os::HapticScale>(*((int *) value + 1));
+ ALOGD("Setting haptic intensity as %d", hapticIntensity);
if (hapticIntensity == os::HapticScale::MUTE) {
context->param.id2Intensity.erase(id);
} else {
@@ -313,6 +342,10 @@
context->param.bsfZeroQ = isnan(qFactor) ? DEFAULT_BSF_POLE_Q : qFactor;
context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
context->param.maxHapticAmplitude = maxAmplitude;
+ ALOGD("Updating vibrator info, resonantFrequency=%f, bsfZeroQ=%f, bsfPoleQ=%f, "
+ "maxHapticAmplitude=%f",
+ context->param.resonantFrequency, context->param.bsfZeroQ, context->param.bsfPoleQ,
+ context->param.maxHapticAmplitude);
if (context->processorsRecord.bpf != nullptr) {
context->processorsRecord.bpf->setCoefficients(
@@ -358,6 +391,11 @@
return in;
}
+void HapticGenerator_Dump(int32_t fd, const struct HapticGeneratorParam& param) {
+ dprintf(fd, "%s", hapticParamToString(param).c_str());
+ dprintf(fd, "%s", hapticSettingToString(param).c_str());
+}
+
} // namespace (anonymous)
//-----------------------------------------------------------------------------
@@ -562,6 +600,10 @@
case EFFECT_CMD_SET_AUDIO_MODE:
break;
+ case EFFECT_CMD_DUMP:
+ HapticGenerator_Dump(*(reinterpret_cast<int32_t*>(cmdData)), context->param);
+ break;
+
default:
ALOGW("HapticGenerator_Command invalid command %u", cmdCode);
return -EINVAL;
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 7d7f8b9..0568fbd 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -11,48 +11,33 @@
cc_test {
name: "EffectReverbTest",
- vendor: true,
- gtest: true,
- host_supported: true,
+ defaults: [
+ "libeffects-test-defaults",
+ ],
srcs: [
"EffectReverbTest.cpp",
- "EffectTestHelper.cpp",
],
static_libs: [
- "libaudioutils",
"libreverb",
"libreverbwrapper",
],
- shared_libs: [
- "liblog",
- ],
header_libs: [
"libaudioeffects",
- "libhardware_headers",
],
}
cc_test {
name: "EffectBundleTest",
- vendor: true,
- gtest: true,
- host_supported: true,
- test_suites: ["device-tests"],
+ defaults: [
+ "libeffects-test-defaults",
+ ],
srcs: [
"EffectBundleTest.cpp",
- "EffectTestHelper.cpp",
],
static_libs: [
- "libaudioutils",
"libbundlewrapper",
"libmusicbundle",
],
- shared_libs: [
- "liblog",
- ],
- header_libs: [
- "libhardware_headers",
- ],
}
cc_test {
diff --git a/media/libeffects/spatializer/benchmarks/Android.bp b/media/libeffects/spatializer/benchmarks/Android.bp
new file mode 100644
index 0000000..ab7e468
--- /dev/null
+++ b/media/libeffects/spatializer/benchmarks/Android.bp
@@ -0,0 +1,21 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_benchmark {
+ name: "spatializer_benchmark",
+ vendor: true,
+ srcs: ["spatializer_benchmark.cpp"],
+ shared_libs: [
+ "libaudioutils",
+ "liblog",
+ ],
+ header_libs: [
+ "libhardware_headers",
+ ],
+}
diff --git a/media/libeffects/spatializer/benchmarks/spatializer_benchmark.cpp b/media/libeffects/spatializer/benchmarks/spatializer_benchmark.cpp
new file mode 100644
index 0000000..e8ac480
--- /dev/null
+++ b/media/libeffects/spatializer/benchmarks/spatializer_benchmark.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include <array>
+#include <dlfcn.h>
+#include <random>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include <hardware/audio_effect.h>
+#include <log/log.h>
+
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = [] {
+ audio_effect_library_t symbol{};
+ void* effectLib = dlopen("libspatialaudio.so", RTLD_NOW);
+ if (effectLib) {
+ audio_effect_library_t* effectInterface =
+ (audio_effect_library_t*)dlsym(effectLib, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
+ if (effectInterface == nullptr) {
+ ALOGE("dlsym failed: %s", dlerror());
+ exit(-1);
+ }
+ symbol = (audio_effect_library_t)(*effectInterface);
+ } else {
+ ALOGE("dlopen failed: %s", dlerror());
+ exit(-1);
+ }
+ return symbol;
+}();
+
+// channel masks
+constexpr int kInputChMask = AUDIO_CHANNEL_OUT_5POINT1;
+
+// sampleRates
+constexpr size_t kSampleRates[] = {
+ 44100,
+ 48000,
+ 96000,
+};
+constexpr size_t kNumSampleRates = std::size(kSampleRates);
+
+// duration in ms
+constexpr size_t kDurations[] = {2, 5, 10};
+constexpr size_t kNumDurations = std::size(kDurations);
+
+// effect uuids
+constexpr effect_uuid_t kEffectUuid = {
+ 0xcc4677de, 0xff72, 0x11eb, 0x9a03, {0x02, 0x42, 0xac, 0x13, 0x00, 0x03}};
+
+constexpr float kMinAmplitude = -1.0f;
+constexpr float kMaxAmplitude = 1.0f;
+
+/*******************************************************************
+ * A test result running on Pixel 5 for comparison.
+ * The first parameter indicates the sample rate.
+ * 0: 44100, 1: 48000, 2: 96000
+ * The second parameter indicates the duration in ms.
+ * 0: 2, 1: 5, 2: 10
+ * -------------------------------------------------------------
+ * Benchmark Time CPU Iterations
+ * -------------------------------------------------------------
+ * BM_SPATIALIZER/0/0 739848 ns 738497 ns 934
+ * BM_SPATIALIZER/0/1 1250503 ns 1248337 ns 480
+ * BM_SPATIALIZER/0/2 2094092 ns 2090092 ns 310
+ * BM_SPATIALIZER/1/0 783114 ns 781626 ns 683
+ * BM_SPATIALIZER/1/1 1332951 ns 1330473 ns 452
+ * BM_SPATIALIZER/1/2 2258313 ns 2254022 ns 289
+ * BM_SPATIALIZER/2/0 1210332 ns 1207957 ns 477
+ * BM_SPATIALIZER/2/1 2356259 ns 2351764 ns 269
+ * BM_SPATIALIZER/2/2 4267814 ns 4259567 ns 155
+ *******************************************************************/
+
+static void BM_SPATIALIZER(benchmark::State& state) {
+ const size_t sampleRate = kSampleRates[state.range(0)];
+ const size_t durationMs = kDurations[state.range(1)];
+ const size_t frameCount = durationMs * sampleRate / 1000;
+ const size_t inputChannelCount = audio_channel_count_from_out_mask(kInputChMask);
+ const size_t outputChannelCount = audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
+
+ // Initialize input buffer with deterministic pseudo-random values
+ std::minstd_rand gen(kInputChMask);
+ std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
+ std::vector<float> input(frameCount * inputChannelCount);
+ for (auto& in : input) {
+ in = dis(gen);
+ }
+
+ effect_handle_t effectHandle = nullptr;
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kEffectUuid, 1 /* sessionId */,
+ 1 /* ioId */, &effectHandle);
+ status != 0) {
+ ALOGE("create_effect returned an error = %d\n", status);
+ return;
+ }
+
+ effect_config_t config{};
+ config.inputCfg.samplingRate = config.outputCfg.samplingRate = sampleRate;
+ config.inputCfg.channels = kInputChMask;
+ config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ if (int status = (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
+ &config, &replySize, &reply);
+ status != 0) {
+ ALOGE("command returned an error = %d\n", status);
+ return;
+ }
+
+ if (int status = (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_ENABLE, sizeof(effect_config_t),
+ &config, &replySize, &reply);
+ status != 0) {
+ ALOGE("command returned an error = %d\n", status);
+ return;
+ }
+
+ // Run the test
+ std::vector<float> output(frameCount * outputChannelCount);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(input.data());
+ benchmark::DoNotOptimize(output.data());
+
+ audio_buffer_t inBuffer = {.frameCount = frameCount, .f32 = input.data()};
+ audio_buffer_t outBuffer = {.frameCount = frameCount, .f32 = output.data()};
+ (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
+
+ benchmark::ClobberMemory();
+ }
+
+ state.SetComplexityN(frameCount);
+
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
+ ALOGE("release_effect returned an error = %d\n", status);
+ return;
+ }
+}
+
+static void SPATIALIZERArgs(benchmark::internal::Benchmark* b) {
+ for (int i = 0; i < kNumSampleRates; i++) {
+ for (int j = 0; j < kNumDurations; ++j) {
+ b->Args({i, j});
+ }
+ }
+}
+
+BENCHMARK(BM_SPATIALIZER)->Apply(SPATIALIZERArgs);
+
+BENCHMARK_MAIN();
diff --git a/media/libeffects/spatializer/tests/Android.bp b/media/libeffects/spatializer/tests/Android.bp
new file mode 100644
index 0000000..704e873
--- /dev/null
+++ b/media/libeffects/spatializer/tests/Android.bp
@@ -0,0 +1,21 @@
+// Build the unit tests for spatializer effect
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+ name: "SpatializerTest",
+ defaults: [
+ "libeffects-test-defaults",
+ ],
+ host_supported: false,
+ srcs: [
+ "SpatializerTest.cpp",
+ ],
+}
diff --git a/media/libeffects/spatializer/tests/SpatializerTest.cpp b/media/libeffects/spatializer/tests/SpatializerTest.cpp
new file mode 100644
index 0000000..110fbb1
--- /dev/null
+++ b/media/libeffects/spatializer/tests/SpatializerTest.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2022 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 "SpatializerTest"
+
+#include <system/audio_effects/effect_spatializer.h>
+#include "EffectTestHelper.h"
+
+using namespace android;
+
+// relying on dlsym to fill the interface context
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = [] {
+ audio_effect_library_t symbol{};
+ void* effectLib = dlopen("libspatialaudio.so", RTLD_NOW);
+ if (effectLib) {
+ audio_effect_library_t* effectInterface =
+ (audio_effect_library_t*)dlsym(effectLib, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
+ if (effectInterface == nullptr) {
+ ALOGE("dlsym failed: %s", dlerror());
+ exit(-1);
+ }
+ symbol = (audio_effect_library_t)(*effectInterface);
+ } else {
+ ALOGE("dlopen failed: %s", dlerror());
+ exit(-1);
+ }
+ return symbol;
+}();
+
+// channel masks
+constexpr audio_channel_mask_t kSpatializerChMasks[] = {
+ AUDIO_CHANNEL_OUT_5POINT1,
+};
+constexpr size_t kNumSpatializerChMasks = std::size(kSpatializerChMasks);
+
+// sampleRates
+// TODO(b/234170025): Add all sampling rates once they are handled by spatializer
+constexpr int kSpatializerSampleRates[] = {44100, 48000, 96000};
+constexpr size_t kNumSpatializerSampleRates = std::size(kSpatializerSampleRates);
+
+// frame counts
+// TODO(b/234620538): Add sizes smaller than 80 once they are handled by spatializer
+constexpr size_t kSpatializerFrameCounts[] = {4800, 1920, 480, 80};
+constexpr size_t kNumSpatializerFrameCounts = std::size(kSpatializerFrameCounts);
+
+// effect uuids
+constexpr effect_uuid_t kSpatializerEffectUuids[] = {
+ {0xcc4677de, 0xff72, 0x11eb, 0x9a03, {0x02, 0x42, 0xac, 0x13, 0x00, 0x03}},
+};
+const size_t kNumSpatializerEffectUuids = std::size(kSpatializerEffectUuids);
+
+constexpr float kMinAmplitude = -1.0f;
+constexpr float kMaxAmplitude = 1.0f;
+constexpr float kSNRThreshold = 100.0f;
+constexpr size_t kNumBufferSplits = 2;
+
+using SingleEffectTestParam = std::tuple<int, int, int, int, int>;
+
+class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
+ public:
+ SingleEffectTest()
+ : mInputChMask(kSpatializerChMasks[std::get<0>(GetParam())]),
+ mInputChannelCount(audio_channel_count_from_out_mask(mInputChMask)),
+ mOutputChMask(AUDIO_CHANNEL_OUT_STEREO),
+ mOutputChannelCount(audio_channel_count_from_out_mask(mOutputChMask)),
+ mSampleRate(kSpatializerSampleRates[std::get<1>(GetParam())]),
+ mFrameCount(kSpatializerFrameCounts[std::get<2>(GetParam())]),
+ mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
+ mTotalFrameCount(mFrameCount * mLoopCount),
+ mUuid(&kSpatializerEffectUuids[std::get<4>(GetParam())]) {}
+ void SetUp() override {
+ ASSERT_EQ(AUDIO_EFFECT_LIBRARY_TAG, AUDIO_EFFECT_LIBRARY_INFO_SYM.tag)
+ << "Invalid effect tag";
+ }
+ const size_t mInputChMask;
+ const size_t mInputChannelCount;
+ const size_t mOutputChMask;
+ const size_t mOutputChannelCount;
+ const size_t mSampleRate;
+ const size_t mFrameCount;
+ const size_t mLoopCount;
+ const size_t mTotalFrameCount;
+ const effect_uuid_t* mUuid;
+};
+
+// Test basic spatializer functionality (does not crash) for various combinations of sampling
+// rates, channel masks and frame counts.
+TEST_P(SingleEffectTest, SimpleProcess) {
+ SCOPED_TRACE(testing::Message()
+ << "chMask: " << mInputChMask << " sampleRate: " << mSampleRate);
+
+ EffectTestHelper effect(mUuid, mInputChMask, mOutputChMask, mSampleRate, mFrameCount,
+ mLoopCount);
+ ASSERT_NO_FATAL_FAILURE(effect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(effect.setConfig());
+
+ // Initialize input buffer with deterministic pseudo-random values
+ std::vector<float> input(mTotalFrameCount * mInputChannelCount);
+ std::vector<float> output(mTotalFrameCount * mOutputChannelCount);
+ std::minstd_rand gen(mInputChMask);
+ std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
+ for (auto& in : input) {
+ in = dis(gen);
+ }
+ ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
+ ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
+}
+
+INSTANTIATE_TEST_SUITE_P(SpatializerTest, SingleEffectTest,
+ ::testing::Combine(::testing::Range(0, (int)kNumSpatializerChMasks),
+ ::testing::Range(0, (int)kNumSpatializerSampleRates),
+ ::testing::Range(0, (int)kNumSpatializerFrameCounts),
+ ::testing::Range(0,
+ (int)EffectTestHelper::kNumLoopCounts),
+ ::testing::Range(0, (int)kNumSpatializerEffectUuids)));
+
+using SingleEffectComparisonTestParam = std::tuple<int, int, int>;
+
+class SingleEffectComparisonTest
+ : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
+ public:
+ SingleEffectComparisonTest()
+ : mInputChMask(kSpatializerChMasks[std::get<0>(GetParam())]),
+ mInputChannelCount(audio_channel_count_from_out_mask(mInputChMask)),
+ mOutputChMask(AUDIO_CHANNEL_OUT_STEREO),
+ mOutputChannelCount(audio_channel_count_from_out_mask(mOutputChMask)),
+ mSampleRate(kSpatializerSampleRates[std::get<1>(GetParam())]),
+ mUuid(&kSpatializerEffectUuids[std::get<2>(GetParam())]) {}
+
+ const size_t mInputChMask;
+ const size_t mInputChannelCount;
+ const size_t mOutputChMask;
+ const size_t mOutputChannelCount;
+ const size_t mSampleRate;
+ const effect_uuid_t* mUuid;
+};
+
+// Ensure that effect produces similar output when an input is fed in a single call
+// or called multiples times with buffer split into smaller parts
+
+// TODO(b/234619903): This is currently disabled as output from the spatializer has
+// an algorithm delay that varies with frame count and hence makes it tricky to
+// compare output from two cases with different frame counts.
+// Feed valid input to spatializer and dump the output to verify spatializer is being
+// correctly initialized and once that is verified, enable the following
+TEST_P(SingleEffectComparisonTest, DISABLED_SimpleProcess) {
+ SCOPED_TRACE(testing::Message()
+ << "chMask: " << mInputChMask << " sampleRate: " << mSampleRate);
+ int testDurationMs = 20; // 20 ms
+ int testFrameCount = (mSampleRate * testDurationMs) / 1000;
+ int totalFrameCount = testFrameCount * kNumBufferSplits;
+ size_t totalInSamples = totalFrameCount * mInputChannelCount;
+ size_t totalOutSamples = totalFrameCount * mOutputChannelCount;
+ std::vector<float> input(totalInSamples);
+ std::vector<float> outRef(totalOutSamples);
+ std::vector<float> outTest(totalOutSamples);
+
+ // Initialize input buffer with deterministic pseudo-random values
+ std::minstd_rand gen(mInputChMask);
+ std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
+ for (auto& in : input) {
+ in = dis(gen);
+ }
+
+ EffectTestHelper refEffect(mUuid, mInputChMask, mOutputChMask, mSampleRate, totalFrameCount, 1);
+ ASSERT_NO_FATAL_FAILURE(refEffect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(refEffect.setConfig());
+ ASSERT_NO_FATAL_FAILURE(refEffect.process(input.data(), outRef.data()));
+ ASSERT_NO_FATAL_FAILURE(refEffect.releaseEffect());
+
+ EffectTestHelper testEffect(mUuid, mInputChMask, mOutputChMask, mSampleRate,
+ totalFrameCount / kNumBufferSplits, kNumBufferSplits);
+ ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
+ ASSERT_NO_FATAL_FAILURE(testEffect.process(input.data(), outTest.data()));
+ ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
+
+ float snr = computeSnr(outTest.data(), outRef.data(), totalOutSamples);
+ ASSERT_GT(snr, kSNRThreshold) << "SNR between reference and test output " << snr
+ << " is lower than required " << kSNRThreshold;
+}
+
+INSTANTIATE_TEST_SUITE_P(SpatializerTest, SingleEffectComparisonTest,
+ ::testing::Combine(::testing::Range(0, (int)kNumSpatializerChMasks),
+ ::testing::Range(0, (int)kNumSpatializerSampleRates),
+ ::testing::Range(0, (int)kNumSpatializerEffectUuids)));
+
+// This test checks if get/set Spatializer effect params are in accordance with documentation. The
+// test doesn't validate the functionality of the params configured. It only checks the return
+// status of API calls.
+TEST(ParameterTests, CheckParameterSupport) {
+ EffectTestHelper effect(&kSpatializerEffectUuids[0], kSpatializerChMasks[0],
+ AUDIO_CHANNEL_OUT_STEREO, kSpatializerSampleRates[0],
+ kSpatializerFrameCounts[0], EffectTestHelper::kLoopCounts[0]);
+ ASSERT_NO_FATAL_FAILURE(effect.createEffect());
+
+ // capture list of channel masks supported
+ std::vector<audio_channel_mask_t> channelMasks;
+ int status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS, channelMasks);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, channelMasks.size());
+ EXPECT_EQ(AUDIO_CHANNEL_OUT_5POINT1, channelMasks[0]);
+ }
+
+ // capture list of spatialization levels supported
+ std::vector<int8_t> spatializationLevels;
+ status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_LEVELS, spatializationLevels);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, spatializationLevels.size());
+ EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, spatializationLevels[0]);
+ }
+
+ // capture list of spatialization modes supported
+ std::vector<int8_t> spatializationModes;
+ status = effect.getParam<true>(SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES,
+ spatializationModes);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, spatializationModes.size());
+ EXPECT_EQ(SPATIALIZATION_MODE_BINAURAL, spatializationModes[0]);
+ }
+
+ // check if head tracking is supported
+ std::vector<int8_t> headTracking;
+ status = effect.getParam<false>(SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED, headTracking);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, headTracking.size());
+ EXPECT_EQ(true, headTracking[0]);
+ }
+
+ // verify spatialization level setting
+ std::vector<int8_t> level;
+ status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, level.size());
+ EXPECT_EQ(SPATIALIZATION_LEVEL_NONE, level[0]);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(effect.setConfig());
+
+ status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, level.size());
+ EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, level[0]);
+ }
+
+ // try setting unsupported parameters
+ level.clear();
+ level.push_back(SPATIALIZATION_LEVEL_MCHAN_BED_PLUS_OBJECTS);
+ ASSERT_EQ(1, level.size());
+ EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_LEVEL, level));
+
+ // Ensure that unsupported level isn't set by above setParam
+ status = effect.getParam<false>(SPATIALIZER_PARAM_LEVEL, level);
+ EXPECT_EQ(status, 0) << "get Param returned an error " << status;
+ if (!status) {
+ EXPECT_EQ(1, level.size());
+ EXPECT_EQ(SPATIALIZATION_LEVEL_MULTICHANNEL, level[0]);
+ }
+
+ std::vector<float> hingeAngle = {3.1415f};
+ ASSERT_EQ(1, hingeAngle.size());
+ EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_HINGE_ANGLE, hingeAngle));
+
+ std::vector<int8_t> headTrackingMode = {2}; // RELATIVE_WORLD
+ ASSERT_EQ(1, headTrackingMode.size());
+ EXPECT_NE(0, effect.setParam(SPATIALIZER_PARAM_HEADTRACKING_MODE, headTrackingMode));
+
+ // try setting supported parameters
+ std::vector<float> vectorFloat = {0.1, 0.2, 0.15, 0.04, 2.23, 3.14};
+ ASSERT_EQ(6, vectorFloat.size());
+ EXPECT_EQ(0, effect.setParam(SPATIALIZER_PARAM_HEAD_TO_STAGE, vectorFloat));
+
+ ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGD("Test result = %d\n", status);
+ return status;
+}
diff --git a/media/libeffects/tests/common/Android.bp b/media/libeffects/tests/common/Android.bp
new file mode 100644
index 0000000..73179fb
--- /dev/null
+++ b/media/libeffects/tests/common/Android.bp
@@ -0,0 +1,45 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+filegroup {
+ name: "libeffects-test-helper-srcs",
+ srcs: [
+ "EffectTestHelper.cpp",
+ ],
+}
+
+cc_library_headers {
+ name: "libeffects-test-helper-headers",
+ vendor: true,
+ host_supported: true,
+ export_include_dirs: [
+ ".",
+ ],
+}
+
+cc_defaults {
+ name: "libeffects-test-defaults",
+ vendor: true,
+ gtest: true,
+ host_supported: true,
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libaudioutils",
+ ],
+ srcs: [
+ ":libeffects-test-helper-srcs",
+ ],
+ header_libs: [
+ "libeffects-test-helper-headers",
+ "libhardware_headers",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+}
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.cpp b/media/libeffects/tests/common/EffectTestHelper.cpp
similarity index 97%
rename from media/libeffects/lvm/tests/EffectTestHelper.cpp
rename to media/libeffects/tests/common/EffectTestHelper.cpp
index ec727c7..db085ba 100644
--- a/media/libeffects/lvm/tests/EffectTestHelper.cpp
+++ b/media/libeffects/tests/common/EffectTestHelper.cpp
@@ -15,7 +15,6 @@
*/
#include "EffectTestHelper.h"
-extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
namespace android {
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.h b/media/libeffects/tests/common/EffectTestHelper.h
similarity index 69%
rename from media/libeffects/lvm/tests/EffectTestHelper.h
rename to media/libeffects/tests/common/EffectTestHelper.h
index bcee84e..c99e27a 100644
--- a/media/libeffects/lvm/tests/EffectTestHelper.h
+++ b/media/libeffects/tests/common/EffectTestHelper.h
@@ -21,6 +21,7 @@
#include <audio_utils/primitives.h>
#include <climits>
#include <cstdlib>
+#include <dlfcn.h>
#include <gtest/gtest.h>
#include <hardware/audio_effect.h>
#include <log/log.h>
@@ -29,7 +30,9 @@
#include <system/audio.h>
#include <vector>
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
namespace android {
+
template <typename T>
static float computeSnr(const T* ref, const T* tst, size_t count) {
double signal{};
@@ -82,6 +85,7 @@
void createEffect();
void releaseEffect();
void setConfig();
+
template <typename VALUE_DTYPE>
void setParam(uint32_t type, VALUE_DTYPE const value) {
int reply = 0;
@@ -101,6 +105,76 @@
ASSERT_EQ(status, 0) << "set_param returned an error " << status;
ASSERT_EQ(reply, 0) << "set_param reply non zero " << reply;
};
+
+ template <bool MULTI_VALUES, typename T>
+ int32_t getParam(uint32_t type, std::vector<T>& values) {
+ const int kMaxEffectParamValues = 10;
+ uint32_t cmd[sizeof(effect_param_t) / sizeof(uint32_t) + 1];
+ uint32_t reply[sizeof(effect_param_t) / sizeof(uint32_t) + 1 + 1 + kMaxEffectParamValues];
+
+ effect_param_t* p = (effect_param_t*)cmd;
+ p->psize = sizeof(uint32_t);
+ if (MULTI_VALUES) {
+ p->vsize = (kMaxEffectParamValues + 1) * sizeof(T);
+ } else {
+ p->vsize = sizeof(T);
+ }
+ *(uint32_t*)p->data = type;
+ uint32_t replySize = sizeof(effect_param_t) + p->psize + p->vsize;
+
+ int32_t status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_GET_PARAM,
+ sizeof(effect_param_t) + sizeof(uint32_t), cmd,
+ &replySize, reply);
+ if (status) {
+ return status;
+ }
+ if (p->status) {
+ return p->status;
+ }
+ if (replySize <
+ sizeof(effect_param_t) + sizeof(uint32_t) + (MULTI_VALUES ? 2 : 1) * sizeof(T)) {
+ return -EINVAL;
+ }
+
+ T* params = (T*)((uint8_t*)reply + sizeof(effect_param_t) + sizeof(uint32_t));
+ int numParams = 1;
+ if (MULTI_VALUES) {
+ numParams = (int)*params++;
+ }
+ if (numParams > kMaxEffectParamValues) {
+ return -EINVAL;
+ }
+ values.clear();
+ std::copy(¶ms[0], ¶ms[numParams], back_inserter(values));
+ return 0;
+ }
+
+ template <typename T>
+ int setParam(uint32_t type, const std::vector<T>& values) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+
+ uint32_t cmd[sizeof(effect_param_t) / sizeof(uint32_t) + 1 + values.size()];
+ effect_param_t* p = (effect_param_t*)cmd;
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(T) * values.size();
+ *(uint32_t*)p->data = type;
+ memcpy((uint32_t*)p->data + 1, values.data(), sizeof(T) * values.size());
+
+ int status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + p->psize + p->vsize, p, &replySize,
+ &reply);
+ if (status) {
+ return status;
+ }
+ if (reply) {
+ return reply;
+ }
+ return 0;
+ }
+
void process(float* input, float* output);
// Corresponds to SNR for 1 bit difference between two int16_t signals
diff --git a/media/liberror/Android.bp b/media/liberror/Android.bp
index f54d354..5e94b0a 100644
--- a/media/liberror/Android.bp
+++ b/media/liberror/Android.bp
@@ -25,7 +25,7 @@
],
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
@@ -51,7 +51,7 @@
min_sdk_version: "29",
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp
index 9d63f9b..7e2c762 100644
--- a/media/libheadtracking/Android.bp
+++ b/media/libheadtracking/Android.bp
@@ -22,6 +22,10 @@
"StillnessDetector.cpp",
"Twist.cpp",
],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ ],
export_include_dirs: [
"include",
],
@@ -39,6 +43,7 @@
"SensorPoseProvider.cpp",
],
shared_libs: [
+ "libbase",
"libheadtracking",
"liblog",
"libsensor",
diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp
index 71fae8a..ccb75af 100644
--- a/media/libheadtracking/HeadTrackingProcessor.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor.cpp
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <inttypes.h>
+#include <android-base/stringprintf.h>
+#include <audio_utils/SimpleLog.h>
#include "media/HeadTrackingProcessor.h"
#include "ModeSelector.h"
@@ -26,6 +29,7 @@
namespace media {
namespace {
+using android::base::StringAppendF;
using Eigen::Quaternionf;
using Eigen::Vector3f;
@@ -84,10 +88,12 @@
}
void calculate(int64_t timestamp) override {
- // Handle the screen first, since it might trigger a recentering of the head.
+ bool screenStable = true;
+
+ // Handle the screen first, since it might: trigger a recentering of the head.
if (mWorldToScreenTimestamp.has_value()) {
const Pose3f worldToLogicalScreen = mScreenPoseBias.getOutput();
- bool screenStable = mScreenStillnessDetector.calculate(timestamp);
+ screenStable = mScreenStillnessDetector.calculate(timestamp);
mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
// Whenever the screen is unstable, recenter the head pose.
if (!screenStable) {
@@ -101,7 +107,8 @@
if (mWorldToHeadTimestamp.has_value()) {
Pose3f worldToHead = mHeadPoseBias.getOutput();
// Auto-recenter.
- if (mHeadStillnessDetector.calculate(timestamp)) {
+ bool headStable = mHeadStillnessDetector.calculate(timestamp);
+ if (headStable || !screenStable) {
recenter(true, false);
worldToHead = mHeadPoseBias.getOutput();
}
@@ -136,10 +143,12 @@
if (recenterHead) {
mHeadPoseBias.recenter();
mHeadStillnessDetector.reset();
+ mLocalLog.log("recenter Head");
}
if (recenterScreen) {
mScreenPoseBias.recenter();
mScreenStillnessDetector.reset();
+ mLocalLog.log("recenter Screen");
}
// If a sensor being recentered is included in the current mode, apply rate limiting to
@@ -152,6 +161,35 @@
}
}
+ std::string toString_l(unsigned level) const override {
+ std::string prefixSpace(level, ' ');
+ std::string ss = prefixSpace + "HeadTrackingProcessor:\n";
+ StringAppendF(&ss, "%smaxTranslationalVelocity: %f\n", prefixSpace.c_str(),
+ mOptions.maxTranslationalVelocity);
+ StringAppendF(&ss, "%smaxRotationalVelocity: %f\n", prefixSpace.c_str(),
+ mOptions.maxRotationalVelocity);
+ StringAppendF(&ss, "%sfreshnessTimeout: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.freshnessTimeout);
+ StringAppendF(&ss, "%spredictionDuration: %f\n", prefixSpace.c_str(),
+ mOptions.predictionDuration);
+ StringAppendF(&ss, "%sautoRecenterWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.autoRecenterWindowDuration);
+ StringAppendF(&ss, "%sautoRecenterTranslationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.autoRecenterTranslationalThreshold);
+ StringAppendF(&ss, "%sautoRecenterRotationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.autoRecenterRotationalThreshold);
+ StringAppendF(&ss, "%sscreenStillnessWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.screenStillnessWindowDuration);
+ StringAppendF(&ss, "%sscreenStillnessTranslationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.screenStillnessTranslationalThreshold);
+ StringAppendF(&ss, "%sscreenStillnessRotationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.screenStillnessRotationalThreshold);
+ ss.append(prefixSpace + "ReCenterHistory:\n");
+ ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
+ // TODO: 233092747 add string from PoseRateLimiter/PoseRateLimiter etc...
+ return ss;
+ }
+
private:
const Options mOptions;
float mPhysicalToLogicalAngle = 0;
@@ -168,6 +206,8 @@
ScreenHeadFusion mScreenHeadFusion;
ModeSelector mModeSelector;
PoseRateLimiter mRateLimiter;
+ static constexpr std::size_t mMaxLocalLogLine = 10;
+ SimpleLog mLocalLog{mMaxLocalLogLine};
};
} // namespace
diff --git a/media/libheadtracking/SensorPoseProvider.cpp b/media/libheadtracking/SensorPoseProvider.cpp
index 8ebaf6e..6c0a96d 100644
--- a/media/libheadtracking/SensorPoseProvider.cpp
+++ b/media/libheadtracking/SensorPoseProvider.cpp
@@ -24,6 +24,7 @@
#include <map>
#include <thread>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <log/log_main.h>
#include <sensor/SensorEventQueue.h>
@@ -36,6 +37,8 @@
namespace media {
namespace {
+using android::base::StringAppendF;
+
// Identifier to use for our event queue on the loop.
// The number 19 is arbitrary, only useful if using multiple objects on the same looper.
constexpr int kIdent = 19;
@@ -153,6 +156,38 @@
mEnabledSensorsExtra.erase(handle);
}
+ std::string toString(unsigned level) override {
+ std::string prefixSpace(level, ' ');
+ std::string ss = prefixSpace + "SensorPoseProvider:\n";
+ bool needUnlock = false;
+
+ prefixSpace += " ";
+ auto now = std::chrono::steady_clock::now();
+ if (!mMutex.try_lock_until(now + media::kSpatializerDumpSysTimeOutInSecond)) {
+ ss.append(prefixSpace).append("try_lock failed, dumpsys below maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ // Enabled sensor information
+ StringAppendF(&ss, "%sSensors total number %zu:\n", prefixSpace.c_str(),
+ mEnabledSensorsExtra.size());
+ for (auto sensor : mEnabledSensorsExtra) {
+ StringAppendF(&ss, "%s[Handle: 0x%08x, Format %s", prefixSpace.c_str(), sensor.first,
+ toString(sensor.second.format).c_str());
+ if (sensor.second.discontinuityCount.has_value()) {
+ StringAppendF(&ss, ", DiscontinuityCount: %d",
+ sensor.second.discontinuityCount.value());
+ }
+ ss += "]\n";
+ }
+
+ if (needUnlock) {
+ mMutex.unlock();
+ }
+ return ss;
+ }
+
private:
enum DataFormat {
kUnknown,
@@ -174,8 +209,7 @@
sp<Looper> mLooper;
Listener* const mListener;
SensorManager* const mSensorManager;
- std::thread mThread;
- std::mutex mMutex;
+ std::timed_mutex mMutex;
std::map<int32_t, SensorEnableGuard> mEnabledSensors;
std::map<int32_t, SensorExtra> mEnabledSensorsExtra GUARDED_BY(mMutex);
sp<SensorEventQueue> mQueue;
@@ -187,12 +221,13 @@
// the worker thread and that thread would notify, via the promise below whenever initialization
// is finished, and whether it was successful.
std::promise<bool> mInitPromise;
+ std::thread mThread;
SensorPoseProviderImpl(const char* packageName, Listener* listener)
: mListener(listener),
- mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))),
- mThread([this] { threadFunc(); }) {}
-
+ mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))) {
+ mThread = std::thread([this] { threadFunc(); });
+ }
void initFinished(bool success) { mInitPromise.set_value(success); }
bool waitInitFinished() { return mInitPromise.get_future().get(); }
@@ -345,6 +380,19 @@
LOG_ALWAYS_FATAL("Unexpected sensor type: %d", static_cast<int>(format));
}
}
+
+ const std::string toString(DataFormat format) {
+ switch (format) {
+ case DataFormat::kUnknown:
+ return "kUnknown";
+ case DataFormat::kQuaternion:
+ return "kQuaternion";
+ case DataFormat::kRotationVectorsAndDiscontinuityCount:
+ return "kRotationVectorsAndDiscontinuityCount";
+ default:
+ return "NotImplemented";
+ }
+ }
};
} // namespace
diff --git a/media/libheadtracking/StillnessDetector.cpp b/media/libheadtracking/StillnessDetector.cpp
index be7c893..5232084 100644
--- a/media/libheadtracking/StillnessDetector.cpp
+++ b/media/libheadtracking/StillnessDetector.cpp
@@ -26,6 +26,9 @@
mFifo.clear();
mWindowFull = false;
mSuppressionDeadline.reset();
+ // A "true" state indicates stillness is detected (default = true)
+ mCurrentState = true;
+ mPreviousState = true;
}
void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) {
@@ -33,7 +36,15 @@
discardOld(timestamp);
}
+bool StillnessDetector::getPreviousState() const {
+ return mPreviousState;
+}
+
bool StillnessDetector::calculate(int64_t timestamp) {
+ // Move the current stillness state to the previous state.
+ // This allows us to detect transitions into and out of stillness.
+ mPreviousState = mCurrentState;
+
discardOld(timestamp);
// Check whether all the poses in the queue are in the proximity of the new one. We want to do
@@ -60,15 +71,17 @@
// If the window has not been full, return the default value.
if (!mWindowFull) {
- return mOptions.defaultValue;
+ mCurrentState = mOptions.defaultValue;
}
-
// Force "in motion" while the suppression deadline is active.
- if (mSuppressionDeadline.has_value()) {
- return false;
+ else if (mSuppressionDeadline.has_value()) {
+ mCurrentState = false;
+ }
+ else {
+ mCurrentState = !moved;
}
- return !moved;
+ return mCurrentState;
}
void StillnessDetector::discardOld(int64_t timestamp) {
diff --git a/media/libheadtracking/StillnessDetector.h b/media/libheadtracking/StillnessDetector.h
index ee4b2d8..abd7cc4 100644
--- a/media/libheadtracking/StillnessDetector.h
+++ b/media/libheadtracking/StillnessDetector.h
@@ -80,7 +80,8 @@
void setInput(int64_t timestamp, const Pose3f& input);
/** Calculate whether the stream is still at the given timestamp. */
bool calculate(int64_t timestamp);
-
+ /** Return the stillness state from the previous call to calculate() */
+ bool getPreviousState() const;
private:
struct TimestampedPose {
int64_t timestamp;
@@ -92,6 +93,8 @@
const float mCosHalfRotationalThreshold;
std::deque<TimestampedPose> mFifo;
bool mWindowFull = false;
+ bool mCurrentState = true;
+ bool mPreviousState = true;
// As soon as motion is detected, this will be set for the time of detection + window duration,
// and during this time we will always consider outselves in motion without checking. This is
// used for hyteresis purposes, since because of the approximate method we use for determining
diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h
index 1744be3..8ef8ab0 100644
--- a/media/libheadtracking/include/media/HeadTrackingProcessor.h
+++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h
@@ -96,8 +96,12 @@
* This causes the current poses for both the head and/or screen to be considered "center".
*/
virtual void recenter(bool recenterHead = true, bool recenterScreen = true) = 0;
-};
+ /**
+ * Dump HeadTrackingProcessor parameters under caller lock.
+ */
+ virtual std::string toString_l(unsigned level) const = 0;
+};
/**
* Creates an instance featuring a default implementation of the HeadTrackingProcessor interface.
*/
diff --git a/media/libheadtracking/include/media/SensorPoseProvider.h b/media/libheadtracking/include/media/SensorPoseProvider.h
index 0f42074..4609e0c 100644
--- a/media/libheadtracking/include/media/SensorPoseProvider.h
+++ b/media/libheadtracking/include/media/SensorPoseProvider.h
@@ -28,6 +28,9 @@
namespace android {
namespace media {
+// Timeout for Spatializer dumpsys trylock, don't block for more than 3 seconds.
+constexpr auto kSpatializerDumpSysTimeOutInSecond = std::chrono::seconds(3);
+
/**
* A utility providing streaming of pose data from motion sensors provided by the Sensor Framework.
*
@@ -100,6 +103,11 @@
* discover properties of the sensor.
*/
virtual std::optional<const Sensor> getSensorByHandle(int32_t handle) = 0;
+
+ /**
+ * Dump SensorPoseProvider parameters and history data.
+ */
+ virtual std::string toString(unsigned level) = 0;
};
} // namespace media
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 1b8656d..691eede 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -344,19 +344,22 @@
mFrameDecoded = false;
mFrameMemory.clear();
- mRetriever = new MediaMetadataRetriever();
- status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
+ sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
+ status_t err = retriever->setDataSource(mDataSource, "image/heif");
if (err != OK) {
ALOGE("failed to set data source!");
-
mRetriever.clear();
mDataSource.clear();
return false;
}
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ mRetriever = retriever;
+ }
ALOGV("successfully set data source.");
- const char* hasImage = mRetriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
- const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
+ const char* hasImage = retriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
+ const char* hasVideo = retriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
mHasImage = hasImage && !strcasecmp(hasImage, "yes");
mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
@@ -364,7 +367,7 @@
HeifFrameInfo* defaultInfo = nullptr;
if (mHasImage) {
// image index < 0 to retrieve primary image
- sp<IMemory> sharedMem = mRetriever->getImageAtIndex(
+ sp<IMemory> sharedMem = retriever->getImageAtIndex(
-1, mOutputColor, true /*metaOnly*/);
if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
@@ -399,7 +402,7 @@
}
if (mHasVideo) {
- sp<IMemory> sharedMem = mRetriever->getFrameAtTime(0,
+ sp<IMemory> sharedMem = retriever->getFrameAtTime(0,
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
mOutputColor, true /*metaOnly*/);
@@ -425,7 +428,7 @@
initFrameInfo(&mSequenceInfo, videoFrame);
- const char* frameCount = mRetriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
+ const char* frameCount = retriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
if (frameCount == nullptr) {
android_errorWriteWithInfoLog(0x534e4554, "215002587", -1, NULL, 0);
ALOGD("No valid sequence information in metadata");
@@ -511,14 +514,27 @@
}
bool HeifDecoderImpl::decodeAsync() {
+ wp<MediaMetadataRetriever> weakRetriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ weakRetriever = mRetriever;
+ mRetriever.clear();
+ }
+
for (size_t i = 1; i < mNumSlices; i++) {
+ sp<MediaMetadataRetriever> retriever = weakRetriever.promote();
+ if (retriever == nullptr) {
+ return false;
+ }
+
ALOGV("decodeAsync(): decoding slice %zu", i);
size_t top = i * mSliceHeight;
size_t bottom = (i + 1) * mSliceHeight;
if (bottom > mImageInfo.mHeight) {
bottom = mImageInfo.mHeight;
}
- sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+
+ sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
-1, mOutputColor, 0, top, mImageInfo.mWidth, bottom);
{
Mutex::Autolock autolock(mLock);
@@ -534,9 +550,6 @@
mScanlineReady.signal();
}
}
- // Aggressive clear to avoid holding on to resources
- mRetriever.clear();
-
// Hold on to mDataSource in case the client wants to redecode.
return false;
}
@@ -549,6 +562,17 @@
return true;
}
+ sp<MediaMetadataRetriever> retriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ if (mRetriever == nullptr) {
+ ALOGE("Failed to get MediaMetadataRetriever!");
+ return false;
+ }
+
+ retriever = mRetriever;
+ }
+
// See if we want to decode in slices to allow client to start
// scanline processing in parallel with decode. If this fails
// we fallback to decoding the full frame.
@@ -563,7 +587,7 @@
if (mNumSlices > 1) {
// get first slice and metadata
- sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+ sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
-1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
@@ -598,9 +622,9 @@
if (mHasImage) {
// image index < 0 to retrieve primary image
- mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
+ mFrameMemory = retriever->getImageAtIndex(-1, mOutputColor);
} else if (mHasVideo) {
- mFrameMemory = mRetriever->getFrameAtTime(0,
+ mFrameMemory = retriever->getFrameAtTime(0,
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
}
@@ -658,7 +682,17 @@
// set total scanline to sequence height now
mTotalScanline = mSequenceInfo.mHeight;
- mFrameMemory = mRetriever->getFrameAtIndex(frameIndex, mOutputColor);
+ sp<MediaMetadataRetriever> retriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ retriever = mRetriever;
+ if (retriever == nullptr) {
+ ALOGE("failed to get MediaMetadataRetriever!");
+ return false;
+ }
+ }
+
+ mFrameMemory = retriever->getFrameAtIndex(frameIndex, mOutputColor);
if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: videoFrame is a nullptr");
return false;
@@ -735,9 +769,9 @@
HeifFrameInfo* info = &mImageInfo;
if (info != nullptr) {
return mImageInfo.mBitDepth;
+ } else {
+ return 0;
}
-
- return 0;
}
} // namespace android
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 86a8628..c1504cd 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -72,6 +72,8 @@
bool mHasVideo;
size_t mSequenceLength;
+ Mutex mRetrieverLock;
+
// Slice decoding only
Mutex mLock;
Condition mScanlineReady;
diff --git a/media/libheif/include/HeifDecoderAPI.h b/media/libheif/include/HeifDecoderAPI.h
index dc12486..56f4765 100644
--- a/media/libheif/include/HeifDecoderAPI.h
+++ b/media/libheif/include/HeifDecoderAPI.h
@@ -47,7 +47,7 @@
int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
uint32_t mBytesPerPixel; // Number of bytes for one pixel
int64_t mDurationUs; // Duration of the frame in us
- uint32_t mBitDepth; // Number of bits for each of the R/G/B channels
+ uint32_t mBitDepth; // Number of bits of R/G/B channel
std::vector<uint8_t> mIccData; // ICC data array
};
@@ -164,7 +164,7 @@
virtual size_t skipScanlines(size_t count) = 0;
/*
- * Returns color depth in bits for each of the R/G/B channels.
+ * Returns color depth of R/G/B channel.
*/
virtual uint32_t getColorDepth() = 0;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 2dd5784..ab1cf69 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -304,6 +304,10 @@
cc_library {
name: "libmedia",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
srcs: [
":mediaextractorservice_aidl",
"IDataSource.cpp",
@@ -356,7 +360,6 @@
shared_libs: [
"android.hidl.token@1.0-utils",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"av-types-aidl-cpp",
"liblog",
diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp
index 2ed3126..d4494f6 100644
--- a/media/libmedia/tests/codeclist/Android.bp
+++ b/media/libmedia/tests/codeclist/Android.bp
@@ -25,7 +25,7 @@
cc_test {
name: "CodecListTest",
- test_suites: ["device-tests", "mts"],
+ test_suites: ["device-tests", "mts-media"],
gtest: true,
// Support multilib variants (using different suffix per sub-architecture), which is needed on
diff --git a/media/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 165a8ad..c66861b 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -20,7 +20,7 @@
},
apex_available: [
"//apex_available:platform",
- "com.android.bluetooth",
+ "com.android.btservices",
"com.android.media",
"com.android.media.swcodec",
],
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index 1c30510..bebd382 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -57,6 +57,10 @@
// The Audio Spatializer key appends the spatializerId (currently 0)
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER AMEDIAMETRICS_KEY_PREFIX_AUDIO "spatializer."
+// The Audio Spatializer device key appends the device type.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER_DEVICE \
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER "device."
+
// The AudioStream key appends the "streamId" to the prefix.
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM AMEDIAMETRICS_KEY_PREFIX_AUDIO "stream."
@@ -107,6 +111,7 @@
// of suppressed in the Time Machine.
#define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
+#define AMEDIAMETRICS_PROP_ADDRESS "address" // string, for example MAC address
#define AMEDIAMETRICS_PROP_ALLOWUID "_allowUid" // int32_t, allow client uid to post
#define AMEDIAMETRICS_PROP_AUDIOMODE "audioMode" // string (audio.flinger)
#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
@@ -116,6 +121,8 @@
#define AMEDIAMETRICS_PROP_CALLERNAME "callerName" // string, eg. "aaudio"
#define AMEDIAMETRICS_PROP_CHANNELCOUNT "channelCount" // int32
#define AMEDIAMETRICS_PROP_CHANNELMASK "channelMask" // int32
+#define AMEDIAMETRICS_PROP_CHANNELMASKS "channelMasks" // string with channelMask values
+ // separated by |.
#define AMEDIAMETRICS_PROP_CONTENTTYPE "contentType" // string attributes (AudioTrack)
#define AMEDIAMETRICS_PROP_CUMULATIVETIMENS "cumulativeTimeNs" // int64_t playback/record time
// since start
@@ -132,6 +139,7 @@
#define AMEDIAMETRICS_PROP_DIRECTION "direction" // string AAudio input or output
#define AMEDIAMETRICS_PROP_DURATIONNS "durationNs" // int64 duration time span
+#define AMEDIAMETRICS_PROP_ENABLED "enabled" // string true/false.
#define AMEDIAMETRICS_PROP_ENCODING "encoding" // string value of format
#define AMEDIAMETRICS_PROP_EVENT "event#" // string value (often func name)
@@ -141,6 +149,8 @@
#define AMEDIAMETRICS_PROP_FLAGS "flags"
#define AMEDIAMETRICS_PROP_FRAMECOUNT "frameCount" // int32
+#define AMEDIAMETRICS_PROP_HASHEADTRACKER "hasHeadTracker" // string true/false
+#define AMEDIAMETRICS_PROP_HEADTRACKERENABLED "headTrackerEnabled" // string true/false
#define AMEDIAMETRICS_PROP_HEADTRACKINGMODES "headTrackingModes" // string |, like modes.
#define AMEDIAMETRICS_PROP_INPUTDEVICES "inputDevices" // string value
#define AMEDIAMETRICS_PROP_INTERNALTRACKID "internalTrackId" // int32
diff --git a/media/libmediametrics/libmediametrics.map.txt b/media/libmediametrics/libmediametrics.map.txt
index c46281a..f37af64 100644
--- a/media/libmediametrics/libmediametrics.map.txt
+++ b/media/libmediametrics/libmediametrics.map.txt
@@ -1,29 +1,29 @@
LIBMEDIAMETRICS_1 {
global:
- mediametrics_addDouble; # apex
- mediametrics_addInt32; # apex
- mediametrics_addInt64; # apex
- mediametrics_addRate; # apex
- mediametrics_count; # apex
- mediametrics_create; # apex
- mediametrics_delete; # apex
- mediametrics_freeCString; # apex
- mediametrics_getAttributes; # apex
- mediametrics_getCString; # apex
- mediametrics_getDouble; # apex
- mediametrics_getInt32; # apex
- mediametrics_getInt64; # apex
- mediametrics_getKey; # apex
- mediametrics_getRate; # apex
- mediametrics_isEnabled; # apex
- mediametrics_readable; # apex
- mediametrics_selfRecord; # apex
- mediametrics_setCString; # apex
- mediametrics_setDouble; # apex
- mediametrics_setInt32; # apex
- mediametrics_setInt64; # apex
- mediametrics_setRate; # apex
- mediametrics_setUid; # apex
+ mediametrics_addDouble; # systemapi
+ mediametrics_addInt32; # systemapi
+ mediametrics_addInt64; # systemapi
+ mediametrics_addRate; # systemapi
+ mediametrics_count; # systemapi
+ mediametrics_create; # systemapi
+ mediametrics_delete; # systemapi
+ mediametrics_freeCString; # systemapi
+ mediametrics_getAttributes; # systemapi
+ mediametrics_getCString; # systemapi
+ mediametrics_getDouble; # systemapi
+ mediametrics_getInt32; # systemapi
+ mediametrics_getInt64; # systemapi
+ mediametrics_getKey; # systemapi
+ mediametrics_getRate; # systemapi
+ mediametrics_isEnabled; # systemapi
+ mediametrics_readable; # systemapi
+ mediametrics_selfRecord; # systemapi
+ mediametrics_setCString; # systemapi
+ mediametrics_setDouble; # systemapi
+ mediametrics_setInt32; # systemapi
+ mediametrics_setInt64; # systemapi
+ mediametrics_setRate; # systemapi
+ mediametrics_setUid; # systemapi
local:
*;
};
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ea1fdf4..a0bc8ca 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1966,6 +1966,10 @@
format->setString("mime", MEDIA_MIMETYPE_VIDEO_HEVC);
break;
+ case VIDEO_ENCODER_DOLBY_VISION:
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+ break;
+
default:
CHECK(!"Should not be here, unsupported video encoding.");
break;
diff --git a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
index 7799f44..a189d04 100644
--- a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
@@ -26,6 +26,8 @@
#include <media/IMediaRecorder.h>
#include <media/IRemoteDisplay.h>
#include <media/IRemoteDisplayClient.h>
+#include <media/MediaHTTPConnection.h>
+#include <media/MediaHTTPService.h>
#include <media/stagefright/RemoteDataSource.h>
#include <media/stagefright/foundation/base64.h>
#include <thread>
@@ -102,6 +104,42 @@
IBinder *onAsBinder() { return nullptr; };
};
+struct TestMediaHTTPConnection : public MediaHTTPConnection {
+ public:
+ TestMediaHTTPConnection() {}
+ virtual ~TestMediaHTTPConnection() {}
+
+ virtual bool connect(const char* /*uri*/, const KeyedVector<String8, String8>* /*headers*/) {
+ return true;
+ }
+
+ virtual void disconnect() { return; }
+
+ virtual ssize_t readAt(off64_t /*offset*/, void* /*data*/, size_t size) { return size; }
+
+ virtual off64_t getSize() { return 0; }
+ virtual status_t getMIMEType(String8* /*mimeType*/) { return NO_ERROR; }
+ virtual status_t getUri(String8* /*uri*/) { return NO_ERROR; }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TestMediaHTTPConnection);
+};
+
+struct TestMediaHTTPService : public BnInterface<IMediaHTTPService> {
+ public:
+ TestMediaHTTPService() {}
+ ~TestMediaHTTPService(){};
+
+ virtual sp<MediaHTTPConnection> makeHTTPConnection() {
+ mMediaHTTPConnection = sp<TestMediaHTTPConnection>::make();
+ return mMediaHTTPConnection;
+ }
+
+ private:
+ sp<TestMediaHTTPConnection> mMediaHTTPConnection = nullptr;
+ DISALLOW_EVIL_CONSTRUCTORS(TestMediaHTTPService);
+};
+
class BinderDeathNotifier : public IBinder::DeathRecipient {
public:
void binderDied(const wp<IBinder> &) { abort(); }
@@ -140,7 +178,9 @@
AString out;
encodeBase64(uriSuffix.data(), uriSuffix.size(), &out);
uri += out.c_str();
- status = mMediaPlayer->setDataSource(nullptr /*httpService*/, uri.c_str(), &headers);
+ sp<TestMediaHTTPService> testService = sp<TestMediaHTTPService>::make();
+ status =
+ mMediaPlayer->setDataSource(testService /*httpService*/, uri.c_str(), &headers);
break;
}
case fd: {
diff --git a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
index c3bd207..25a8ae4 100644
--- a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
+++ b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
@@ -59,11 +59,10 @@
if (mPowerManager != NULL) {
sp<IBinder> binder = new BBinder();
int64_t token = IPCThreadState::self()->clearCallingIdentity();
- binder::Status status = mPowerManager->acquireWakeLock(
+ binder::Status status = mPowerManager->acquireWakeLockAsync(
binder, POWERMANAGER_PARTIAL_WAKE_LOCK,
String16("AWakeLock"), String16("media"),
- {} /* workSource */, {} /* historyTag */, -1 /* displayId */,
- nullptr /* callback */);
+ {} /* workSource */, {} /* historyTag */);
IPCThreadState::self()->restoreCallingIdentity(token);
if (status.isOk()) {
mWakeLockToken = binder;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 45fac76..0382df3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1947,22 +1947,12 @@
CHECK(format->findInt32("channel-count", &numChannels));
// channel mask info as read from the audio format
- int32_t channelMaskFromFormat;
+ int32_t mediaFormatChannelMask;
// channel mask to use for native playback
audio_channel_mask_t channelMask;
- if (format->findInt32("channel-mask", &channelMaskFromFormat)) {
+ if (format->findInt32("channel-mask", &mediaFormatChannelMask)) {
// KEY_CHANNEL_MASK follows the android.media.AudioFormat java mask
- // which is left-bitshifted by 2 relative to the native mask
- if ((channelMaskFromFormat & 0b11) != 0) {
- // received an unexpected mask (supposed to follow AudioFormat constants
- // for output masks with the 2 least-significant bits at 0), but
- // it may come from an extractor that uses native masks: keeping
- // the mask as given is ok as it contains at least mono or stereo
- // and potentially the haptic channels
- channelMask = static_cast<audio_channel_mask_t>(channelMaskFromFormat);
- } else {
- channelMask = static_cast<audio_channel_mask_t>(channelMaskFromFormat >> 2);
- }
+ channelMask = audio_channel_mask_from_media_format_mask(mediaFormatChannelMask);
} else {
// no mask found: the mask will be derived from the channel count
channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
diff --git a/media/libnblog/Reader.cpp b/media/libnblog/Reader.cpp
index 67d028d..d6232d4 100644
--- a/media/libnblog/Reader.cpp
+++ b/media/libnblog/Reader.cpp
@@ -208,11 +208,14 @@
}
while (back + Entry::kPreviousLengthOffset >= front) {
const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
- const Event type = (const Event)prev[offsetof(entry, type)];
if (prev < front
- || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back
- || type <= EVENT_RESERVED || type >= EVENT_UPPER_BOUND) {
- // prev points to an out of limits or inconsistent entry
+ || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back) {
+ // prev points to an out of limits entry
+ return nullptr;
+ }
+ const Event type = (const Event)prev[offsetof(entry, type)];
+ if (type <= EVENT_RESERVED || type >= EVENT_UPPER_BOUND) {
+ // prev points to an inconsistent entry
return nullptr;
}
// if invalidTypes does not contain the type, then the type is valid.
diff --git a/media/libstagefright/DataConverter.cpp b/media/libstagefright/DataConverter.cpp
index 52be054..b53ac77 100644
--- a/media/libstagefright/DataConverter.cpp
+++ b/media/libstagefright/DataConverter.cpp
@@ -24,6 +24,10 @@
#include <media/MediaCodecBuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
+#include <system/audio.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+
namespace android {
@@ -81,12 +85,38 @@
return numSamples * mTargetSampleSize;
}
+static audio_format_t getAudioFormat(AudioEncoding e) {
+ audio_format_t format = AUDIO_FORMAT_INVALID;
+ switch (e) {
+ case kAudioEncodingPcm16bit:
+ format = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ case kAudioEncodingPcm8bit:
+ format = AUDIO_FORMAT_PCM_8_BIT;
+ break;
+ case kAudioEncodingPcmFloat:
+ format = AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case kAudioEncodingPcm24bitPacked:
+ format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ break;
+ case kAudioEncodingPcm32bit:
+ format = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ default:
+ ALOGE("Invalid AudioEncoding %d", e);
+ }
+ return format;
+}
static size_t getAudioSampleSize(AudioEncoding e) {
switch (e) {
- case kAudioEncodingPcm16bit: return 2;
- case kAudioEncodingPcm8bit: return 1;
- case kAudioEncodingPcmFloat: return 4;
+ case kAudioEncodingPcm16bit:
+ case kAudioEncodingPcm8bit:
+ case kAudioEncodingPcmFloat:
+ case kAudioEncodingPcm24bitPacked:
+ case kAudioEncodingPcm32bit:
+ return audio_bytes_per_sample(getAudioFormat(e));
default: return 0;
}
}
@@ -116,7 +146,15 @@
} else if (mTo == kAudioEncodingPcmFloat && mFrom == kAudioEncodingPcm16bit) {
memcpy_to_float_from_i16((float*)tgt->base(), (const int16_t*)src->data(), src->size() / 2);
} else {
- return INVALID_OPERATION;
+ audio_format_t srcFormat = getAudioFormat(mFrom);
+ audio_format_t dstFormat = getAudioFormat(mTo);
+
+ if ((srcFormat == AUDIO_FORMAT_INVALID) || (dstFormat == AUDIO_FORMAT_INVALID))
+ return INVALID_OPERATION;
+
+ size_t frames = src->size() / audio_bytes_per_sample(srcFormat);
+ memcpy_by_audio_format((void*)tgt->base(), dstFormat, (void*)src->data(),
+ srcFormat, frames);
}
return OK;
}
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 3df8766..6de112a 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -349,6 +349,10 @@
status_t err = OK;
bool done = false;
size_t retriesLeft = kRetryCount;
+ if (!mDecoder) {
+ ALOGE("decoder is not initialized");
+ return NO_INIT;
+ }
do {
size_t index;
int64_t ptsUs = 0LL;
@@ -913,7 +917,7 @@
return ERROR_MALFORMED;
}
- int32_t width, height, stride, srcFormat;
+ int32_t width, height, stride;
if (outputFormat->findInt32("width", &width) == false) {
ALOGE("MediaImageDecoder::onOutputReceived:width is missing in outputFormat");
return ERROR_MALFORMED;
@@ -926,10 +930,9 @@
ALOGE("MediaImageDecoder::onOutputReceived:stride is missing in outputFormat");
return ERROR_MALFORMED;
}
- if (outputFormat->findInt32("color-format", &srcFormat) == false) {
- ALOGE("MediaImageDecoder::onOutputReceived: color format is missing in outputFormat");
- return ERROR_MALFORMED;
- }
+
+ int32_t srcFormat;
+ CHECK(outputFormat->findInt32("color-format", &srcFormat));
uint32_t bitDepth = 8;
if (COLOR_FormatYUVP010 == srcFormat) {
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
index 5f9c20e..60df162 100644
--- a/media/libstagefright/HevcUtils.cpp
+++ b/media/libstagefright/HevcUtils.cpp
@@ -102,10 +102,11 @@
static bool findParam(uint32_t key, T *param,
KeyedVector<uint32_t, uint64_t> ¶ms) {
CHECK(param);
- if (params.indexOfKey(key) < 0) {
+ ssize_t index = params.indexOfKey(key);
+ if (index < 0) {
return false;
}
- *param = (T) params[key];
+ *param = (T) params[index];
return true;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 63d3180..9ff2177 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -155,6 +155,7 @@
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
bool isHevc() const { return mIsHevc; }
+ bool isAv1() const { return mIsAv1; }
bool isHeic() const { return mIsHeic; }
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
@@ -319,6 +320,7 @@
volatile bool mStarted;
bool mIsAvc;
bool mIsHevc;
+ bool mIsAv1;
bool mIsDovi;
bool mIsAudio;
bool mIsVideo;
@@ -467,6 +469,7 @@
void writePaspBox();
void writeAvccBox();
void writeHvccBox();
+ void writeAv1cBox();
void writeDoviConfigBox();
void writeUrlBox();
void writeDrefBox();
@@ -660,6 +663,8 @@
return "avc1";
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
return "hvc1";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
+ return "av01";
}
} else if (!strncasecmp(mime, "application/", 12)) {
return "mett";
@@ -1541,6 +1546,15 @@
writeFourcc("isom");
writeFourcc("mp42");
}
+ // If an AV1 video track is present, write "av01" as one of the
+ // compatible brands.
+ for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end();
+ ++it) {
+ if ((*it)->isAv1()) {
+ writeFourcc("av01");
+ break;
+ }
+ }
}
endBox();
@@ -2205,6 +2219,7 @@
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
+ mIsAv1 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1);
mIsDovi = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
mIsAudio = !strncasecmp(mime, "audio/", 6);
mIsVideo = !strncasecmp(mime, "video/", 6);
@@ -2639,6 +2654,8 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mMeta->findData(kKeyHVCC, &type, &data, &size);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
+ mMeta->findData(kKeyAV1C, &type, &data, &size);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
getDolbyVisionProfile();
if (!mMeta->findData(kKeyAVCC, &type, &data, &size) &&
@@ -4262,6 +4279,7 @@
!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime) ||
!strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
if (!mCodecSpecificData ||
@@ -4433,6 +4451,8 @@
writeAvccBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
writeHvccBox();
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
+ writeAv1cBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime)) {
if (mDoviProfile <= DolbyVisionProfileDvheSt) {
writeHvccBox();
@@ -5000,6 +5020,15 @@
mOwner->endBox(); // hvcC
}
+void MPEG4Writer::Track::writeAv1cBox() {
+ CHECK(mCodecSpecificData);
+ CHECK_GE(mCodecSpecificDataSize, 4u);
+
+ mOwner->beginBox("av1C");
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+ mOwner->endBox(); // av1C
+}
+
void MPEG4Writer::Track::writeDoviConfigBox() {
CHECK_NE(mDoviProfile, 0u);
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 24608a7..ed0819d 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -110,8 +110,12 @@
if (mAnchorTimeRealUs != -1) {
int64_t oldNowMediaUs =
mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
- if (nowMediaUs < oldNowMediaUs + kAnchorFluctuationAllowedUs
- && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) {
+ // earlier, we ensured that the anchor times are non-negative and the
+ // math to calculate the now/oldNow times stays non-negative.
+ // by casting into uint64_t, we gain headroom to avoid any overflows at the upper end
+ // when adding the fluctuation allowance.
+ if ((uint64_t)nowMediaUs < (uint64_t)oldNowMediaUs + kAnchorFluctuationAllowedUs
+ && (uint64_t)nowMediaUs + kAnchorFluctuationAllowedUs > (uint64_t)oldNowMediaUs) {
return;
}
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e50880a..c963e19 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -3534,6 +3534,17 @@
}
setState(STARTED);
postPendingRepliesAndDeferredMessages("kWhatStartCompleted");
+
+ // Now that the codec has started, configure, by default, the peek behavior to
+ // be undefined for backwards compatibility with older releases. Later, if an
+ // app explicitly enables or disables peek, the parameter will be turned off and
+ // the legacy undefined behavior is disallowed.
+ // See updateTunnelPeek called in onSetParameters for more details.
+ if (mTunneled && mTunnelPeekState == TunnelPeekState::kLegacyMode) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("android._tunnel-peek-set-legacy", 1);
+ mCodec->signalSetParameters(params);
+ }
break;
}
@@ -3973,14 +3984,6 @@
mTunneled = false;
}
- // If mTunnelPeekState is still in kLegacyMode at this point,
- // configure the codec in legacy mode
- if (mTunneled && (mTunnelPeekState == TunnelPeekState::kLegacyMode)) {
- sp<AMessage> params = new AMessage;
- params->setInt32("android._tunnel-peek-set-legacy", 1);
- onSetParameters(params);
- }
-
int32_t background = 0;
if (format->findInt32("android._background-mode", &background) && background) {
androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND);
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index a443ed9..900ac32 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1911,10 +1911,10 @@
if (msg->findString("ts-schema", &tsSchema)) {
unsigned int numLayers = 0;
unsigned int numBLayers = 0;
- char dummy;
+ char placeholder;
int tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
- &numLayers, &dummy, &numBLayers, &dummy);
- if ((tags == 1 || (tags == 3 && dummy == '+'))
+ &numLayers, &placeholder, &numBLayers, &placeholder);
+ if ((tags == 1 || (tags == 3 && placeholder == '+'))
&& numLayers > 0 && numLayers < UINT32_MAX - numBLayers
&& numLayers + numBLayers <= INT32_MAX) {
meta->setInt32(kKeyTemporalLayerCount, numLayers + numBLayers);
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index 0e2b472..a4f0fb3 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -12,6 +12,7 @@
double_loadable: true,
srcs: [
+ ":libgui_frame_event_aidl",
"FrameDropper.cpp",
"GraphicBufferSource.cpp",
],
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index fb4f9a0..d667685 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -73,7 +73,7 @@
<MediaCodec name="c2.android.raw.decoder" type="audio/raw">
<Alias name="OMX.google.raw.decoder" />
<Limit name="channel-count" max="8" />
- <Limit name="sample-rate" ranges="8000-96000" />
+ <Limit name="sample-rate" ranges="8000-192000" />
<Limit name="bitrate" range="1-10000000" />
</MediaCodec>
<MediaCodec name="c2.android.flac.decoder" type="audio/flac">
diff --git a/media/libstagefright/exports.lds b/media/libstagefright/exports.lds
index f5ddf1e..7fe6d6c 100644
--- a/media/libstagefright/exports.lds
+++ b/media/libstagefright/exports.lds
@@ -8,19 +8,19 @@
_ZNK7android14ColorConverter*;
_ZN7android16SoftwareRenderer*;
ABGRToARGB;
- ABGRToI420;
+ ABGRToI420*;
ABGRToUVRow_Any_NEON*;
ABGRToUVRow_C;
ABGRToUVRow_NEON*;
ABGRToYRow_Any_NEON*;
ABGRToYRow_C;
ABGRToYRow_NEON*;
- Android420ToI420;
+ Android420ToI420*;
ARGB1555ToARGB;
ARGB1555ToARGBRow_Any_NEON*;
ARGB1555ToARGBRow_C;
ARGB1555ToARGBRow_NEON*;
- ARGB1555ToI420;
+ ARGB1555ToI420*;
ARGB1555ToUVRow_Any_NEON*;
ARGB1555ToUVRow_C;
ARGB1555ToUVRow_NEON*;
@@ -31,7 +31,7 @@
ARGB4444ToARGBRow_Any_NEON*;
ARGB4444ToARGBRow_C;
ARGB4444ToARGBRow_NEON*;
- ARGB4444ToI420;
+ ARGB4444ToI420*;
ARGB4444ToUVRow_Any_NEON*;
ARGB4444ToUVRow_C;
ARGB4444ToUVRow_NEON*;
@@ -115,7 +115,7 @@
ARGBToARGB4444Row_C;
ARGBToARGB4444Row_NEON*;
ARGBToBGRA;
- ARGBToI420;
+ ARGBToI420*;
ARGBToRAWRow_Any_NEON*;
ARGBToRAWRow_C;
ARGBToRAWRow_NEON*;
@@ -147,7 +147,7 @@
ARGBUnattenuateRow_C;
ArmCpuCaps*;
BGRAToARGB;
- BGRAToI420;
+ BGRAToI420*;
BGRAToUVRow_Any_NEON*;
BGRAToUVRow_C;
BGRAToUVRow_NEON*;
@@ -156,9 +156,9 @@
BGRAToYRow_NEON*;
BlendPlane;
BlendPlaneRow_C;
- CanonicalFourCC;
+ CanonicalFourCC*;
ComputeCumulativeSumRow_C;
- ConvertFromI420;
+ ConvertFromI420*;
CopyPlane;
CopyPlane_16;
CopyRow_16_C;
@@ -182,40 +182,40 @@
HalfFloatRow_Any_NEON*;
HalfFloatRow_C;
HalfFloatRow_NEON*;
- I400Copy;
+ I400Copy*;
I400Mirror;
I400ToARGB;
I400ToARGBRow_Any_NEON*;
I400ToARGBRow_C;
I400ToARGBRow_NEON*;
I400ToI400;
- I400ToI420;
+ I400ToI420*;
I420AlphaToABGR;
I420AlphaToARGB;
I420Blend;
- I420Copy;
+ I420Copy*;
I420Interpolate;
I420Mirror;
I420Rect;
- I420Scale;
- I420Scale_16;
+ I420Scale*;
+ I420Scale_16*;
I420ToABGR;
I420ToARGB;
I420ToARGB1555;
I420ToARGB4444;
I420ToBGRA;
I420ToI400;
- I420ToI422;
- I420ToI444;
- I420ToNV12;
- I420ToNV21;
+ I420ToI422*;
+ I420ToI444*;
+ I420ToNV12*;
+ I420ToNV21*;
I420ToRAW;
I420ToRGB24;
I420ToRGB565;
I420ToRGB565Dither;
I420ToRGBA;
- I420ToUYVY;
- I420ToYUY2;
+ I420ToUYVY*;
+ I420ToYUY2*;
I422AlphaToARGBRow_Any_NEON*;
I422AlphaToARGBRow_C;
I422AlphaToARGBRow_NEON*;
@@ -232,7 +232,7 @@
I422ToARGBRow_C;
I422ToARGBRow_NEON*;
I422ToBGRA;
- I422ToI420;
+ I422ToI420*;
I422ToRGB24Row_Any_NEON*;
I422ToRGB24Row_C;
I422ToRGB24Row_NEON*;
@@ -244,11 +244,11 @@
I422ToRGBARow_Any_NEON*;
I422ToRGBARow_C;
I422ToRGBARow_NEON*;
- I422ToUYVY;
+ I422ToUYVY*;
I422ToUYVYRow_Any_NEON*;
I422ToUYVYRow_C;
I422ToUYVYRow_NEON*;
- I422ToYUY2;
+ I422ToYUY2*;
I422ToYUY2Row_Any_NEON*;
I422ToYUY2Row_C;
I422ToYUY2Row_NEON*;
@@ -258,7 +258,7 @@
I444ToARGBRow_Any_NEON*;
I444ToARGBRow_C;
I444ToARGBRow_NEON*;
- I444ToI420;
+ I444ToI420*;
InitCpuFlags*;
InterpolatePlane;
InterpolateRow_16_C;
@@ -280,8 +280,8 @@
kYvuH709Constants;
kYvuI601Constants;
kYvuJPEGConstants;
- M420ToARGB;
- M420ToI420;
+ M420ToARGB*;
+ M420ToI420*;
MaskCpuFlags*;
MergeUVPlane;
MergeUVRow_Any_NEON*;
@@ -297,7 +297,7 @@
NV12ToARGBRow_Any_NEON*;
NV12ToARGBRow_C;
NV12ToARGBRow_NEON*;
- NV12ToI420;
+ NV12ToI420*;
NV12ToRGB565;
NV12ToRGB565Row_Any_NEON*;
NV12ToRGB565Row_C;
@@ -306,12 +306,12 @@
NV21ToARGBRow_Any_NEON*;
NV21ToARGBRow_C;
NV21ToARGBRow_NEON*;
- NV21ToI420;
+ NV21ToI420*;
RAWToARGB;
RAWToARGBRow_Any_NEON*;
RAWToARGBRow_C;
RAWToARGBRow_NEON*;
- RAWToI420;
+ RAWToI420*;
RAWToRGB24;
RAWToRGB24Row_Any_NEON*;
RAWToRGB24Row_C;
@@ -326,7 +326,7 @@
RGB24ToARGBRow_Any_NEON*;
RGB24ToARGBRow_C;
RGB24ToARGBRow_NEON*;
- RGB24ToI420;
+ RGB24ToI420*;
RGB24ToUVRow_Any_NEON*;
RGB24ToUVRow_C;
RGB24ToUVRow_NEON*;
@@ -337,7 +337,7 @@
RGB565ToARGBRow_Any_NEON*;
RGB565ToARGBRow_C;
RGB565ToARGBRow_NEON*;
- RGB565ToI420;
+ RGB565ToI420*;
RGB565ToUVRow_Any_NEON*;
RGB565ToUVRow_C;
RGB565ToUVRow_NEON*;
@@ -345,7 +345,7 @@
RGB565ToYRow_C;
RGB565ToYRow_NEON*;
RGBAToARGB;
- RGBAToI420;
+ RGBAToI420*;
RGBAToUVRow_Any_NEON*;
RGBAToUVRow_C;
RGBAToUVRow_NEON*;
@@ -355,7 +355,7 @@
RGBColorMatrix;
RGBColorTable;
RGBColorTableRow_C;
- Scale;
+ Scale*;
ScaleAddRow_16_C;
ScaleAddRow_C;
ScaleAddRows_NEON*;
@@ -395,12 +395,12 @@
ScaleFilterCols_NEON*;
ScaleFilterReduce;
ScaleFilterRows_NEON*;
- ScalePlane;
- ScalePlane_16;
- ScalePlaneBilinearDown;
- ScalePlaneBilinearDown_16;
- ScalePlaneBilinearUp;
- ScalePlaneBilinearUp_16;
+ ScalePlane*;
+ ScalePlane_16*;
+ ScalePlaneBilinearDown*;
+ ScalePlaneBilinearDown_16*;
+ ScalePlaneBilinearUp*;
+ ScalePlaneBilinearUp_16*;
ScalePlaneVertical;
ScalePlaneVertical_16;
ScaleRowDown2_16_C;
@@ -475,7 +475,7 @@
UYVYToARGBRow_Any_NEON*;
UYVYToARGBRow_C;
UYVYToARGBRow_NEON*;
- UYVYToI420;
+ UYVYToI420*;
UYVYToI422;
UYVYToNV12;
UYVYToUV422Row_Any_NEON*;
@@ -491,7 +491,7 @@
YUY2ToARGBRow_Any_NEON*;
YUY2ToARGBRow_C;
YUY2ToARGBRow_NEON*;
- YUY2ToI420;
+ YUY2ToI420*;
YUY2ToI422;
YUY2ToNV12;
YUY2ToUV422Row_Any_NEON*;
diff --git a/media/libstagefright/omx/OMXStore.cpp b/media/libstagefright/omx/OMXStore.cpp
index 4827d9e..0906433 100644
--- a/media/libstagefright/omx/OMXStore.cpp
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -140,7 +140,8 @@
Vector<String8> roles;
OMX_ERRORTYPE err = plugin->getRolesOfComponent(name, &roles);
- if (err == OMX_ErrorNone) {
+ static_assert(std::string_view("OMX.google.").size() == 11);
+ if (err == OMX_ErrorNone && strncmp(name, "OMX.google.", 11) == 0) {
bool skip = false;
for (String8 role : roles) {
if (role.find("video_decoder") != -1 || role.find("video_encoder") != -1) {
diff --git a/media/libstagefright/renderfright/gl/ProgramCache.cpp b/media/libstagefright/renderfright/gl/ProgramCache.cpp
index 3ae35ec..af55172 100644
--- a/media/libstagefright/renderfright/gl/ProgramCache.cpp
+++ b/media/libstagefright/renderfright/gl/ProgramCache.cpp
@@ -299,8 +299,8 @@
highp vec3 ScaleLuminance(highp vec3 color) {
// The formula is:
// alpha * pow(Y, gamma - 1.0) * color + beta;
- // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
- return color * 1000.0 * pow(color.y, 0.2);
+ // where alpha is displayMaxLuminance, gamma is 1.2, beta is 0.0.
+ return color * displayMaxLuminance * pow(color.y, 0.2);
}
)__SHADER__";
break;
@@ -316,7 +316,6 @@
// Tone map absolute light to display luminance range.
switch (needs.getInputTF()) {
case Key::INPUT_TF_ST2084:
- case Key::INPUT_TF_HLG:
switch (needs.getOutputTF()) {
case Key::OUTPUT_TF_HLG:
// Right now when mixed PQ and HLG contents are presented,
@@ -374,7 +373,11 @@
return color * slope;
} else if (nits < x1) {
// scale [x0, x1] to [y0, y1] linearly
- float slope = (y1 - y0) / (x1 - x0);
+ // Use highp since some compilers may do this
+ // operation as reciprocal multiplication with
+ // re-association that could exceed the range
+ // of mediump float.
+ highp float slope = (y1 - y0) / (x1 - x0);
nits = y0 + (nits - x0) * slope;
} else if (nits < x2) {
// scale [x1, x2] to [y1, y2] using Hermite interp
@@ -396,6 +399,14 @@
break;
}
break;
+ case Key::INPUT_TF_HLG:
+ // HLG OOTF is already applied as part of ScaleLuminance
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ break;
default:
// inverse tone map; the output luminance can be up to maxOutLumi.
fs << R"__SHADER__(
diff --git a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
index 2697ff4..3a67cc2 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
@@ -60,7 +60,7 @@
}
static sp<GraphicBuffer> allocateDefaultBuffer() {
- return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+ return sp<GraphicBuffer>::make(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER,
@@ -69,7 +69,7 @@
// Allocates a 1x1 buffer to fill with a solid color
static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
- return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ return sp<GraphicBuffer>::make(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_TEXTURE,
"input");
@@ -275,7 +275,7 @@
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
// Meaningless buffer since we don't do any drawing
- sp<GraphicBuffer> buffer = new GraphicBuffer();
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
invokeDraw(settings, layers, buffer);
}
diff --git a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
index 97c7442..b82586b 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
@@ -70,7 +70,7 @@
}
TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
+ sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
.WillOnce(Return(NO_ERROR));
status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
@@ -83,7 +83,7 @@
}
TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
+ sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
mThreadedRE->cacheExternalTextureBuffer(buf);
}
@@ -198,7 +198,7 @@
TEST_F(RenderEngineThreadedTest, drawLayers) {
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
- sp<GraphicBuffer> buffer = new GraphicBuffer();
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
base::unique_fd bufferFence;
base::unique_fd drawFence;
diff --git a/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.cpp b/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.cpp
index d94c8ff..9f46a74 100644
--- a/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.cpp
+++ b/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.cpp
@@ -66,8 +66,8 @@
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
- const char *trackMime;
- if (!strcasecmp(mime.c_str(), trackMime)) {
+ std::string trackMime = dataProvider->PickValueInArray(kTestedMimeTypes);
+ if (!strcasecmp(mime.c_str(), trackMime.c_str())) {
sp<IMediaSource> track = extractor->getTrack(i);
if (track == NULL) {
return NULL;
diff --git a/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.h b/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.h
index 98bfb94..6856ac0 100644
--- a/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.h
+++ b/media/libstagefright/tests/fuzzers/FuzzerMediaUtility.h
@@ -42,6 +42,51 @@
kMaxValue = MPEG2TS,
};
+static std::string kTestedMimeTypes[] = {"audio/3gpp",
+ "audio/amr-wb",
+ "audio/vorbis",
+ "audio/opus",
+ "audio/mp4a-latm",
+ "audio/mpeg",
+ "audio/mpeg-L1",
+ "audio/mpeg-L2",
+ "audio/midi",
+ "audio/qcelp",
+ "audio/g711-alaw",
+ "audio/g711-mlaw",
+ "audio/flac",
+ "audio/aac-adts",
+ "audio/gsm",
+ "audio/ac3",
+ "audio/eac3",
+ "audio/eac3-joc",
+ "audio/ac4",
+ "audio/scrambled",
+ "audio/alac",
+ "audio/x-ms-wma",
+ "audio/x-adpcm-ms",
+ "audio/x-adpcm-dvi-ima",
+ "video/avc",
+ "video/hevc",
+ "video/mp4v-es",
+ "video/3gpp",
+ "video/x-vnd.on2.vp8",
+ "video/x-vnd.on2.vp9",
+ "video/av01",
+ "video/mpeg2",
+ "video/dolby-vision",
+ "video/scrambled",
+ "video/divx",
+ "video/divx3",
+ "video/xvid",
+ "video/x-motion-jpeg",
+ "text/3gpp-tt",
+ "application/x-subrip",
+ "text/vtt",
+ "text/cea-608",
+ "text/cea-708",
+ "application/x-id3v4"};
+
std::string genMimeType(FuzzedDataProvider *dataProvider);
sp<IMediaExtractor> genMediaExtractor(FuzzedDataProvider *dataProvider, uint16_t dataAmount);
sp<MediaSource> genMediaSource(FuzzedDataProvider *dataProvider, uint16_t maxMediaBlobSize);
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
index edddaa4..19f9549 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -26,7 +26,33 @@
],
}
-cc_binary {
+prebuilt_etc {
+ name: "mediaserver.zygote64_32.rc",
+ src: "mediaserver.zygote64_32.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
+ name: "mediaserver.zygote64.rc",
+ src: "mediaserver.zygote64.rc",
+ sub_dir: "init/hw",
+}
+
+soong_config_module_type {
+ name: "mediaserver_cc_binary",
+ module_type: "cc_binary",
+ config_namespace: "ANDROID",
+ bool_variables: ["TARGET_DYNAMIC_64_32_MEDIASERVER"],
+ properties: [
+ "compile_multilib",
+ "init_rc",
+ "multilib.lib32.suffix",
+ "multilib.lib64.suffix",
+ "required",
+ ],
+}
+
+mediaserver_cc_binary {
name: "mediaserver",
srcs: ["main_mediaserver.cpp"],
@@ -55,12 +81,32 @@
// ****************************************************************
compile_multilib: "prefer32",
- init_rc: ["mediaserver.rc"],
-
cflags: [
"-Werror",
"-Wall",
],
vintf_fragments: ["manifest_media_c2_software.xml"],
+
+ soong_config_variables: {
+ TARGET_DYNAMIC_64_32_MEDIASERVER: {
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ required: [
+ "mediaserver.zygote64_32.rc",
+ "mediaserver.zygote64.rc",
+ ],
+ init_rc: ["mediaserver_dynamic.rc"],
+ conditions_default: {
+ init_rc: ["mediaserver.rc"],
+ },
+ },
+ },
}
diff --git a/media/mediaserver/mediaserver.zygote64.rc b/media/mediaserver/mediaserver.zygote64.rc
new file mode 100644
index 0000000..8842b01
--- /dev/null
+++ b/media/mediaserver/mediaserver.zygote64.rc
@@ -0,0 +1,6 @@
+service media /system/bin/mediaserver64
+ class main
+ user media
+ group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
+ ioprio rt 4
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/media/mediaserver/mediaserver.zygote64_32.rc b/media/mediaserver/mediaserver.zygote64_32.rc
new file mode 100644
index 0000000..4039073
--- /dev/null
+++ b/media/mediaserver/mediaserver.zygote64_32.rc
@@ -0,0 +1,6 @@
+service media /system/bin/mediaserver32
+ class main
+ user media
+ group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
+ ioprio rt 4
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/media/mediaserver/mediaserver_dynamic.rc b/media/mediaserver/mediaserver_dynamic.rc
new file mode 100644
index 0000000..65d5c40
--- /dev/null
+++ b/media/mediaserver/mediaserver_dynamic.rc
@@ -0,0 +1,4 @@
+on property:init.svc.media=*
+ setprop init.svc.mediadrm ${init.svc.media}
+
+import /system/etc/init/hw/mediaserver.${ro.zygote}.rc
diff --git a/media/mtp/Android.bp b/media/mtp/Android.bp
index 97e2a22..719d05a 100644
--- a/media/mtp/Android.bp
+++ b/media/mtp/Android.bp
@@ -31,8 +31,8 @@
],
}
-cc_library_shared {
- name: "libmtp",
+cc_defaults {
+ name: "libmtp_defaults",
srcs: [
"MtpDataPacket.cpp",
"MtpDebug.cpp",
@@ -71,3 +71,14 @@
],
header_libs: ["libcutils_headers"],
}
+
+cc_library_shared {
+ name: "libmtp",
+ defaults: ["libmtp_defaults"],
+}
+
+cc_library_shared {
+ name: "libmtp_fuzz",
+ defaults: ["libmtp_defaults"],
+ cflags: ["-DMTP_FUZZER"],
+}
diff --git a/media/mtp/MtpDescriptors.h b/media/mtp/MtpDescriptors.h
index d600a24..9b98a71 100644
--- a/media/mtp/MtpDescriptors.h
+++ b/media/mtp/MtpDescriptors.h
@@ -23,6 +23,17 @@
namespace android {
+#ifdef MTP_FUZZER
+constexpr char FFS_MTP_EP0[] = "/data/local/tmp/usb-ffs/mtp/ep0";
+constexpr char FFS_MTP_EP_IN[] = "/data/local/tmp/usb-ffs/mtp/ep1";
+constexpr char FFS_MTP_EP_OUT[] = "/data/local/tmp/usb-ffs/mtp/ep2";
+constexpr char FFS_MTP_EP_INTR[] = "/data/local/tmp/usb-ffs/mtp/ep3";
+
+constexpr char FFS_PTP_EP0[] = "/data/local/tmp/usb-ffs/ptp/ep0";
+constexpr char FFS_PTP_EP_IN[] = "/data/local/tmp/usb-ffs/ptp/ep1";
+constexpr char FFS_PTP_EP_OUT[] = "/data/local/tmp/usb-ffs/ptp/ep2";
+constexpr char FFS_PTP_EP_INTR[] = "/data/local/tmp/usb-ffs/ptp/ep3";
+#else
constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";
constexpr char FFS_MTP_EP_IN[] = "/dev/usb-ffs/mtp/ep1";
constexpr char FFS_MTP_EP_OUT[] = "/dev/usb-ffs/mtp/ep2";
@@ -32,6 +43,7 @@
constexpr char FFS_PTP_EP_IN[] = "/dev/usb-ffs/ptp/ep1";
constexpr char FFS_PTP_EP_OUT[] = "/dev/usb-ffs/ptp/ep2";
constexpr char FFS_PTP_EP_INTR[] = "/dev/usb-ffs/ptp/ep3";
+#endif
constexpr int MAX_PACKET_SIZE_FS = 64;
constexpr int MAX_PACKET_SIZE_HS = 512;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6fcf119..d917772 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -985,7 +985,7 @@
int type = storage->getType();
if (type == MTP_STORAGE_REMOVABLE_RAM) {
- std::string str = android::base::Trim((const char*)name);
+ std::string str = android::base::Trim(name);
name.set(str.c_str());
}
ALOGV("name: %s format: 0x%04X (%s)\n", (const char*)name, format,
diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h
index 4cec58a..30d1bc1 100644
--- a/media/mtp/MtpStringBuffer.h
+++ b/media/mtp/MtpStringBuffer.h
@@ -20,6 +20,7 @@
#include <log/log.h>
#include <stdint.h>
#include <string>
+#include <string_view>
// Max Character number of a MTP String
#define MTP_STRING_MAX_CHARACTER_NUMBER 255
@@ -55,6 +56,7 @@
inline int size() const { return mString.length(); }
inline operator const char*() const { return mString.c_str(); }
+ operator std::string_view() const { return mString; }
};
inline void MtpStringBuffer::append(const char* other) {
diff --git a/media/mtp/tests/MtpFuzzer/Android.bp b/media/mtp/tests/MtpFuzzer/Android.bp
index 5365f4b..9e41680 100644
--- a/media/mtp/tests/MtpFuzzer/Android.bp
+++ b/media/mtp/tests/MtpFuzzer/Android.bp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -6,29 +22,20 @@
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_av_media_mtp_license"],
}
-
-cc_fuzz {
- name: "mtp_fuzzer",
- srcs: [
- "mtp_fuzzer.cpp",
- "MtpMockDatabase.cpp",
- ],
+cc_defaults {
+ name: "mtp_fuzzer_defaults",
shared_libs: [
- "libmtp",
- "libbase",
- "liblog",
- "libutils",
+ "libbase",
+ "liblog",
+ "libutils",
],
+ static_libs: ["libc++fs",],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
- "-DMTP_DEVICE",
"-Wno-unused-parameter",
],
- dictionary: "mtp_fuzzer.dict",
- corpus: ["corpus/*"],
-
fuzz_config: {
cc: ["jameswei@google.com"],
@@ -38,3 +45,107 @@
],
},
}
+cc_fuzz {
+ name: "mtp_fuzzer",
+ srcs: [
+ "mtp_fuzzer.cpp",
+ "MtpMockDatabase.cpp",
+ ],
+ cflags: ["-DMTP_DEVICE",],
+ shared_libs: ["libmtp",],
+ defaults: ["mtp_fuzzer_defaults"],
+ dictionary: "mtp_fuzzer.dict",
+ corpus: ["corpus/*"],
+}
+
+cc_defaults {
+ name: "mtp_property_fuzzer_defaults",
+ srcs: ["mtp_property_fuzzer.cpp"],
+ shared_libs: [
+ "libmtp",
+ "libasyncio",
+ "libusbhost",
+ ],
+ defaults: ["mtp_fuzzer_defaults"],
+}
+
+cc_fuzz {
+ name: "mtp_device_property_fuzzer",
+ defaults: ["mtp_property_fuzzer_defaults"],
+ cflags: [
+ "-DMTP_DEVICE",
+ ],
+}
+
+cc_fuzz {
+ name: "mtp_host_property_fuzzer",
+ defaults: ["mtp_property_fuzzer_defaults"],
+ cflags: [
+ "-DMTP_HOST",
+ ],
+}
+
+cc_fuzz {
+ name: "mtp_handle_fuzzer",
+ srcs: ["mtp_handle_fuzzer.cpp"],
+ shared_libs: ["libmtp_fuzz"],
+ defaults: ["mtp_fuzzer_defaults"],
+ cflags: ["-DMTP_FUZZER"],
+}
+
+cc_defaults {
+ name: "mtp_packet_defaults",
+ shared_libs: [
+ "libmtp",
+ "libasyncio",
+ "libusbhost",
+ ],
+ defaults: ["mtp_fuzzer_defaults"],
+ cflags: [
+ "-DMTP_HOST",
+ "-DMTP_DEVICE",
+ ],
+}
+
+cc_fuzz {
+ name: "mtp_packet_fuzzer",
+ srcs: ["mtp_packet_fuzzer.cpp"],
+ defaults: ["mtp_packet_defaults"],
+}
+
+cc_fuzz {
+ name: "mtp_device_fuzzer",
+ srcs: ["mtp_device_fuzzer.cpp"],
+ shared_libs: [
+ "libmtp",
+ "libusbhost",
+ ],
+ defaults: ["mtp_fuzzer_defaults"],
+ cflags: [
+ "-DMTP_DEVICE",
+ ],
+}
+
+cc_fuzz {
+ name: "mtp_request_packet_fuzzer",
+ srcs: ["mtp_request_packet_fuzzer.cpp"],
+ defaults: ["mtp_packet_defaults"],
+}
+
+cc_fuzz {
+ name: "mtp_event_packet_fuzzer",
+ srcs: ["mtp_event_packet_fuzzer.cpp"],
+ defaults: ["mtp_packet_defaults"],
+}
+
+cc_fuzz {
+ name: "mtp_response_packet_fuzzer",
+ srcs: ["mtp_response_packet_fuzzer.cpp"],
+ defaults: ["mtp_packet_defaults"],
+}
+
+cc_fuzz {
+ name: "mtp_data_packet_fuzzer",
+ srcs: ["mtp_data_packet_fuzzer.cpp"],
+ defaults: ["mtp_packet_defaults"],
+}
diff --git a/media/mtp/tests/MtpFuzzer/MtpPacketFuzzerUtils.h b/media/mtp/tests/MtpFuzzer/MtpPacketFuzzerUtils.h
new file mode 100644
index 0000000..87fea9f
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpPacketFuzzerUtils.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpStringBuffer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <linux/usbdevice_fs.h>
+#include <sys/mman.h>
+#include <usbhost/usbhost.h>
+#include <MtpTypes.h>
+
+using namespace android;
+constexpr UrbPacketDivisionMode kUrbPacketDivisionModes[] = {FIRST_PACKET_ONLY_HEADER,
+ FIRST_PACKET_HAS_PAYLOAD};
+
+constexpr size_t kMinSize = 0;
+constexpr size_t kMaxSize = 1000;
+constexpr size_t kMaxLength = 1000;
+
+class MtpPacketFuzzerUtils {
+ protected:
+ struct usb_request mUsbRequest;
+ struct usbdevfs_urb* mUsbDevFsUrb;
+ std::string mPath;
+
+ void fillFd(int32_t& fd, FuzzedDataProvider* fdp) {
+ if (fdp->ConsumeBool()) {
+ std::string text = fdp->ConsumeRandomLengthString(kMaxLength);
+ write(fd, text.c_str(), text.length());
+ }
+ };
+
+ void fillFilePath(FuzzedDataProvider* fdp) {
+ mPath= fdp->ConsumeRandomLengthString(kMaxLength);
+ };
+
+ void fillUsbDevFsUrb(FuzzedDataProvider* fdp) {
+ mUsbDevFsUrb->type = fdp->ConsumeIntegral<unsigned char>();
+ mUsbDevFsUrb->endpoint = fdp->ConsumeIntegral<unsigned char>();
+ mUsbDevFsUrb->flags = fdp->ConsumeIntegral<uint32_t>();
+ std::vector<uint8_t> buffer =
+ fdp->ConsumeBytes<uint8_t>(fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ mUsbDevFsUrb->buffer = static_cast<void*>(buffer.data());
+ mUsbDevFsUrb->buffer_length = buffer.size();
+ mUsbDevFsUrb->actual_length = fdp->ConsumeIntegral<uint32_t>();
+ mUsbDevFsUrb->start_frame = fdp->ConsumeIntegral<uint32_t>();
+ mUsbDevFsUrb->number_of_packets = fdp->ConsumeIntegral<uint32_t>();
+ mUsbDevFsUrb->stream_id = fdp->ConsumeIntegral<uint32_t>();
+ mUsbDevFsUrb->error_count = fdp->ConsumeIntegral<size_t>();
+ mUsbDevFsUrb->signr = fdp->ConsumeIntegral<uint32_t>();
+ std::vector<uint8_t> userBuffer = (fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize)));
+ mUsbDevFsUrb->usercontext = static_cast<void*>(userBuffer.data());
+ mUsbDevFsUrb->iso_frame_desc[0].length = fdp->ConsumeIntegral<uint32_t>();
+ mUsbDevFsUrb->iso_frame_desc[0].actual_length = fdp->ConsumeIntegral<uint32_t>();
+ };
+
+ void fillUsbRequest(int32_t& fd, FuzzedDataProvider* fdp) {
+ fillUsbDevFsUrb(fdp);
+ fillFd(fd, fdp);
+ std::vector<uint8_t> buffer =
+ fdp->ConsumeBytes<uint8_t>(fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ mUsbRequest.buffer = static_cast<void*>(buffer.data());
+ mUsbRequest.buffer_length = buffer.size();
+ mUsbRequest.actual_length = fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ mUsbRequest.max_packet_size = fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ mUsbRequest.private_data = static_cast<void*>(mUsbDevFsUrb);
+ mUsbRequest.endpoint = fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ std::vector<uint8_t> clientBuffer = (fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize)));
+ mUsbRequest.client_data = static_cast<void*>(clientBuffer.data());
+ };
+
+ template <typename Object>
+ void writeHandle(Object obj, FuzzedDataProvider* fdp) {
+ MtpDevHandle handle;
+ std::vector<uint8_t> initData =
+ fdp->ConsumeBytes<uint8_t>(fdp->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ handle.write(initData.data(), initData.size());
+ obj->write(&handle);
+ };
+};
diff --git a/media/mtp/tests/MtpFuzzer/README.md b/media/mtp/tests/MtpFuzzer/README.md
new file mode 100644
index 0000000..7efaf67
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/README.md
@@ -0,0 +1,210 @@
+# Fuzzers for libmtp
+
+## Table of contents
++ [mtp_fuzzer](#MtpServer)
++ [mtp_host_property_fuzzer](#MtpHostProperty)
++ [mtp_device_property_fuzzer](#MtpDeviceProperty)
++ [mtp_handle_fuzzer](#MtpHandle)
++ [mtp_packet_fuzzer](#MtpPacket)
+ + [mtp_device_fuzzer](#MtpDevice)
++ [mtp_request_packet_fuzzer](#MtpRequestPacket)
++ [mtp_event_packet_fuzzer](#MtpEventPacket)
++ [mtp_response_packet_fuzzer](#MtpResponsePacket)
++ [mtp_data_packet_fuzzer](#MtpDataPacket)
+
+# <a name="MtpServer"></a> Fuzzer for MtpServer
+
+MtpServer supports the following parameters:
+1. PacketData (parameter name: "packetData")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`packetData`| `String` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_fuzzer/mtp_fuzzer corpus/ -dict=mtp_fuzzer.dict
+```
+
+# <a name="MtpHostProperty"></a> Fuzzer for MtpHostProperty
+
+MtpHostProperty supports the following parameters:
+1. Feasible Type (parameter name: "kFeasibleTypes")
+2. UrbPacket Division Mode (parameter name: "kUrbPacketDivisionModes")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+| `kFeasibleType`| 1. `MTP_TYPE_UNDEFINED`, 2. `MTP_TYPE_INT8`, 3.`MTP_TYPE_UINT8`, 4.`MTP_TYPE_INT16`, 5.`MTP_TYPE_UINT16`, 6.`MTP_TYPE_INT32`, 7.`MTP_TYPE_UINT32`, 8.`MTP_TYPE_INT64`, 9.`MTP_TYPE_UINT64`, 10.`MTP_TYPE_INT128`, 11.`MTP_TYPE_UINT128`, 12.`MTP_TYPE_AINT8`, 13.`MTP_TYPE_AUINT8`, 14.`MTP_TYPE_AINT16`, 15.`MTP_TYPE_AUINT16`, 16.`MTP_TYPE_AINT32`, 17.`MTP_TYPE_AUINT32`, 18.`MTP_TYPE_AINT64`, 19.`MTP_TYPE_AUINT64`, 20.`MTP_TYPE_AINT128`, 21.`MTP_TYPE_AUINT128`, 22.`MTP_TYPE_STR`,| Value obtained from FuzzedDataProvider|
+|`kUrbPacketDivisionMode`| 1. `FIRST_PACKET_ONLY_HEADER`, 2. `FIRST_PACKET_HAS_PAYLOAD`, |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_host_property_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_host_property_fuzzer/mtp_host_property_fuzzer
+```
+
+# <a name="MtpDeviceProperty"></a> Fuzzer for MtpDeviceProperty
+
+MtpDeviceProperty supports the following parameters:
+1. Feasible Type (parameter name: "kFeasibleType")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+| `kFeasibleType`| 1. `MTP_TYPE_UNDEFINED`, 2. `MTP_TYPE_INT8`, 3.`MTP_TYPE_UINT8`, 4.`MTP_TYPE_INT16`, 5.`MTP_TYPE_UINT16`, 6.`MTP_TYPE_INT32`, 7.`MTP_TYPE_UINT32`, 8.`MTP_TYPE_INT64`, 9.`MTP_TYPE_UINT64`, 10.`MTP_TYPE_INT128`, 11.`MTP_TYPE_UINT128`, 12.`MTP_TYPE_AINT8`, 13.`MTP_TYPE_AUINT8`, 14.`MTP_TYPE_AINT16`, 15.`MTP_TYPE_AUINT16`, 16.`MTP_TYPE_AINT32`, 17.`MTP_TYPE_AUINT32`, 18.`MTP_TYPE_AINT64`, 19.`MTP_TYPE_AUINT64`, 20.`MTP_TYPE_AINT128`, 21.`MTP_TYPE_AUINT128`, 22.`MTP_TYPE_STR`,| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_device_property_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_device_property_fuzzer/mtp_device_property_fuzzer
+```
+
+# <a name="MtpHandle"></a>Fuzzer for MtpHandle
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_handle_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_handle_fuzzer/mtp_handle_fuzzer
+```
+
+# <a name="MtpPacket"></a> Fuzzer for MtpPacket
+
+MtpPacket supports the following parameters:
+1. bufferSize (parameter name: "size")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`bufferSize`| Integer `1` to `1000`, |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_packet_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_packet_fuzzer/mtp_packet_fuzzer
+```
+
+# <a name="MtpDevice"></a> Fuzzer for MtpDevice
+
+MtpDevice supports the following parameters:
+1. Device Name (parameter name: "deviceName")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`deviceName`| `String` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_device_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_device_fuzzer/mtp_device_fuzzer
+```
+
+# <a name="MtpRequestPacket"></a> Fuzzer for MtpRequestPacket
+
+MtpRequestPacket supports the following parameters:
+1. Data (parameter name: "data")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`data`| Vector of positive Integer |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_request_packet_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_request_packet_fuzzer/mtp_request_packet_fuzzer
+```
+
+# <a name="MtpEventPacket"></a> Fuzzer for MtpEventPacket
+
+MtpEventPacket supports the following parameters:
+1. Size (parameter name: "size")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`size`| Integer `1` to `1000`, |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_event_packet_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_event_packet_fuzzer/mtp_event_packet_fuzzer
+```
+
+# <a name="MtpResponsePacket"></a> Fuzzer for MtpResponsePacket
+
+MtpResponsePacket supports the following parameters:
+1. Size (parameter name: "size")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`size`| Integer `1` to `1000`, |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_response_packet_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_response_packet_fuzzer/mtp_response_packet_fuzzer
+```
+
+# <a name="MtpDataPacket"></a> Fuzzer for MtpDataPacket
+
+MtpDataPacket supports the following parameters:
+1. UrbPacket Division Mode (parameter name: "kUrbPacketDivisionModes")
+2. Size (parameter name: "size")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`kUrbPacketDivisionMode`| 1. `FIRST_PACKET_ONLY_HEADER`, 2. `FIRST_PACKET_HAS_PAYLOAD`, |Value obtained from FuzzedDataProvider|
+|`size`| Integer `1` to `1000`, |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) mtp_data_packet_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mtp_data_packet_fuzzer/mtp_data_packet_fuzzer
+```
diff --git a/media/mtp/tests/MtpFuzzer/corpus/6-mtp-open_session_send_object_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/6-mtp-open_session_send_object_info.pkt
new file mode 100644
index 0000000..71f2836
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/6-mtp-open_session_send_object_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/mtp_data_packet_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_data_packet_fuzzer.cpp
new file mode 100644
index 0000000..f5faf77
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_data_packet_fuzzer.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDataPacket.h>
+#include <MtpDevHandle.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+using namespace android;
+
+class MtpDataPacketFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpDataPacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mUsbDevFsUrb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) +
+ sizeof(struct usbdevfs_iso_packet_desc));
+ };
+ ~MtpDataPacketFuzzer() { free(mUsbDevFsUrb); };
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+void MtpDataPacketFuzzer::process() {
+ MtpDataPacket mtpDataPacket;
+ while (mFdp.remaining_bytes() > 0) {
+ auto mtpDataAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() { mtpDataPacket.allocate(mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize)); },
+ [&]() { mtpDataPacket.reset(); },
+ [&]() {
+ mtpDataPacket.setOperationCode(mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize));
+ },
+ [&]() {
+ mtpDataPacket.setTransactionID(mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize));
+ },
+ [&]() {
+ Int8List* result = mtpDataPacket.getAInt8();
+ delete result;
+ },
+ [&]() {
+ Int16List* result = mtpDataPacket.getAInt16();
+ delete result;
+ },
+ [&]() {
+ Int32List* result = mtpDataPacket.getAInt32();
+ delete result;
+ },
+ [&]() {
+ Int64List* result = mtpDataPacket.getAInt64();
+ delete result;
+ },
+ [&]() {
+ UInt8List* result = mtpDataPacket.getAUInt8();
+ delete result;
+ },
+ [&]() {
+ UInt16List* result = mtpDataPacket.getAUInt16();
+ delete result;
+ },
+ [&]() {
+ UInt32List* result = mtpDataPacket.getAUInt32();
+ delete result;
+ },
+ [&]() {
+ UInt64List* result = mtpDataPacket.getAUInt64();
+ delete result;
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<uint8_t> initData =
+ mFdp.ConsumeBytes<uint8_t>(mFdp.ConsumeIntegral<uint8_t>());
+ mtpDataPacket.putAUInt8(initData.data(), initData.size());
+ } else {
+ mtpDataPacket.putAUInt8(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ uint16_t arr[size];
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<uint16_t>();
+ }
+ mtpDataPacket.putAUInt16(arr, size);
+ } else {
+ mtpDataPacket.putAUInt16(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ uint32_t arr[size];
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<uint32_t>();
+ }
+ mtpDataPacket.putAUInt32(arr, size);
+ } else {
+ mtpDataPacket.putAUInt32(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ uint64_t arr[size];
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<uint64_t>();
+ }
+ mtpDataPacket.putAUInt64(arr, size);
+ } else {
+ mtpDataPacket.putAUInt64(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ int64_t arr[size];
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<int64_t>();
+ }
+ mtpDataPacket.putAInt64(arr, size);
+ } else {
+ mtpDataPacket.putAInt64(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<uint16_t> arr;
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr.push_back(mFdp.ConsumeIntegral<uint16_t>());
+ }
+ mtpDataPacket.putAUInt16(&arr);
+ } else {
+ mtpDataPacket.putAUInt16(nullptr);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<uint32_t> arr;
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr.push_back(mFdp.ConsumeIntegral<uint32_t>());
+ }
+ mtpDataPacket.putAUInt32(&arr);
+ } else {
+ mtpDataPacket.putAUInt32(nullptr);
+ }
+ },
+
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ size_t size = mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+ int32_t arr[size];
+ for (size_t idx = 0; idx < size; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<int32_t>();
+ }
+ mtpDataPacket.putAInt32(arr, size);
+ } else {
+ mtpDataPacket.putAInt32(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ mtpDataPacket.putString(
+ (mFdp.ConsumeRandomLengthString(kMaxLength)).c_str());
+ } else {
+ mtpDataPacket.putString(static_cast<char*>(nullptr));
+ }
+ },
+ [&]() {
+ android::MtpStringBuffer sBuffer(
+ (mFdp.ConsumeRandomLengthString(kMaxLength)).c_str());
+ if (mFdp.ConsumeBool()) {
+ mtpDataPacket.getString(sBuffer);
+ } else {
+ mtpDataPacket.putString(sBuffer);
+ }
+ },
+ [&]() {
+ MtpDevHandle handle;
+ handle.start(mFdp.ConsumeBool());
+ std::string text = mFdp.ConsumeRandomLengthString(kMaxLength);
+ char* data = const_cast<char*>(text.c_str());
+ handle.read(static_cast<void*>(data), text.length());
+ if (mFdp.ConsumeBool()) {
+ mtpDataPacket.read(&handle);
+ } else if (mFdp.ConsumeBool()) {
+ mtpDataPacket.write(&handle);
+ } else {
+ std::string textData = mFdp.ConsumeRandomLengthString(kMaxLength);
+ char* Data = const_cast<char*>(textData.c_str());
+ mtpDataPacket.writeData(&handle, static_cast<void*>(Data),
+ textData.length());
+ }
+ handle.close();
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::string str = mFdp.ConsumeRandomLengthString(kMaxLength);
+ android::String16 s(str.c_str());
+ char16_t* data = const_cast<char16_t*>(s.string());
+ mtpDataPacket.putString(reinterpret_cast<uint16_t*>(data));
+ } else {
+ mtpDataPacket.putString(static_cast<uint16_t*>(nullptr));
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<int8_t> data = mFdp.ConsumeBytes<int8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ mtpDataPacket.putAInt8(data.data(), data.size());
+ } else {
+ mtpDataPacket.putAInt8(nullptr, 0);
+ }
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<uint8_t> data = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ mtpDataPacket.putAUInt8(data.data(), data.size());
+ } else {
+ mtpDataPacket.putAUInt8(nullptr, 0);
+ }
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ std::vector<int8_t> data = mFdp.ConsumeBytes<int8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ mtpDataPacket.readData(&mUsbRequest, data.data(), data.size());
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.write(
+ &mUsbRequest,
+ mFdp.PickValueInArray<UrbPacketDivisionMode>(kUrbPacketDivisionModes),
+ fd, mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize));
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.read(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.write(&mUsbRequest, mFdp.PickValueInArray<UrbPacketDivisionMode>(
+ kUrbPacketDivisionModes));
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.readDataHeader(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.readDataAsync(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpDataPacket.readDataWait(mUsbRequest.dev);
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ if (mFdp.ConsumeBool()) {
+ std::vector<int16_t> data;
+ for (size_t idx = 0;
+ idx < mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {
+ data.push_back(mFdp.ConsumeIntegral<int16_t>());
+ }
+ mtpDataPacket.putAInt16(data.data(), data.size());
+ } else {
+ mtpDataPacket.putAInt16(nullptr, 0);
+ }
+ },
+ [&]() {
+ int32_t arr[4];
+ for (size_t idx = 0; idx < 4; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<int32_t>();
+ }
+ mtpDataPacket.putInt128(arr);
+ },
+ [&]() { mtpDataPacket.putInt64(mFdp.ConsumeIntegral<int64_t>()); },
+ [&]() {
+ int16_t out;
+ mtpDataPacket.getInt16(out);
+ },
+ [&]() {
+ int32_t out;
+ mtpDataPacket.getInt32(out);
+ },
+ [&]() {
+ int8_t out;
+ mtpDataPacket.getInt8(out);
+ },
+ [&]() {
+ uint32_t arr[4];
+ for (size_t idx = 0; idx < 4; ++idx) {
+ arr[idx] = mFdp.ConsumeIntegral<uint32_t>();
+ }
+ if (mFdp.ConsumeBool()) {
+ mtpDataPacket.putUInt128(arr);
+ } else {
+ mtpDataPacket.getUInt128(arr);
+ }
+ },
+ [&]() { mtpDataPacket.putUInt64(mFdp.ConsumeIntegral<uint64_t>()); },
+ [&]() {
+ uint64_t out;
+ mtpDataPacket.getUInt64(out);
+ },
+ [&]() { mtpDataPacket.putInt128(mFdp.ConsumeIntegral<int64_t>()); },
+ [&]() { mtpDataPacket.putUInt128(mFdp.ConsumeIntegral<uint64_t>()); },
+ [&]() {
+ int32_t length;
+ void* data = mtpDataPacket.getData(&length);
+ free(data);
+ },
+ });
+ mtpDataAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpDataPacketFuzzer mtpDataPacketFuzzer(data, size);
+ mtpDataPacketFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_device_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_device_fuzzer.cpp
new file mode 100644
index 0000000..c4dd564
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_device_fuzzer.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDevHandle.h>
+#include <MtpDevice.h>
+#include <MtpDeviceInfo.h>
+#include <MtpObjectInfo.h>
+#include <MtpProperty.h>
+#include <MtpStorageInfo.h>
+#include <MtpStringBuffer.h>
+#include <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <linux/usb/ch9.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <usbhost/usbhost.h>
+
+using namespace android;
+
+constexpr int32_t kMaxStringLength = 20;
+constexpr int32_t kMaxBytes = 200;
+constexpr int32_t kMaxDataSize = 20;
+constexpr uint16_t kWMaxPacketSize = 64;
+constexpr uint16_t kEndpointsCount = 3;
+const std::string kInputFile = "/dev/null";
+const std::string kConfigFilePath = "/data/local/tmp/config";
+
+static bool readCallback(void* data, uint32_t offset, uint32_t length, void* clientData) {
+ return true;
+}
+
+struct fdDescriptors {
+ struct usb_interface_descriptor interface;
+ struct usb_endpoint_descriptor ep[kEndpointsCount];
+};
+
+fdDescriptors writeDescriptorsToFd(int32_t fd, FuzzedDataProvider& fdp) {
+ fdDescriptors desc;
+ desc.interface.bLength = sizeof(desc.interface);
+ desc.interface.bDescriptorType = USB_DT_INTERFACE;
+ desc.interface.bInterfaceNumber = fdp.ConsumeIntegral<uint8_t>();
+ desc.interface.bNumEndpoints = kEndpointsCount;
+ desc.interface.bInterfaceClass =
+ fdp.ConsumeBool() ? USB_CLASS_STILL_IMAGE : USB_CLASS_VENDOR_SPEC;
+ desc.interface.bInterfaceSubClass = fdp.ConsumeBool() ? 1 : 0xFF;
+ desc.interface.bInterfaceProtocol = fdp.ConsumeBool() ? 1 : 0;
+ desc.interface.iInterface = fdp.ConsumeIntegral<uint8_t>();
+ for (uint16_t idx = 0; idx < kEndpointsCount; ++idx) {
+ desc.ep[idx].bLength = sizeof(desc.ep[idx]);
+ desc.ep[idx].bDescriptorType = USB_DT_ENDPOINT;
+ desc.ep[idx].bEndpointAddress = idx | (fdp.ConsumeBool() ? USB_DIR_OUT : USB_DIR_IN);
+ desc.ep[idx].bmAttributes =
+ fdp.ConsumeBool() ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_INT;
+ desc.ep[idx].wMaxPacketSize = kWMaxPacketSize;
+ }
+ write(fd, &desc, sizeof(fdDescriptors));
+ return desc;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ int32_t fd = memfd_create(kConfigFilePath.c_str(), MFD_ALLOW_SEALING);
+ fdDescriptors descriptor = writeDescriptorsToFd(fd, fdp);
+ std::string deviceName = fdp.ConsumeRandomLengthString(kMaxStringLength);
+ usb_device* device = usb_device_new(deviceName.c_str(), fd);
+ MtpDevice mtpDevice(device, fdp.ConsumeIntegral<int32_t>(), &descriptor.ep[0],
+ &descriptor.ep[1], &descriptor.ep[2]);
+ MtpObjectInfo objectinfo(fdp.ConsumeIntegral<uint32_t>());
+ MtpStorageInfo storageInfo(fdp.ConsumeIntegral<uint32_t>());
+ while (fdp.remaining_bytes()) {
+ auto mtpDeviceFunction = fdp.PickValueInArray<const std::function<void()>>(
+ {[&]() { mtpDevice.getStorageIDs(); },
+ [&]() {
+ mtpDevice.getStorageInfo(fdp.ConsumeIntegral<int32_t>() /* storageID */);
+ },
+ [&]() {
+ mtpDevice.getObjectHandles(fdp.ConsumeIntegral<uint32_t>() /* storageID */,
+ fdp.ConsumeIntegral<uint16_t>() /* format */,
+ fdp.ConsumeIntegral<uint32_t>() /* parent */);
+ },
+ [&]() { mtpDevice.initialize(); },
+ [&]() {
+ int32_t outLength = 0;
+ mtpDevice.getThumbnail(fdp.ConsumeIntegral<uint32_t>() /* handle */,
+ outLength);
+ },
+ [&]() {
+ MtpObjectInfo mtpObjectInfo(fdp.ConsumeIntegral<uint32_t>() /* handle */);
+ std::string name = fdp.ConsumeRandomLengthString(kMaxStringLength);
+ std::string keywords = fdp.ConsumeRandomLengthString(kMaxStringLength);
+ mtpObjectInfo.mName = strdup(name.c_str());
+ mtpObjectInfo.mKeywords = strdup(keywords.c_str());
+ mtpDevice.sendObjectInfo(&mtpObjectInfo);
+ },
+ [&]() {
+ mtpDevice.sendObject(fdp.ConsumeIntegral<uint32_t>() /* handle */,
+ fdp.ConsumeIntegral<uint32_t>() /* size */, fd);
+ },
+ [&]() { mtpDevice.deleteObject(fdp.ConsumeIntegral<uint32_t>() /* handle */); },
+ [&]() {
+ mtpDevice.getObjectPropsSupported(
+ fdp.ConsumeIntegral<uint16_t>() /* format */);
+ },
+ [&]() {
+ MtpDataType dataType = fdp.ConsumeIntegral<int16_t>();
+ MtpProperty mtpProperty(fdp.ConsumeIntegral<int16_t>() /* propCode */,
+ dataType, fdp.ConsumeBool() /* writeable */,
+ fdp.ConsumeIntegral<int32_t>() /* defaultValue */);
+ if (dataType == MTP_TYPE_STR) {
+ mtpProperty.setCurrentValue(
+ fdp.ConsumeRandomLengthString(kMaxStringLength).c_str());
+ }
+ mtpDevice.setDevicePropValueStr(&mtpProperty);
+ },
+ [&]() {
+ mtpDevice.getObjectPropDesc(fdp.ConsumeIntegral<uint16_t>() /* code */,
+ fdp.ConsumeIntegral<uint16_t>() /* format */);
+ },
+ [&]() {
+ MtpProperty property;
+ mtpDevice.getObjectPropValue(fdp.ConsumeIntegral<uint16_t>() /* handle */,
+ &property);
+ },
+ [&]() {
+ std::vector<uint8_t> clientData = fdp.ConsumeBytes<uint8_t>(kMaxDataSize);
+ mtpDevice.readObject(
+ fdp.ConsumeIntegral<uint32_t>() /* handle */, readCallback,
+ fdp.ConsumeIntegral<uint32_t>() /* objectSize */, &clientData);
+ },
+ [&]() {
+ std::vector<uint8_t> clientData = fdp.ConsumeBytes<uint8_t>(kMaxDataSize);
+ uint32_t writtenSize = 0;
+ mtpDevice.readPartialObject(fdp.ConsumeIntegral<uint32_t>() /* handle */,
+ fdp.ConsumeIntegral<uint32_t>() /* offset */,
+ fdp.ConsumeIntegral<uint32_t>() /* size */,
+ &writtenSize, readCallback, &clientData);
+ },
+ [&]() {
+ std::vector<uint8_t> clientData = fdp.ConsumeBytes<uint8_t>(kMaxDataSize);
+ uint32_t writtenSize = 0;
+ mtpDevice.readPartialObject(fdp.ConsumeIntegral<uint32_t>() /* handle */,
+ fdp.ConsumeIntegral<uint64_t>() /* offset */,
+ fdp.ConsumeIntegral<uint32_t>() /* size */,
+ &writtenSize, readCallback, &clientData);
+ },
+ [&]() {
+ if (mtpDevice.submitEventRequest() != -1) {
+ uint32_t parameters[3];
+ mtpDevice.reapEventRequest(fdp.ConsumeIntegral<int32_t>() /* handle */,
+ ¶meters);
+ }
+ },
+ [&]() {
+ mtpDevice.discardEventRequest(fdp.ConsumeIntegral<int32_t>() /*handle*/);
+ },
+ [&]() {
+ mtpDevice.discardEventRequest(fdp.ConsumeIntegral<int32_t>() /* handle */);
+ },
+ [&]() { mtpDevice.print(); },
+ [&]() { mtpDevice.getDeviceName(); },
+ [&]() { mtpDevice.getObjectInfo(fdp.ConsumeIntegral<uint32_t>() /* handle */); },
+ [&]() { mtpDevice.getParent(fdp.ConsumeIntegral<uint32_t>() /* handle */); },
+ [&]() { mtpDevice.getStorageID(fdp.ConsumeIntegral<uint32_t>() /* handle */); },
+ [&]() { mtpDevice.getDevicePropDesc(fdp.ConsumeIntegral<uint16_t>() /* code */); },
+ [&]() {
+ mtpDevice.readObject(
+ fdp.ConsumeIntegral<uint32_t>() /* handle */,
+ fdp.ConsumeRandomLengthString(kMaxStringLength).c_str() /* destPath */,
+ fdp.ConsumeIntegral<int32_t>() /* group */,
+ fdp.ConsumeIntegral<int32_t>() /* perm */);
+ },
+ [&]() {
+ int32_t filefd = open(kConfigFilePath.c_str(), O_CREAT | O_RDWR);
+ mtpDevice.readObject(fdp.ConsumeIntegral<uint16_t>() /* handle */, filefd);
+ close(filefd);
+ },
+ [&]() { MtpDevice::open(deviceName.c_str(), fd); },
+ [&]() {
+ MtpDataPacket mtpDataPacket;
+ MtpDevHandle devHandle;
+ std::vector<uint8_t> packet = fdp.ConsumeBytes<uint8_t>(kMaxBytes);
+ mtpDataPacket.writeData(&devHandle, packet.data(), packet.size());
+ objectinfo.read(mtpDataPacket);
+ objectinfo.print();
+ },
+ [&]() {
+ MtpDataPacket mtpDataPacket;
+ MtpDevHandle devHandle;
+ std::vector<uint8_t> packet = fdp.ConsumeBytes<uint8_t>(kMaxBytes);
+ mtpDataPacket.writeData(&devHandle, packet.data(), packet.size());
+ storageInfo.read(mtpDataPacket);
+ storageInfo.print();
+ }});
+ mtpDeviceFunction();
+ }
+ close(fd);
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_event_packet_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_event_packet_fuzzer.cpp
new file mode 100644
index 0000000..3bd3be2
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_event_packet_fuzzer.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDevHandle.h>
+#include <MtpEventPacket.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace android;
+
+class MtpEventPacketFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpEventPacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mUsbDevFsUrb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) +
+ sizeof(struct usbdevfs_iso_packet_desc));
+ };
+ ~MtpEventPacketFuzzer() { free(mUsbDevFsUrb); };
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+void MtpEventPacketFuzzer::process() {
+ MtpEventPacket mtpEventPacket;
+ while (mFdp.remaining_bytes() > 0) {
+ auto mtpEventAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() { mtpEventPacket.allocate(mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize)); },
+ [&]() { mtpEventPacket.reset(); },
+ [&]() { writeHandle(&mtpEventPacket, &mFdp); },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpEventPacket.sendRequest(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillFd(fd, &mFdp);
+ struct usb_device* device = usb_device_new(mPath.c_str(), fd);
+ mtpEventPacket.readResponse(device);
+ usb_device_close(device);
+ },
+ });
+ mtpEventAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpEventPacketFuzzer mtpEventPacketFuzzer(data, size);
+ mtpEventPacketFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
index f578462..e886816 100644
--- a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
+++ b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
+#include <fuzzer/FuzzedDataProvider.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
-
+#include <filesystem>
+#include <fstream>
#include <string>
#define LOG_TAG "MtpFuzzer"
@@ -32,38 +35,40 @@
#include "MtpStorage.h"
#include "MtpUtils.h"
-const char* storage_desc = "Fuzz Storage";
+constexpr int32_t kMinFiles = 0;
+constexpr int32_t kMaxFiles = 5;
+constexpr int32_t kMaxBytes = 128;
+constexpr float kMinDataSizeFactor = 0.8;
// prefer tmpfs for file operations to avoid wearing out flash
const char* storage_path = "/storage/fuzzer/0";
-const char* source_database = "srcdb/";
+const char* source_database = "/data/local/tmp/srcdb/";
+const std::string test_path = std::string(source_database) + "TestDir/";
+const std::string kPropertyKey = "sys.fuse.transcode_mtp";
namespace android {
class MtpMockServer {
-public:
- std::unique_ptr<MtpMockHandle> mHandle;
- std::unique_ptr<MtpStorage> mStorage;
- std::unique_ptr<MtpMockDatabase> mDatabase;
- std::unique_ptr<MtpServer> mMtp;
- int mStorageId;
-
- MtpMockServer(const char* storage_path) : mStorageId(0) {
- bool ptp = false;
- const char* manu = "Google";
- const char* model = "Pixel 3XL";
- const char* version = "1.0";
- const char* serial = "ABDEF1231";
-
+ public:
+ MtpMockServer(const uint8_t* data, size_t size) : mFdp(data, size) {
// This is unused in our harness
int controlFd = -1;
mHandle = std::make_unique<MtpMockHandle>();
- mStorage = std::make_unique<MtpStorage>(mStorageId, storage_path, storage_desc, true,
- 0x200000000L);
+ mStorage = std::make_unique<MtpStorage>(
+ mFdp.ConsumeIntegral<uint32_t>() /* storageId */, storage_path,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* descriptor */,
+ mFdp.ConsumeBool() /* removable */,
+ mFdp.ConsumeIntegral<uint64_t>() /* maxFileSize */);
mDatabase = std::make_unique<MtpMockDatabase>();
mDatabase->addStorage(mStorage.get());
- mMtp = std::make_unique<MtpServer>(mDatabase.get(), controlFd, ptp, manu, model, version,
- serial);
+ init(data, size);
+
+ mMtp = std::make_unique<MtpServer>(
+ mDatabase.get(), controlFd, mFdp.ConsumeBool() /* ptp */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* manu */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* model */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* version */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* serial */);
mMtp->addStorage(mStorage.get());
// clear the old handle first, so we don't leak memory
@@ -71,7 +76,76 @@
mMtp->mHandle = mHandle.get();
}
- void run() { mMtp->run(); }
+ void process() {
+ if (mFdp.ConsumeBool()) {
+ createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
+ }
+
+ while (mFdp.remaining_bytes()) {
+ MtpStorage storage(mFdp.ConsumeIntegral<uint32_t>() /* id */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* filePath */,
+ mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* description */,
+ mFdp.ConsumeBool() /* removable */,
+ mFdp.ConsumeIntegral<uint64_t>() /* maxFileSize */);
+
+ auto invokeMtpServerAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() { mMtp->run(); },
+ [&]() { mMtp->sendObjectAdded(mFdp.ConsumeIntegral<uint32_t>()); },
+ [&]() { mMtp->sendObjectRemoved(mFdp.ConsumeIntegral<uint32_t>()); },
+ [&]() { mMtp->sendObjectInfoChanged(mFdp.ConsumeIntegral<uint32_t>()); },
+ [&]() { mMtp->sendDevicePropertyChanged(mFdp.ConsumeIntegral<uint16_t>()); },
+ [&]() { mMtp->addStorage(&storage); },
+ [&]() { mMtp->removeStorage(&storage); },
+ });
+
+ invokeMtpServerAPI();
+ }
+
+ std::filesystem::remove_all(source_database);
+ }
+
+ private:
+ void createFiles(std::string path, size_t fileCount) {
+ std::ofstream file;
+ for (size_t idx = 0; idx < fileCount; ++idx) {
+ file.open(path.append(std::to_string(idx)));
+ file.close();
+ }
+ }
+
+ void addPackets(const uint8_t* data, size_t size) {
+ size_t off = 0;
+ for (size_t i = 0; i < size; ++i) {
+ // A longer delimiter could be used, but this worked in practice
+ if (data[i] == '@') {
+ size_t pktsz = i - off;
+ if (pktsz > 0) {
+ packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
+ // insert into packet buffer
+ mHandle->add_packet(pkt);
+ off = i;
+ }
+ }
+ }
+ }
+
+ void init(const uint8_t* data, size_t size) {
+ std::vector<uint8_t> packetData = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinDataSizeFactor * size, size));
+
+ // Packetize the input stream
+ addPackets(packetData.data(), packetData.size());
+
+ // Setting the property to true/false to randomly fuzz the PoC depended on it
+ base::SetProperty(kPropertyKey, mFdp.ConsumeBool() ? "true" : "false");
+
+ std::filesystem::create_directories(source_database);
+ if (mFdp.ConsumeBool()) {
+ std::filesystem::create_directories(test_path);
+ createFiles(test_path, mFdp.ConsumeIntegralInRange<size_t>(kMinFiles, kMaxFiles));
+ }
+ createFiles(source_database, mFdp.ConsumeIntegralInRange<size_t>(kMinFiles, kMaxFiles));
+ }
int createDatabaseFromSourceDir(const char* fromPath, const char* toPath,
MtpObjectHandle parentHandle) {
@@ -130,8 +204,14 @@
closedir(dir);
return ret;
}
+
+ FuzzedDataProvider mFdp;
+ std::unique_ptr<MtpMockHandle> mHandle;
+ std::unique_ptr<MtpStorage> mStorage;
+ std::unique_ptr<MtpMockDatabase> mDatabase;
+ std::unique_ptr<MtpServer> mMtp;
};
-}; // namespace android
+}; // namespace android
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) __attribute__((optnone)) {
// reset our storage (from MtpUtils.h)
@@ -140,26 +220,9 @@
android::makeFolder(storage_path);
std::unique_ptr<android::MtpMockServer> mtp =
- std::make_unique<android::MtpMockServer>(storage_path);
+ std::make_unique<android::MtpMockServer>(data, size);
+ mtp->process();
- size_t off = 0;
-
- // Packetize the input stream
- for (size_t i = 0; i < size; i++) {
- // A longer delimiter could be used, but this worked in practice
- if (data[i] == '@') {
- size_t pktsz = i - off;
- if (pktsz > 0) {
- packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
- // insert into packet buffer
- mtp->mHandle->add_packet(pkt);
- off = i;
- }
- }
- }
-
- mtp->createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
- mtp->run();
-
+ std::filesystem::remove_all("/storage/fuzzer");
return 0;
}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_handle_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_handle_fuzzer.cpp
new file mode 100644
index 0000000..676345a
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_handle_fuzzer.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <MtpDescriptors.h>
+#include <MtpFfsCompatHandle.h>
+#include <android-base/file.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <mtp.h>
+
+using namespace android;
+
+constexpr int32_t kMaxStringLength = 64;
+constexpr int32_t kMinAPICase = 0;
+constexpr int32_t kMaxMtpHandleAPI = 5;
+constexpr int32_t kMinBufferSize = 0;
+constexpr uint32_t kMaxMtpFileSize = 0xFFFFFFFF;
+constexpr float kDataSizeFactor = 0.1;
+
+const std::string kTempPath = "/data/local/tmp/";
+const std::string kFuzzerUsbDirPath = kTempPath + "usb-ffs";
+const std::string kFuzzerMtpPath = kFuzzerUsbDirPath + "/mtp";
+const std::string kFuzzerPtpPath = kFuzzerUsbDirPath + "/ptp";
+const std::string kFuzzerTestFile = kTempPath + "FuzzerTestDescriptorFile";
+const std::string kFuzzerMtpInputFile = kTempPath + "FuzzerMtpInputFile";
+const std::string kFuzzerMtpOutputFile = kTempPath + "FuzzerMtpOutputFile";
+
+const std::string kDeviceFilePaths[] = {FFS_MTP_EP0, FFS_MTP_EP_IN, FFS_MTP_EP_INTR,
+ FFS_PTP_EP0, FFS_PTP_EP_IN, FFS_PTP_EP_INTR,
+ FFS_MTP_EP_OUT, FFS_PTP_EP_OUT};
+
+class MtpFfsHandleFuzzer {
+ public:
+ MtpFfsHandleFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mDataSize = kDataSizeFactor * size;
+ createFiles();
+ };
+ void process();
+
+ ~MtpFfsHandleFuzzer() { removeFiles(); };
+
+ private:
+ FuzzedDataProvider mFdp;
+ void invokeWriteDescriptor();
+ void invokeMtpFfsHandle();
+ void createFiles();
+ void removeFiles();
+ void createDeviceFile(const char* file);
+ void writeDeviceFile(const char* file);
+ int32_t writeInputFile(int32_t fd);
+ uint32_t mDataSize = 0;
+};
+
+int32_t MtpFfsHandleFuzzer::writeInputFile(int32_t fd) {
+ uint32_t minFileSize = std::min((uint32_t)MTP_BUFFER_SIZE, mDataSize);
+ uint32_t maxFileSize = std::min(mDataSize, kMaxMtpFileSize);
+ std::vector<char> dataBuffer = mFdp.ConsumeBytes<char>(
+ mFdp.ConsumeIntegralInRange<uint32_t>(minFileSize, maxFileSize));
+ write(fd, dataBuffer.data(), dataBuffer.size());
+ lseek(fd, 0, SEEK_SET);
+ return dataBuffer.size();
+}
+
+void MtpFfsHandleFuzzer::createDeviceFile(const char* file) {
+ int32_t fd = open(file, O_CREAT | O_RDWR | O_NONBLOCK);
+ close(fd);
+}
+
+void MtpFfsHandleFuzzer::writeDeviceFile(const char* file) {
+ int32_t fd = open(file, O_RDWR | O_NONBLOCK);
+ writeInputFile(fd);
+ close(fd);
+}
+
+void MtpFfsHandleFuzzer::createFiles() {
+ mkdir(kFuzzerUsbDirPath.c_str(), 0755);
+ mkdir(kFuzzerMtpPath.c_str(), 0755);
+ mkdir(kFuzzerPtpPath.c_str(), 0755);
+
+ for (auto path : kDeviceFilePaths) {
+ createDeviceFile(path.c_str());
+ }
+
+ writeDeviceFile(FFS_MTP_EP_OUT);
+ writeDeviceFile(FFS_PTP_EP_OUT);
+}
+
+void MtpFfsHandleFuzzer::removeFiles() {
+ for (auto path : kDeviceFilePaths) {
+ remove(path.c_str());
+ }
+
+ rmdir(kFuzzerMtpPath.c_str());
+ rmdir(kFuzzerPtpPath.c_str());
+ rmdir(kFuzzerUsbDirPath.c_str());
+}
+
+void MtpFfsHandleFuzzer::invokeWriteDescriptor() {
+ while (mFdp.remaining_bytes() > 0) {
+ int32_t controlFd = mFdp.ConsumeBool()
+ ? -1 /* Invalid fd*/
+ : open(kFuzzerTestFile.c_str(), O_CREAT | O_RDWR | O_NONBLOCK);
+ std::unique_ptr<MtpFfsHandle> handle(new MtpFfsHandle(controlFd));
+ handle->writeDescriptors(mFdp.ConsumeBool());
+ handle->close();
+ close(controlFd);
+ remove(kFuzzerTestFile.c_str());
+ }
+}
+
+void MtpFfsHandleFuzzer::invokeMtpFfsHandle() {
+ while (mFdp.remaining_bytes() > 0) {
+ int32_t controlFd = open(kFuzzerTestFile.c_str(), O_CREAT | O_RDWR | O_NONBLOCK);
+ writeInputFile(controlFd);
+
+ std::unique_ptr<IMtpHandle> handle;
+ if (mFdp.ConsumeBool()) {
+ std::unique_ptr<IMtpHandle> mtpCompactHandle(new MtpFfsCompatHandle(controlFd));
+ handle = move(mtpCompactHandle);
+ } else {
+ std::unique_ptr<IMtpHandle> mtpHandle(new MtpFfsHandle(controlFd));
+ handle = move(mtpHandle);
+ }
+
+ int32_t mtpHandle = mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxMtpHandleAPI);
+ switch (mtpHandle) {
+ case 0: {
+ handle->start(mFdp.ConsumeBool());
+ break;
+ }
+ case 1: {
+ std::string data = mFdp.ConsumeRandomLengthString(MTP_BUFFER_SIZE);
+ handle->write(data.c_str(), data.length());
+ break;
+ }
+ case 2: {
+ int32_t bufferSize =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBufferSize, MTP_BUFFER_SIZE);
+ uint8_t buffer[bufferSize + 1];
+ handle->read(buffer, bufferSize);
+ break;
+ }
+ case 3: {
+ mtp_file_range mfr;
+ mfr.fd = open(kFuzzerMtpInputFile.c_str(), O_CREAT | O_RDWR | O_NONBLOCK);
+ mfr.length = writeInputFile(mfr.fd);
+ mfr.offset = 0; /* Offset point to the start of the file */
+ mfr.command = mFdp.ConsumeIntegral<uint16_t>();
+ mfr.transaction_id = mFdp.ConsumeIntegral<uint32_t>();
+ handle->sendFile(mfr);
+ close(mfr.fd);
+ remove(kFuzzerMtpInputFile.c_str());
+ break;
+ }
+ case 4: {
+ struct mtp_event event;
+ std::string dataValue = mFdp.ConsumeRandomLengthString(kMaxStringLength);
+ event.data = const_cast<char*>(dataValue.c_str());
+ event.length = dataValue.length();
+ handle->sendEvent(event);
+ break;
+ }
+ case 5:
+ default: {
+ mtp_file_range mfr;
+ mfr.fd = open(kFuzzerMtpOutputFile.c_str(), O_CREAT | O_RDWR | O_NONBLOCK);
+ mfr.offset = 0; /* Offset point to the start of the file */
+ mfr.length = kMaxMtpFileSize;
+ handle->receiveFile(mfr, mFdp.ConsumeBool());
+ close(mfr.fd);
+ remove(kFuzzerMtpOutputFile.c_str());
+ break;
+ }
+ }
+ handle->close();
+ close(controlFd);
+ remove(kFuzzerTestFile.c_str());
+ }
+}
+
+void MtpFfsHandleFuzzer::process() {
+ if (mFdp.ConsumeBool()) {
+ invokeMtpFfsHandle();
+ } else {
+ invokeWriteDescriptor();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpFfsHandleFuzzer mtpFfsHandleFuzzer(data, size);
+ mtpFfsHandleFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_packet_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_packet_fuzzer.cpp
new file mode 100644
index 0000000..6fc2a96
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_packet_fuzzer.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDevHandle.h>
+#include <MtpPacket.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace android;
+
+class MtpPacketFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpPacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mUsbDevFsUrb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) +
+ sizeof(struct usbdevfs_iso_packet_desc));
+ };
+ ~MtpPacketFuzzer() { free(mUsbDevFsUrb); };
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+void MtpPacketFuzzer::process() {
+ MtpPacket mtpPacket(mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize)); /*bufferSize*/
+ while (mFdp.remaining_bytes() > 0) {
+ auto mtpPacketAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ mtpPacket.allocate(mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ },
+ [&]() { mtpPacket.reset(); },
+ [&]() { mtpPacket.getContainerType(); },
+ [&]() { mtpPacket.getContainerCode(); },
+ [&]() { mtpPacket.dump(); },
+ [&]() { mtpPacket.getTransactionID(); },
+ [&]() {
+ mtpPacket.setContainerCode(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+ },
+ [&]() {
+ mtpPacket.setTransactionID(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+ },
+ [&]() {
+ mtpPacket.getParameter(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+ },
+ [&]() {
+ mtpPacket.setParameter(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize),
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+ },
+ [&]() {
+ MtpPacket testMtpPacket(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+ testMtpPacket.copyFrom(mtpPacket);
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpPacket.transfer(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ });
+ mtpPacketAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpPacketFuzzer mtpPacketFuzzer(data, size);
+ mtpPacketFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_property_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_property_fuzzer.cpp
new file mode 100644
index 0000000..8577e62
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_property_fuzzer.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDataPacket.h>
+#include <MtpDevHandle.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <MtpProperty.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+using namespace android;
+
+constexpr uint16_t kFeasibleTypes[] = {
+ MTP_TYPE_UNDEFINED, MTP_TYPE_INT8, MTP_TYPE_UINT8, MTP_TYPE_INT16, MTP_TYPE_UINT16,
+ MTP_TYPE_INT32, MTP_TYPE_UINT32, MTP_TYPE_INT64, MTP_TYPE_UINT64, MTP_TYPE_INT128,
+ MTP_TYPE_UINT128, MTP_TYPE_AINT8, MTP_TYPE_AUINT8, MTP_TYPE_AINT16, MTP_TYPE_AUINT16,
+ MTP_TYPE_AINT32, MTP_TYPE_AUINT32, MTP_TYPE_AINT64, MTP_TYPE_AUINT64, MTP_TYPE_AINT128,
+ MTP_TYPE_AUINT128, MTP_TYPE_STR,
+};
+
+class MtpPropertyFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpPropertyFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+void MtpPropertyFuzzer::process() {
+ MtpProperty* mtpProperty;
+ if (mFdp.ConsumeBool()) {
+ mtpProperty = new MtpProperty();
+ } else {
+ uint16_t type = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint16_t>()
+ : mFdp.PickValueInArray<uint16_t>(kFeasibleTypes);
+ mtpProperty = new MtpProperty(mFdp.ConsumeIntegral<uint16_t>(), type, mFdp.ConsumeBool(),
+ mFdp.ConsumeIntegral<uint16_t>());
+ }
+
+ while (mFdp.remaining_bytes() > 0) {
+ auto invokeMtpPropertyFuzzer = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ MtpDataPacket mtpDataPacket;
+ if (mFdp.ConsumeBool()) {
+ mtpProperty->read(mtpDataPacket);
+
+ } else {
+ if (mFdp.ConsumeBool()) {
+#ifdef MTP_DEVICE
+ android::IMtpHandle* h = new MtpDevHandle();
+ h->start(mFdp.ConsumeBool());
+ std::string text = mFdp.ConsumeRandomLengthString(kMaxLength);
+ char* data = const_cast<char*>(text.c_str());
+ h->read(static_cast<void*>(data), text.length());
+ mtpDataPacket.write(h);
+ h->close();
+ delete h;
+#endif
+
+#ifdef MTP_HOST
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mtpDataPacket.write(&mUsbRequest,
+ mFdp.PickValueInArray<UrbPacketDivisionMode>(
+ kUrbPacketDivisionModes),
+ fd,
+ mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize));
+ usb_device_close(mUsbRequest.dev);
+#endif
+ }
+
+ if (mFdp.ConsumeBool()) {
+ mtpProperty->write(mtpDataPacket);
+ } else {
+ mtpProperty->setCurrentValue(mtpDataPacket);
+ }
+ }
+ },
+ [&]() {
+ char16_t* data = nullptr;
+ std::string str = mFdp.ConsumeRandomLengthString(kMaxLength);
+ android::String16 s(str.c_str());
+ if (mFdp.ConsumeBool()) {
+ data = const_cast<char16_t*>(s.string());
+ }
+
+ if (mFdp.ConsumeBool()) {
+ mtpProperty->setDefaultValue(reinterpret_cast<uint16_t*>(data));
+ } else if (mFdp.ConsumeBool()) {
+ mtpProperty->setCurrentValue(reinterpret_cast<uint16_t*>(data));
+ } else {
+ mtpProperty->setCurrentValue(str.c_str());
+ }
+ },
+ [&]() {
+ mtpProperty->setFormRange(mFdp.ConsumeIntegral<int32_t>(),
+ mFdp.ConsumeIntegral<int32_t>(),
+ mFdp.ConsumeIntegral<int32_t>());
+ },
+ [&]() {
+ std::vector<int32_t> init;
+ for (size_t idx = 0; idx < mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize);
+ ++idx) {
+ init.push_back(mFdp.ConsumeIntegral<int32_t>());
+ }
+ mtpProperty->setFormEnum(init.data(), init.size());
+ },
+ });
+ invokeMtpPropertyFuzzer();
+ }
+
+ delete (mtpProperty);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpPropertyFuzzer mtpPropertyFuzzer(data, size);
+ mtpPropertyFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_request_packet_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_request_packet_fuzzer.cpp
new file mode 100644
index 0000000..19fbc5b
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_request_packet_fuzzer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDevHandle.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <MtpRequestPacket.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <fstream>
+
+using namespace android;
+
+std::string kMtpDevPath = "/dev/mtp_usb";
+constexpr int32_t kMaxBytes = 100000;
+
+class MtpRequestPacketFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpRequestPacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mUsbDevFsUrb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) +
+ sizeof(struct usbdevfs_iso_packet_desc));
+ };
+ ~MtpRequestPacketFuzzer() { free(mUsbDevFsUrb); };
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ void makeFile(std::string s);
+};
+
+void MtpRequestPacketFuzzer::process() {
+ MtpRequestPacket mtpRequestPacket;
+ while (mFdp.remaining_bytes() > 0) {
+ auto mtpRequestAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ mtpRequestPacket.allocate(mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize));
+ },
+ [&]() { mtpRequestPacket.reset(); },
+ [&]() {
+ MtpDevHandle handle;
+ makeFile(kMtpDevPath);
+ handle.start(mFdp.ConsumeBool());
+ std::vector<uint8_t> data = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+ handle.write(data.data(), data.size());
+ mtpRequestPacket.read(&handle);
+ handle.close();
+ remove(kMtpDevPath.c_str());
+ },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpRequestPacket.write(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ });
+ mtpRequestAPI();
+ }
+}
+
+void MtpRequestPacketFuzzer::makeFile(std::string s) {
+ std::ofstream out;
+ out.open(s, std::ios::binary | std::ofstream::trunc);
+ for (int32_t idx = 0; idx < mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize); ++idx) {
+ out << mFdp.ConsumeRandomLengthString(kMaxBytes) << "\n";
+ }
+ out.close();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpRequestPacketFuzzer mtpRequestPacketFuzzer(data, size);
+ mtpRequestPacketFuzzer.process();
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_response_packet_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_response_packet_fuzzer.cpp
new file mode 100644
index 0000000..697785f
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_response_packet_fuzzer.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <MtpDevHandle.h>
+#include <MtpPacketFuzzerUtils.h>
+#include <MtpResponsePacket.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace android;
+
+class MtpResponsePacketFuzzer : MtpPacketFuzzerUtils {
+ public:
+ MtpResponsePacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mUsbDevFsUrb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) +
+ sizeof(struct usbdevfs_iso_packet_desc));
+ };
+ ~MtpResponsePacketFuzzer() { free(mUsbDevFsUrb); };
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+void MtpResponsePacketFuzzer::process() {
+ MtpResponsePacket mtpResponsePacket;
+ while (mFdp.remaining_bytes() > 0) {
+ auto mtpResponseAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ mtpResponsePacket.allocate(
+ mFdp.ConsumeIntegralInRange(kMinSize, kMaxSize)); /*size*/
+ },
+ [&]() { mtpResponsePacket.reset(); },
+ [&]() { writeHandle(&mtpResponsePacket, &mFdp); },
+ [&]() {
+ fillFilePath(&mFdp);
+ int32_t fd = memfd_create(mPath.c_str(), MFD_ALLOW_SEALING);
+ fillUsbRequest(fd, &mFdp);
+ mUsbRequest.dev = usb_device_new(mPath.c_str(), fd);
+ mtpResponsePacket.read(&mUsbRequest);
+ usb_device_close(mUsbRequest.dev);
+ },
+ });
+ mtpResponseAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ MtpResponsePacketFuzzer mtpResponsePacketFuzzer(data, size);
+ mtpResponsePacketFuzzer.process();
+ return 0;
+}
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 12a0d53..0bdb41b 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -24,6 +24,7 @@
#include <android_media_Utils.h>
#include <private/android/AHardwareBufferHelpers.h>
+#include <ui/PublicFormat.h>
#include <utils/Log.h>
using namespace android;
@@ -34,6 +35,8 @@
int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes) :
mReader(reader), mFormat(format), mUsage(usage), mBuffer(buffer), mLockedBuffer(nullptr),
mTimestamp(timestamp), mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
+ PublicFormat publicFormat = static_cast<PublicFormat>(format);
+ mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat);
LOG_FATAL_IF(reader == nullptr, "AImageReader shouldn't be null while creating AImage");
}
@@ -156,6 +159,20 @@
return AMEDIA_OK;
}
+media_status_t
+AImage::getDataSpace(android_dataspace* dataSpace) const {
+ if (dataSpace == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *dataSpace = static_cast<android_dataspace>(-1);
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *dataSpace = mHalDataSpace;
+ return AMEDIA_OK;
+}
+
media_status_t AImage::lockImage() {
if (mBuffer == nullptr || mBuffer->mGraphicBuffer == nullptr) {
LOG_ALWAYS_FATAL("%s: AImage %p has no buffer.", __FUNCTION__, this);
@@ -762,3 +779,15 @@
}
return image->getHardwareBuffer(buffer);
}
+
+EXPORT
+media_status_t AImage_getDataSpace(
+ const AImage* image, /*out*/int32_t* dataSpace) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (image == nullptr || dataSpace == nullptr) {
+ ALOGE("%s: bad argument. image %p dataSpace %p", __FUNCTION__, image, dataSpace);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getDataSpace((android_dataspace*)(dataSpace));
+}
\ No newline at end of file
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index 05115b9..dc10a6a 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -82,6 +82,7 @@
media_status_t getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const;
media_status_t getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const;
media_status_t getHardwareBuffer(/*out*/AHardwareBuffer** buffer) const;
+ media_status_t getDataSpace(/*out*/android_dataspace* dataSpace) const;
private:
// AImage should be deleted through free() API.
@@ -101,6 +102,7 @@
const int32_t mWidth;
const int32_t mHeight;
const int32_t mNumPlanes;
+ android_dataspace mHalDataSpace = HAL_DATASPACE_UNKNOWN;
bool mIsClosed = false;
mutable Mutex mLock;
};
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 1067e24..ac5cba8 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -46,6 +46,9 @@
static constexpr int kWindowHalTokenSizeMax = 256;
+static media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
+ uint64_t usage, int32_t maxImages,
+ /*out*/ AImageReader**& reader);
static native_handle_t *convertHalTokenToNativeHandle(const HalToken &halToken);
bool
@@ -263,13 +266,17 @@
int32_t height,
int32_t format,
uint64_t usage,
- int32_t maxImages)
+ int32_t maxImages,
+ uint32_t hardwareBufferFormat,
+ android_dataspace dataSpace)
: mWidth(width),
mHeight(height),
mFormat(format),
mUsage(usage),
mMaxImages(maxImages),
mNumPlanes(getNumPlanesForFormat(format)),
+ mHalFormat(hardwareBufferFormat),
+ mHalDataSpace(dataSpace),
mFrameListener(new FrameListener(this)),
mBufferRemovedListener(new BufferRemovedListener(this)) {}
@@ -280,9 +287,6 @@
media_status_t
AImageReader::init() {
- PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
- mHalFormat = mapPublicFormatToHalFormat(publicFormat);
- mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat);
mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
sp<IGraphicBufferProducer> gbProducer;
@@ -646,6 +650,41 @@
}
}
+static
+media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
+ uint64_t usage, int32_t maxImages,
+ /*out*/ AImageReader**& reader) {
+ if (reader == nullptr) {
+ ALOGE("%s: reader argument is null", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (width < 1 || height < 1) {
+ ALOGE("%s: image dimension must be positive: w:%d h:%d",
+ __FUNCTION__, width, height);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (maxImages < 1) {
+ ALOGE("%s: max outstanding image count must be at least 1 (%d)",
+ __FUNCTION__, maxImages);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
+ __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
+ ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
+ __FUNCTION__, format, usage);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return AMEDIA_OK;
+}
+
static native_handle_t *convertHalTokenToNativeHandle(
const HalToken &halToken) {
// We attempt to store halToken in the ints of the native_handle_t after its
@@ -696,42 +735,32 @@
} //extern "C"
EXPORT
+media_status_t AImageReader_newWithDataSpace(
+ int32_t width, int32_t height, uint64_t usage, int32_t maxImages,
+ uint32_t hardwareBufferFormat, int32_t dataSpace,
+ /*out*/ AImageReader** reader) {
+ ALOGV("%s", __FUNCTION__);
+
+ android_dataspace halDataSpace = static_cast<android_dataspace>(dataSpace);
+ int32_t format = static_cast<int32_t>(
+ mapHalFormatDataspaceToPublicFormat(hardwareBufferFormat, halDataSpace));
+ return AImageReader_newWithUsage(width, height, format, usage, maxImages, reader);
+}
+
+EXPORT
media_status_t AImageReader_newWithUsage(
int32_t width, int32_t height, int32_t format, uint64_t usage,
int32_t maxImages, /*out*/ AImageReader** reader) {
ALOGV("%s", __FUNCTION__);
- if (width < 1 || height < 1) {
- ALOGE("%s: image dimension must be positive: w:%d h:%d",
- __FUNCTION__, width, height);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
+ validateParameters(width, height, format, usage, maxImages, reader);
- if (maxImages < 1) {
- ALOGE("%s: max outstanding image count must be at least 1 (%d)",
- __FUNCTION__, maxImages);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
- ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
- __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
- ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
- __FUNCTION__, format, usage);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (reader == nullptr) {
- ALOGE("%s: reader argument is null", __FUNCTION__);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
+ PublicFormat publicFormat = static_cast<PublicFormat>(format);
+ uint32_t halFormat = mapPublicFormatToHalFormat(publicFormat);
+ android_dataspace halDataSpace = mapPublicFormatToHalDataspace(publicFormat);
AImageReader* tmpReader = new AImageReader(
- width, height, format, usage, maxImages);
+ width, height, format, usage, maxImages, halFormat, halDataSpace);
if (tmpReader == nullptr) {
ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
return AMEDIA_ERROR_UNKNOWN;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 37c606e..0199616 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -56,10 +56,12 @@
int32_t height,
int32_t format,
uint64_t usage,
- int32_t maxImages);
+ int32_t maxImages,
+ uint32_t hardwareBufferFormat,
+ android_dataspace dataSpace);
~AImageReader();
- // Inintialize AImageReader, uninitialized or failed to initialize AImageReader
+ // Initialize AImageReader, uninitialized or failed to initialize AImageReader
// should never be passed to application
media_status_t init();
@@ -79,7 +81,6 @@
void close();
private:
-
friend struct AImage; // for grabing reader lock
BufferItem* getBufferItemLocked();
@@ -118,13 +119,16 @@
const int32_t mWidth;
const int32_t mHeight;
- const int32_t mFormat;
+ int32_t mFormat;
const uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
const int32_t mMaxImages;
// TODO(jwcai) Seems completely unused in AImageReader class.
const int32_t mNumPlanes;
+ uint32_t mHalFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ android_dataspace mHalDataSpace = HAL_DATASPACE_UNKNOWN;
+
struct FrameListener : public ConsumerBase::FrameAvailableListener {
public:
explicit FrameListener(AImageReader* parent) : mReader(parent) {}
@@ -155,8 +159,6 @@
};
sp<BufferRemovedListener> mBufferRemovedListener;
- int mHalFormat;
- android_dataspace mHalDataSpace;
uint64_t mHalUsage;
sp<IGraphicBufferProducer> mProducer;
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 38e422d..ed31c02 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <charconv>
#include <inttypes.h>
#include <mutex>
#include <set>
@@ -324,29 +325,61 @@
}
AMessage::Type type;
- int64_t mediaTimeUs, systemNano;
- size_t index = 0;
+ size_t n = data->countEntries();
- // TODO. This code has dependency with MediaCodec::CreateFramesRenderedMessage.
- for (size_t ix = 0; ix < data->countEntries(); ix++) {
- AString name = data->getEntryNameAt(ix, &type);
- if (name.startsWith(AStringPrintf("%zu-media-time-us", index).c_str())) {
- AMessage::ItemData data = msg->getEntryAt(index);
- data.find(&mediaTimeUs);
- } else if (name.startsWith(AStringPrintf("%zu-system-nano", index).c_str())) {
- AMessage::ItemData data = msg->getEntryAt(index);
- data.find(&systemNano);
+ thread_local std::vector<std::optional<int64_t>> mediaTimesInUs;
+ thread_local std::vector<std::optional<int64_t>> systemTimesInNs;
+ mediaTimesInUs.resize(n);
+ systemTimesInNs.resize(n);
+ std::fill_n(mediaTimesInUs.begin(), n, std::nullopt);
+ std::fill_n(systemTimesInNs.begin(), n, std::nullopt);
+ for (size_t i = 0; i < n; i++) {
+ AString name = data->getEntryNameAt(i, &type);
+ if (name.endsWith("-media-time-us")) {
+ int64_t mediaTimeUs;
+ AMessage::ItemData itemData = data->getEntryAt(i);
+ itemData.find(&mediaTimeUs);
- Mutex::Autolock _l(mCodec->mFrameRenderedCallbackLock);
- if (mCodec->mFrameRenderedCallback != NULL) {
+ int index = -1;
+ std::from_chars_result result = std::from_chars(
+ name.c_str(), name.c_str() + name.find("-"), index);
+ if (result.ec == std::errc() && 0 <= index && index < n) {
+ mediaTimesInUs[index] = mediaTimeUs;
+ } else {
+ std::error_code ec = std::make_error_code(result.ec);
+ ALOGE("Unexpected media time index: #%d with value %lldus (err=%d %s)",
+ index, (long long)mediaTimeUs, ec.value(), ec.message().c_str());
+ }
+ } else if (name.endsWith("-system-nano")) {
+ int64_t systemNano;
+ AMessage::ItemData itemData = data->getEntryAt(i);
+ itemData.find(&systemNano);
+
+ int index = -1;
+ std::from_chars_result result = std::from_chars(
+ name.c_str(), name.c_str() + name.find("-"), index);
+ if (result.ec == std::errc() && 0 <= index && index < n) {
+ systemTimesInNs[index] = systemNano;
+ } else {
+ std::error_code ec = std::make_error_code(result.ec);
+ ALOGE("Unexpected system time index: #%d with value %lldns (err=%d %s)",
+ index, (long long)systemNano, ec.value(), ec.message().c_str());
+ }
+ }
+ }
+
+ Mutex::Autolock _l(mCodec->mFrameRenderedCallbackLock);
+ if (mCodec->mFrameRenderedCallback != NULL) {
+ for (size_t i = 0; i < n; ++i) {
+ if (mediaTimesInUs[i] && systemTimesInNs[i]) {
mCodec->mFrameRenderedCallback(
mCodec,
mCodec->mFrameRenderedCallbackUserData,
- mediaTimeUs,
- systemNano);
+ mediaTimesInUs[i].value(),
+ systemTimesInNs[i].value());
+ } else {
+ break;
}
-
- index++;
}
}
break;
@@ -529,22 +562,31 @@
AMediaCodecOnAsyncNotifyCallback callback,
void *userdata) {
- Mutex::Autolock _l(mData->mAsyncCallbackLock);
-
- if (mData->mAsyncNotify == NULL) {
- mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+ {
+ Mutex::Autolock _l(mData->mAsyncCallbackLock);
+ if (mData->mAsyncNotify == NULL) {
+ mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+ }
+ // we set this ahead so that we can be ready
+ // to receive callbacks as soon as the next call is a
+ // success.
+ mData->mAsyncCallback = callback;
+ mData->mAsyncCallbackUserData = userdata;
}
// always call, codec may have been reset/re-configured since last call.
status_t err = mData->mCodec->setCallback(mData->mAsyncNotify);
if (err != OK) {
+ {
+ //The setup gone wrong. clean up the pointers.
+ Mutex::Autolock _l(mData->mAsyncCallbackLock);
+ mData->mAsyncCallback = {};
+ mData->mAsyncCallbackUserData = nullptr;
+ }
ALOGE("setAsyncNotifyCallback: err(%d), failed to set async callback", err);
return translate_error(err);
}
- mData->mAsyncCallback = callback;
- mData->mAsyncCallbackUserData = userdata;
-
return AMEDIA_OK;
}
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 59c1103..f4674de 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -183,7 +183,7 @@
AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
int32_t dataSize = data.size();
const uint8_t *dataPtr = data.data();
- if (dataSize > 0) {
+ if (dataSize >= 0) {
(*mEventListener)(mObj, &asid, ndkEventType, 0, dataPtr, dataSize);
} else {
ALOGE("invalid event data size=%d", dataSize);
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 923453a..a95e874 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -147,38 +147,80 @@
EXPORT
bool AMediaFormat_getInt32(AMediaFormat* format, const char *name, int32_t *out) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findInt32(name, out);
}
EXPORT
bool AMediaFormat_getInt64(AMediaFormat* format, const char *name, int64_t *out) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findInt64(name, out);
}
EXPORT
bool AMediaFormat_getFloat(AMediaFormat* format, const char *name, float *out) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findFloat(name, out);
}
EXPORT
bool AMediaFormat_getDouble(AMediaFormat* format, const char *name, double *out) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findDouble(name, out);
}
EXPORT
bool AMediaFormat_getSize(AMediaFormat* format, const char *name, size_t *out) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findSize(name, out);
}
EXPORT
bool AMediaFormat_getRect(AMediaFormat* format, const char *name,
int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) {
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
return format->mFormat->findRect(name, left, top, right, bottom);
}
EXPORT
bool AMediaFormat_getBuffer(AMediaFormat* format, const char *name, void** data, size_t *outsize) {
sp<ABuffer> buf;
+ if (format == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
if (format->mFormat->findBuffer(name, &buf)) {
*data = buf->data() + buf->offset();
*outsize = buf->size();
@@ -189,7 +231,12 @@
EXPORT
bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **out) {
-
+ if (mData == nullptr) {
+ return false;
+ }
+ if (name == nullptr) {
+ return false;
+ }
for (size_t i = 0; i < mData->mStringCache.size(); i++) {
if (strcmp(mData->mStringCache.keyAt(i).string(), name) == 0) {
mData->mStringCache.removeItemsAt(i, 1);
@@ -212,43 +259,91 @@
EXPORT
void AMediaFormat_setInt32(AMediaFormat* format, const char *name, int32_t value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setInt32(name, value);
}
EXPORT
void AMediaFormat_setInt64(AMediaFormat* format, const char *name, int64_t value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setInt64(name, value);
}
EXPORT
void AMediaFormat_setFloat(AMediaFormat* format, const char* name, float value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setFloat(name, value);
}
EXPORT
void AMediaFormat_setDouble(AMediaFormat* format, const char* name, double value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setDouble(name, value);
}
EXPORT
void AMediaFormat_setSize(AMediaFormat* format, const char* name, size_t value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setSize(name, value);
}
EXPORT
void AMediaFormat_setRect(AMediaFormat* format, const char *name,
int32_t left, int32_t top, int32_t right, int32_t bottom) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
format->mFormat->setRect(name, left, top, right, bottom);
}
EXPORT
void AMediaFormat_setString(AMediaFormat* format, const char* name, const char* value) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
// AMessage::setString() makes a copy of the string
format->mFormat->setString(name, value, strlen(value));
}
EXPORT
void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, const void* data, size_t size) {
+ if (format == nullptr) {
+ return;
+ }
+ if (name == nullptr) {
+ return;
+ }
// the ABuffer(void*, size_t) constructor doesn't take ownership of the data, so create
// a new buffer and copy the data into it
sp<ABuffer> buf = new ABuffer(size);
diff --git a/media/ndk/fuzzer/Android.bp b/media/ndk/fuzzer/Android.bp
new file mode 100644
index 0000000..a3d6a96
--- /dev/null
+++ b/media/ndk/fuzzer/Android.bp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_ndk_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_ndk_license"],
+}
+
+cc_defaults {
+ name: "libmediandk_fuzzer_defaults",
+ shared_libs: [
+ "libandroid_runtime_lazy",
+ "libbase",
+ "libdatasource",
+ "libmedia",
+ "libmediadrm",
+ "libmedia_omx",
+ "libmedia_jni_utils",
+ "libstagefright",
+ "libstagefright_foundation",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "libnativewindow",
+ "libhidlbase",
+ "libgui",
+ "libui",
+ "libmediandk",
+ ],
+ static_libs: [
+ "libmediandk_utils",
+ "libnativehelper_lazy",
+ ],
+ header_libs: [
+ "media_ndk_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_fuzz {
+ name: "ndk_crypto_fuzzer",
+ srcs: ["ndk_crypto_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults"],
+}
+
+cc_fuzz {
+ name: "ndk_image_reader_fuzzer",
+ srcs: [
+ "ndk_image_reader_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ ],
+ cflags: [
+ "-D__ANDROID_VNDK__",
+ ],
+ defaults: ["libmediandk_fuzzer_defaults"],
+}
+
+cc_fuzz {
+ name: "ndk_extractor_fuzzer",
+ srcs: ["ndk_extractor_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults"],
+ shared_libs: ["libbinder_ndk",],
+ corpus: ["corpus/*"],
+}
+
+cc_fuzz {
+ name: "ndk_mediaformat_fuzzer",
+ srcs: ["ndk_mediaformat_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults",],
+}
+
+cc_fuzz {
+ name: "ndk_drm_fuzzer",
+ srcs: ["ndk_drm_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults",],
+}
+
+cc_fuzz {
+ name: "ndk_mediamuxer_fuzzer",
+ srcs: ["ndk_mediamuxer_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults"],
+ shared_libs: ["libbinder_ndk",],
+}
+
+cc_fuzz {
+ name: "ndk_sync_codec_fuzzer",
+ srcs: [
+ "ndk_sync_codec_fuzzer.cpp",
+ "NdkMediaCodecFuzzerBase.cpp",
+ ],
+ header_libs: ["libnativewindow_headers",],
+ defaults: ["libmediandk_fuzzer_defaults",],
+}
diff --git a/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.cpp b/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.cpp
new file mode 100644
index 0000000..c7ce950
--- /dev/null
+++ b/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <NdkMediaCodecFuzzerBase.h>
+
+static const std::string kMimeTypes[] = {
+ MIMETYPE_AUDIO_AMR_NB, MIMETYPE_AUDIO_AMR_WB, MIMETYPE_AUDIO_MPEG,
+ MIMETYPE_AUDIO_AAC, MIMETYPE_AUDIO_FLAC, MIMETYPE_AUDIO_VORBIS,
+ MIMETYPE_AUDIO_OPUS, MIMETYPE_AUDIO_RAW, MIMETYPE_AUDIO_MSGSM,
+ MIMETYPE_AUDIO_EAC3, MIMETYPE_AUDIO_SCRAMBLED, MIMETYPE_VIDEO_VP8,
+ MIMETYPE_VIDEO_VP9, MIMETYPE_VIDEO_AV1, MIMETYPE_VIDEO_AVC,
+ MIMETYPE_VIDEO_HEVC, MIMETYPE_VIDEO_MPEG4, MIMETYPE_VIDEO_H263,
+ MIMETYPE_VIDEO_MPEG2, MIMETYPE_VIDEO_RAW, MIMETYPE_VIDEO_SCRAMBLED};
+
+static const std::string kEncoderNames[] = {
+ "c2.android.avc.encoder", "c2.android.vp8.encoder", "c2.android.vp9.encoder",
+ "c2.android.hevc.encoder", "c2.android.mpeg2.encoder", "c2.android.mpeg4.encoder",
+ "c2.android.opus.encoder", "c2.android.amrnb.encoder", "c2.android.flac.encoder",
+ "c2.android.av1-aom.encoder"};
+
+static const std::string kDecoderNames[] = {"c2.android.avc.decoder",
+ "c2.android.vp8.decoder",
+ "c2.android.vp9.decoder"
+ "c2.android.hevc.decoder",
+ "c2.android.mpeg2.decoder",
+ "c2.android.mpeg4.decoder",
+ "c2.android.opus.decoder",
+ "c2.android.amrnb.decoder",
+ "c2.android.flac.decoder",
+ "c2.android.av1-aom.decoder"};
+
+static const std::string kFormatIntKeys[] = {AMEDIAFORMAT_KEY_BIT_RATE,
+ AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_WIDTH,
+ AMEDIAFORMAT_KEY_HEIGHT,
+ AMEDIAFORMAT_KEY_FRAME_RATE,
+ AMEDIAFORMAT_KEY_COLOR_FORMAT,
+ AMEDIAFORMAT_VIDEO_QP_P_MIN,
+ AMEDIAFORMAT_VIDEO_QP_P_MAX,
+ AMEDIAFORMAT_VIDEO_QP_MIN,
+ AMEDIAFORMAT_VIDEO_QP_MAX,
+ AMEDIAFORMAT_VIDEO_QP_I_MIN,
+ AMEDIAFORMAT_VIDEO_QP_I_MAX,
+ AMEDIAFORMAT_VIDEO_QP_B_MIN,
+ AMEDIAFORMAT_VIDEO_QP_B_MAX,
+ AMEDIAFORMAT_KEY_VIDEO_QP_AVERAGE,
+ AMEDIAFORMAT_KEY_VIDEO_ENCODING_STATISTICS_LEVEL,
+ AMEDIAFORMAT_KEY_VALID_SAMPLES,
+ AMEDIAFORMAT_KEY_TRACK_INDEX,
+ AMEDIAFORMAT_KEY_TRACK_ID,
+ AMEDIAFORMAT_KEY_TILE_WIDTH,
+ AMEDIAFORMAT_KEY_TILE_HEIGHT,
+ AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH,
+ AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT,
+ AMEDIAFORMAT_KEY_STRIDE,
+ AMEDIAFORMAT_KEY_SLICE_HEIGHT,
+ AMEDIAFORMAT_KEY_SAR_WIDTH,
+ AMEDIAFORMAT_KEY_SAR_HEIGHT,
+ AMEDIAFORMAT_KEY_ROTATION,
+ AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN,
+ AMEDIAFORMAT_KEY_PROFILE,
+ AMEDIAFORMAT_KEY_PRIORITY,
+ AMEDIAFORMAT_KEY_PICTURE_TYPE,
+ AMEDIAFORMAT_KEY_PCM_ENCODING,
+ AMEDIAFORMAT_KEY_OPERATING_RATE,
+ AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT,
+ AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION,
+ AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE,
+ AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER,
+ AMEDIAFORMAT_KEY_LOW_LATENCY,
+ AMEDIAFORMAT_KEY_LOOP,
+ AMEDIAFORMAT_KEY_LEVEL,
+ AMEDIAFORMAT_KEY_LATENCY,
+ AMEDIAFORMAT_KEY_IS_SYNC_FRAME,
+ AMEDIAFORMAT_KEY_IS_DEFAULT,
+ AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD,
+ AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_GRID_ROWS,
+ AMEDIAFORMAT_KEY_GRID_COLUMNS,
+ AMEDIAFORMAT_KEY_FRAME_COUNT,
+ AMEDIAFORMAT_KEY_ENCODER_PADDING,
+ AMEDIAFORMAT_KEY_ENCODER_DELAY,
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
+ AMEDIAFORMAT_KEY_DISPLAY_CROP,
+ AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK,
+ AMEDIAFORMAT_KEY_CRYPTO_MODE,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK,
+ AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE,
+ AMEDIAFORMAT_KEY_COLOR_TRANSFER,
+ AMEDIAFORMAT_KEY_COLOR_STANDARD,
+ AMEDIAFORMAT_KEY_COLOR_RANGE,
+ AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE,
+ AMEDIAFORMAT_KEY_BITRATE_MODE,
+ AMEDIAFORMAT_KEY_AUDIO_SESSION_ID,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PROGRAM_ID,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PRESENTATION_ID,
+ AMEDIAFORMAT_KEY_AAC_SBR_MODE,
+ AMEDIAFORMAT_KEY_AAC_PROFILE,
+ AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_AAC_ENCODED_TARGET_LEVEL,
+ AMEDIAFORMAT_KEY_AAC_DRC_TARGET_REFERENCE_LEVEL,
+ AMEDIAFORMAT_KEY_AAC_DRC_HEAVY_COMPRESSION,
+ AMEDIAFORMAT_KEY_AAC_DRC_BOOST_FACTOR,
+ AMEDIAFORMAT_KEY_AAC_DRC_ATTENUATION_FACTOR,
+ AMEDIAFORMAT_KEY_XMP_SIZE,
+ AMEDIAFORMAT_KEY_XMP_OFFSET,
+ AMEDIAFORMAT_KEY_TIME_US,
+ AMEDIAFORMAT_KEY_THUMBNAIL_TIME,
+ AMEDIAFORMAT_KEY_TARGET_TIME,
+ AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND,
+ AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET,
+ AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK,
+ AMEDIAFORMAT_KEY_EXIF_SIZE,
+ AMEDIAFORMAT_KEY_EXIF_OFFSET,
+ AMEDIAFORMAT_KEY_DURATION};
+
+static const std::string kFormatBufferKeys[] = {
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C,
+ AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA,
+ AMEDIAFORMAT_KEY_SEI,
+ AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP,
+ AMEDIAFORMAT_KEY_PSSH,
+ AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS,
+ AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER,
+ AMEDIAFORMAT_KEY_MPEG_USER_DATA,
+ AMEDIAFORMAT_KEY_ICC_PROFILE,
+ AMEDIAFORMAT_KEY_HDR10_PLUS_INFO,
+ AMEDIAFORMAT_KEY_HDR_STATIC_INFO,
+ AMEDIAFORMAT_KEY_ESDS,
+ AMEDIAFORMAT_KEY_D263,
+ AMEDIAFORMAT_KEY_CSD_HEVC,
+ AMEDIAFORMAT_KEY_CSD_AVC,
+ AMEDIAFORMAT_KEY_CSD_2,
+ AMEDIAFORMAT_KEY_CSD_1,
+ AMEDIAFORMAT_KEY_CSD_0,
+ AMEDIAFORMAT_KEY_CSD,
+ AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES,
+ AMEDIAFORMAT_KEY_CRYPTO_KEY,
+ AMEDIAFORMAT_KEY_CRYPTO_IV,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES,
+ AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO,
+ AMEDIAFORMAT_KEY_ALBUMART,
+};
+
+static const std::string kFormatFloatKeys[] = {AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
+ AMEDIAFORMAT_KEY_CAPTURE_RATE};
+
+static const std::string kFormatStringKeys[] = {AMEDIAFORMAT_KEY_YEAR,
+ AMEDIAFORMAT_KEY_TITLE,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYERING,
+ AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS,
+ AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER,
+ AMEDIAFORMAT_KEY_MANUFACTURER,
+ AMEDIAFORMAT_KEY_LYRICIST,
+ AMEDIAFORMAT_KEY_LOCATION,
+ AMEDIAFORMAT_KEY_LANGUAGE,
+ AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE,
+ AMEDIAFORMAT_KEY_IS_AUTOSELECT,
+ AMEDIAFORMAT_KEY_IS_ADTS,
+ AMEDIAFORMAT_KEY_GENRE,
+ AMEDIAFORMAT_KEY_DISCNUMBER,
+ AMEDIAFORMAT_KEY_DATE,
+ AMEDIAFORMAT_KEY_COMPOSER,
+ AMEDIAFORMAT_KEY_COMPILATION,
+ AMEDIAFORMAT_KEY_COMPLEXITY,
+ AMEDIAFORMAT_KEY_CDTRACKNUMBER,
+ AMEDIAFORMAT_KEY_AUTHOR,
+ AMEDIAFORMAT_KEY_ARTIST,
+ AMEDIAFORMAT_KEY_ALBUMARTIST,
+ AMEDIAFORMAT_KEY_ALBUM};
+
+void formatSetString(AMediaFormat* format, const char* AMEDIAFORMAT_KEY, FuzzedDataProvider* fdp) {
+ if (fdp->ConsumeBool()) {
+ std::string keyValue = fdp->ConsumeRandomLengthString(kMaxBytes);
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY, keyValue.c_str());
+ }
+}
+
+void formatSetInt(AMediaFormat* format, const char* AMEDIAFORMAT_KEY, FuzzedDataProvider* fdp) {
+ if (fdp->ConsumeBool()) {
+ int32_t keyValue = fdp->ConsumeIntegralInRange<size_t>(kMinIntKeyValue, kMaxIntKeyValue);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY, keyValue);
+ }
+}
+
+void formatSetFloat(AMediaFormat* format, const char* AMEDIAFORMAT_KEY, FuzzedDataProvider* fdp) {
+ if (fdp->ConsumeBool()) {
+ float keyValue =
+ fdp->ConsumeFloatingPointInRange<float>(kMinFloatKeyValue, kMaxFloatKeyValue);
+ AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY, keyValue);
+ }
+}
+
+void formatSetBuffer(AMediaFormat* format, const char* AMEDIAFORMAT_KEY, FuzzedDataProvider* fdp) {
+ if (fdp->ConsumeBool()) {
+ std::vector<uint8_t> buffer = fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaFormat_setBuffer(format, AMEDIAFORMAT_KEY, buffer.data(), buffer.size());
+ }
+}
+
+AMediaCodec* NdkMediaCodecFuzzerBase::createAMediaCodecByname(bool isEncoder,
+ bool isCodecForClient) {
+ std::string name;
+ if (isEncoder) {
+ name = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kEncoderNames)
+ : mFdp->ConsumeRandomLengthString(kMaxBytes);
+ } else {
+ name = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kDecoderNames)
+ : mFdp->ConsumeRandomLengthString(kMaxBytes);
+ }
+
+ if (isCodecForClient) {
+ pid_t pid = mFdp->ConsumeIntegral<pid_t>();
+ uid_t uid = mFdp->ConsumeIntegral<uid_t>();
+ return AMediaCodec_createCodecByNameForClient(name.c_str(), pid, uid);
+
+ } else {
+ return AMediaCodec_createCodecByName(name.c_str());
+ }
+}
+
+AMediaCodec* NdkMediaCodecFuzzerBase::createAMediaCodecByType(bool isEncoder,
+ bool isCodecForClient) {
+ std::string mimeType;
+ const char* mime = nullptr;
+
+ if (mFdp->ConsumeBool()) {
+ mimeType = mFdp->ConsumeRandomLengthString(kMaxBytes);
+ mime = mimeType.c_str();
+ } else {
+ AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ }
+
+ if (isCodecForClient) {
+ pid_t pid = mFdp->ConsumeIntegral<pid_t>();
+ uid_t uid = mFdp->ConsumeIntegral<uid_t>();
+ return isEncoder ? AMediaCodec_createEncoderByTypeForClient(mime, pid, uid)
+ : AMediaCodec_createDecoderByTypeForClient(mime, pid, uid);
+ } else {
+ return isEncoder ? AMediaCodec_createEncoderByType(mime)
+ : AMediaCodec_createDecoderByType(mime);
+ }
+}
+
+AMediaFormat* NdkMediaCodecFuzzerBase::getSampleCodecFormat() {
+ AMediaFormat* format = AMediaFormat_new();
+ std::string value;
+ int32_t count = 0;
+ int32_t maxFormatKeys = 0;
+
+ /*set mimeType*/
+ if (mFdp->ConsumeBool()) {
+ value = mFdp->ConsumeRandomLengthString(kMaxBytes);
+ } else {
+ value = mFdp->PickValueInArray(kMimeTypes);
+ }
+ if (mFdp->ConsumeBool()) {
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, value.c_str());
+ }
+
+ maxFormatKeys = mFdp->ConsumeIntegralInRange<int32_t>(0, std::size(kFormatStringKeys));
+ for (count = 0; count < maxFormatKeys; ++count) {
+ std::string formatKey = mFdp->PickValueInArray(kFormatStringKeys);
+ formatSetString(format, formatKey.c_str(), mFdp);
+ }
+
+ maxFormatKeys = mFdp->ConsumeIntegralInRange<int32_t>(0, std::size(kFormatIntKeys));
+ for (count = 0; count < maxFormatKeys; ++count) {
+ std::string formatKey = mFdp->PickValueInArray(kFormatIntKeys);
+ formatSetInt(format, formatKey.c_str(), mFdp);
+ }
+
+ maxFormatKeys = mFdp->ConsumeIntegralInRange<int32_t>(0, std::size(kFormatFloatKeys));
+ for (count = 0; count < maxFormatKeys; ++count) {
+ std::string formatKey = mFdp->PickValueInArray(kFormatFloatKeys);
+ formatSetFloat(format, formatKey.c_str(), mFdp);
+ }
+
+ maxFormatKeys = mFdp->ConsumeIntegralInRange<int32_t>(0, std::size(kFormatBufferKeys));
+ for (count = 0; count < maxFormatKeys; ++count) {
+ std::string formatKey = mFdp->PickValueInArray(kFormatBufferKeys);
+ formatSetBuffer(format, formatKey.c_str(), mFdp);
+ }
+ return format;
+}
+
+AMediaCodec* NdkMediaCodecFuzzerBase::createCodec(bool isEncoder, bool isCodecForClient) {
+ mFormat = getSampleCodecFormat();
+ return (mFdp->ConsumeBool() ? createAMediaCodecByname(isEncoder, isCodecForClient)
+ : createAMediaCodecByType(isEncoder, isCodecForClient));
+}
+
+void NdkMediaCodecFuzzerBase::invokeCodecFormatAPI(AMediaCodec* codec) {
+ AMediaFormat* codecFormat = nullptr;
+ size_t codecFormatAPI = mFdp->ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxCodecFormatAPIs);
+ switch (codecFormatAPI) {
+ case 0: {
+ codecFormat = AMediaCodec_getInputFormat(codec);
+ break;
+ }
+ case 1: {
+ codecFormat = AMediaCodec_getOutputFormat(codec);
+ break;
+ }
+ case 2:
+ default: {
+ AMediaCodecBufferInfo info;
+ int64_t timeOutUs = mFdp->ConsumeIntegralInRange<size_t>(kMinTimeOutUs, kMaxTimeOutUs);
+ ssize_t bufferIndex = 0;
+ if (mFdp->ConsumeBool()) {
+ bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, timeOutUs);
+ } else {
+ bufferIndex =
+ mFdp->ConsumeIntegralInRange<size_t>(kMinBufferIndex, kMaxBufferIndex);
+ }
+ codecFormat = AMediaCodec_getBufferFormat(codec, bufferIndex);
+ break;
+ }
+ }
+ if (codecFormat) {
+ AMediaFormat_delete(codecFormat);
+ }
+}
+
+void NdkMediaCodecFuzzerBase::invokeInputBufferOperationAPI(AMediaCodec* codec) {
+ size_t bufferSize = 0;
+ ssize_t bufferIndex = 0;
+ int64_t timeOutUs = mFdp->ConsumeIntegralInRange<size_t>(kMinTimeOutUs, kMaxTimeOutUs);
+ if (mFdp->ConsumeBool()) {
+ bufferIndex = AMediaCodec_dequeueInputBuffer(codec, timeOutUs);
+ } else {
+ bufferIndex = mFdp->ConsumeIntegralInRange<size_t>(kMinBufferIndex, kMaxBufferIndex);
+ }
+
+ uint8_t* buffer = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufferSize);
+ if (buffer) {
+ std::vector<uint8_t> bytesRead = mFdp->ConsumeBytes<uint8_t>(
+ std::min(mFdp->ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes), bufferSize));
+ memcpy(buffer, bytesRead.data(), bytesRead.size());
+ bufferSize = bytesRead.size();
+ }
+
+ int32_t flag = mFdp->ConsumeIntegralInRange<size_t>(AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
+ AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME);
+ if (mFdp->ConsumeBool()) {
+ AMediaCodec_queueInputBuffer(codec, bufferIndex, 0 /* offset */, bufferSize, 0 /* time */,
+ flag);
+ } else {
+ AMediaCodecCryptoInfo* cryptoInfo = getAMediaCodecCryptoInfo();
+ AMediaCodec_queueSecureInputBuffer(codec, bufferIndex, 0 /* offset */, cryptoInfo,
+ 0 /* time */, flag);
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ }
+}
+
+void NdkMediaCodecFuzzerBase::invokeOutputBufferOperationAPI(AMediaCodec* codec) {
+ ssize_t bufferIndex = 0;
+ int64_t timeOutUs = mFdp->ConsumeIntegralInRange<size_t>(kMinTimeOutUs, kMaxTimeOutUs);
+ if (mFdp->ConsumeBool()) {
+ AMediaCodecBufferInfo info;
+ bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, timeOutUs);
+ } else {
+ bufferIndex = mFdp->ConsumeIntegralInRange<size_t>(kMinBufferIndex, kMaxBufferIndex);
+ }
+
+ if (mFdp->ConsumeBool()) {
+ size_t bufferSize = 0;
+ (void)AMediaCodec_getOutputBuffer(codec, bufferIndex, &bufferSize);
+ }
+
+ if (mFdp->ConsumeBool()) {
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, mFdp->ConsumeBool());
+ } else {
+ AMediaCodec_releaseOutputBufferAtTime(codec, bufferIndex, timeOutUs);
+ }
+}
+
+AMediaCodecCryptoInfo* NdkMediaCodecFuzzerBase::getAMediaCodecCryptoInfo() {
+ uint8_t key[kMaxCryptoKey];
+ uint8_t iv[kMaxCryptoKey];
+ size_t clearBytes[kMaxCryptoKey];
+ size_t encryptedBytes[kMaxCryptoKey];
+
+ for (int32_t i = 0; i < kMaxCryptoKey; ++i) {
+ key[i] = mFdp->ConsumeIntegral<uint8_t>();
+ iv[i] = mFdp->ConsumeIntegral<uint8_t>();
+ clearBytes[i] = mFdp->ConsumeIntegral<size_t>();
+ encryptedBytes[i] = mFdp->ConsumeIntegral<size_t>();
+ }
+
+ return AMediaCodecCryptoInfo_new(kMaxCryptoKey, key, iv, AMEDIACODECRYPTOINFO_MODE_CLEAR,
+ clearBytes, encryptedBytes);
+}
diff --git a/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.h b/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.h
new file mode 100644
index 0000000..42ef6ea
--- /dev/null
+++ b/media/ndk/fuzzer/NdkMediaCodecFuzzerBase.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <android/native_window.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaCodecPlatform.h>
+#include <media/NdkMediaFormat.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+constexpr int32_t kMinBytes = 1;
+constexpr int32_t kMaxBytes = 256;
+constexpr int32_t kMinIntKeyValue = 0;
+constexpr int32_t kMaxIntKeyValue = 6000000;
+constexpr int32_t kMinFloatKeyValue = 1.0f;
+constexpr int32_t kMaxFloatKeyValue = 500.f;
+constexpr int32_t kMinTimeOutUs = 0;
+constexpr int32_t kMaxTimeOutUs = 5000;
+constexpr int32_t kMinAPICase = 0;
+constexpr int32_t kMaxCodecFormatAPIs = 2;
+constexpr int32_t kMaxCryptoKey = 16;
+constexpr int32_t kMinIterations = 10;
+constexpr int32_t kMaxIterations = 100;
+constexpr size_t kMinBufferIndex = 1;
+constexpr size_t kMaxBufferIndex = 128;
+
+class NdkMediaCodecFuzzerBase {
+ public:
+ void invokeCodecFormatAPI(AMediaCodec* codec);
+ void invokeInputBufferOperationAPI(AMediaCodec* codec);
+ void invokeOutputBufferOperationAPI(AMediaCodec* codec);
+ AMediaCodecCryptoInfo* getAMediaCodecCryptoInfo();
+ AMediaCodec* createCodec(bool isEncoder, bool isCodecForClient);
+ AMediaFormat* getCodecFormat() { return mFormat; };
+ void setFdp(FuzzedDataProvider* fdp) { mFdp = fdp; }
+
+ private:
+ AMediaCodec* createAMediaCodecByname(bool isEncoder, bool isCodecForClient);
+ AMediaCodec* createAMediaCodecByType(bool isEncoder, bool isCodecForClient);
+ AMediaFormat* getSampleAudioFormat();
+ AMediaFormat* getSampleVideoFormat();
+ AMediaFormat* getSampleCodecFormat();
+ AMediaFormat* mFormat = nullptr;
+ FuzzedDataProvider* mFdp = nullptr;
+};
diff --git a/media/ndk/fuzzer/README.md b/media/ndk/fuzzer/README.md
new file mode 100644
index 0000000..0fd08b0
--- /dev/null
+++ b/media/ndk/fuzzer/README.md
@@ -0,0 +1,158 @@
+# Fuzzers for libmediandk
+
+## Table of contents
++ [ndk_crypto_fuzzer](#NdkCrypto)
++ [ndk_image_reader_fuzzer](#NdkImageReader)
++ [ndk_extractor_fuzzer](#NdkExtractor)
++ [ndk_mediaformat_fuzzer](#NdkMediaFormat)
++ [ndk_drm_fuzzer](#NdkDrm)
++ [ndk_mediamuxer_fuzzer](#NdkMediaMuxer)
++ [ndk_sync_codec_fuzzer](#NdkSyncCodec)
+
+# <a name="NdkCrypto"></a> Fuzzer for NdkCrypto
+
+NdkCrypto supports the following parameters:
+ UniversalIdentifier (parameter name: "uuid")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+| `uuid`| `Array`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_crypto_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_crypto_fuzzer/ndk_crypto_fuzzer
+```
+
+# <a name="NdkImageReader"></a> Fuzzer for NdkImageReader
+
+NdkImageReader supports the following parameters:
+1. Width (parameter name: "imageWidth")
+2. Height (parameter name: "imageHeight")
+3. Format (parameter name: "imageFormat")
+4. Usage (parameter name: "imageUsage")
+5. Max images (parameter name: "imageMaxCount")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+| `width`| `1 to INT_MAX`| Value obtained from FuzzedDataProvider|
+| `height`| `1 to INT_MAX`| Value obtained from FuzzedDataProvider|
+| `format`| `1 to INT_MAX`| Value obtained from FuzzedDataProvider|
+| `usage`| `1 to INT_MAX`| Value obtained from FuzzedDataProvider|
+| `maxImages`| `1 to android::BufferQueue::MAX_MAX_ACQUIRED_BUFFERS`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_image_reader_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_image_reader_fuzzer/ndk_image_reader_fuzzer
+```
+
+# <a name="NdkExtractor"></a>Fuzzer for NdkExtractor
+
+NdkExtractor supports the following parameters:
+1. SeekMode (parameter name: "mode")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`mode`|0.`AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC`,<br/>1.`AMEDIAEXTRACTOR_SEEK_NEXT_SYNC`,<br/>2.`AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_extractor_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_extractor_fuzzer/ndk_extractor_fuzzer /data/fuzz/${TARGET_ARCH}/ndk_extractor_fuzzer/corpus
+```
+
+
+# <a name="NdkMediaFormat"></a>Fuzzer for NdkMediaFormat
+
+NdkMediaFormat supports the following parameters:
+1. Name (parameter name: "name")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`name`|1.`AMEDIAFORMAT_KEY_AAC_DRC_ATTENUATION_FACTOR`, 2.`AMEDIAFORMAT_KEY_AAC_DRC_BOOST_FACTOR`, 3.`AMEDIAFORMAT_KEY_AAC_DRC_HEAVY_COMPRESSION`, 4.`AMEDIAFORMAT_KEY_AAC_DRC_TARGET_REFERENCE_LEVEL`, 5.`AMEDIAFORMAT_KEY_AAC_ENCODED_TARGET_LEVEL`, 6.`AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT`, 7.`AMEDIAFORMAT_KEY_AAC_PROFILE`, 8.`AMEDIAFORMAT_KEY_AAC_SBR_MODE`, 9.`AMEDIAFORMAT_KEY_ALBUM`, 10.`AMEDIAFORMAT_KEY_ALBUMART`, 11.`AMEDIAFORMAT_KEY_ALBUMARTIST`, 12.`AMEDIAFORMAT_KEY_ARTIST`, 13.`AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO`, 14.`AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PRESENTATION_ID`, 15.`AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PROGRAM_ID`, 16.`AMEDIAFORMAT_KEY_AUDIO_SESSION_ID`, 17.`AMEDIAFORMAT_KEY_AUTHOR`, 18.`AMEDIAFORMAT_KEY_BITRATE_MODE`, 19.`AMEDIAFORMAT_KEY_BIT_RATE`, 20.`AMEDIAFORMAT_KEY_BITS_PER_SAMPLE`, 21.`AMEDIAFORMAT_KEY_CAPTURE_RATE`, 22.`AMEDIAFORMAT_KEY_CDTRACKNUMBER`, 23.`AMEDIAFORMAT_KEY_CHANNEL_COUNT`, 24.`AMEDIAFORMAT_KEY_CHANNEL_MASK`, 25.`AMEDIAFORMAT_KEY_COLOR_FORMAT`, 26.`AMEDIAFORMAT_KEY_COLOR_RANGE`, 27.`AMEDIAFORMAT_KEY_COLOR_STANDARD`, 28.`AMEDIAFORMAT_KEY_COLOR_TRANSFER`, 29.`AMEDIAFORMAT_KEY_COMPILATION`, 30.`AMEDIAFORMAT_KEY_COMPLEXITY`, 31.`AMEDIAFORMAT_KEY_COMPOSER`, 32.`AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED`, 33.`AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE`, 34.`AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK`, 35.`AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES`, 36.`AMEDIAFORMAT_KEY_CRYPTO_IV`, 37.`AMEDIAFORMAT_KEY_CRYPTO_KEY`, 38.`AMEDIAFORMAT_KEY_CRYPTO_MODE`, 39.`AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES`, 40.`AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK`, 41.`AMEDIAFORMAT_KEY_CSD`, 42.`AMEDIAFORMAT_KEY_CSD_0`, 43.`AMEDIAFORMAT_KEY_CSD_1`, 44.`AMEDIAFORMAT_KEY_CSD_2`, 45.`AMEDIAFORMAT_KEY_CSD_AVC`, 46.`AMEDIAFORMAT_KEY_CSD_HEVC`, 47.`AMEDIAFORMAT_KEY_D263`, 48.`AMEDIAFORMAT_KEY_DATE`, 49.`AMEDIAFORMAT_KEY_DISCNUMBER`, 50.`AMEDIAFORMAT_KEY_DISPLAY_CROP`, 51.`AMEDIAFORMAT_KEY_DISPLAY_HEIGHT`, 52.`AMEDIAFORMAT_KEY_DISPLAY_WIDTH`, 53.`AMEDIAFORMAT_KEY_DURATION`, 54.`AMEDIAFORMAT_KEY_ENCODER_DELAY`, 55.`AMEDIAFORMAT_KEY_ENCODER_PADDING`, 56.`AMEDIAFORMAT_KEY_ESDS`, 57.`AMEDIAFORMAT_KEY_EXIF_OFFSET`, 58.`AMEDIAFORMAT_KEY_EXIF_SIZE`, 59.`AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL`, 60.`AMEDIAFORMAT_KEY_FRAME_COUNT`, 61.`AMEDIAFORMAT_KEY_FRAME_RATE`, 62.`AMEDIAFORMAT_KEY_GENRE`, 63.`AMEDIAFORMAT_KEY_GRID_COLUMNS`, 64.`AMEDIAFORMAT_KEY_GRID_ROWS`, 65.`AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT`, 66.`AMEDIAFORMAT_KEY_HDR_STATIC_INFO`, 67.`AMEDIAFORMAT_KEY_HDR10_PLUS_INFO`, 68.`AMEDIAFORMAT_KEY_HEIGHT`, 69.`AMEDIAFORMAT_KEY_ICC_PROFILE`, 70.`AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD`, 71.`AMEDIAFORMAT_KEY_IS_ADTS`, 72.`AMEDIAFORMAT_KEY_IS_AUTOSELECT`, 73.`AMEDIAFORMAT_KEY_IS_DEFAULT`, 74.`AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE`, 75.`AMEDIAFORMAT_KEY_IS_SYNC_FRAME`, 76.`AMEDIAFORMAT_KEY_I_FRAME_INTERVAL`, 77.`AMEDIAFORMAT_KEY_LANGUAGE`, 78.`AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK`, 79.`AMEDIAFORMAT_KEY_LATENCY`, 80.`AMEDIAFORMAT_KEY_LEVEL`, 81.`AMEDIAFORMAT_KEY_LOCATION`, 82.`AMEDIAFORMAT_KEY_LOOP`, 83.`AMEDIAFORMAT_KEY_LOW_LATENCY`, 84.`AMEDIAFORMAT_KEY_LYRICIST`, 85.`AMEDIAFORMAT_KEY_MANUFACTURER`, 86.`AMEDIAFORMAT_KEY_MAX_BIT_RATE`, 87.`AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER`, 88.`AMEDIAFORMAT_KEY_MAX_HEIGHT`, 89.`AMEDIAFORMAT_KEY_MAX_INPUT_SIZE`, 90.`AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER`, 91.`AMEDIAFORMAT_KEY_MAX_WIDTH`, 92.`AMEDIAFORMAT_KEY_MIME`, 93.`AMEDIAFORMAT_KEY_MPEG_USER_DATA`, 94.`AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER`, 95.`AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS`, 96.`AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION`, 97.`AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT`, 98.`AMEDIAFORMAT_KEY_OPERATING_RATE`, 99.`AMEDIAFORMAT_KEY_PCM_ENCODING`, 100.`AMEDIAFORMAT_KEY_PICTURE_TYPE`, 101.`AMEDIAFORMAT_KEY_PRIORITY`, 102.`AMEDIAFORMAT_KEY_PROFILE`, 103.`AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN`, 104.`AMEDIAFORMAT_KEY_PSSH`, 105.`AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP`, 106.`AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER`, 107.`AMEDIAFORMAT_KEY_ROTATION`, 108.`AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET`, 109.`AMEDIAFORMAT_KEY_SAMPLE_RATE`, 110.`AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND`, 111.`AMEDIAFORMAT_KEY_SAR_HEIGHT`, 112.`AMEDIAFORMAT_KEY_SAR_WIDTH`, 113.`AMEDIAFORMAT_KEY_SEI`, 114.`AMEDIAFORMAT_KEY_SLICE_HEIGHT`, 115.`AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS`, 116.`AMEDIAFORMAT_KEY_STRIDE`, 117.`AMEDIAFORMAT_KEY_TARGET_TIME`, 118.`AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT`, 119.`AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID`, 120.`AMEDIAFORMAT_KEY_TEMPORAL_LAYERING`, 121.`AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA`, 122.`AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C`, 123.`AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC`, 124.`AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT`, 125.`AMEDIAFORMAT_KEY_THUMBNAIL_TIME`, 126.`AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH`, 127.`AMEDIAFORMAT_KEY_TILE_HEIGHT`, 128.`AMEDIAFORMAT_KEY_TILE_WIDTH`, 129.`AMEDIAFORMAT_KEY_TIME_US`, 130.`AMEDIAFORMAT_KEY_TITLE`, 131.`AMEDIAFORMAT_KEY_TRACK_ID`, 132.`AMEDIAFORMAT_KEY_TRACK_INDEX`, 133.`AMEDIAFORMAT_KEY_VALID_SAMPLES`, 134.`AMEDIAFORMAT_KEY_VIDEO_ENCODING_STATISTICS_LEVEL`, 135.`AMEDIAFORMAT_KEY_VIDEO_QP_AVERAGE`, 136.`AMEDIAFORMAT_VIDEO_QP_B_MAX`, 137.`AMEDIAFORMAT_VIDEO_QP_B_MIN`, 138.`AMEDIAFORMAT_VIDEO_QP_I_MAX`, 139.`AMEDIAFORMAT_VIDEO_QP_I_MIN`, 140.`AMEDIAFORMAT_VIDEO_QP_MAX`, 141.`AMEDIAFORMAT_VIDEO_QP_MIN`, 142.`AMEDIAFORMAT_VIDEO_QP_P_MAX`, 143.`AMEDIAFORMAT_VIDEO_QP_P_MIN`, 144.`AMEDIAFORMAT_KEY_WIDTH`, 145.`AMEDIAFORMAT_KEY_XMP_OFFSET`, 146.`AMEDIAFORMAT_KEY_XMP_SIZE`, 147.`AMEDIAFORMAT_KEY_YEAR`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_mediaformat_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/${TARGET_ARCH}/ndk_mediaformat_fuzzer/ndk_mediaformat_fuzzer /data/fuzz/${TARGET_ARCH}/ndk_mediaformat_fuzzer/corpus
+```
+
+# <a name="NdkDrm"></a> Fuzzer for NdkDrm
+
+NdkDrm supports the following parameters:
+1. ValidUUID(parameter name: "kCommonPsshBoxUUID" and "kClearKeyUUID")
+2. MimeType(parameter name: "kMimeType")
+3. MediaUUID(parameter name: "MediaUUID")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`ValidUUID`| 0.`kCommonPsshBoxUUID`,<br/> 1.`kClearKeyUUID`,<br/> 2.`kInvalidUUID`|Value obtained from FuzzedDataProvider|
+|`kMimeType`| 0.`video/mp4`,<br/> 1.`audio/mp4`|Value obtained from FuzzedDataProvider|
+|`MediaUUID`| 0.`INVALID_UUID`,<br/> 1.`PSSH_BOX_UUID`,<br/> 2.`CLEARKEY_UUID`|Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_drm_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_drm_fuzzer/ndk_drm_fuzzer
+```
+
+# <a name="NdkMediaMuxer"></a>Fuzzer for NdkMediaMuxer
+
+NdkMediaMuxer supports the following parameters:
+1. OutputFormat (parameter name: "outputFormat")
+2. AppendMode (parameter name: "appendMode")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`outputFormat`|0.`AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4`,<br/>1.`AMEDIAMUXER_OUTPUT_FORMAT_WEBM`,<br/>2.`AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP`| Value obtained from FuzzedDataProvider|
+|`appendMode`|0.`AMEDIAMUXER_APPEND_IGNORE_LAST_VIDEO_GOP`,<br/>1.`AMEDIAMUXER_APPEND_TO_EXISTING_DATA`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_mediamuxer_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_mediamuxer_fuzzer/ndk_mediamuxer_fuzzer
+```
+
+# <a name="NdkSyncCodec"></a>Fuzzer for NdkSyncCodec
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_sync_codec_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_sync_codec_fuzzer/ndk_sync_codec_fuzzer
+```
diff --git a/media/ndk/fuzzer/corpus/2822a2c3bcf57f46cb2bf142448d5baeead4d738 b/media/ndk/fuzzer/corpus/2822a2c3bcf57f46cb2bf142448d5baeead4d738
new file mode 100755
index 0000000..47d79c8
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/2822a2c3bcf57f46cb2bf142448d5baeead4d738
Binary files differ
diff --git a/media/ndk/fuzzer/corpus/58833c3691292c199fa601fd51339d85c1f11ca6 b/media/ndk/fuzzer/corpus/58833c3691292c199fa601fd51339d85c1f11ca6
new file mode 100755
index 0000000..17c934c
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/58833c3691292c199fa601fd51339d85c1f11ca6
Binary files differ
diff --git a/media/ndk/fuzzer/corpus/8556a97764e65bf337b5593058fa92adb68074ce b/media/ndk/fuzzer/corpus/8556a97764e65bf337b5593058fa92adb68074ce
new file mode 100755
index 0000000..00a32e2
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/8556a97764e65bf337b5593058fa92adb68074ce
Binary files differ
diff --git a/media/ndk/fuzzer/corpus/8f76e2e87f79fe213f5cc8c71e5f91d1dcfc5950 b/media/ndk/fuzzer/corpus/8f76e2e87f79fe213f5cc8c71e5f91d1dcfc5950
new file mode 100755
index 0000000..86d4001
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/8f76e2e87f79fe213f5cc8c71e5f91d1dcfc5950
Binary files differ
diff --git a/media/ndk/fuzzer/corpus/d702878cb53fb474230fb7b1a5c035bbb7c21c8d b/media/ndk/fuzzer/corpus/d702878cb53fb474230fb7b1a5c035bbb7c21c8d
new file mode 100755
index 0000000..496c7f3
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/d702878cb53fb474230fb7b1a5c035bbb7c21c8d
Binary files differ
diff --git a/media/ndk/fuzzer/corpus/edc2485f3927e07d7ab705337f16f0b978c57d0a b/media/ndk/fuzzer/corpus/edc2485f3927e07d7ab705337f16f0b978c57d0a
new file mode 100755
index 0000000..55437ac
--- /dev/null
+++ b/media/ndk/fuzzer/corpus/edc2485f3927e07d7ab705337f16f0b978c57d0a
Binary files differ
diff --git a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
new file mode 100644
index 0000000..2b22f0f
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaCrypto.h>
+
+constexpr size_t kMaxString = 256;
+constexpr size_t kMinBytes = 0;
+constexpr size_t kMaxBytes = 1000;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ AMediaUUID uuid = {};
+ int32_t maxLen = fdp.ConsumeIntegralInRange<size_t>(kMinBytes, (size_t)sizeof(AMediaUUID));
+ for (size_t idx = 0; idx < maxLen; ++idx) {
+ uuid[idx] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ std::vector<uint8_t> initData =
+ fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCrypto* crypto = AMediaCrypto_new(uuid, initData.data(), initData.size());
+ while (fdp.remaining_bytes()) {
+ auto invokeNdkCryptoFuzzer = fdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ AMediaCrypto_requiresSecureDecoderComponent(
+ fdp.ConsumeRandomLengthString(kMaxString).c_str());
+ },
+ [&]() { AMediaCrypto_isCryptoSchemeSupported(uuid); },
+ });
+ invokeNdkCryptoFuzzer();
+ }
+ AMediaCrypto_delete(crypto);
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_drm_fuzzer.cpp b/media/ndk/fuzzer/ndk_drm_fuzzer.cpp
new file mode 100644
index 0000000..8c11c9d
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_drm_fuzzer.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <media/NdkMediaCrypto.h>
+#include <media/NdkMediaDrm.h>
+#include "fuzzer/FuzzedDataProvider.h"
+
+constexpr int32_t kMinBytes = 1;
+constexpr int32_t kMaxBytes = 256;
+constexpr int32_t kMinParamVal = 0;
+constexpr int32_t kMaxParamVal = 3;
+constexpr int32_t kMediaUUIdSize = sizeof(AMediaUUID);
+constexpr int32_t kMinProvisionResponseSize = 0;
+constexpr int32_t kMaxProvisionResponseSize = 16;
+constexpr int32_t kMessageSize = 16;
+constexpr int32_t kMinAPIcase = 0;
+constexpr int32_t kMaxdecryptEncryptAPIs = 10;
+constexpr int32_t kMaxpropertyAPIs = 3;
+constexpr int32_t kMaxsetListenerAPIs = 2;
+constexpr int32_t kMaxndkDrmAPIs = 3;
+uint8_t signature[kMessageSize];
+
+enum MediaUUID { INVALID_UUID = 0, PSSH_BOX_UUID, CLEARKEY_UUID, kMaxValue = CLEARKEY_UUID };
+
+constexpr uint8_t kCommonPsshBoxUUID[] = {0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+ 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
+
+constexpr uint8_t kClearKeyUUID[] = {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+ 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
+
+constexpr uint8_t kInvalidUUID[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+
+uint8_t kClearkeyPssh[] = {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00};
+
+std::string kPropertyName = "clientId";
+std::string kMimeType[] = {"video/mp4", "audio/mp4"};
+std::string kCipherAlgorithm[] = {"AES/CBC/NoPadding", ""};
+std::string kMacAlgorithm[] = {"HmacSHA256", ""};
+
+class NdkMediaDrmFuzzer {
+ public:
+ NdkMediaDrmFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void invokeNdkDrm();
+ static void KeysChangeListener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
+ const AMediaDrmKeyStatus* keysStatus, size_t numKeys,
+ bool hasNewUsableKey) {
+ (void)drm;
+ (void)sessionId;
+ (void)keysStatus;
+ (void)numKeys;
+ (void)hasNewUsableKey;
+ };
+
+ static void ExpirationUpdateListener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
+ int64_t expiryTimeInMS) {
+ (void)drm;
+ (void)sessionId;
+ (void)expiryTimeInMS;
+ };
+
+ static void listener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
+ AMediaDrmEventType eventType, int extra, const uint8_t* data,
+ size_t dataSize) {
+ (void)drm;
+ (void)sessionId;
+ (void)eventType;
+ (void)extra;
+ (void)data;
+ (void)dataSize;
+ }
+
+ private:
+ FuzzedDataProvider mFdp;
+ void invokeDrmCreatePlugin();
+ void invokeDrmSetListener();
+ void invokeDrmPropertyAPI();
+ void invokeDrmDecryptEncryptAPI();
+ void invokeDrmSecureStopAPI();
+ AMediaDrmSessionId mSessionId = {};
+ AMediaDrm* mDrm = nullptr;
+};
+
+void NdkMediaDrmFuzzer::invokeDrmCreatePlugin() {
+ const uint8_t* mediaUUID = nullptr;
+ uint32_t uuidEnum = mFdp.ConsumeEnum<MediaUUID>();
+ switch (uuidEnum) {
+ case INVALID_UUID: {
+ mediaUUID = kInvalidUUID;
+ break;
+ }
+ case PSSH_BOX_UUID: {
+ mediaUUID = kCommonPsshBoxUUID;
+ break;
+ }
+ case CLEARKEY_UUID:
+ default: {
+ mediaUUID = kClearKeyUUID;
+ break;
+ }
+ }
+ mDrm = AMediaDrm_createByUUID(mediaUUID);
+}
+
+void NdkMediaDrmFuzzer::invokeDrmSecureStopAPI() {
+ // get maximum number of secure stops
+ AMediaDrmSecureStop secureStops;
+ size_t numSecureStops = kMaxParamVal;
+ // The API behavior could change based on the drm object (clearkey or
+ // psshbox) This API detects secure stops msg and release them.
+ AMediaDrm_getSecureStops(mDrm, &secureStops, &numSecureStops);
+ AMediaDrm_releaseSecureStops(mDrm, &secureStops);
+}
+
+void NdkMediaDrmFuzzer::invokeDrmSetListener() {
+ int32_t setListenerAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxsetListenerAPIs);
+ switch (setListenerAPI) {
+ case 0: { // set on key change listener
+ AMediaDrm_setOnKeysChangeListener(mDrm, KeysChangeListener);
+ break;
+ }
+ case 1: { // set on expiration on update listener
+ AMediaDrm_setOnExpirationUpdateListener(mDrm, ExpirationUpdateListener);
+ break;
+ }
+ case 2:
+ default: { // set on event listener
+ AMediaDrm_setOnEventListener(mDrm, listener);
+ break;
+ }
+ }
+}
+
+void NdkMediaDrmFuzzer::invokeDrmPropertyAPI() {
+ int32_t propertyAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxpropertyAPIs);
+ switch (propertyAPI) {
+ case 0: { // set property byte array
+ uint8_t value[kMediaUUIdSize];
+ std::string name =
+ mFdp.ConsumeBool() ? kPropertyName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ const char* propertyName = name.c_str();
+ AMediaDrm_setPropertyByteArray(mDrm, propertyName, value, sizeof(value));
+ break;
+ }
+ case 1: { // get property in byte array
+ AMediaDrmByteArray array;
+ std::string name =
+ mFdp.ConsumeBool() ? kPropertyName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ const char* propertyName = name.c_str();
+ AMediaDrm_getPropertyByteArray(mDrm, propertyName, &array);
+ break;
+ }
+ case 2: { // set string type property
+ std::string propertyName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ std::string value = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ AMediaDrm_setPropertyString(mDrm, propertyName.c_str(), value.c_str());
+ break;
+ }
+ case 3:
+ default: { // get property in string
+ const char* stringValue = nullptr;
+ std::string propertyName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ AMediaDrm_getPropertyString(mDrm, propertyName.c_str(), &stringValue);
+ break;
+ }
+ }
+}
+
+void NdkMediaDrmFuzzer::invokeDrmDecryptEncryptAPI() {
+ int32_t decryptEncryptAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxdecryptEncryptAPIs);
+ switch (decryptEncryptAPI) {
+ case 0: { // Check if crypto scheme is supported
+ std::string mimeType = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kMimeType)
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ AMediaDrm_isCryptoSchemeSupported(kClearKeyUUID, mimeType.c_str());
+ break;
+ }
+ case 1: { // get a provision request byte array
+ const uint8_t* legacyRequest;
+ size_t legacyRequestSize = 1;
+ const char* legacyDefaultUrl;
+ AMediaDrm_getProvisionRequest(mDrm, &legacyRequest, &legacyRequestSize,
+ &legacyDefaultUrl);
+ break;
+ }
+ case 2: { // provide a response to the DRM engine plugin
+ const int32_t provisionresponseSize = mFdp.ConsumeIntegralInRange<size_t>(
+ kMinProvisionResponseSize, kMaxProvisionResponseSize);
+ uint8_t provisionResponse[provisionresponseSize];
+ AMediaDrm_provideProvisionResponse(mDrm, provisionResponse, sizeof(provisionResponse));
+ break;
+ }
+ case 3: { // get key request
+ const uint8_t* keyRequest = nullptr;
+ size_t keyRequestSize = 0;
+ std::string mimeType = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kMimeType)
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ size_t numOptionalParameters =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinParamVal, kMaxParamVal);
+ AMediaDrmKeyValue optionalParameters[numOptionalParameters];
+ std::string keys[numOptionalParameters];
+ std::string values[numOptionalParameters];
+ for (int i = 0; i < numOptionalParameters; ++i) {
+ keys[i] = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ values[i] = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ optionalParameters[i].mKey = keys[i].c_str();
+ optionalParameters[i].mValue = values[i].c_str();
+ }
+ AMediaDrmKeyType keyType = (AMediaDrmKeyType)mFdp.ConsumeIntegralInRange<int>(
+ KEY_TYPE_STREAMING, KEY_TYPE_RELEASE);
+ AMediaDrm_getKeyRequest(mDrm, &mSessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
+ mimeType.c_str(), keyType, optionalParameters,
+ numOptionalParameters, &keyRequest, &keyRequestSize);
+ break;
+ }
+ case 4: { // query key status
+ size_t numPairs = mFdp.ConsumeIntegralInRange<size_t>(kMinParamVal, kMaxParamVal);
+ AMediaDrmKeyValue keyStatus[numPairs];
+ AMediaDrm_queryKeyStatus(mDrm, &mSessionId, keyStatus, &numPairs);
+ break;
+ }
+ case 5: { // provide key response
+ std::string key = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ const char* keyResponse = key.c_str();
+ AMediaDrmKeySetId keySetId;
+ AMediaDrm_provideKeyResponse(mDrm, &mSessionId,
+ reinterpret_cast<const uint8_t*>(keyResponse),
+ sizeof(keyResponse), &keySetId);
+ break;
+ }
+ case 6: { // restore key
+ AMediaDrmKeySetId keySetId;
+ AMediaDrm_restoreKeys(mDrm, &mSessionId, &keySetId);
+ break;
+ }
+
+ case 7: { // Check signature verification using the specified Algorithm
+ std::string algorithm = kMacAlgorithm[mFdp.ConsumeBool()];
+ std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> message = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaDrm_verify(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), message.data(),
+ message.size(), signature, sizeof(signature));
+ break;
+ }
+ case 8: { // Generate a signature using the specified Algorithm
+ std::string algorithm = kMacAlgorithm[mFdp.ConsumeBool()];
+ std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> message = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ size_t signatureSize = sizeof(signature);
+ AMediaDrm_sign(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), message.data(),
+ message.size(), signature, &signatureSize);
+ break;
+ }
+ case 9: { // Decrypt the data using algorithm
+ std::string algorithm = kCipherAlgorithm[mFdp.ConsumeBool()];
+ std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> iv = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> input = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ uint8_t output[kMessageSize];
+ AMediaDrm_decrypt(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), iv.data(),
+ input.data(), output, input.size());
+ break;
+ }
+ case 10:
+ default: { // Encrypt the data using algorithm
+ std::string algorithm = kCipherAlgorithm[mFdp.ConsumeBool()];
+ std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> iv = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ std::vector<uint8_t> input = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ uint8_t output[kMessageSize];
+ AMediaDrm_encrypt(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), iv.data(),
+ input.data(), output, input.size());
+ break;
+ }
+ }
+ AMediaDrm_removeKeys(mDrm, &mSessionId);
+}
+
+void NdkMediaDrmFuzzer::invokeNdkDrm() {
+ while (mFdp.remaining_bytes() > 0) {
+ // The API is called at start as it creates a AMediaDrm Object.
+ // mDrm AMediaDrm object is used in the below APIs.
+ invokeDrmCreatePlugin();
+ if (mDrm) {
+ // The API opens session and returns "mSessionId" session Id.
+ // "mSessionId" is required in the below APIs.
+ AMediaDrm_openSession(mDrm, &mSessionId);
+ int32_t ndkDrmAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxndkDrmAPIs);
+ switch (ndkDrmAPI) {
+ case 0: {
+ invokeDrmDecryptEncryptAPI();
+ break;
+ }
+ case 1: {
+ invokeDrmPropertyAPI();
+ break;
+ }
+ case 2: {
+ invokeDrmSetListener();
+ break;
+ }
+ case 3:
+ default: {
+ invokeDrmSecureStopAPI();
+ break;
+ }
+ }
+ AMediaDrm_closeSession(mDrm, &mSessionId);
+ AMediaDrm_release(mDrm);
+ }
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ NdkMediaDrmFuzzer ndkMediaDrmFuzzer(data, size);
+ ndkMediaDrmFuzzer.invokeNdkDrm();
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_extractor_fuzzer.cpp b/media/ndk/fuzzer/ndk_extractor_fuzzer.cpp
new file mode 100644
index 0000000..9bbb79c
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_extractor_fuzzer.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <android/binder_process.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaExtractor.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+constexpr int32_t kCaseStart = 0;
+constexpr int32_t kCaseEnd = 8;
+constexpr float kMinDataSizeFactor = 0.5;
+constexpr int32_t kMaxIterations = 1000;
+const std::string kPathPrefix = "file://";
+
+constexpr SeekMode kSeekMode[] = {AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC,
+ AMEDIAEXTRACTOR_SEEK_NEXT_SYNC,
+ AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC};
+
+class NdkExtractorFuzzer {
+ public:
+ NdkExtractorFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+ mDataSourceFd = mkstemp(mTestPath);
+ std::vector<char> dataBuffer = mFdp.ConsumeBytes<char>(
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinDataSizeFactor * size, size));
+ mDataSize = dataBuffer.size();
+ write(mDataSourceFd, dataBuffer.data(), dataBuffer.size());
+ };
+
+ ~NdkExtractorFuzzer() {
+ close(mDataSourceFd);
+ remove(mTestPath);
+ };
+
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ int32_t mDataSourceFd = 0;
+ int32_t mDataSize = 0;
+
+ // Defined a mutable TestSource file path for mkstemp().
+ char mTestPath[64] = "/data/local/tmp/TestSource_XXXXXX";
+};
+
+void NdkExtractorFuzzer::process() {
+ AMediaExtractor* mMediaExtractor = AMediaExtractor_new();
+ AMediaDataSource* mDataSource = nullptr;
+
+ if (mFdp.ConsumeBool()) {
+ AMediaExtractor_setDataSourceFd(mMediaExtractor, mDataSourceFd, 0, mDataSize);
+ } else {
+ mDataSource = AMediaDataSource_newUri((kPathPrefix + mTestPath).c_str(), 0 /* numkeys */,
+ nullptr /* keyvalues */);
+ AMediaExtractor_setDataSourceCustom(mMediaExtractor, mDataSource);
+ }
+
+ /**
+ * Limiting the number of iterations of while loop
+ * to prevent a possible timeout.
+ */
+ int32_t count = 0;
+ while (mFdp.remaining_bytes() && count++ < kMaxIterations) {
+ switch (mFdp.ConsumeIntegralInRange<int32_t>(kCaseStart, kCaseEnd)) {
+ case 0:{
+ AMediaExtractor_selectTrack(mMediaExtractor,
+ mFdp.ConsumeIntegral<size_t>() /* idx */);
+ break;
+ }
+ case 1:{
+ AMediaExtractor_unselectTrack(mMediaExtractor,
+ mFdp.ConsumeIntegral<size_t>() /* idx */);
+ break;
+ }
+ case 2:{
+ int32_t sampleSize = AMediaExtractor_getSampleSize(mMediaExtractor);
+ if (sampleSize > 0) {
+ std::vector<uint8_t> buffer(sampleSize);
+ AMediaExtractor_readSampleData(
+ mMediaExtractor, buffer.data(),
+ mFdp.ConsumeIntegralInRange<size_t>(0, sampleSize) /* capacity */);
+ }
+ break;
+ }
+ case 3:{
+ AMediaExtractor_getSampleFlags(mMediaExtractor);
+ break;
+ }
+ case 4:{
+ AMediaExtractor_getSampleCryptoInfo(mMediaExtractor);
+ break;
+ }
+ case 5:{
+ AMediaExtractor_getPsshInfo(mMediaExtractor);
+ break;
+ }
+ case 6:{
+ AMediaExtractor_advance(mMediaExtractor);
+ break;
+ }
+ case 7:{
+ AMediaFormat* mediaFormat = mFdp.ConsumeBool() ? AMediaFormat_new() : nullptr;
+ AMediaExtractor_getSampleFormat(mMediaExtractor, mediaFormat);
+ AMediaFormat_delete(mediaFormat);
+ break;
+ }
+ case 8:{
+ AMediaExtractor_seekTo(mMediaExtractor,
+ mFdp.ConsumeIntegral<int64_t>() /* seekPosUs */,
+ mFdp.PickValueInArray(kSeekMode) /* mode */);
+ break;
+ }
+ };
+ }
+
+ AMediaDataSource_delete(mDataSource);
+ AMediaExtractor_delete(mMediaExtractor);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ /**
+ * Create a threadpool for incoming binder transactions,
+ * without this extractor results in a DoS after few instances.
+ */
+ ABinderProcess_startThreadPool();
+
+ NdkExtractorFuzzer ndkExtractorFuzzer(data, size);
+ ndkExtractorFuzzer.process();
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp b/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp
new file mode 100644
index 0000000..6c11798
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_image_reader_fuzzer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <cutils/native_handle.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gui/BufferQueue.h>
+#include <media/NdkImageReader.h>
+
+constexpr int32_t kMaxSize = INT_MAX;
+constexpr int32_t kMinSize = 1;
+constexpr int32_t kMinImages = 1;
+
+class NdkImageReaderFuzzer {
+ public:
+ NdkImageReaderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ static void onImageAvailable(void*, AImageReader*){};
+ static void onBufferRemoved(void*, AImageReader*, AHardwareBuffer*){};
+};
+
+void NdkImageReaderFuzzer::process() {
+ AImageReader* reader = nullptr;
+ AImage* img = nullptr;
+ native_handle_t* handle = nullptr;
+ int32_t* acquireFenceFd = nullptr;
+ int32_t imageWidth = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ int32_t imageHeight = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ int32_t imageFormat = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ int32_t imageUsage = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ int32_t imageMaxCount = mFdp.ConsumeIntegralInRange<int32_t>(
+ kMinImages, android::BufferQueue::MAX_MAX_ACQUIRED_BUFFERS);
+ AImageReader_ImageListener readerAvailableCb{this, NdkImageReaderFuzzer::onImageAvailable};
+ AImageReader_BufferRemovedListener readerDetachedCb{this, onBufferRemoved};
+
+ if (mFdp.ConsumeBool()) {
+ AImageReader_new(imageWidth, imageHeight, imageFormat, imageMaxCount, &reader);
+ } else {
+ AImageReader_newWithUsage(imageWidth, imageHeight, imageFormat, imageUsage, imageMaxCount,
+ &reader);
+ }
+ while (mFdp.remaining_bytes()) {
+ auto ndkImageFunction = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() { AImageReader_acquireNextImage(reader, &img); },
+ [&]() { AImageReader_acquireLatestImage(reader, &img); },
+ [&]() { AImageReader_setImageListener(reader, &readerAvailableCb); },
+ [&]() { AImageReader_acquireNextImageAsync(reader, &img, acquireFenceFd); },
+ [&]() { AImageReader_acquireLatestImageAsync(reader, &img, acquireFenceFd); },
+ [&]() { AImageReader_setBufferRemovedListener(reader, &readerDetachedCb); },
+ [&]() { AImageReader_getWindowNativeHandle(reader, &handle); },
+ });
+ ndkImageFunction();
+ }
+ AImageReader_delete(reader);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ NdkImageReaderFuzzer ndkImageReaderFuzzer(data, size);
+ ndkImageReaderFuzzer.process();
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp b/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp
new file mode 100644
index 0000000..c19ea13
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <datasource/FileSource.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaFormat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <fstream>
+
+const char* kValidKeys[] = {
+ AMEDIAFORMAT_KEY_AAC_DRC_ATTENUATION_FACTOR,
+ AMEDIAFORMAT_KEY_AAC_DRC_BOOST_FACTOR,
+ AMEDIAFORMAT_KEY_AAC_DRC_HEAVY_COMPRESSION,
+ AMEDIAFORMAT_KEY_AAC_DRC_TARGET_REFERENCE_LEVEL,
+ AMEDIAFORMAT_KEY_AAC_ENCODED_TARGET_LEVEL,
+ AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_AAC_PROFILE,
+ AMEDIAFORMAT_KEY_AAC_SBR_MODE,
+ AMEDIAFORMAT_KEY_ALBUM,
+ AMEDIAFORMAT_KEY_ALBUMART,
+ AMEDIAFORMAT_KEY_ALBUMARTIST,
+ AMEDIAFORMAT_KEY_ARTIST,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PRESENTATION_ID,
+ AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PROGRAM_ID,
+ AMEDIAFORMAT_KEY_AUDIO_SESSION_ID,
+ AMEDIAFORMAT_KEY_AUTHOR,
+ AMEDIAFORMAT_KEY_BITRATE_MODE,
+ AMEDIAFORMAT_KEY_BIT_RATE,
+ AMEDIAFORMAT_KEY_BITS_PER_SAMPLE,
+ AMEDIAFORMAT_KEY_CAPTURE_RATE,
+ AMEDIAFORMAT_KEY_CDTRACKNUMBER,
+ AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ AMEDIAFORMAT_KEY_COLOR_FORMAT,
+ AMEDIAFORMAT_KEY_COLOR_RANGE,
+ AMEDIAFORMAT_KEY_COLOR_STANDARD,
+ AMEDIAFORMAT_KEY_COLOR_TRANSFER,
+ AMEDIAFORMAT_KEY_COMPILATION,
+ AMEDIAFORMAT_KEY_COMPLEXITY,
+ AMEDIAFORMAT_KEY_COMPOSER,
+ AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED,
+ AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK,
+ AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES,
+ AMEDIAFORMAT_KEY_CRYPTO_IV,
+ AMEDIAFORMAT_KEY_CRYPTO_KEY,
+ AMEDIAFORMAT_KEY_CRYPTO_MODE,
+ AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES,
+ AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK,
+ AMEDIAFORMAT_KEY_CSD,
+ AMEDIAFORMAT_KEY_CSD_0,
+ AMEDIAFORMAT_KEY_CSD_1,
+ AMEDIAFORMAT_KEY_CSD_2,
+ AMEDIAFORMAT_KEY_CSD_AVC,
+ AMEDIAFORMAT_KEY_CSD_HEVC,
+ AMEDIAFORMAT_KEY_D263,
+ AMEDIAFORMAT_KEY_DATE,
+ AMEDIAFORMAT_KEY_DISCNUMBER,
+ AMEDIAFORMAT_KEY_DISPLAY_CROP,
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
+ AMEDIAFORMAT_KEY_DURATION,
+ AMEDIAFORMAT_KEY_ENCODER_DELAY,
+ AMEDIAFORMAT_KEY_ENCODER_PADDING,
+ AMEDIAFORMAT_KEY_ESDS,
+ AMEDIAFORMAT_KEY_EXIF_OFFSET,
+ AMEDIAFORMAT_KEY_EXIF_SIZE,
+ AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL,
+ AMEDIAFORMAT_KEY_FRAME_COUNT,
+ AMEDIAFORMAT_KEY_FRAME_RATE,
+ AMEDIAFORMAT_KEY_GENRE,
+ AMEDIAFORMAT_KEY_GRID_COLUMNS,
+ AMEDIAFORMAT_KEY_GRID_ROWS,
+ AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT,
+ AMEDIAFORMAT_KEY_HDR_STATIC_INFO,
+ AMEDIAFORMAT_KEY_HDR10_PLUS_INFO,
+ AMEDIAFORMAT_KEY_HEIGHT,
+ AMEDIAFORMAT_KEY_ICC_PROFILE,
+ AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD,
+ AMEDIAFORMAT_KEY_IS_ADTS,
+ AMEDIAFORMAT_KEY_IS_AUTOSELECT,
+ AMEDIAFORMAT_KEY_IS_DEFAULT,
+ AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE,
+ AMEDIAFORMAT_KEY_IS_SYNC_FRAME,
+ AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
+ AMEDIAFORMAT_KEY_LANGUAGE,
+ AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK,
+ AMEDIAFORMAT_KEY_LATENCY,
+ AMEDIAFORMAT_KEY_LEVEL,
+ AMEDIAFORMAT_KEY_LOCATION,
+ AMEDIAFORMAT_KEY_LOOP,
+ AMEDIAFORMAT_KEY_LOW_LATENCY,
+ AMEDIAFORMAT_KEY_LYRICIST,
+ AMEDIAFORMAT_KEY_MANUFACTURER,
+ AMEDIAFORMAT_KEY_MAX_BIT_RATE,
+ AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER,
+ AMEDIAFORMAT_KEY_MAX_HEIGHT,
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE,
+ AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER,
+ AMEDIAFORMAT_KEY_MAX_WIDTH,
+ AMEDIAFORMAT_KEY_MIME,
+ AMEDIAFORMAT_KEY_MPEG_USER_DATA,
+ AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER,
+ AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS,
+ AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION,
+ AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT,
+ AMEDIAFORMAT_KEY_OPERATING_RATE,
+ AMEDIAFORMAT_KEY_PCM_ENCODING,
+ AMEDIAFORMAT_KEY_PICTURE_TYPE,
+ AMEDIAFORMAT_KEY_PRIORITY,
+ AMEDIAFORMAT_KEY_PROFILE,
+ AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN,
+ AMEDIAFORMAT_KEY_PSSH,
+ AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP,
+ AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER,
+ AMEDIAFORMAT_KEY_ROTATION,
+ AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET,
+ AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND,
+ AMEDIAFORMAT_KEY_SAR_HEIGHT,
+ AMEDIAFORMAT_KEY_SAR_WIDTH,
+ AMEDIAFORMAT_KEY_SEI,
+ AMEDIAFORMAT_KEY_SLICE_HEIGHT,
+ AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS,
+ AMEDIAFORMAT_KEY_STRIDE,
+ AMEDIAFORMAT_KEY_TARGET_TIME,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYERING,
+ AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA,
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C,
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
+ AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT,
+ AMEDIAFORMAT_KEY_THUMBNAIL_TIME,
+ AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH,
+ AMEDIAFORMAT_KEY_TILE_HEIGHT,
+ AMEDIAFORMAT_KEY_TILE_WIDTH,
+ AMEDIAFORMAT_KEY_TIME_US,
+ AMEDIAFORMAT_KEY_TITLE,
+ AMEDIAFORMAT_KEY_TRACK_ID,
+ AMEDIAFORMAT_KEY_TRACK_INDEX,
+ AMEDIAFORMAT_KEY_VALID_SAMPLES,
+ AMEDIAFORMAT_KEY_VIDEO_ENCODING_STATISTICS_LEVEL,
+ AMEDIAFORMAT_KEY_VIDEO_QP_AVERAGE,
+ AMEDIAFORMAT_VIDEO_QP_B_MAX,
+ AMEDIAFORMAT_VIDEO_QP_B_MIN,
+ AMEDIAFORMAT_VIDEO_QP_I_MAX,
+ AMEDIAFORMAT_VIDEO_QP_I_MIN,
+ AMEDIAFORMAT_VIDEO_QP_MAX,
+ AMEDIAFORMAT_VIDEO_QP_MIN,
+ AMEDIAFORMAT_VIDEO_QP_P_MAX,
+ AMEDIAFORMAT_VIDEO_QP_P_MIN,
+ AMEDIAFORMAT_KEY_WIDTH,
+ AMEDIAFORMAT_KEY_XMP_OFFSET,
+ AMEDIAFORMAT_KEY_XMP_SIZE,
+ AMEDIAFORMAT_KEY_YEAR,
+};
+constexpr size_t kMinBytes = 0;
+constexpr size_t kMaxBytes = 1000;
+constexpr size_t kMinChoice = 0;
+constexpr size_t kMaxChoice = 9;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ AMediaFormat* mediaFormat = AMediaFormat_new();
+ while (fdp.remaining_bytes()) {
+ const char* name = nullptr;
+ std::string nameString;
+ if (fdp.ConsumeBool()) {
+ nameString =
+ fdp.ConsumeBool()
+ ? fdp.PickValueInArray(kValidKeys)
+ : fdp.ConsumeRandomLengthString(
+ fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ name = nameString.c_str();
+ }
+ switch (fdp.ConsumeIntegralInRange<int32_t>(kMinChoice, kMaxChoice)) {
+ case 0: {
+ AMediaFormat_setInt32(mediaFormat, name,
+ fdp.ConsumeIntegral<int32_t>() /* value */);
+ break;
+ }
+ case 1: {
+ AMediaFormat_setInt64(mediaFormat, name,
+ fdp.ConsumeIntegral<int64_t>() /* value */);
+ break;
+ }
+ case 2: {
+ AMediaFormat_setFloat(mediaFormat, name,
+ fdp.ConsumeFloatingPoint<float>() /* value */);
+ break;
+ }
+ case 3: {
+ AMediaFormat_setDouble(mediaFormat, name,
+ fdp.ConsumeFloatingPoint<double>() /* value */);
+ break;
+ }
+ case 4: {
+ AMediaFormat_setSize(mediaFormat, name, fdp.ConsumeIntegral<size_t>() /* value */);
+ break;
+ }
+ case 5: {
+ std::string value;
+ if (fdp.ConsumeBool()) {
+ value = fdp.ConsumeRandomLengthString(
+ fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ }
+ AMediaFormat_setString(mediaFormat, name,
+ fdp.ConsumeBool() ? nullptr : value.c_str());
+ break;
+ }
+ case 6: {
+ AMediaFormat_setRect(mediaFormat, name, fdp.ConsumeIntegral<int32_t>() /* left */,
+ fdp.ConsumeIntegral<int32_t>() /* top */,
+ fdp.ConsumeIntegral<int32_t>() /* bottom */,
+ fdp.ConsumeIntegral<int32_t>() /* right */);
+ break;
+ }
+ case 7: {
+ std::vector<uint8_t> bufferData = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaFormat_setBuffer(mediaFormat, name, bufferData.data(), bufferData.size());
+ break;
+ }
+ case 8: {
+ AMediaFormat_toString(mediaFormat);
+ break;
+ }
+ default: {
+ AMediaFormat* format = fdp.ConsumeBool() ? nullptr : AMediaFormat_new();
+ AMediaFormat_copy(format, mediaFormat);
+ AMediaFormat_delete(format);
+ break;
+ }
+ }
+ }
+ AMediaFormat_clear(mediaFormat);
+ AMediaFormat_delete(mediaFormat);
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_mediamuxer_fuzzer.cpp b/media/ndk/fuzzer/ndk_mediamuxer_fuzzer.cpp
new file mode 100644
index 0000000..8c49d28
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_mediamuxer_fuzzer.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaMuxer.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+const std::string kMuxerFile = "mediaMuxer";
+const std::string kAppendFile = "mediaAppend";
+constexpr size_t kMinBytes = 0;
+constexpr size_t kMaxBytes = 1000;
+constexpr size_t kMinChoice = 0;
+constexpr size_t kMaxChoice = 7;
+constexpr size_t kMaxStringLength = 20;
+constexpr size_t kOffset = 0;
+
+constexpr OutputFormat kOutputFormat[] = {AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4,
+ AMEDIAMUXER_OUTPUT_FORMAT_WEBM,
+ AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP};
+constexpr AppendMode kAppendMode[] = {AMEDIAMUXER_APPEND_IGNORE_LAST_VIDEO_GOP,
+ AMEDIAMUXER_APPEND_TO_EXISTING_DATA};
+
+const std::string kAudioMimeType[] = {"audio/3gpp", "audio/amr-wb", "audio/mp4a-latm",
+ "audio/flac", "audio/vorbis", "audio/opus"};
+
+const std::string kVideoMimeType[] = {"video/x-vnd.on2.vp8", "video/x-vnd.on2.vp9", "video/av01",
+ "video/avc", "video/hevc", "video/mp4v-es",
+ "video/3gpp"};
+
+void getSampleAudioFormat(FuzzedDataProvider& fdp, AMediaFormat* format) {
+ std::string mimeType = fdp.ConsumeBool() ? fdp.ConsumeRandomLengthString(kMaxStringLength)
+ : fdp.PickValueInArray(kAudioMimeType);
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mimeType.c_str());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt64(format, AMEDIAFORMAT_KEY_DURATION, fdp.ConsumeIntegral<int64_t>());
+}
+
+void getSampleVideoFormat(FuzzedDataProvider& fdp, AMediaFormat* format) {
+ std::string mimeType = fdp.ConsumeBool() ? fdp.ConsumeRandomLengthString(kMaxStringLength)
+ : fdp.PickValueInArray(kAudioMimeType);
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mimeType.c_str());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, fdp.ConsumeIntegral<int32_t>());
+ AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
+ fdp.ConsumeFloatingPoint<float>());
+ AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_CAPTURE_RATE, fdp.ConsumeFloatingPoint<float>());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, fdp.ConsumeIntegral<int32_t>());
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ /**
+ * Create a threadpool for incoming binder transactions,
+ * without this muxer results in a DoS after few instances.
+ */
+ ABinderProcess_startThreadPool();
+ FuzzedDataProvider fdp(data, size);
+ /**
+ * memfd_create() creates an anonymous file and returns a file
+ * descriptor that refers to it. MFD_ALLOW_SEALING allow sealing
+ * operations on this file.
+ */
+ int32_t fd = -1;
+ AMediaMuxer* muxer = nullptr;
+ if (fdp.ConsumeBool()) {
+ fd = memfd_create(kMuxerFile.c_str(), MFD_ALLOW_SEALING);
+ muxer = AMediaMuxer_new(fd, fdp.ConsumeBool()
+ ? fdp.PickValueInArray(kOutputFormat)
+ : (OutputFormat)fdp.ConsumeIntegral<int32_t>());
+ } else {
+ fd = memfd_create(kAppendFile.c_str(), MFD_ALLOW_SEALING);
+ std::vector<uint8_t> appendData =
+ fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ write(fd, appendData.data(), appendData.size());
+ muxer = AMediaMuxer_append(fd, fdp.PickValueInArray(kAppendMode) /* mode */);
+ }
+ if (!muxer) {
+ close(fd);
+ return 0;
+ }
+ AMediaFormat* mediaFormat = nullptr;
+ ssize_t trackIdx = 0;
+ while (fdp.remaining_bytes()) {
+ int32_t kSwitchChoice = fdp.ConsumeIntegralInRange<int32_t>(kMinChoice, kMaxChoice);
+ switch (kSwitchChoice) {
+ case 0: {
+ AMediaMuxer_setLocation(muxer, fdp.ConsumeFloatingPoint<float>() /* latitude */,
+ fdp.ConsumeFloatingPoint<float>() /* longitude */);
+ break;
+ }
+ case 1: {
+ AMediaMuxer_setOrientationHint(muxer, fdp.ConsumeIntegral<int32_t>() /* degrees */);
+ break;
+ }
+ case 2: {
+ AMediaMuxer_start(muxer);
+ break;
+ }
+ case 3: {
+ AMediaMuxer_stop(muxer);
+ break;
+ }
+ case 4: {
+ AMediaMuxer_getTrackCount(muxer);
+ break;
+ }
+ case 5: {
+ AMediaFormat* getFormat =
+ AMediaMuxer_getTrackFormat(muxer, fdp.ConsumeIntegral<size_t>() /* idx */);
+ AMediaFormat_delete(getFormat);
+ break;
+ }
+ case 6: {
+ mediaFormat = AMediaFormat_new();
+ fdp.ConsumeBool() ? getSampleAudioFormat(fdp, mediaFormat)
+ : getSampleVideoFormat(fdp, mediaFormat);
+ trackIdx = AMediaMuxer_addTrack(muxer, mediaFormat);
+ AMediaFormat_delete(mediaFormat);
+ break;
+ }
+ default: {
+ std::vector<uint8_t> sampleData = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCodecBufferInfo codecBuffer;
+ codecBuffer.size = sampleData.size();
+ codecBuffer.offset = kOffset;
+ codecBuffer.presentationTimeUs = fdp.ConsumeIntegral<int64_t>();
+ codecBuffer.flags = fdp.ConsumeIntegral<uint32_t>();
+ AMediaMuxer_writeSampleData(
+ muxer,
+ fdp.ConsumeBool() ? trackIdx : fdp.ConsumeIntegral<size_t>() /* trackIdx */,
+ sampleData.data(), &codecBuffer);
+ break;
+ }
+ }
+ }
+ AMediaMuxer_delete(muxer);
+ close(fd);
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_sync_codec_fuzzer.cpp b/media/ndk/fuzzer/ndk_sync_codec_fuzzer.cpp
new file mode 100644
index 0000000..a3f3650
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_sync_codec_fuzzer.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#include <NdkMediaCodecFuzzerBase.h>
+
+constexpr int32_t kMaxNdkCodecAPIs = 12;
+
+class NdkSyncCodecFuzzer : public NdkMediaCodecFuzzerBase {
+ public:
+ NdkSyncCodecFuzzer(const uint8_t* data, size_t size)
+ : NdkMediaCodecFuzzerBase(), mFdp(data, size) {
+ setFdp(&mFdp);
+ };
+ void invokeSyncCodeConfigAPI();
+
+ static void CodecOnFrameRendered(AMediaCodec* codec, void* userdata, int64_t mediaTimeUs,
+ int64_t systemNano) {
+ (void)codec;
+ (void)userdata;
+ (void)mediaTimeUs;
+ (void)systemNano;
+ };
+
+ private:
+ FuzzedDataProvider mFdp;
+ AMediaCodec* mCodec = nullptr;
+ void invokekSyncCodecAPIs(bool isEncoder);
+};
+
+void NdkSyncCodecFuzzer::invokekSyncCodecAPIs(bool isEncoder) {
+ ANativeWindow* nativeWindow = nullptr;
+ AMediaFormat* format = getCodecFormat();
+ int32_t numOfFrames = mFdp.ConsumeIntegralInRange<size_t>(kMinIterations, kMaxIterations);
+ int32_t count = 0;
+ while (++count <= numOfFrames) {
+ int32_t ndkcodecAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxNdkCodecAPIs);
+ switch (ndkcodecAPI) {
+ case 0: { // configure the codec
+ AMediaCodec_configure(mCodec, format, nativeWindow, nullptr /* crypto */,
+ (isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0));
+ break;
+ }
+ case 1: { // start codec
+ AMediaCodec_start(mCodec);
+ break;
+ }
+ case 2: { // stop codec
+ AMediaCodec_stop(mCodec);
+ break;
+ }
+ case 3: { // create persistent input surface
+ AMediaCodec_createPersistentInputSurface(&nativeWindow);
+ break;
+ }
+ case 4: { // buffer operation APIs
+ invokeInputBufferOperationAPI(mCodec);
+ break;
+ }
+ case 5: {
+ invokeOutputBufferOperationAPI(mCodec);
+ break;
+ }
+ case 6: { // get input and output Format
+ invokeCodecFormatAPI(mCodec);
+ break;
+ }
+ case 7: {
+ AMediaCodec_signalEndOfInputStream(mCodec);
+ break;
+ }
+ case 8: { // set parameters
+ // Create a new parameter and set
+ AMediaFormat* params = AMediaFormat_new();
+ AMediaFormat_setInt32(
+ params, "video-bitrate",
+ mFdp.ConsumeIntegralInRange<size_t>(kMinIntKeyValue, kMaxIntKeyValue));
+ AMediaCodec_setParameters(mCodec, params);
+ AMediaFormat_delete(params);
+ break;
+ }
+ case 9: { // flush codec
+ AMediaCodec_flush(mCodec);
+ if (mFdp.ConsumeBool()) {
+ AMediaCodec_start(mCodec);
+ }
+ break;
+ }
+ case 10: { // get the codec name
+ char* name = nullptr;
+ AMediaCodec_getName(mCodec, &name);
+ AMediaCodec_releaseName(mCodec, name);
+ break;
+ }
+ case 11: { // set callback API for frame render output
+ std::vector<uint8_t> userData = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCodecOnFrameRendered callback = CodecOnFrameRendered;
+ AMediaCodec_setOnFrameRenderedCallback(mCodec, callback, userData.data());
+ break;
+ }
+ case 12:
+ default: { // set persistent input surface
+ AMediaCodec_setInputSurface(mCodec, nativeWindow);
+ }
+ }
+ }
+ if (nativeWindow) {
+ ANativeWindow_release(nativeWindow);
+ }
+ if (format) {
+ AMediaFormat_delete(format);
+ }
+}
+
+void NdkSyncCodecFuzzer::invokeSyncCodeConfigAPI() {
+ while (mFdp.remaining_bytes() > 0) {
+ bool isEncoder = mFdp.ConsumeBool();
+ mCodec = createCodec(isEncoder, mFdp.ConsumeBool() /* isCodecForClient */);
+ if (mCodec) {
+ invokekSyncCodecAPIs(isEncoder);
+ AMediaCodec_delete(mCodec);
+ }
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ NdkSyncCodecFuzzer ndkSyncCodecFuzzer(data, size);
+ ndkSyncCodecFuzzer.invokeSyncCodeConfigAPI();
+ return 0;
+}
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index 71bc6d9..814a327 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -583,7 +583,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param width the width of the image will be filled here if the method call succeeeds.
+ * @param width the width of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -599,7 +599,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param height the height of the image will be filled here if the method call succeeeds.
+ * @param height the height of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -617,7 +617,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param format the format of the image will be filled here if the method call succeeeds.
+ * @param format the format of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -636,7 +636,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param rect the cropped rectangle of the image will be filled here if the method call succeeeds.
+ * @param rect the cropped rectangle of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -662,7 +662,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param timestampNs the timestamp of the image will be filled here if the method call succeeeds.
+ * @param timestampNs the timestamp of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -682,7 +682,7 @@
*
* @param image the {@link AImage} of interest.
* @param numPlanes the number of planes of the image will be filled here if the method call
- * succeeeds.
+ * succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -706,7 +706,7 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param pixelStride the pixel stride of the image will be filled here if the method call succeeeds.
+ * @param pixelStride the pixel stride of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -735,7 +735,7 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param rowStride the row stride of the image will be filled here if the method call succeeeds.
+ * @param rowStride the row stride of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -762,8 +762,8 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param data the data pointer of the image will be filled here if the method call succeeeds.
- * @param dataLength the valid length of data will be filled here if the method call succeeeds.
+ * @param data the data pointer of the image will be filled here if the method call succeeds.
+ * @param dataLength the valid length of data will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -826,6 +826,25 @@
*/
media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer) __INTRODUCED_IN(26);
+/**
+ * Query the dataspace of the input {@link AImage}.
+ *
+ * Available since API level 33.
+ *
+ * @param image the {@link AImage} of interest.
+ * @param dataSpace the dataspace of the image will be filled here if the method call succeeds.
+ * This must be one of the ADATASPACE_* enum value defined in
+ * {@link ADataSpace}.
+ *
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if image or dataSpace is NULL.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this
+ * image has been deleted.</li></ul>
+ */
+media_status_t AImage_getDataSpace(const AImage* image,
+ /*out*/int32_t* dataSpace) __INTRODUCED_IN(33);
+
__END_DECLS
#endif //_NDK_IMAGE_H
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index 4bd7f2a..992955b 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -79,7 +79,7 @@
* by the user, one of them has to be released before a new {@link AImage} will become
* available for access through {@link AImageReader_acquireLatestImage} or
* {@link AImageReader_acquireNextImage}. Must be greater than 0.
- * @param reader The created image reader will be filled here if the method call succeeeds.
+ * @param reader The created image reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -133,7 +133,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param width the default width of the reader will be filled here if the method call succeeeds.
+ * @param width the default width of the reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -151,7 +151,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param height the default height of the reader will be filled here if the method call succeeeds.
+ * @param height the default height of the reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -165,7 +165,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param format the fromat of the reader will be filled here if the method call succeeeds. The
+ * @param format the format of the reader will be filled here if the method call succeeds. The
* value will be one of the AIMAGE_FORMAT_* enum value defiend in {@link NdkImage.h}.
*
* @return <ul>
@@ -181,7 +181,7 @@
*
* @param reader The image reader of interest.
* @param maxImages the maximum number of concurrently acquired images of the reader will be filled
- * here if the method call succeeeds.
+ * here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -212,7 +212,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param image the acquired {@link AImage} will be filled here if the method call succeeeds.
+ * @param image the acquired {@link AImage} will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -257,7 +257,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param image the acquired {@link AImage} will be filled here if the method call succeeeds.
+ * @param image the acquired {@link AImage} will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -387,6 +387,44 @@
/*out*/ AImageReader** reader) __INTRODUCED_IN(26);
/**
+ * AImageReader constructor similar to {@link AImageReader_newWithUsage} that takes
+ * two additional parameters to build the format of the Image. All other parameters
+ * and the return values are identical to those passed to {@link AImageReader_newWithUsage}.
+ *
+ * <p>Instead of passing {@code format} parameter, this constructor accepts
+ * the combination of {@code hardwareBufferFormat} and {@code dataSpace} for the
+ * format of the Image that the reader will produce.</p>
+ *
+ * Available since API level 33.
+ *
+ * @param width The default width in pixels of the Images that this reader will produce.
+ * @param height The default height in pixels of the Images that this reader will produce.
+ * @param usage specifies how the consumer will access the AImage.
+ * See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @param maxImages The maximum number of images the user will want to access simultaneously.
+ * See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @param hardwareBufferFormat The hardware buffer format passed by the producer.
+ * This must be one of the AHARDWAREBUFFER_FORMAT_* enum values defined
+ * in {@link hardware_buffer.h}.
+ * @param dataSpace The dataspace of the Image passed by the producer.
+ * This must be one of the ADATASPACE_* enum values defined in
+ * {@link ADataSpace}.
+ * @param reader The created image reader will be filled here if the method call succeeds.
+ *
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL, or one or more of width,
+ * height, maxImages, hardwareBufferFormat or dataSpace arguments
+ * is not supported.</li>
+ * <li>{@link AMEDIA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
+ *
+ * @see AImageReader_newWithUsage
+ */
+media_status_t AImageReader_newWithDataSpace(int32_t width, int32_t height, uint64_t usage,
+ int32_t maxImages, uint32_t hardwareBufferFormat, int32_t dataSpace,
+ /*out*/ AImageReader** reader) __INTRODUCED_IN(33);
+
+/**
* Acquire the next {@link AImage} from the image reader's queue asynchronously.
*
* <p>AImageReader acquire method similar to {@link AImageReader_acquireNextImage} that takes an
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index c8faced..bac4b22 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -13,11 +13,13 @@
AImageReader_getWindow; # introduced=24
AImageReader_new; # introduced=24
AImageReader_newWithUsage; # introduced=26
+ AImageReader_newWithDataSpace; # introduced=Tiramisu
AImageReader_setBufferRemovedListener; # introduced=26
AImageReader_setImageListener; # introduced=24
AImage_delete; # introduced=24
AImage_deleteAsync; # introduced=26
AImage_getCropRect; # introduced=24
+ AImage_getDataSpace; # introduced=Tiramisu
AImage_getFormat; # introduced=24
AImage_getHardwareBuffer; # introduced=26
AImage_getHeight; # introduced=24
diff --git a/media/ndk/tests/NdkMediaFormat_test.cpp b/media/ndk/tests/NdkMediaFormat_test.cpp
index 668d0a4..18690b8 100644
--- a/media/ndk/tests/NdkMediaFormat_test.cpp
+++ b/media/ndk/tests/NdkMediaFormat_test.cpp
@@ -51,6 +51,13 @@
EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
EXPECT_EQ(i32, 5);
+ // verify detecting some bad parameters.
+ AMediaFormat_setInt32(nullptr, "whatever", 6);
+ AMediaFormat_setInt32(fmt1, nullptr, 6);
+
+ EXPECT_FALSE(AMediaFormat_getInt32(nullptr, "whatever", &i32));
+ EXPECT_FALSE(AMediaFormat_getInt32(fmt1, nullptr, &i32));
+
AMediaFormat_delete(fmt1);
}
@@ -67,6 +74,13 @@
EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
EXPECT_EQ(i64, -1);
+ // verify detecting some bad parameters.
+ AMediaFormat_setInt64(nullptr, "whatever", 6);
+ AMediaFormat_setInt64(fmt1, nullptr, 6);
+
+ EXPECT_FALSE(AMediaFormat_getInt64(nullptr, "whatever", &i64));
+ EXPECT_FALSE(AMediaFormat_getInt64(fmt1, nullptr, &i64));
+
AMediaFormat_delete(fmt1);
}
@@ -80,6 +94,13 @@
EXPECT_TRUE(AMediaFormat_getSize(fmt1, "medium", &size));
EXPECT_EQ(size, 10);
+ // verify detecting some bad parameters.
+ AMediaFormat_setSize(nullptr, "whatever", 6);
+ AMediaFormat_setSize(fmt1, nullptr, 6);
+
+ EXPECT_FALSE(AMediaFormat_getSize(nullptr, "whatever", &size));
+ EXPECT_FALSE(AMediaFormat_getSize(fmt1, nullptr, &size));
+
AMediaFormat_delete(fmt1);
}
@@ -90,6 +111,14 @@
AMediaFormat_setFloat(fmt1, "ship", 0.5);
EXPECT_TRUE(AMediaFormat_getFloat(fmt1, "boat", &f));
EXPECT_EQ(f, 1.5);
+
+ // verify detecting some bad parameters.
+ AMediaFormat_setFloat(nullptr, "whatever", 1.5);
+ AMediaFormat_setFloat(fmt1, nullptr, 1.5);
+
+ EXPECT_FALSE(AMediaFormat_getFloat(nullptr, "whatever", &f));
+ EXPECT_FALSE(AMediaFormat_getFloat(fmt1, nullptr, &f));
+
AMediaFormat_delete(fmt1);
}
@@ -100,6 +129,14 @@
AMediaFormat_setDouble(fmt1, "dip", 0.5);
EXPECT_TRUE(AMediaFormat_getDouble(fmt1, "trouble", &d));
EXPECT_EQ(d, 100.5);
+
+ // verify detecting some bad parameters.
+ AMediaFormat_setDouble(nullptr, "whatever", 1.5);
+ AMediaFormat_setDouble(fmt1, nullptr, 1.5);
+
+ EXPECT_FALSE(AMediaFormat_getDouble(nullptr, "whatever", &d));
+ EXPECT_FALSE(AMediaFormat_getDouble(fmt1, nullptr, &d));
+
AMediaFormat_delete(fmt1);
}
@@ -111,8 +148,16 @@
AMediaFormat_setString(fmt1, "stringtheory", content);
EXPECT_TRUE(AMediaFormat_getString(fmt1, "stringtheory", &out));
EXPECT_NE(out, nullptr);
+ EXPECT_NE(out, content); // should not be the original
EXPECT_EQ(strcmp(out,content), 0);
+ // verify detecting some bad parameters.
+ AMediaFormat_setString(nullptr, "whatever", content);
+ AMediaFormat_setString(fmt1, nullptr, content);
+
+ EXPECT_FALSE(AMediaFormat_getString(nullptr, "whatever", &out));
+ EXPECT_FALSE(AMediaFormat_getString(fmt1, nullptr, &out));
+
AMediaFormat_delete(fmt1);
}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
index 40a8c9e..ba3e81a 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -50,7 +50,7 @@
{
lock_guard<mutex> lock(mMutex);
needsNotify = mQueue.empty();
- mQueue.push(move(elem));
+ mQueue.push(std::move(elem));
}
if (needsNotify) mQueueNotEmptyCondition.notify_one();
}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index a38ef57..04d9ed9 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -21,23 +21,76 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_library {
- name: "libmediautils",
+cc_defaults {
+ name: "libmediautils_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+filegroup {
+ name: "libmediautils_core_srcs",
srcs: [
"AImageReaderUtils.cpp",
- "BatteryNotifier.cpp",
"ISchedulingPolicyService.cpp",
"Library.cpp",
- "LimitProcessMemory.cpp",
"MediaUtilsDelayed.cpp",
- "MemoryLeakTrackUtil.cpp",
"MethodStatistics.cpp",
"Process.cpp",
- "ProcessInfo.cpp",
"SchedulingPolicyService.cpp",
- "ServiceUtilities.cpp",
"ThreadSnapshot.cpp",
+ ],
+}
+
+cc_library_headers {
+ name: "libmediautils_headers",
+ host_supported: true,
+ vendor_available: true, // required for platform/hardware/interfaces
+ shared_libs: [
+ "liblog",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libmediautils_core",
+ defaults: ["libmediautils_defaults"],
+ host_supported: true,
+ srcs: [":libmediautils_core_srcs"],
+ shared_libs: [
+ "libaudioutils", // for clock.h, Statistics.h
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpermission",
+ "libutils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hidl.token@1.0-utils",
+ "packagemanager_aidl-cpp",
+ ],
+
+ export_shared_lib_headers: [
+ "libpermission",
+ ],
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libmediautils",
+ defaults: ["libmediautils_defaults"],
+ srcs: [
+ ":libmediautils_core_srcs",
+ "BatteryNotifier.cpp",
+ "MemoryLeakTrackUtil.cpp",
+ "LimitProcessMemory.cpp",
+ "ProcessInfo.cpp",
+ "ServiceUtilities.cpp",
"TimeCheck.cpp",
"TimerThread.cpp",
],
@@ -49,12 +102,13 @@
shared_libs: [
"libaudioclient_aidl_conversion",
"libaudioutils", // for clock.h, Statistics.h
+ "libbase",
"libbinder",
"libcutils",
- "liblog",
- "libutils",
"libhidlbase",
+ "liblog",
"libpermission",
+ "libutils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hidl.token@1.0-utils",
"packagemanager_aidl-cpp",
@@ -65,12 +119,6 @@
logtags: ["EventLogTags.logtags"],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-
header_libs: [
"bionic_libc_platform_headers",
"libmedia_headers",
@@ -81,7 +129,7 @@
],
required: [
- "libmediautils_delayed", // lazy loaded
+ "libmediautils_delayed", // lazy loaded
],
include_dirs: [
@@ -94,14 +142,10 @@
cc_library {
name: "libmediautils_delayed", // match with MEDIAUTILS_DELAYED_LIBRARY_NAME
+ defaults: ["libmediautils_defaults"],
srcs: [
"MediaUtilsDelayedLibrary.cpp",
],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
shared_libs: [
"liblog",
"libutils",
@@ -111,16 +155,12 @@
cc_library {
name: "libmediautils_vendor",
- vendor_available: true, // required for platform/hardware/interfaces
+ defaults: ["libmediautils_defaults"],
+ vendor_available: true, // required for platform/hardware/interfaces
srcs: [
"MemoryLeakTrackUtil.cpp",
],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
shared_libs: [
"liblog",
"libutils",
@@ -137,23 +177,3 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
-
-
-cc_library_headers {
- name: "libmediautils_headers",
- vendor_available: true, // required for platform/hardware/interfaces
-
- export_include_dirs: ["include"],
-}
-
-cc_test {
- name: "libmediautils_test",
- srcs: [
- "memory-test.cpp",
- "TimerThread-test.cpp",
- ],
- shared_libs: [
- "libmediautils",
- "libutils",
- ]
-}
diff --git a/media/utils/MethodStatistics.cpp b/media/utils/MethodStatistics.cpp
index b179b20..086757b 100644
--- a/media/utils/MethodStatistics.cpp
+++ b/media/utils/MethodStatistics.cpp
@@ -22,7 +22,8 @@
std::shared_ptr<std::vector<std::string>>
getStatisticsClassesForModule(std::string_view moduleName) {
- static const std::map<std::string, std::shared_ptr<std::vector<std::string>>> m {
+ static const std::map<std::string, std::shared_ptr<std::vector<std::string>>,
+ std::less<> /* transparent comparator */> m {
{
METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL,
std::shared_ptr<std::vector<std::string>>(
@@ -34,13 +35,14 @@
})
},
};
- auto it = m.find({moduleName.begin(), moduleName.end()});
+ auto it = m.find(moduleName);
if (it == m.end()) return {};
return it->second;
}
static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &classNames,
- std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> &map) {
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+ std::less<> /* transparent comparator */> &map) {
if (classNames) {
for (const auto& className : *classNames) {
map.emplace(className, std::make_shared<MethodStatistics<std::string>>());
@@ -51,17 +53,18 @@
// singleton statistics for DeviceHalHidl StreamOutHalHidl StreamInHalHidl
std::shared_ptr<MethodStatistics<std::string>>
getStatisticsForClass(std::string_view className) {
- static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m =
+ static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+ std::less<> /* transparent comparator */> m =
// copy elided initialization of map m.
[](){
- std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m;
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>, std::less<>> m;
addClassesToMap(
getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
m);
return m;
}();
- auto it = m.find({className.begin(), className.end()});
+ auto it = m.find(className);
if (it == m.end()) return {};
return it->second;
}
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 0848eb3..e53d70f 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <audio_utils/clock.h>
#include <mediautils/EventLog.h>
+#include <mediautils/FixedString.h>
#include <mediautils/MethodStatistics.h>
#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
@@ -138,11 +139,11 @@
return getTimeCheckThread().toString();
}
-TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
+TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
bool crashOnTimeout)
- : mTimeCheckHandler(new TimeCheckHandler{
- std::move(tag), std::move(onTimer), crashOnTimeout,
- std::chrono::system_clock::now(), gettid()})
+ : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>(
+ tag, std::move(onTimer), crashOnTimeout,
+ std::chrono::system_clock::now(), gettid()) }
, mTimerHandle(timeoutMs == 0
? getTimeCheckThread().trackTask(mTimeCheckHandler->tag)
: getTimeCheckThread().scheduleTask(
@@ -231,14 +232,14 @@
mediautils::getStatisticsForClass(className);
if (!statistics) return {}; // empty TimeCheck.
return mediautils::TimeCheck(
- std::string(className).append("::").append(methodName),
- [ clazz = std::string(className), method = std::string(methodName),
+ FixedString62(className).append("::").append(methodName),
+ [ safeMethodName = FixedString30(methodName),
stats = std::move(statistics) ]
(bool timeout, float elapsedMs) {
if (timeout) {
; // ignored, there is no timeout value.
} else {
- stats->event(method, elapsedMs);
+ stats->event(safeMethodName.asStringView(), elapsedMs);
}
}, 0 /* timeoutMs */);
}
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 6de6b13..09783ed 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -31,17 +31,15 @@
extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);
TimerThread::Handle TimerThread::scheduleTask(
- std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
+ std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
const auto now = std::chrono::system_clock::now();
- std::shared_ptr<const Request> request{
- new Request{ now, now + timeout, gettid(), std::move(tag) }};
+ auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag);
return mMonitorThread.add(std::move(request), std::move(func), timeout);
}
-TimerThread::Handle TimerThread::trackTask(std::string tag) {
+TimerThread::Handle TimerThread::trackTask(std::string_view tag) {
const auto now = std::chrono::system_clock::now();
- std::shared_ptr<const Request> request{
- new Request{ now, now, gettid(), std::move(tag) }};
+ auto request = std::make_shared<const Request>(now, now, gettid(), tag);
return mNoTimeoutMap.add(std::move(request));
}
@@ -106,10 +104,10 @@
//
/* static */
bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
- const size_t hidlPos = request->tag.find("Hidl");
+ const size_t hidlPos = request->tag.asStringView().find("Hidl");
if (hidlPos == std::string::npos) return false;
// should be a separator afterwards Hidl which indicates the string was in the class.
- const size_t separatorPos = request->tag.find("::", hidlPos);
+ const size_t separatorPos = request->tag.asStringView().find("::", hidlPos);
return separatorPos != std::string::npos;
}
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index d26e6c2..fc4c2f9 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -9,14 +9,13 @@
cc_defaults {
name: "libmediautils_fuzzer_defaults",
+ host_supported: true,
shared_libs: [
- "libbatterystats_aidl",
"libbinder",
- "libcutils",
"liblog",
- "libmediautils",
+ "libcutils",
+ "libmediautils_core",
"libutils",
- "libbinder",
"framework-permission-aidl-cpp",
"packagemanager_aidl-cpp",
],
@@ -27,33 +26,36 @@
"-Werror",
"-Wno-c++2a-extensions",
],
-
- header_libs: [
- "bionic_libc_platform_headers",
- "libmedia_headers",
- ],
}
cc_fuzz {
name: "libmediautils_fuzzer_battery_notifier",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["BatteryNotifierFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_scheduling_policy_service",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["SchedulingPolicyServiceFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_service_utilities",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["ServiceUtilitiesFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_time_check",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["TimeCheckFuzz.cpp"],
}
diff --git a/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
index 130feee..32fc3be 100644
--- a/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
+++ b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
@@ -34,11 +34,16 @@
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != nullptr) {
const String16 name("batterystats");
- batteryStatService = checked_interface_cast<IBatteryStats>(sm->checkService(name));
- if (batteryStatService == nullptr) {
+ sp<IBinder> obj = sm->checkService(name);
+ if (!obj) {
ALOGW("batterystats service unavailable!");
return nullptr;
}
+ batteryStatService = checked_interface_cast<IBatteryStats>(obj);
+ if (batteryStatService == nullptr) {
+ ALOGW("batterystats service interface is invalid");
+ return nullptr;
+ }
}
return batteryStatService;
}
diff --git a/media/utils/include/mediautils/ExtendedAccumulator.h b/media/utils/include/mediautils/ExtendedAccumulator.h
new file mode 100644
index 0000000..7e3e170
--- /dev/null
+++ b/media/utils/include/mediautils/ExtendedAccumulator.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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 <atomic>
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <log/log.h>
+
+namespace android::mediautils {
+
+// The goal of this class is to detect and accumulate wraparound occurrences on a
+// lower sized integer.
+
+// This class assumes that the underlying unsigned type is either incremented or
+// decremented by at most the underlying signed type between any two subsequent
+// polls (or construction). This is well-defined as the modular nature of
+// unsigned arithmetic ensures that every new value maps 1-1 to an
+// increment/decrement over the same sized signed type. It also ensures that our
+// counter will be equivalent mod the size of the integer even if the underlying
+// type is modified outside of this range.
+//
+// For convenience, this class is thread compatible. Additionally, it is safe
+// as long as there is only one writer.
+template <typename Integral = uint32_t, typename AccumulatingType = uint64_t>
+class ExtendedAccumulator {
+ static_assert(sizeof(Integral) < sizeof(AccumulatingType),
+ "Accumulating type should be larger than underlying type");
+ static_assert(std::is_integral_v<Integral> && std::is_unsigned_v<Integral>,
+ "Wraparound behavior is only well-defiend for unsigned ints");
+ static_assert(std::is_integral_v<AccumulatingType>);
+
+ public:
+ enum class Wrap {
+ NORMAL = 0,
+ UNDERFLOW = 1,
+ OVERFLOW = 2,
+ };
+
+ using UnsignedInt = Integral;
+ using SignedInt = std::make_signed_t<UnsignedInt>;
+
+ explicit ExtendedAccumulator(AccumulatingType initial = 0) : mAccumulated(initial) {}
+
+ // Returns a pair of the calculated change on the accumulating value, and a
+ // Wrap type representing the type of wraparound (if any) which occurred.
+ std::pair<SignedInt, Wrap> poll(UnsignedInt value) {
+ auto acc = mAccumulated.load(std::memory_order_relaxed);
+ const auto bottom_bits = static_cast<UnsignedInt>(acc);
+ std::pair<SignedInt, Wrap> res = {0, Wrap::NORMAL};
+ const bool overflow = __builtin_sub_overflow(value, bottom_bits, &res.first);
+
+ if (overflow) {
+ res.second = (res.first > 0) ? Wrap::OVERFLOW : Wrap::UNDERFLOW;
+ }
+
+ const bool acc_overflow = __builtin_add_overflow(acc, res.first, &acc);
+ // If our *accumulating* type overflows or underflows (depending on its
+ // signedness), we should abort.
+ if (acc_overflow) LOG_ALWAYS_FATAL("Unexpected overflow/underflow in %s", __func__);
+
+ mAccumulated.store(acc, std::memory_order_relaxed);
+ return res;
+ }
+
+ AccumulatingType getValue() const { return mAccumulated.load(std::memory_order_relaxed); }
+
+ private:
+ // Invariant - the bottom underlying bits of accumulated are the same as the
+ // last value provided to poll.
+ std::atomic<AccumulatingType> mAccumulated;
+};
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/FixedString.h b/media/utils/include/mediautils/FixedString.h
new file mode 100644
index 0000000..047aa82
--- /dev/null
+++ b/media/utils/include/mediautils/FixedString.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 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 <algorithm>
+#include <string>
+#include <string_view>
+
+namespace android::mediautils {
+
+/*
+ * FixedString is a stack allocatable string buffer that supports
+ * simple appending of other strings and string_views.
+ *
+ * It is designed for no-malloc operation when std::string
+ * small buffer optimization is insufficient.
+ *
+ * To keep code small, use asStringView() for operations on this.
+ *
+ * Notes:
+ * 1) Appending beyond the internal buffer size results in truncation.
+ *
+ * Alternatives:
+ * 1) If you want a sharable copy-on-write string implementation,
+ * consider using the legacy android::String8().
+ * 2) Using std::string with a fixed stack allocator may suit your needs,
+ * but exception avoidance is tricky.
+ * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't
+ * need backing store. Be careful about allocation with ranges.
+ *
+ * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62.
+ *
+ * Implementation Notes:
+ * 1) No iterators or [] for FixedString - please convert to std::string_view.
+ * 2) For small N (e.g. less than 62), consider a change to always zero fill and
+ * potentially prevent always zero terminating (if one only does append).
+ *
+ * Possible arguments to create/append:
+ * 1) A FixedString.
+ * 2) A single char.
+ * 3) A char * pointer.
+ * 4) A std::string.
+ * 5) A std::string_view (or something convertible to it).
+ *
+ * Example:
+ *
+ * FixedString s1(std::string("a")); // ctor
+ * s1 << "bc" << 'd' << '\n'; // streaming append
+ * s1 += "hello"; // += append
+ * ASSERT_EQ(s1, "abcd\nhello");
+ */
+template <uint32_t N>
+struct FixedString
+{
+ // Find the best size type.
+ using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>;
+
+ // constructors
+ FixedString() { // override default
+ buffer_[0] = '\0';
+ }
+
+ FixedString(const FixedString& other) { // override default.
+ copyFrom<N>(other);
+ }
+
+ // The following constructor is not explicit to allow
+ // FixedString<8> s = "abcd";
+ template <typename ...Types>
+ FixedString(Types&&... args) {
+ append(std::forward<Types>(args)...);
+ }
+
+ // copy assign (copyFrom checks for equality and returns *this).
+ FixedString& operator=(const FixedString& other) { // override default.
+ return copyFrom<N>(other);
+ }
+
+ template <typename ...Types>
+ FixedString& operator=(Types&&... args) {
+ size_ = 0;
+ return append(std::forward<Types>(args)...);
+ }
+
+ // operator equals
+ bool operator==(const char *s) const {
+ return strncmp(c_str(), s, capacity() + 1) == 0;
+ }
+
+ bool operator==(std::string_view s) const {
+ return size() == s.size() && memcmp(data(), s.data(), size()) == 0;
+ }
+
+ // operator not-equals
+ template <typename T>
+ bool operator!=(const T& other) const {
+ return !operator==(other);
+ }
+
+ // operator +=
+ template <typename ...Types>
+ FixedString& operator+=(Types&&... args) {
+ return append(std::forward<Types>(args)...);
+ }
+
+ // conversion to std::string_view.
+ operator std::string_view() const {
+ return asStringView();
+ }
+
+ // basic observers
+ size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); }
+ static constexpr uint32_t capacity() { return N; }
+ uint32_t size() const { return size_; }
+ uint32_t remaining() const { return size_ >= N ? 0 : N - size_; }
+ bool empty() const { return size_ == 0; }
+ bool full() const { return size_ == N; } // when full, possible truncation risk.
+ char * data() { return buffer_; }
+ const char * data() const { return buffer_; }
+ const char * c_str() const { return buffer_; }
+
+ inline std::string_view asStringView() const {
+ return { buffer_, static_cast<size_t>(size_) };
+ }
+ inline std::string asString() const {
+ return { buffer_, static_cast<size_t>(size_) };
+ }
+
+ void clear() { size_ = 0; buffer_[0] = 0; }
+
+ // Implementation of append - using templates
+ // to guarantee precedence in the presence of ambiguity.
+ //
+ // Consider C++20 template overloading through constraints and concepts.
+ template <typename T>
+ FixedString& append(const T& t) {
+ using decayT = std::decay_t<T>;
+ if constexpr (is_specialization_v<decayT, FixedString>) {
+ // A FixedString<U>
+ if (size_ == 0) {
+ // optimization to erase everything.
+ return copyFrom(t);
+ } else {
+ return appendStringView({t.data(), t.size()});
+ }
+ } else if constexpr(std::is_same_v<decayT, char>) {
+ if (size_ < N) {
+ buffer_[size_++] = t;
+ buffer_[size_] = '\0';
+ }
+ return *this;
+ } else if constexpr(std::is_same_v<decayT, char *>) {
+ // Some char* ptr.
+ return appendString(t);
+ } else if constexpr (std::is_convertible_v<decayT, std::string_view>) {
+ // std::string_view, std::string, or some other convertible type.
+ return appendStringView(t);
+ } else /* constexpr */ {
+ static_assert(dependent_false_v<T>, "non-matching append type");
+ }
+ }
+
+ FixedString& appendStringView(std::string_view s) {
+ uint32_t total = std::min(static_cast<size_t>(N - size_), s.size());
+ memcpy(buffer_ + size_, s.data(), total);
+ size_ += total;
+ buffer_[size_] = '\0';
+ return *this;
+ }
+
+ FixedString& appendString(const char *s) {
+ // strncpy zero pads to the end,
+ // strlcpy returns total expected length,
+ // we don't have strncpy_s in Bionic,
+ // so we write our own here.
+ while (size_ < N && *s != '\0') {
+ buffer_[size_++] = *s++;
+ }
+ buffer_[size_] = '\0';
+ return *this;
+ }
+
+ // Copy initialize the struct.
+ // Note: We are POD but customize the copying for acceleration
+ // of moving small strings embedded in a large buffers.
+ template <uint32_t U>
+ FixedString& copyFrom(const FixedString<U>& other) {
+ if ((void*)this != (void*)&other) { // not a self-assignment
+ if (other.size() == 0) {
+ size_ = 0;
+ buffer_[0] = '\0';
+ return *this;
+ }
+ constexpr size_t kSizeToCopyWhole = 64;
+ if constexpr (N == U &&
+ sizeof(*this) == sizeof(other) &&
+ sizeof(*this) <= kSizeToCopyWhole) {
+ // As we have the same str size type, we can just
+ // memcpy with fixed size, which can be easily optimized.
+ memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
+ return *this;
+ }
+ if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) {
+ constexpr size_t kAlign = 8; // align to a multiple of 8.
+ static_assert((kAlign & (kAlign - 1)) == 0); // power of 2.
+ // we check any alignment issues.
+ if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) {
+ // improve on standard POD copying by reducing size.
+ const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */;
+ const size_t maxcpy = std::min(sizeof(*this), sizeof(other));
+ const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy);
+ memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize);
+ return *this;
+ }
+ }
+ size_ = std::min(other.size(), capacity());
+ memcpy(buffer_, other.data(), size_);
+ buffer_[size_] = '\0'; // zero terminate.
+ }
+ return *this;
+ }
+
+private:
+ // Template helper methods
+
+ template <typename Test, template <uint32_t> class Ref>
+ struct is_specialization : std::false_type {};
+
+ template <template <uint32_t> class Ref, uint32_t UU>
+ struct is_specialization<Ref<UU>, Ref>: std::true_type {};
+
+ template <typename Test, template <uint32_t> class Ref>
+ static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+ // For static assert(false) we need a template version to avoid early failure.
+ template <typename T>
+ static inline constexpr bool dependent_false_v = false;
+
+ // POD variables
+ strsize_t size_ = 0;
+ char buffer_[N + 1 /* allow zero termination */];
+};
+
+// Stream operator syntactic sugar.
+// Example:
+// s << 'b' << "c" << "d" << '\n';
+template <uint32_t N, typename ...Types>
+FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) {
+ return fs.append(std::forward<Types>(args)...);
+}
+
+// We do not use a default size for fixed string as changing
+// the default size would lead to different behavior - we want the
+// size to be explicitly known.
+
+// FixedString62 of 62 chars fits in one typical cache line.
+using FixedString62 = FixedString<62>;
+
+// Slightly smaller
+using FixedString30 = FixedString<30>;
+
+// Since we have added copy and assignment optimizations,
+// we are no longer trivially assignable and copyable.
+// But we check standard layout here to prevent inclusion of unacceptable members or virtuals.
+static_assert(std::is_standard_layout_v<FixedString62>);
+static_assert(std::is_standard_layout_v<FixedString30>);
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/InPlaceFunction.h b/media/utils/include/mediautils/InPlaceFunction.h
new file mode 100644
index 0000000..17c6274
--- /dev/null
+++ b/media/utils/include/mediautils/InPlaceFunction.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 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 <cstdlib>
+#include <functional>
+#include <memory>
+#include <type_traits>
+
+namespace android::mediautils {
+
+namespace detail {
+// Vtable interface for erased types
+template <typename Ret, typename... Args>
+struct ICallableTable {
+ // Destroy the erased type
+ void (*destroy)(void* storage) = nullptr;
+ // Call the erased object
+ Ret (*invoke)(void* storage, Args&&...) = nullptr;
+ // **Note** the next two functions only copy object data, not the vptr
+ // Copy the erased object to a new InPlaceFunction buffer
+ void (*copy_to)(const void* storage, void* other) = nullptr;
+ // Move the erased object to a new InPlaceFunction buffer
+ void (*move_to)(void* storage, void* other) = nullptr;
+};
+} // namespace detail
+
+// This class is an *almost* drop-in replacement for std::function which is guaranteed to never
+// allocate, and always holds the type erased functional object in an in-line small buffer of
+// templated size. If the object is too large to hold, the type will fail to instantiate.
+//
+// Some notable differences are:
+// - operator() is not const (unlike std::function where the call operator is
+// const even if the erased type is not const callable). This retains const
+// correctness by default. A workaround is keeping InPlaceFunction mutable.
+// - Moving from an InPlaceFunction leaves the object in a valid state (operator
+// bool remains true), similar to std::optional/std::variant.
+// Calls to the object are still defined (and are equivalent
+// to calling the underlying type after it has been moved from). To opt-out
+// (and/or ensure safety), clearing the object is recommended:
+// func1 = std::move(func2); // func2 still valid (and moved-from) after this line
+// func2 = nullptr; // calling func2 will now abort
+// - Unsafe implicit conversions of the return value to a reference type are
+// prohibited due to the risk of dangling references (some of this safety was
+// added to std::function in c++23). Only converting a reference to a reference to base class is
+// permitted:
+// std::function<Base&()> = []() -> Derived& {...}
+// - Some (current libc++ implementation) implementations of std::function
+// incorrectly fail to handle returning non-moveable types which is valid given
+// mandatory copy elision.
+//
+// Additionally, the stored functional will use the typical rules of overload
+// resolution to disambiguate the correct call, except, the target class will
+// always be implicitly a non-const lvalue when called. If a different overload
+// is preferred, wrapping the target class in a lambda with explicit casts is
+// recommended (or using inheritance, mixins or CRTP). This avoids the
+// complexity of utilizing abonimable function types as template params.
+template <typename, size_t BufferSize = 32>
+class InPlaceFunction;
+// We partially specialize to match types which are spelled like functions
+template <typename Ret, typename... Args, size_t BufferSize>
+class InPlaceFunction<Ret(Args...), BufferSize> {
+ public:
+ // Storage Type Details
+ static constexpr size_t Size = BufferSize;
+ static constexpr size_t Alignment = alignof(std::max_align_t);
+ using Buffer_t = std::aligned_storage_t<Size, Alignment>;
+ template <typename T, size_t Other>
+ friend class InPlaceFunction;
+
+ private:
+ // Callable which is used for empty InPlaceFunction objects (to match the
+ // std::function interface).
+ struct BadCallable {
+ [[noreturn]] Ret operator()(Args...) { std::abort(); }
+ };
+ static_assert(std::is_trivially_destructible_v<BadCallable>);
+
+ // Implementation of vtable interface for erased types.
+ // Contains only static vtable instantiated once for each erased type and
+ // static helpers.
+ template <typename T>
+ struct TableImpl {
+ // T should be a decayed type
+ static_assert(std::is_same_v<T, std::decay_t<T>>);
+
+ // Helper functions to get an unerased reference to the type held in the
+ // buffer. std::launder is required to avoid strict aliasing rules.
+ // The cast is always defined, as a precondition for these calls is that
+ // (exactly) a T was placement new constructed into the buffer.
+ constexpr static T& getRef(void* storage) {
+ return *std::launder(reinterpret_cast<T*>(storage));
+ }
+
+ constexpr static const T& getRef(const void* storage) {
+ return *std::launder(reinterpret_cast<const T*>(storage));
+ }
+
+ // Constexpr implies inline
+ constexpr static detail::ICallableTable<Ret, Args...> table = {
+ // Stateless lambdas are convertible to function ptrs
+ .destroy = [](void* storage) { getRef(storage).~T(); },
+ .invoke = [](void* storage, Args&&... args) -> Ret {
+ if constexpr (std::is_void_v<Ret>) {
+ std::invoke(getRef(storage), std::forward<Args>(args)...);
+ } else {
+ return std::invoke(getRef(storage), std::forward<Args>(args)...);
+ }
+ },
+ .copy_to = [](const void* storage,
+ void* other) { ::new (other) T(getRef(storage)); },
+ .move_to = [](void* storage,
+ void* other) { ::new (other) T(std::move(getRef(storage))); },
+ };
+ };
+
+ // Check size/align requirements for the T in Buffer_t.
+ template <typename T>
+ static constexpr bool WillFit_v = sizeof(T) <= Size && alignof(T) <= Alignment;
+
+ // Check size/align requirements for a function to function conversion
+ template <typename T>
+ static constexpr bool ConversionWillFit_v = (T::Size < Size) && (T::Alignment <= Alignment);
+
+ template <typename T>
+ struct IsInPlaceFunction : std::false_type {};
+
+ template <size_t BufferSize_>
+ struct IsInPlaceFunction<InPlaceFunction<Ret(Args...), BufferSize_>> : std::true_type {};
+
+ template <typename T>
+ static T BetterDeclval();
+ template <typename T>
+ static void CheckImplicitConversion(T);
+
+ template <class T, class U, class = void>
+ struct CanImplicitConvert : std::false_type {};
+
+ // std::is_convertible/std::invokeable has a bug (in libc++) regarding
+ // mandatory copy elision for non-moveable types. So, we roll our own.
+ // https://github.com/llvm/llvm-project/issues/55346
+ template <class From, class To>
+ struct CanImplicitConvert<From, To,
+ decltype(CheckImplicitConversion<To>(BetterDeclval<From>()))>
+ : std::true_type {};
+
+ // Check if the provided type is a valid functional to be type-erased.
+ // if constexpr utilized for short-circuit behavior
+ template <typename T>
+ static constexpr bool isValidFunctional() {
+ using Target = std::decay_t<T>;
+ if constexpr (IsInPlaceFunction<Target>::value || std::is_same_v<Target, std::nullptr_t>) {
+ // Other overloads handle these cases
+ return false;
+ } else if constexpr (std::is_invocable_v<Target, Args...>) {
+ // The target type is a callable (with some unknown return value)
+ if constexpr (std::is_void_v<Ret>) {
+ // Any return value can be dropped to model a void returning
+ // function.
+ return WillFit_v<Target>;
+ } else {
+ using RawRet = std::invoke_result_t<Target, Args...>;
+ if constexpr (CanImplicitConvert<RawRet, Ret>::value) {
+ if constexpr (std::is_reference_v<Ret>) {
+ // If the return type is a reference, in order to
+ // avoid dangling references, we only permit functionals
+ // which return a reference to the exact type, or a base
+ // type.
+ if constexpr (std::is_reference_v<RawRet> &&
+ (std::is_same_v<std::decay_t<Ret>, std::decay_t<RawRet>> ||
+ std::is_base_of_v<std::decay_t<Ret>,
+ std::decay_t<RawRet>>)) {
+ return WillFit_v<Target>;
+ }
+ return false;
+ }
+ return WillFit_v<Target>;
+ }
+ // If we can't convert the raw return type, the functional is invalid.
+ return false;
+ }
+ }
+ return false;
+ }
+
+ template <typename T>
+ static constexpr bool IsValidFunctional_v = isValidFunctional<T>();
+ // Check if the type is a strictly smaller sized InPlaceFunction
+ template <typename T>
+ static constexpr bool isConvertibleFunc() {
+ using Target = std::decay_t<T>;
+ if constexpr (IsInPlaceFunction<Target>::value) {
+ return ConversionWillFit_v<Target>;
+ }
+ return false;
+ }
+
+ template <typename T>
+ static constexpr bool IsConvertibleFunc_v = isConvertibleFunc<T>();
+
+ // Members below
+ // This must come first for alignment
+ Buffer_t storage_;
+ const detail::ICallableTable<Ret, Args...>* vptr_;
+
+ constexpr void copy_to(InPlaceFunction& other) const {
+ vptr_->copy_to(std::addressof(storage_), std::addressof(other.storage_));
+ other.vptr_ = vptr_;
+ }
+
+ constexpr void move_to(InPlaceFunction& other) {
+ vptr_->move_to(std::addressof(storage_), std::addressof(other.storage_));
+ other.vptr_ = vptr_;
+ }
+
+ constexpr void destroy() { vptr_->destroy(std::addressof(storage_)); }
+
+ template <typename T, typename Target = std::decay_t<T>>
+ constexpr void genericInit(T&& t) {
+ vptr_ = &TableImpl<Target>::table;
+ ::new (std::addressof(storage_)) Target(std::forward<T>(t));
+ }
+
+ template <typename T, typename Target = std::decay_t<T>>
+ constexpr void convertingInit(T&& smallerFunc) {
+ // Redundant, but just in-case
+ static_assert(Target::Size < Size && Target::Alignment <= Alignment);
+ if constexpr (std::is_lvalue_reference_v<T>) {
+ smallerFunc.vptr_->copy_to(std::addressof(smallerFunc.storage_),
+ std::addressof(storage_));
+ } else {
+ smallerFunc.vptr_->move_to(std::addressof(smallerFunc.storage_),
+ std::addressof(storage_));
+ }
+ vptr_ = smallerFunc.vptr_;
+ }
+
+ public:
+ // Public interface
+ template <typename T, std::enable_if_t<IsValidFunctional_v<T>>* = nullptr>
+ constexpr InPlaceFunction(T&& t) {
+ genericInit(std::forward<T>(t));
+ }
+
+ // Conversion from smaller functions.
+ template <typename T, std::enable_if_t<IsConvertibleFunc_v<T>>* = nullptr>
+ constexpr InPlaceFunction(T&& t) {
+ convertingInit(std::forward<T>(t));
+ }
+
+ constexpr InPlaceFunction(const InPlaceFunction& other) { other.copy_to(*this); }
+
+ constexpr InPlaceFunction(InPlaceFunction&& other) { other.move_to(*this); }
+
+ // Making functions default constructible has pros and cons, we will align
+ // with the standard
+ constexpr InPlaceFunction() : InPlaceFunction(BadCallable{}) {}
+
+ constexpr InPlaceFunction(std::nullptr_t) : InPlaceFunction(BadCallable{}) {}
+
+#if __cplusplus >= 202002L
+ constexpr ~InPlaceFunction() {
+#else
+ ~InPlaceFunction() {
+#endif
+ destroy();
+ }
+
+ // The std::function call operator is marked const, but this violates const
+ // correctness. We deviate from the standard and do not mark the operator as
+ // const. Collections of InPlaceFunctions should probably be mutable.
+ constexpr Ret operator()(Args... args) {
+ if constexpr (std::is_void_v<Ret>) {
+ vptr_->invoke(std::addressof(storage_), std::forward<Args>(args)...);
+ } else {
+ return vptr_->invoke(std::addressof(storage_), std::forward<Args>(args)...);
+ }
+ }
+
+ constexpr InPlaceFunction& operator=(const InPlaceFunction& other) {
+ if (std::addressof(other) == this) return *this;
+ destroy();
+ other.copy_to(*this);
+ return *this;
+ }
+
+ constexpr InPlaceFunction& operator=(InPlaceFunction&& other) {
+ if (std::addressof(other) == this) return *this;
+ destroy();
+ other.move_to(*this);
+ return *this;
+ }
+
+ template <typename T, std::enable_if_t<IsValidFunctional_v<T>>* = nullptr>
+ constexpr InPlaceFunction& operator=(T&& t) {
+ // We can't assign to ourselves, since T is a different type
+ destroy();
+ genericInit(std::forward<T>(t));
+ return *this;
+ }
+
+ // Explicitly defining this function saves a move/dtor
+ template <typename T, std::enable_if_t<IsConvertibleFunc_v<T>>* = nullptr>
+ constexpr InPlaceFunction& operator=(T&& t) {
+ // We can't assign to ourselves, since T is different type
+ destroy();
+ convertingInit(std::forward<T>(t));
+ return *this;
+ }
+
+ constexpr InPlaceFunction& operator=(std::nullptr_t) { return operator=(BadCallable{}); }
+
+ // Moved from InPlaceFunctions are still considered valid (similar to
+ // std::optional). If using std::move on a function object explicitly, it is
+ // recommended that the object is reset using nullptr.
+ constexpr explicit operator bool() const { return vptr_ != &TableImpl<BadCallable>::table; }
+
+ constexpr void swap(InPlaceFunction& other) {
+ if (std::addressof(other) == this) return;
+ InPlaceFunction tmp{std::move(other)};
+ other.destroy();
+ move_to(other);
+ destroy();
+ tmp.move_to(*this);
+ }
+
+ friend constexpr void swap(InPlaceFunction& lhs, InPlaceFunction& rhs) { lhs.swap(rhs); }
+};
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
index 700fbaa..6d7e990 100644
--- a/media/utils/include/mediautils/MethodStatistics.h
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -55,15 +55,23 @@
/**
* Adds a method event, typically execution time in ms.
*/
- void event(Code code, FloatType executeMs) {
+ template <typename C>
+ void event(C&& code, FloatType executeMs) {
std::lock_guard lg(mLock);
- mStatisticsMap[code].add(executeMs);
+ auto it = mStatisticsMap.lower_bound(code);
+ if (it != mStatisticsMap.end() && it->first == code) {
+ it->second.add(executeMs);
+ } else {
+ // StatsType ctor takes an optional array of data for initialization.
+ FloatType dataArray[1] = { executeMs };
+ mStatisticsMap.emplace_hint(it, std::forward<C>(code), dataArray);
+ }
}
/**
* Returns the name for the method code.
*/
- std::string getMethodForCode(Code code) const {
+ std::string getMethodForCode(const Code& code) const {
auto it = mMethodMap.find(code);
return it == mMethodMap.end() ? std::to_string((int)code) : it->second;
}
@@ -71,7 +79,7 @@
/**
* Returns the number of times the method was invoked by event().
*/
- size_t getMethodCount(Code code) const {
+ size_t getMethodCount(const Code& code) const {
std::lock_guard lg(mLock);
auto it = mStatisticsMap.find(code);
return it == mStatisticsMap.end() ? 0 : it->second.getN();
@@ -80,7 +88,7 @@
/**
* Returns the statistics object for the method.
*/
- StatsType getStatistics(Code code) const {
+ StatsType getStatistics(const Code& code) const {
std::lock_guard lg(mLock);
auto it = mStatisticsMap.find(code);
return it == mStatisticsMap.end() ? StatsType{} : it->second;
@@ -107,9 +115,10 @@
}
private:
- const std::map<Code, std::string> mMethodMap;
+ // Note: we use a transparent comparator std::less<> for heterogeneous key lookup.
+ const std::map<Code, std::string, std::less<>> mMethodMap;
mutable std::mutex mLock;
- std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
+ std::map<Code, StatsType, std::less<>> mStatisticsMap GUARDED_BY(mLock);
};
// Managed Statistics support.
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index de20d55..3d7981a 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -130,7 +130,7 @@
std::optional<bool> doIsAllowed(uid_t uid);
sp<content::pm::IPackageManagerNative> retrievePackageManager();
sp<content::pm::IPackageManagerNative> mPackageManager; // To check apps manifest
- uint_t mPackageManagerErrors = 0;
+ unsigned int mPackageManagerErrors = 0;
struct Package {
std::string name;
bool playbackCaptureAllowed = false;
diff --git a/media/utils/include/mediautils/StaticStringView.h b/media/utils/include/mediautils/StaticStringView.h
new file mode 100644
index 0000000..14be240
--- /dev/null
+++ b/media/utils/include/mediautils/StaticStringView.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 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 <string_view>
+#include <type_traits>
+
+#pragma push_macro("EXPLICIT_CONVERSION_GENERATE_OPERATOR")
+#undef EXPLICIT_CONVERSION_GENERATE_OPERATOR
+#define EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, op) \
+ friend constexpr bool operator op(T lhs, T rhs) { \
+ return operator op(static_cast<U>(lhs), static_cast<U>(rhs)); \
+ } \
+ friend constexpr bool operator op(T lhs, U rhs) { \
+ return operator op(static_cast<U>(lhs), rhs); \
+ } \
+ friend constexpr bool operator op(U lhs, T rhs) { \
+ return operator op(lhs, static_cast<U>(rhs)); \
+ }
+
+#pragma push_macro("EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS")
+#undef EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS
+// Generate comparison operator friend functions for types (appropriately
+// const/ref qualified) where T is **explicitly** convertible to U.
+#define EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS(T, U) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, ==) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, !=) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, <) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, <=) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, >) \
+ EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, >=)
+
+namespace android::mediautils {
+
+// This class a reference to a string with static storage duration
+// which is const (i.e. a string view). We expose an identical API to
+// string_view, however we do not publicly inherit to avoid potential mis-use of
+// non-virtual dtors/methods.
+//
+// We can create APIs which consume only static strings, which
+// avoids allocation/deallocation of the string locally, as well as potential
+// lifetime issues caused by consuming raw pointers (or string_views).
+// Equivalently, a string_view which is always valid, and whose underlying data
+// can never change.
+//
+// In most cases, the string_view should be initialized at compile time (and there are
+// helpers to do so below). In order to initialize a non-constexpr array,
+// the second template param must be false (i.e. opt-in).
+// Construction/usage as follows (constexpr required unless second template param is false):
+//
+// constexpr static std::array<char, 12> debugString = toStdArray("MyMethodName");
+// constexpr auto myStaticStringView = StaticStringView::create<debugString>();
+// const auto size_t length = myStaticStringView.length() // can call any string_view methods
+// globalLog(myStaticStringView, ...); // Pass to APIs consuming StaticStringViews
+//
+struct StaticStringView final : private std::string_view {
+ template <typename T>
+ struct is_const_char_array : std::false_type {};
+
+ // Use templated value helper
+ template <size_t N>
+ struct is_const_char_array<const std::array<char, N>> : std::true_type {};
+
+ template <typename T>
+ static constexpr bool is_const_char_array_v =
+ is_const_char_array<std::remove_reference_t<T>>::value;
+
+ template <auto& val, std::enable_if_t<is_const_char_array_v<decltype(val)>, bool> Check = true>
+ static constexpr StaticStringView create() {
+ if constexpr (Check) {
+ // If this static_assert fails to compile, this method was called
+ // with a non-constexpr
+ static_assert(val[0]);
+ }
+ return StaticStringView{val.data(), val.size()};
+ }
+
+ // We can copy/move assign/construct from other StaticStringViews as their validity is already
+ // ensured
+ constexpr StaticStringView(const StaticStringView& other) = default;
+ constexpr StaticStringView& operator=(const StaticStringView& other) = default;
+ constexpr StaticStringView(StaticStringView&& other) = default;
+ constexpr StaticStringView& operator=(StaticStringView&& other) = default;
+
+ // Explicitly convert to a std::string_view (this is a strict loss of
+ // information so should only be used across APIs which intend to consume
+ // any std::string_view).
+ constexpr std::string_view getStringView() const { return *this; }
+
+ // The following methods expose an identical API to std::string_view
+ using std::string_view::begin;
+ using std::string_view::cbegin;
+ using std::string_view::cend;
+ using std::string_view::crbegin;
+ using std::string_view::crend;
+ using std::string_view::end;
+ using std::string_view::rbegin;
+ using std::string_view::rend;
+ using std::string_view::operator[];
+ using std::string_view::at;
+ using std::string_view::back;
+ using std::string_view::data;
+ using std::string_view::empty;
+ using std::string_view::front;
+ using std::string_view::length;
+ using std::string_view::max_size;
+ using std::string_view::size;
+ // These modifiers are valid because the resulting view is a
+ // substring of the original static string
+ using std::string_view::remove_prefix;
+ using std::string_view::remove_suffix;
+ // Skip swap
+ using std::string_view::compare;
+ using std::string_view::copy;
+ using std::string_view::find;
+ using std::string_view::find_first_not_of;
+ using std::string_view::find_first_of;
+ using std::string_view::find_last_not_of;
+ using std::string_view::find_last_of;
+ using std::string_view::rfind;
+ using std::string_view::substr;
+#if __cplusplus >= 202202L
+ using std::string_view::ends_with;
+ using std::string_view::starts_with;
+#endif
+ using std::string_view::npos;
+
+ // Non-member friend functions to follow. Identical API to std::string_view
+ template <class CharT, class Traits>
+ friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ StaticStringView v) {
+ return os << static_cast<std::string_view&>(v);
+ }
+
+ EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS(const StaticStringView&,
+ const std::string_view&)
+
+ private:
+ constexpr StaticStringView(const char* ptr, size_t sz) : std::string_view(ptr, sz){};
+
+ public:
+ // The next two functions are logically consteval (only avail in c++20).
+ // We can't use templates as params, as they would require references to
+ // static which would unnecessarily bloat executable size.
+ template <typename T, size_t N, size_t M>
+ static constexpr std::array<T, N + M> concatArray(const std::array<T, N>& a,
+ const std::array<T, M>& b) {
+ std::array<T, N + M> res{};
+ for (size_t i = 0; i < N; i++) {
+ res[i] = a[i];
+ }
+ for (size_t i = 0; i < M; i++) {
+ res[N + i] = b[i];
+ }
+ return res;
+ }
+
+ static void arrayIsNotNullTerminated();
+
+ // This method should only be called on C-style char arrays which are
+ // null-terminated. Calling this method on a char array with intermediate null
+ // characters (i.e. "hello\0" or "hel\0lo" will result in a std::array with null
+ // characters, which is most likely not intended.
+ // We attempt to detect a non-null terminated char array at link-time, but
+ // this is best effort. A consequence of this approach is that this method
+ // will fail to link for extern args, or when not inlined. Since this method
+ // is intended to be used constexpr, this is not an issue.
+ template <size_t N>
+ static constexpr std::array<char, N - 1> toStdArray(const char (&input)[N]) {
+ std::array<char, N - 1> res{};
+ for (size_t i = 0; i < N - 1; i++) {
+ res[i] = input[i];
+ }
+ // A workaround to generate a link-time error if toStdArray is not called on
+ // a null-terminated char array.
+ if (input[N - 1] != 0) arrayIsNotNullTerminated();
+ return res;
+ }
+};
+} // namespace android::mediautils
+
+// Specialization of std::hash for use with std::unordered_map
+namespace std {
+template <>
+struct hash<android::mediautils::StaticStringView> {
+ constexpr size_t operator()(const android::mediautils::StaticStringView& val) {
+ return std::hash<std::string_view>{}(val.getStringView());
+ }
+};
+} // namespace std
+
+#pragma pop_macro("EXPLICIT_CONVERSION_GENERATE_OPERATOR")
+#pragma pop_macro("EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS")
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index ef03aef..038e7dd 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -60,7 +60,7 @@
* the TimeCheck object is destroyed or leaves scope.
* \param crashOnTimeout true if the object issues an abort on timeout.
*/
- explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {},
+ explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {},
uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true);
TimeCheck() = default;
@@ -79,7 +79,17 @@
// The usage here is const safe.
class TimeCheckHandler {
public:
- const std::string tag;
+ template <typename S, typename F>
+ TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout,
+ const std::chrono::system_clock::time_point& _startTime,
+ pid_t _tid)
+ : tag(std::forward<S>(_tag))
+ , onTimer(std::forward<F>(_onTimer))
+ , crashOnTimeout(_crashOnTimeout)
+ , startTime(_startTime)
+ , tid(_tid)
+ {}
+ const FixedString62 tag;
const OnTimerFunc onTimer;
const bool crashOnTimeout;
const std::chrono::system_clock::time_point startTime;
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index ffee602..349bffd 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -27,6 +27,8 @@
#include <android-base/thread_annotations.h>
+#include <mediautils/FixedString.h>
+
namespace android::mediautils {
/**
@@ -54,7 +56,7 @@
* \returns a handle that can be used for cancellation.
*/
Handle scheduleTask(
- std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
+ std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
/**
* Tracks a task that shows up on toString() until cancelled.
@@ -62,7 +64,7 @@
* \param tag string associated with the task.
* \returns a handle that can be used for cancellation.
*/
- Handle trackTask(std::string tag);
+ Handle trackTask(std::string_view tag);
/**
* Cancels a task previously scheduled with scheduleTask()
@@ -129,12 +131,22 @@
// To minimize movement of data, we pass around shared_ptrs to Requests.
// These are allocated and deallocated outside of the lock.
struct Request {
+ Request(const std::chrono::system_clock::time_point& _scheduled,
+ const std::chrono::system_clock::time_point& _deadline,
+ pid_t _tid,
+ std::string_view _tag)
+ : scheduled(_scheduled)
+ , deadline(_deadline)
+ , tid(_tid)
+ , tag(_tag)
+ {}
+
const std::chrono::system_clock::time_point scheduled;
const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout
// if deadline == scheduled, no
// timeout, task not executed.
const pid_t tid;
- const std::string tag;
+ const FixedString62 tag;
std::string toString() const;
};
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 1024018..48d18b0 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -7,78 +7,106 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_test_library {
- name: "libsharedtest",
+// general test config
+cc_defaults {
+ name: "libmediautils_tests_config",
+
+ host_supported: true,
+
cflags: [
"-Wall",
"-Werror",
"-Wextra",
],
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
+ sanitize: {
+ address: true,
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
},
+}
+
+cc_defaults {
+ name: "libmediautils_tests_defaults",
+
+ defaults: ["libmediautils_tests_config"],
+
+ host_supported: true,
shared_libs: [
"liblog",
+ "libutils",
],
+ static_libs: [
+ "libmediautils_core",
+ ],
+}
+
+cc_defaults {
+ name: "libmediautils_tests_host_unavail",
+
+ defaults: ["libmediautils_tests_config"],
+
+ host_supported: false,
+
+ shared_libs: [
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+}
+
+cc_test_library {
+ name: "libsharedtest",
+
+ defaults: ["libmediautils_tests_defaults"],
+
srcs: [
"sharedtest.cpp",
- ]
+ ],
}
cc_test {
name: "library_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
- },
-
- shared_libs: [
- "libbase",
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_defaults"],
data_libs: [
"libsharedtest",
],
+ shared_libs: [
+ "libbase",
+ ],
+
srcs: [
"library_tests.cpp",
],
}
cc_test {
- name: "media_process_tests",
+ name: "libmediautils_test",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
shared_libs: [
- "liblog",
"libmediautils",
- "libutils",
],
srcs: [
+ "memory-test.cpp",
+ "TimerThread-test.cpp",
+ ],
+}
+
+cc_test {
+ name: "media_process_tests",
+
+ defaults: ["libmediautils_tests_host_unavail"],
+
+ srcs: [
"media_process_tests.cpp",
],
}
@@ -86,17 +114,7 @@
cc_test {
name: "media_synchronization_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"media_synchronization_tests.cpp",
@@ -106,17 +124,7 @@
cc_test {
name: "media_threadsnapshot_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"media_threadsnapshot_tests.cpp",
@@ -124,19 +132,26 @@
}
cc_test {
- name: "mediautils_scopedstatistics_tests",
+ name: "mediautils_fixedstring_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_defaults"],
shared_libs: [
"libaudioutils",
- "liblog",
- "libmediautils",
- "libutils",
+ ],
+
+ srcs: [
+ "mediautils_fixedstring_tests.cpp",
+ ],
+}
+
+cc_test {
+ name: "mediautils_scopedstatistics_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ shared_libs: [
+ "libaudioutils",
],
srcs: [
@@ -147,17 +162,10 @@
cc_test {
name: "methodstatistics_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_defaults"],
shared_libs: [
"libaudioutils",
- "liblog",
- "libmediautils",
- "libutils",
],
srcs: [
@@ -166,28 +174,41 @@
}
cc_test {
+ name: "static_string_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "static_string_view_tests.cpp",
+ ],
+}
+
+cc_test {
name: "timecheck_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
- },
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"timecheck_tests.cpp",
],
}
+
+cc_test {
+ name: "extended_accumulator_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "extended_accumulator_tests.cpp",
+ ],
+}
+
+cc_test {
+ name: "inplace_function_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "inplace_function_tests.cpp"
+ ],
+}
diff --git a/media/utils/TimerThread-test.cpp b/media/utils/tests/TimerThread-test.cpp
similarity index 70%
rename from media/utils/TimerThread-test.cpp
rename to media/utils/tests/TimerThread-test.cpp
index 93cd64c..1fbe894 100644
--- a/media/utils/TimerThread-test.cpp
+++ b/media/utils/tests/TimerThread-test.cpp
@@ -40,8 +40,10 @@
std::this_thread::sleep_for(100ms - kJitter);
ASSERT_FALSE(taskRan);
std::this_thread::sleep_for(2 * kJitter);
- ASSERT_TRUE(taskRan);
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_TRUE(taskRan); // timed-out called.
+ ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // nothing cancelled
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, Cancel) {
@@ -53,8 +55,10 @@
ASSERT_FALSE(taskRan);
ASSERT_TRUE(thread.cancelTask(handle));
std::this_thread::sleep_for(2 * kJitter);
- ASSERT_FALSE(taskRan);
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_FALSE(taskRan); // timed-out did not call.
+ ASSERT_EQ(0ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // task cancelled.
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, CancelAfterRun) {
@@ -63,9 +67,11 @@
TimerThread::Handle handle =
thread.scheduleTask("CancelAfterRun", [&taskRan] { taskRan = true; }, 100ms);
std::this_thread::sleep_for(100ms + kJitter);
- ASSERT_TRUE(taskRan);
+ ASSERT_TRUE(taskRan); // timed-out called.
ASSERT_FALSE(thread.cancelTask(handle));
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // nothing actually cancelled
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, MultipleTasks) {
@@ -82,9 +88,9 @@
thread.scheduleTask("5", [&taskRan] { taskRan[5] = true; }, 200ms);
// 6 tasks pending
- ASSERT_EQ(6, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(6ul, countChars(thread.pendingToString(), REQUEST_START));
// 0 tasks completed
- ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
// Task 1 should trigger around 100ms.
std::this_thread::sleep_until(startTime + 100ms - kJitter);
@@ -141,9 +147,10 @@
ASSERT_TRUE(taskRan[5]);
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
- // 4 tasks ran and 1 cancelled
- ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
+ // 4 tasks called on timeout, and 1 cancelled
+ ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
// Task 3 should trigger around 400ms.
std::this_thread::sleep_until(startTime + 400ms - kJitter);
@@ -154,8 +161,9 @@
ASSERT_FALSE(taskRan[4]);
ASSERT_TRUE(taskRan[5]);
- // 4 tasks ran and 1 cancelled
- ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ // 4 tasks called on timeout and 1 cancelled
+ ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
std::this_thread::sleep_until(startTime + 400ms + kJitter);
ASSERT_TRUE(taskRan[0]);
@@ -166,9 +174,10 @@
ASSERT_TRUE(taskRan[5]);
// 0 tasks pending
- ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
- // 5 tasks ran and 1 cancelled
- ASSERT_EQ(5 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
+ // 5 tasks called on timeout and 1 cancelled
+ ASSERT_EQ(5ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, TrackedTasks) {
@@ -179,47 +188,47 @@
auto handle2 = thread.trackTask("2");
// 3 tasks pending
- ASSERT_EQ(3, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(3ul, countChars(thread.pendingToString(), REQUEST_START));
// 0 tasks retired
- ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle0));
ASSERT_TRUE(thread.cancelTask(handle1));
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
// handle1 is stale, cancel returns false.
ASSERT_FALSE(thread.cancelTask(handle1));
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
// Add another tracked task.
auto handle3 = thread.trackTask("3");
// 2 tasks pending
- ASSERT_EQ(2, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle2));
// 1 tasks pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 3 tasks retired
- ASSERT_EQ(3, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(3ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle3));
// 0 tasks pending
- ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
// 4 tasks retired
- ASSERT_EQ(4, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(4ul, countChars(thread.retiredToString(), REQUEST_START));
}
} // namespace
diff --git a/media/utils/tests/extended_accumulator_tests.cpp b/media/utils/tests/extended_accumulator_tests.cpp
new file mode 100644
index 0000000..e243e7e
--- /dev/null
+++ b/media/utils/tests/extended_accumulator_tests.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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 "extended_accumulator_tests"
+
+#include <mediautils/ExtendedAccumulator.h>
+
+#include <type_traits>
+#include <cstdint>
+#include <limits.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android;
+using namespace android::mediautils;
+
+// Conditionally choose a base accumulating counter value in order to prevent
+// unsigned underflow on the accumulator from aborting the tests.
+template <typename TType, typename CType>
+static constexpr CType getBase() {
+ static_assert(sizeof(TType) < sizeof(CType));
+ if constexpr (std::is_unsigned_v<CType>) {
+ return std::numeric_limits<TType>::max() + 1;
+ } else {
+ return 0;
+ }
+}
+
+// Since the entire state of this utility is the previous value, and the
+// behavior is isomorphic mod the underlying type on the previous value, we can
+// test combinations of the previous value of the underlying type and a
+// hypothetical signed update to that type and ensure the accumulator moves
+// correctly and reports overflow correctly.
+template <typename TestUInt, typename CType>
+void testPair(TestUInt prevVal, std::make_signed_t<TestUInt> delta) {
+ using TestDetect = ExtendedAccumulator<TestUInt, CType>;
+ using TestInt = typename TestDetect::SignedInt;
+ static_assert(std::is_same_v<typename TestDetect::UnsignedInt, TestUInt>);
+ static_assert(std::is_same_v<TestInt, std::make_signed_t<TestUInt>>);
+ static_assert(sizeof(TestUInt) < sizeof(CType));
+
+ // To safely detect underflow/overflow for testing
+ // Should be 0 mod TestUInt, max + 1 is convenient
+ static constexpr CType base = getBase<TestUInt, CType>();
+ const CType prev = base + prevVal;
+ TestDetect test{prev};
+ EXPECT_EQ(test.getValue(), prev);
+ // Prevent unsigned wraparound abort
+ CType next;
+ const auto err = __builtin_add_overflow(prev, delta, &next);
+ LOG_ALWAYS_FATAL_IF(err, "Unexpected wrap in tests");
+ const auto [result, status] = test.poll(static_cast<TestUInt>(next));
+ EXPECT_EQ(test.getValue(), next);
+ EXPECT_EQ(result, delta);
+
+ // Test overflow/underflow event reporting.
+ if (next < base) EXPECT_EQ(TestDetect::Wrap::UNDERFLOW, status);
+ else if (next > base + std::numeric_limits<TestUInt>::max())
+ EXPECT_EQ(TestDetect::Wrap::OVERFLOW, status);
+ else EXPECT_EQ(TestDetect::Wrap::NORMAL, status);
+}
+
+// Test this utility on every combination of prior and update value for the
+// type uint8_t, with an unsigned containing type.
+TEST(wraparound_tests, cover_u8_u64) {
+ using TType = uint8_t;
+ using CType = uint64_t;
+ static constexpr CType max = std::numeric_limits<TType>::max();
+ for (CType i = 0; i <= max; i++) {
+ for (CType j = 0; j <= max; j++) {
+ testPair<TType, CType>(i, static_cast<int64_t>(j));
+ }
+ }
+}
+
+// Test this utility on every combination of prior and update value for the
+// type uint8_t, with a signed containing type.
+TEST(wraparound_tests, cover_u8_s64) {
+ using TType = uint8_t;
+ using CType = int64_t;
+ static constexpr CType max = std::numeric_limits<TType>::max();
+ for (CType i = 0; i <= max; i++) {
+ for (CType j = 0; j <= max; j++) {
+ testPair<TType, CType>(i, static_cast<int64_t>(j));
+ }
+ }
+}
diff --git a/media/utils/tests/inplace_function_tests.cpp b/media/utils/tests/inplace_function_tests.cpp
new file mode 100644
index 0000000..6172aa4
--- /dev/null
+++ b/media/utils/tests/inplace_function_tests.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2022 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 "inplace_function_tests"
+
+#include <mediautils/InPlaceFunction.h>
+
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android;
+using namespace android::mediautils;
+
+struct BigCallable {
+ BigCallable(size_t* x, size_t val1, size_t val2) : ptr(x), a(val1), b(val2) {}
+ size_t* ptr;
+ size_t a;
+ size_t b;
+ size_t operator()(size_t input) const {
+ *ptr += a * 100 + b * 10 + input;
+ return 8;
+ }
+};
+
+TEST(InPlaceFunctionTests, Basic) {
+ size_t x = 5;
+ InPlaceFunction<size_t(size_t)> func;
+ {
+ BigCallable test{&x, 2, 3};
+ func = test;
+ }
+ EXPECT_EQ(func(2), 8ull);
+ EXPECT_EQ(x, 232ull + 5);
+}
+
+TEST(InPlaceFunctionTests, Invalid) {
+ InPlaceFunction<size_t(size_t)> func;
+ EXPECT_TRUE(!func);
+ InPlaceFunction<size_t(size_t)> func2{nullptr};
+ EXPECT_TRUE(!func2);
+ InPlaceFunction<size_t(size_t)> func3 = [](size_t x) { return x; };
+ EXPECT_TRUE(!(!func3));
+ func3 = nullptr;
+ EXPECT_TRUE(!func3);
+}
+
+TEST(InPlaceFunctionTests, MultiArg) {
+ InPlaceFunction<size_t(size_t, size_t, size_t)> func = [](size_t a, size_t b, size_t c) {
+ return a + b + c;
+ };
+ EXPECT_EQ(func(2, 3, 5), 2ull + 3 + 5);
+}
+struct Record {
+ Record(size_t m, size_t c, size_t d) : move_called(m), copy_called(c), dtor_called(d) {}
+ Record() {}
+ size_t move_called = 0;
+ size_t copy_called = 0;
+ size_t dtor_called = 0;
+ friend std::ostream& operator<<(std::ostream& os, const Record& record) {
+ return os << "Record, moves: " << record.move_called << ", copies: " << record.copy_called
+ << ", dtor: " << record.dtor_called << '\n';
+ }
+};
+
+bool operator==(const Record& lhs, const Record& rhs) {
+ return lhs.move_called == rhs.move_called && lhs.copy_called == rhs.copy_called &&
+ lhs.dtor_called == rhs.dtor_called;
+}
+
+struct Noisy {
+ Record& ref;
+ size_t state;
+ Noisy(Record& record, size_t val) : ref(record), state(val) {}
+ Noisy(const Noisy& other) : ref(other.ref), state(other.state) { ref.copy_called++; }
+
+ Noisy(Noisy&& other) : ref(other.ref), state(other.state) { ref.move_called++; }
+ ~Noisy() { ref.dtor_called++; }
+
+ size_t operator()() { return state; }
+};
+
+TEST(InPlaceFunctionTests, CtorForwarding) {
+ Record record;
+ Noisy noisy{record, 17};
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func(), 17ull);
+ Record record2;
+ Noisy noisy2{record2, 13};
+ InPlaceFunction<size_t()> func2{std::move(noisy2)};
+ EXPECT_EQ(record2, Record(1, 0, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), 13ull);
+}
+
+TEST(InPlaceFunctionTests, FunctionCtorForwarding) {
+ {
+ Record record;
+ Noisy noisy{record, 17};
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func(), 17ull);
+ InPlaceFunction<size_t()> func2{func};
+ EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), 17ull);
+ }
+ Record record;
+ Noisy noisy{record, 13};
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func(), 13ull);
+ InPlaceFunction<size_t()> func2{std::move(func)};
+ EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), 13ull);
+ // We expect moved from functions to still be valid
+ EXPECT_TRUE(!(!func));
+ EXPECT_EQ(static_cast<bool>(func), static_cast<bool>(func2));
+ EXPECT_EQ(func(), 13ull);
+}
+
+TEST(InPlaceFunctionTests, Dtor) {
+ Record record;
+ {
+ InPlaceFunction<size_t()> func;
+ {
+ Noisy noisy{record, 17};
+ func = noisy;
+ }
+ EXPECT_EQ(func(), 17ull);
+ EXPECT_EQ(record.dtor_called, 1ull);
+ }
+ EXPECT_EQ(record.dtor_called, 2ull);
+}
+
+TEST(InPlaceFunctionTests, Assignment) {
+ {
+ Record record;
+ Record record2;
+ Noisy noisy{record, 17};
+ Noisy noisy2{record2, 5};
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(func(), 17ull);
+ EXPECT_EQ(record.dtor_called, 0ull);
+ func = noisy2;
+ EXPECT_EQ(record.dtor_called, 1ull);
+ EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func(), 5ull);
+ }
+ {
+ Record record;
+ Record record2;
+ Noisy noisy{record, 17};
+ Noisy noisy2{record2, 5};
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(func(), 17ull);
+ EXPECT_EQ(record.dtor_called, 0ull);
+ func = std::move(noisy2);
+ EXPECT_EQ(record.dtor_called, 1ull);
+ EXPECT_EQ(record2, Record(1, 0, 0)); // move, copy, dtor
+ EXPECT_EQ(func(), 5ull);
+ }
+
+ {
+ Record record;
+ Record record2;
+ Noisy noisy{record, 17};
+ Noisy noisy2{record2, 13};
+ {
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(func(), 17ull);
+ InPlaceFunction<size_t()> func2{noisy2};
+ EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(record.dtor_called, 0ull);
+ func = func2;
+ EXPECT_EQ(record.dtor_called, 1ull);
+ EXPECT_EQ(func(), 13ull);
+ EXPECT_EQ(record2, Record(0, 2, 0)); // move, copy, dtor
+ EXPECT_TRUE(static_cast<bool>(func2));
+ EXPECT_EQ(func2(), 13ull);
+ }
+ EXPECT_EQ(record2, Record(0, 2, 2)); // move, copy, dtor
+ }
+
+ {
+ Record record;
+ Record record2;
+ Noisy noisy{record, 17};
+ Noisy noisy2{record2, 13};
+ {
+ InPlaceFunction<size_t()> func{noisy};
+ EXPECT_EQ(func(), 17ull);
+ InPlaceFunction<size_t()> func2{noisy2};
+ EXPECT_EQ(record.dtor_called, 0ull);
+ EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
+ func = std::move(func2);
+ EXPECT_EQ(record.dtor_called, 1ull);
+ EXPECT_EQ(func(), 13ull);
+ EXPECT_EQ(record2, Record(1, 1, 0)); // move, copy, dtor
+ // Moved from function is still valid
+ EXPECT_TRUE(static_cast<bool>(func2));
+ EXPECT_EQ(func2(), 13ull);
+ }
+ EXPECT_EQ(record2, Record(1, 1, 2)); // move, copy, dtor
+ }
+}
+
+TEST(InPlaceFunctionTests, Swap) {
+ Record record1;
+ Record record2;
+ InPlaceFunction<size_t()> func1 = Noisy{record1, 5};
+ InPlaceFunction<size_t()> func2 = Noisy{record2, 7};
+ EXPECT_EQ(record1, Record(1, 0, 1)); // move, copy, dtor
+ EXPECT_EQ(record2, Record(1, 0, 1)); // move, copy, dtor
+ EXPECT_EQ(func1(), 5ull);
+ EXPECT_EQ(func2(), 7ull);
+ func1.swap(func2);
+ EXPECT_EQ(record1, Record(2, 0, 2)); // move, copy, dtor
+ // An additional move and destroy into the temporary object
+ EXPECT_EQ(record2, Record(3, 0, 3)); // move, copy, dtor
+ EXPECT_EQ(func1(), 7ull);
+ EXPECT_EQ(func2(), 5ull);
+}
+
+TEST(InPlaceFunctionTests, Conversion) {
+ Record record;
+ Noisy noisy{record, 15};
+ {
+ InPlaceFunction<size_t(), 16> func2 = noisy;
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ {
+ InPlaceFunction<size_t(), 32> func{func2};
+ EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), func());
+ }
+ EXPECT_EQ(record, Record(0, 2, 1)); // move, copy, dtor
+ }
+ EXPECT_EQ(record, Record(0, 2, 2)); // move, copy, dtor
+}
+
+TEST(InPlaceFunctionTests, ConversionMove) {
+ Record record;
+ Noisy noisy{record, 15};
+ {
+ InPlaceFunction<size_t(), 16> func2 = noisy;
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ {
+ InPlaceFunction<size_t(), 32> func{std::move(func2)};
+ EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), func());
+ }
+ EXPECT_EQ(record, Record(1, 1, 1)); // move, copy, dtor
+ }
+ EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
+}
+
+TEST(InPlaceFunctionTests, ConversionAssign) {
+ Record record;
+ Noisy noisy{record, 15};
+ {
+ InPlaceFunction<size_t(), 32> func;
+ {
+ InPlaceFunction<size_t(), 16> func2 = noisy;
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ func = func2;
+ EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), func());
+ }
+ EXPECT_EQ(record, Record(0, 2, 1)); // move, copy, dtor
+ }
+ EXPECT_EQ(record, Record(0, 2, 2)); // move, copy, dtor
+}
+
+TEST(InPlaceFunctionTests, ConversionAssignMove) {
+ Record record;
+ Noisy noisy{record, 15};
+ {
+ InPlaceFunction<size_t(), 32> func;
+ {
+ InPlaceFunction<size_t(), 16> func2 = noisy;
+ EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
+ func = std::move(func2);
+ EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
+ EXPECT_EQ(func2(), func());
+ }
+ EXPECT_EQ(record, Record(1, 1, 1)); // move, copy, dtor
+ }
+ EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
+}
+
+struct NoMoveCopy {
+ NoMoveCopy() = default;
+ NoMoveCopy(const NoMoveCopy&) = delete;
+ NoMoveCopy(NoMoveCopy&&) = delete;
+};
+struct TestCallable {
+ NoMoveCopy& operator()(NoMoveCopy& x) { return x; }
+};
+
+TEST(InPlaceFunctionTests, ArgumentForwarding) {
+ const auto lambd = [](NoMoveCopy& x) -> NoMoveCopy& { return x; };
+ InPlaceFunction<NoMoveCopy&(NoMoveCopy&)> func = lambd;
+ const auto lambd2 = [](NoMoveCopy&& x) -> NoMoveCopy&& { return std::move(x); };
+ InPlaceFunction<NoMoveCopy && (NoMoveCopy &&)> func2 = lambd2;
+ auto lvalue = NoMoveCopy{};
+ func(lvalue);
+ func2(NoMoveCopy{});
+ InPlaceFunction<void(NoMoveCopy&)> func3 = [](const NoMoveCopy&) {};
+ func3(lvalue);
+ InPlaceFunction<void(NoMoveCopy &&)> func4 = [](const NoMoveCopy&) {};
+ func4(std::move(lvalue));
+ InPlaceFunction<void(const NoMoveCopy&)> func5 = [](const NoMoveCopy&) {};
+ func5(lvalue);
+ InPlaceFunction<void(const NoMoveCopy&&)> func6 = [](const NoMoveCopy&) {};
+ func6(std::move(lvalue));
+ InPlaceFunction<void(const NoMoveCopy&&)> func7 = [](const NoMoveCopy&&) {};
+ func7(std::move(lvalue));
+ InPlaceFunction<void(NoMoveCopy &&)> func8 = [](const NoMoveCopy&&) {};
+ func8(std::move(lvalue));
+
+ {
+ Record record;
+ Noisy noisy{record, 5};
+ const auto lambd3 = [](Noisy) {};
+ InPlaceFunction<void(Noisy)> func3{lambd3};
+ EXPECT_EQ(record, Record(0, 0, 0)); // move, copy, dtor
+ func3(std::move(noisy));
+ EXPECT_EQ(record, Record(2, 0, 2)); // move, copy, dtor
+ }
+
+ {
+ Record record;
+ Noisy noisy{record, 5};
+ const auto lambd3 = [](Noisy) {};
+ InPlaceFunction<void(Noisy)> func3{lambd3};
+ EXPECT_EQ(record, Record(0, 0, 0)); // move, copy, dtor
+ func3(noisy);
+ EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
+ }
+}
+
+TEST(InPlaceFunctionTests, VoidFunction) {
+ InPlaceFunction<void(size_t)> func = [](size_t x) -> size_t { return x; };
+ func(5);
+ InPlaceFunction<void(void)> func2 = []() -> size_t { return 5; };
+ func2();
+}
+NoMoveCopy foo() {
+ return NoMoveCopy();
+}
+struct Test {
+ NoMoveCopy operator()() { return NoMoveCopy{}; }
+};
+
+TEST(InPlaceFunctionTests, FullElision) {
+ InPlaceFunction<NoMoveCopy()> func = foo;
+}
+
+TEST(InPlaceFunctionTests, ReturnConversion) {
+ const auto lambd = [](int&& x) -> int&& { return std::move(x); };
+ InPlaceFunction<int && (int&& x)> func = lambd;
+ func(5);
+ InPlaceFunction<void(int)> func3 = [](double) {};
+ func3(5);
+ InPlaceFunction<double()> func4 = []() -> int { return 5; };
+ func4();
+}
+
+struct Overloaded {
+ int operator()() & { return 2; }
+ int operator()() const& { return 3; }
+ int operator()() && { return 4; }
+ int operator()() const&& { return 5; }
+};
+
+TEST(InPlaceFunctionTests, OverloadResolution) {
+ InPlaceFunction<int()> func = Overloaded{};
+ EXPECT_EQ(func(), 2);
+ EXPECT_EQ(std::move(func()), 2);
+}
+
+template <class T, class U, class = void>
+struct can_assign : std::false_type {};
+
+template <class T, class U>
+struct can_assign<T, U, typename std::void_t<decltype(T().operator=(U()))>> : std::true_type {};
+
+template <class From, class To, bool Expected>
+static constexpr bool Convertible =
+ (can_assign<To, From>::value ==
+ std::is_constructible_v<To, From>)&&(std::is_constructible_v<To, From> == Expected);
+
+struct TooBig {
+ std::array<uint64_t, 5> big = {1, 2, 3, 4, 5};
+ size_t operator()() { return static_cast<size_t>(big[0] + big[1] + big[2] + big[3] + big[4]); }
+};
+static_assert(sizeof(TooBig) == 40);
+struct NotCallable {};
+struct WrongArg {
+ void operator()(NotCallable) {}
+};
+struct WrongRet {
+ NotCallable operator()(size_t) { return NotCallable{}; }
+};
+
+static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 32>, true>);
+static_assert(
+ Convertible<InPlaceFunction<size_t(size_t), 32>, InPlaceFunction<size_t(), 32>, false>);
+static_assert(Convertible<InPlaceFunction<void(), 32>, InPlaceFunction<size_t(), 32>, false>);
+static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 32>, false>);
+static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 40>, true>);
+static_assert(Convertible<NotCallable, InPlaceFunction<size_t(), 40>, false>);
+static_assert(Convertible<WrongArg, InPlaceFunction<void(size_t), 40>, false>);
+static_assert(Convertible<WrongRet, InPlaceFunction<size_t(size_t), 40>, false>);
+// Void returning functions are modelled by any return type
+static_assert(Convertible<WrongRet, InPlaceFunction<void(size_t), 40>, true>);
+
+// Check constructibility/assignability from smaller function types
+static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 24>, false>);
+static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 40>, true>);
+static_assert(
+ Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(size_t), 40>, false>);
+static_assert(
+ Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<NotCallable(), 40>, false>);
+
+struct BadLambd {
+ int operator()(int&& x) { return std::move(x); }
+};
+
+static_assert(Convertible<BadLambd, InPlaceFunction<int(int&&), 32>, true>);
+static_assert(Convertible<BadLambd, InPlaceFunction<int&(int&&), 32>, false>);
+static_assert(Convertible<BadLambd, InPlaceFunction<const int&(int&&), 32>, false>);
+static_assert(Convertible<BadLambd, InPlaceFunction<int && (int&&), 32>, false>);
+static_assert(Convertible<BadLambd, InPlaceFunction<const int && (int&&), 32>, false>);
+
+struct Base {};
+struct Derived : Base {};
+struct Converted {
+ Converted(const Derived&) {}
+};
+
+struct ConvertCallable {
+ Derived operator()() { return Derived{}; }
+ Derived& operator()(Derived& x) { return x; }
+ Derived&& operator()(Derived&& x) { return std::move(x); }
+ const Derived& operator()(const Derived& x) { return x; }
+ const Derived&& operator()(const Derived&& x) { return std::move(x); }
+};
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&()>, false>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&()>, false>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived()>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Base()>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted()>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted&()>, false>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted && ()>, false>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted&()>, false>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted && ()>, false>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&(Derived&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&(Derived&)>, true>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived && (Derived &&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<Base && (Derived &&)>, true>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(const Derived&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(const Derived&)>, true>);
+
+static_assert(
+ Convertible<ConvertCallable, InPlaceFunction<const Derived && (const Derived&&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (const Derived&&)>, true>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&)>, true>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived && (Derived &&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (Derived &&)>, true>);
+
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&&)>, true>);
+static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&&)>, true>);
diff --git a/media/utils/tests/media_process_tests.cpp b/media/utils/tests/media_process_tests.cpp
index 2ae3f70..6e738b1 100644
--- a/media/utils/tests/media_process_tests.cpp
+++ b/media/utils/tests/media_process_tests.cpp
@@ -24,6 +24,14 @@
using namespace android;
using namespace android::mediautils;
+// Disables false-positives from base::Split()
+//
+// See mismatched sanitized libraries here:
+// https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
+extern "C" const char* __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+
TEST(media_process_tests, basic) {
const std::string schedString = getThreadSchedAsString(gettid());
diff --git a/media/utils/tests/media_threadsnapshot_tests.cpp b/media/utils/tests/media_threadsnapshot_tests.cpp
index c7a45e2..fc9aeab 100644
--- a/media/utils/tests/media_threadsnapshot_tests.cpp
+++ b/media/utils/tests/media_threadsnapshot_tests.cpp
@@ -27,6 +27,14 @@
using namespace android;
using namespace android::mediautils;
+// Disables false-positives from base::Split()
+//
+// See mismatched sanitized libraries here:
+// https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
+extern "C" const char* __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+
TEST(media_threadsnapshot_tests, basic) {
using namespace std::chrono_literals;
diff --git a/media/utils/tests/mediautils_fixedstring_tests.cpp b/media/utils/tests/mediautils_fixedstring_tests.cpp
new file mode 100644
index 0000000..7ab9a9f
--- /dev/null
+++ b/media/utils/tests/mediautils_fixedstring_tests.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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 "mediautils_fixedstring_tests"
+
+#include <mediautils/FixedString.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::mediautils;
+
+TEST(mediautils_fixedstring_tests, ctor) {
+ FixedString<8> s0("abcde");
+
+ ASSERT_FALSE(s0.empty());
+ ASSERT_EQ(8U, s0.capacity());
+
+ ASSERT_EQ(5U, s0.size());
+ ASSERT_EQ(3U, s0.remaining());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcde"));
+
+ ASSERT_EQ(0, strcmp(s0.data(), "abcde"));
+
+ // overflow
+ FixedString<8> s1("abcdefghijk");
+ ASSERT_EQ(8U, s1.size());
+ ASSERT_TRUE(s1.full());
+ ASSERT_EQ(0U, s1.remaining());
+ ASSERT_EQ(0, strcmp(s1.c_str(), "abcdefgh"));
+
+ // overflow
+ FixedString<8> s2(std::string("abcdefghijk"));
+ ASSERT_TRUE(s2.full());
+
+ ASSERT_EQ(8U, s2.size());
+ ASSERT_EQ(0, strcmp(s2.c_str(), "abcdefgh"));
+
+ // complex
+ ASSERT_EQ(s1, s2);
+ ASSERT_EQ(FixedString<12>().append(s1), s2);
+ ASSERT_NE(s1, "bcd");
+
+ // string and stringview
+ ASSERT_EQ(s1.asString(), s1.asStringView());
+
+ FixedString30 s3;
+ s3 = std::string("abcd");
+ ASSERT_EQ(s3, "abcd");
+
+ s3.clear();
+ ASSERT_EQ(s3, "");
+ ASSERT_NE(s3, "abcd");
+ ASSERT_EQ(0U, s3.size());
+}
+
+TEST(mediautils_fixedstring_tests, append) {
+ FixedString<8> s0;
+ ASSERT_EQ(0U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+ ASSERT_TRUE(s0.empty());
+ ASSERT_FALSE(s0.full());
+
+ s0.append("abc");
+ ASSERT_EQ(3U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abc"));
+
+ s0.append(std::string("d"));
+ ASSERT_EQ(4U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcd"));
+
+ // overflow
+ s0.append("efghijk");
+ ASSERT_EQ(8U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcdefgh"));
+ ASSERT_TRUE(s0.full());
+
+ // concatenated
+ ASSERT_EQ(FixedString62("abcd"),
+ FixedString<8>("ab").append("c").append(FixedString<8>("d")));
+ ASSERT_EQ(FixedString<12>("a").append(FixedString<12>("b")), "ab");
+}
+
+TEST(mediautils_fixedstring_tests, plus_equals) {
+ FixedString<8> s0;
+ ASSERT_EQ(0U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+
+ s0 += "abc";
+ s0 += "def";
+ ASSERT_EQ(s0, "abcdef");
+}
+
+TEST(mediautils_fixedstring_tests, stream_operator) {
+ FixedString<8> s0('a');
+
+ s0 << 'b' << "c" << "d" << '\n';
+ ASSERT_EQ(s0, "abcd\n");
+}
+
+TEST(mediautils_fixedstring_tests, examples) {
+ FixedString30 s1(std::string("a"));
+ s1 << "bc" << 'd' << '\n';
+ s1 += "hello";
+
+ ASSERT_EQ(s1, "abcd\nhello");
+
+ FixedString30 s2;
+ for (const auto &c : s1.asStringView()) {
+ s2.append(c);
+ };
+ ASSERT_EQ(s1, s2);
+
+ FixedString30 s3(std::move(s1));
+}
+
+// Ensure type alias works fine as well.
+using FixedString1024 = FixedString<1024>;
+
+TEST(mediautils_fixedstring_tests, copy) {
+ FixedString1024 s0("abc");
+ FixedString62 s1(s0);
+
+ ASSERT_EQ(3U, s1.size());
+ ASSERT_EQ(0, strcmp(s1.c_str(), "abc"));
+ ASSERT_EQ(s0, s1);
+
+ FixedString<1024> s2(s1);
+ ASSERT_EQ(3U, s2.size());
+ ASSERT_EQ(0, strcmp(s2.c_str(), "abc"));
+ ASSERT_EQ(s2, "abc");
+ ASSERT_NE(s2, "def");
+ ASSERT_EQ(s2, std::string("abc"));
+ ASSERT_NE(s2, std::string("def"));
+ ASSERT_EQ(s1, s2);
+ ASSERT_EQ(s0, s2);
+ ASSERT_EQ(s2, FixedString62(FixedString1024("abc")));
+}
diff --git a/media/utils/memory-test.cpp b/media/utils/tests/memory-test.cpp
similarity index 100%
rename from media/utils/memory-test.cpp
rename to media/utils/tests/memory-test.cpp
diff --git a/media/utils/tests/static_string_view_tests.cpp b/media/utils/tests/static_string_view_tests.cpp
new file mode 100644
index 0000000..c00de68
--- /dev/null
+++ b/media/utils/tests/static_string_view_tests.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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 "StaticStringViewTests"
+
+#include <mediautils/StaticStringView.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android::mediautils;
+
+template <auto& T, class = void>
+struct CanCreate : std::false_type {};
+
+template <auto& T>
+struct CanCreate<T, typename std::void_t<decltype(StaticStringView::create<T>)>> : std::true_type {
+};
+
+static constexpr std::array<char, 2> global = {'a', 'b'};
+
+TEST(StaticStringViewTests, CreateTicket) {
+ // This will always fail due to template param binding rules
+ // const std::array<char,2> nonstatic = {'a', 'b'};
+ // static_assert(can_assign<nonstatic>::value == false);
+ static std::array<char, 2> nonconst = {'a', 'b'};
+ static const std::array<char, 2> nonconstexpr = {'a', 'b'};
+ static constexpr std::array<int, 2> nonchar = {1, 2};
+ static constexpr size_t nonarray = 2;
+
+ static_assert(CanCreate<nonconst>::value == false);
+ static_assert(CanCreate<nonarray>::value == false);
+ static_assert(CanCreate<nonchar>::value == false);
+ static_assert(CanCreate<nonconstexpr>::value == false);
+
+ static constexpr std::array<char, 2> scoped = {'a', 'b'};
+ constexpr StaticStringView Ticket1 = StaticStringView::create<global>();
+ constexpr StaticStringView Ticket2 = StaticStringView::create<scoped>();
+ const StaticStringView Ticket3 = StaticStringView::create<scoped>();
+ EXPECT_EQ(Ticket3, Ticket2);
+ EXPECT_EQ(Ticket1.getStringView(), Ticket2.getStringView());
+ EXPECT_EQ(std::string_view{"ab"}, Ticket1.getStringView());
+}
+TEST(StaticStringViewTests, CompileTimeConvert) {
+ static constexpr std::array<char, 4> converted = StaticStringView::toStdArray("test");
+ constexpr StaticStringView ticket = StaticStringView::create<converted>();
+ EXPECT_EQ(ticket, std::string_view{"test"});
+ // Unchecked constexpr construction
+ static const std::array<char, 5> converted2 = StaticStringView::toStdArray("test2");
+ constexpr auto ticket2 = StaticStringView::create<converted2, false>();
+ EXPECT_EQ(ticket2, std::string_view{"test2"});
+ constexpr char stack_array[4] = {'a', 'b', 'c', '\0'};
+ static constexpr auto converted3 = StaticStringView::toStdArray(stack_array);
+ constexpr auto ticket3 = StaticStringView::create<converted3>();
+ EXPECT_EQ(ticket3, std::string_view{"abc"});
+}
+
+TEST(StaticStringViewTests, CompileTimeConcat) {
+ // temporaries should not be static to prevent odr use
+ constexpr std::array<char, 3> arr1 = {'a', 'b', 'c'};
+ constexpr std::array<char, 4> arr2 = {'d', 'e', 'f', 'g'};
+ static constexpr std::array<char, 7> res = StaticStringView::concatArray(arr1, arr2);
+ static constexpr std::array<char, 7> expected = {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
+ EXPECT_EQ(res, expected);
+}
+
+TEST(StaticStringViewTests, StringViewForwarding) {
+ static constexpr auto converted = StaticStringView::toStdArray("test");
+ constexpr auto ticket = StaticStringView::create<converted>();
+ EXPECT_EQ(ticket.length(), ticket.getStringView().length());
+ EXPECT_TRUE(ticket == ticket.getStringView());
+ EXPECT_TRUE(ticket == ticket);
+ EXPECT_TRUE(ticket.getStringView() == ticket);
+ EXPECT_TRUE(ticket > "abc");
+ EXPECT_TRUE("abc" < ticket);
+}
diff --git a/services/OWNERS b/services/OWNERS
deleted file mode 100644
index 17e605d..0000000
--- a/services/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-elaurent@google.com
-essick@google.com
-etalvala@google.com
-hunga@google.com
-nchalko@google.com
-quxiangfang@google.com
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 763c070..87c67bd 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -22,6 +22,10 @@
cc_library_shared {
name: "libaudioflinger",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
srcs: [
"AudioFlinger.cpp",
"AudioHwDevice.cpp",
@@ -55,7 +59,6 @@
],
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioflinger-aidl-cpp",
"audioclient-types-aidl-cpp",
"av-types-aidl-cpp",
@@ -71,6 +74,7 @@
"liblog",
"libbinder",
"libaudioclient",
+ "libaudiomanager",
"libmedialogservice",
"libmediametrics",
"libmediautils",
@@ -78,7 +82,6 @@
"libnblog",
"libpermission",
"libpowermanager",
- "libmediautils",
"libmemunreachable",
"libmedia_helper",
"libshmemcompat",
@@ -114,3 +117,8 @@
},
}
+
+cc_library_headers {
+ name: "libaudioflinger_headers",
+ export_include_dirs: ["."],
+}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f7576f6..7f0fc1f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -54,7 +54,7 @@
#include <cutils/properties.h>
#include <system/audio.h>
-#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
#include "AudioFlinger.h"
#include "NBAIO_Tee.h"
@@ -114,11 +114,12 @@
using media::audio::common::AudioMMapPolicyType;
using android::content::AttributionSourceState;
-static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
-static const char kHardwareLockedString[] = "Hardware lock is taken\n";
-static const char kClientLockedString[] = "Client lock is taken\n";
-static const char kNoEffectsFactory[] = "Effects Factory is absent\n";
+static constexpr char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
+static constexpr char kHardwareLockedString[] = "Hardware lock is taken\n";
+static constexpr char kClientLockedString[] = "Client lock is taken\n";
+static constexpr char kNoEffectsFactory[] = "Effects Factory is absent\n";
+static constexpr char kAudioServiceName[] = "audio";
nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
@@ -226,6 +227,9 @@
BINDER_METHOD_ENTRY(getAAudioMixerBurstCount) \
BINDER_METHOD_ENTRY(getAAudioHardwareBurstMinUsec) \
BINDER_METHOD_ENTRY(setDeviceConnectedState) \
+BINDER_METHOD_ENTRY(setRequestedLatencyMode) \
+BINDER_METHOD_ENTRY(getSupportedLatencyModes) \
+
// singleton for Binder Method Statistics for IAudioFlinger
static auto& getIAudioFlingerStatistics() {
@@ -593,6 +597,11 @@
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
deviceId, &portId, &secondaryOutputs, &isSpatialized);
+ if (ret != NO_ERROR) {
+ config->sample_rate = fullConfig.sample_rate;
+ config->channel_mask = fullConfig.channel_mask;
+ config->format = fullConfig.format;
+ }
ALOGW_IF(!secondaryOutputs.empty(),
"%s does not support secondary outputs, ignoring them", __func__);
} else {
@@ -654,6 +663,7 @@
void AudioFlinger::onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration) {
sp<os::IExternalVibratorService> evs = getExternalVibratorService();
if (evs != 0) {
+ ALOGD("%s, stopping external vibration", __func__);
evs->onExternalVibrationStop(*externalVibration);
}
}
@@ -765,6 +775,11 @@
(uint32_t)(mStandbyTimeInNsecs / 1000000));
result.append(buffer);
write(fd, result.string(), result.size());
+
+ dprintf(fd, "Vibrator infos(size=%zu):\n", mAudioVibratorInfos.size());
+ for (const auto& vibratorInfo : mAudioVibratorInfos) {
+ dprintf(fd, " - %s\n", vibratorInfo.toString().c_str());
+ }
}
void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args __unused)
@@ -1583,6 +1598,32 @@
return NO_ERROR;
}
+status_t AudioFlinger::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == nullptr) {
+ return BAD_VALUE;
+ }
+ return thread->setRequestedLatencyMode(mode);
+}
+
+status_t AudioFlinger::getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) {
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == nullptr) {
+ return BAD_VALUE;
+ }
+ return thread->getSupportedLatencyModes(modes);
+}
+
status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted)
{
// check calling permissions
@@ -2082,6 +2123,21 @@
}
}
+void AudioFlinger::onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) {
+ int32_t outputAidl = VALUE_OR_FATAL(legacy2aidl_audio_io_handle_t_int32_t(output));
+ std::vector<media::LatencyMode> modesAidl = VALUE_OR_FATAL(
+ convertContainer<std::vector<media::LatencyMode>>(modes,
+ legacy2aidl_audio_latency_mode_t_LatencyMode));
+
+ Mutex::Autolock _l(mClientLock);
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ mNotificationClients.valueAt(i)->audioFlingerClient()
+ ->onSupportedLatencyModesChanged(outputAidl, modesAidl);
+ }
+}
+
// removeClient_l() must be called with AudioFlinger::mClientLock held
void AudioFlinger::removeClient_l(pid_t pid)
{
@@ -2672,9 +2728,28 @@
ThreadBase *thread = (ThreadBase *)mMmapThreads.valueAt(i).get();
thread->systemReady();
}
+
+ // Java services are ready, so we can create a reference to AudioService
+ getOrCreateAudioManager();
+
return NO_ERROR;
}
+sp<IAudioManager> AudioFlinger::getOrCreateAudioManager()
+{
+ if (mAudioManager.load() == nullptr) {
+ // use checkService() to avoid blocking
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16(kAudioServiceName));
+ if (binder != nullptr) {
+ mAudioManager = interface_cast<IAudioManager>(binder);
+ } else {
+ ALOGE("%s(): binding to audio service failed.", __func__);
+ }
+ }
+ return mAudioManager.load();
+}
+
status_t AudioFlinger::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
{
AutoMutex lock(mHardwareLock);
@@ -4476,6 +4551,8 @@
case TransactionCode::SET_RECORD_SILENCED:
case TransactionCode::AUDIO_POLICY_READY:
case TransactionCode::SET_DEVICE_CONNECTED_STATE:
+ case TransactionCode::SET_REQUESTED_LATENCY_MODE:
+ case TransactionCode::GET_SUPPORTED_LATENCY_MODES:
ALOGW("%s: transaction %d received from PID %d",
__func__, code, IPCThreadState::self()->getCallingPid());
// return status only for non void methods
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8e4383c..08594e2 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -117,6 +117,7 @@
class DevicesFactoryHalInterface;
class EffectsFactoryHalInterface;
class FastMixer;
+class IAudioManager;
class PassthruBufferProvider;
class RecordBufferConverter;
class ServerProxy;
@@ -293,6 +294,12 @@
virtual status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected);
+ virtual status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode);
+
+ virtual status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes);
+
status_t onTransactWrapper(TransactionCode code, const Parcel& data, uint32_t flags,
const std::function<status_t()>& delegate) override;
@@ -764,6 +771,8 @@
void ioConfigChanged(audio_io_config_event_t event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
+ void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes);
// Allocate an audio_unique_id_t.
// Specific types are audio_io_handle_t, audio_session_t, effect ID (int),
@@ -980,6 +989,8 @@
size_t rejectedKVPSize, const String8& rejectedKVPs,
uid_t callingUid);
+ sp<IAudioManager> getOrCreateAudioManager();
+
public:
// These methods read variables atomically without mLock,
// though the variables are updated with mLock.
@@ -1021,6 +1032,9 @@
std::vector<media::audio::common::AudioMMapPolicyInfo>> mPolicyInfos;
int32_t mAAudioBurstsPerBuffer = 0;
int32_t mAAudioHwBurstMinMicros = 0;
+
+ /** Interface for interacting with the AudioService. */
+ mediautils::atomic_sp<IAudioManager> mAudioManager;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 53ac5cb..4f3ed0a 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -106,8 +106,13 @@
if (lStatus == NO_ERROR) {
lStatus = effect->addHandle(handle.get());
if (lStatus == NO_ERROR) {
- effect->init(patches);
- mDeviceEffects.emplace(device, effect);
+ lStatus = effect->init(patches);
+ if (lStatus == NAME_NOT_FOUND) {
+ lStatus = NO_ERROR;
+ }
+ if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
+ mDeviceEffects.emplace(device, effect);
+ }
}
}
}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index e6d7cf7..193e270 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -569,7 +569,8 @@
mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
mDisableWaitCnt(0), // set by process() and updateState()
mOffloaded(false),
- mAddedToHal(false)
+ mAddedToHal(false),
+ mIsOutput(false)
#ifdef FLOAT_EFFECT_CHAIN
, mSupportsFloat(false)
#endif
@@ -962,6 +963,7 @@
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.inputCfg.buffer.frameCount = callback->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
+ mIsOutput = callback->isOutput();
ALOGV("configure() %p chain %p buffer %p framecount %zu",
this, callback->chain().promote().get(),
@@ -980,7 +982,7 @@
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
- callback->isOutput() &&
+ mIsOutput &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
@@ -1056,6 +1058,13 @@
&size,
&cmdStatus);
}
+
+ if (isVolumeControl()) {
+ // Force initializing the volume as 0 for volume control effect for safer ramping
+ uint32_t left = 0;
+ uint32_t right = 0;
+ setVolumeInternal(&left, &right, true /*controller*/);
+ }
}
// mConfig.outputCfg.buffer.frameCount cannot be zero.
@@ -1429,23 +1438,24 @@
((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_MONITOR)) {
- uint32_t volume[2];
- uint32_t *pVolume = NULL;
- uint32_t size = sizeof(volume);
- volume[0] = *left;
- volume[1] = *right;
- if (controller) {
- pVolume = volume;
- }
- status = mEffectInterface->command(EFFECT_CMD_SET_VOLUME,
- size,
- volume,
- &size,
- pVolume);
- if (controller && status == NO_ERROR && size == sizeof(volume)) {
- *left = volume[0];
- *right = volume[1];
- }
+ status = setVolumeInternal(left, right, controller);
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setVolumeInternal(
+ uint32_t *left, uint32_t *right, bool controller) {
+ uint32_t volume[2] = {*left, *right};
+ uint32_t *pVolume = controller ? volume : nullptr;
+ uint32_t size = sizeof(volume);
+ status_t status = mEffectInterface->command(EFFECT_CMD_SET_VOLUME,
+ size,
+ volume,
+ &size,
+ pVolume);
+ if (controller && status == NO_ERROR && size == sizeof(volume)) {
+ *left = volume[0];
+ *right = volume[1];
}
return status;
}
@@ -1643,6 +1653,22 @@
return status;
}
+status_t AudioFlinger::EffectModule::getConfigs(
+ audio_config_base_t* inputCfg, audio_config_base_t* outputCfg, bool* isOutput) const {
+ Mutex::Autolock _l(mLock);
+ if (mConfig.inputCfg.mask == 0 || mConfig.outputCfg.mask == 0) {
+ return NO_INIT;
+ }
+ inputCfg->sample_rate = mConfig.inputCfg.samplingRate;
+ inputCfg->channel_mask = static_cast<audio_channel_mask_t>(mConfig.inputCfg.channels);
+ inputCfg->format = static_cast<audio_format_t>(mConfig.inputCfg.format);
+ outputCfg->sample_rate = mConfig.outputCfg.samplingRate;
+ outputCfg->channel_mask = static_cast<audio_channel_mask_t>(mConfig.outputCfg.channels);
+ outputCfg->format = static_cast<audio_format_t>(mConfig.outputCfg.format);
+ *isOutput = mIsOutput;
+ return NO_ERROR;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -1760,6 +1786,7 @@
BINDER_METHOD_ENTRY(command) \
BINDER_METHOD_ENTRY(disconnect) \
BINDER_METHOD_ENTRY(getCblk) \
+BINDER_METHOD_ENTRY(getConfig) \
// singleton for Binder Method Statistics for IEffect
mediautils::MethodStatistics<int>& getIEffectStatistics() {
@@ -1803,6 +1830,13 @@
*_aidl_return = (code); \
return Status::ok();
+#define VALUE_OR_RETURN_STATUS_AS_OUT(exp) \
+ ({ \
+ auto _tmp = (exp); \
+ if (!_tmp.ok()) { RETURN(_tmp.error()); } \
+ std::move(_tmp.value()); \
+ })
+
Status AudioFlinger::EffectHandle::enable(int32_t* _aidl_return)
{
AutoMutex _l(mLock);
@@ -1914,6 +1948,32 @@
return Status::ok();
}
+Status AudioFlinger::EffectHandle::getConfig(
+ media::EffectConfig* _config, int32_t* _aidl_return) {
+ AutoMutex _l(mLock);
+ sp<EffectBase> effect = mEffect.promote();
+ if (effect == nullptr || mDisconnected) {
+ RETURN(DEAD_OBJECT);
+ }
+ sp<EffectModule> effectModule = effect->asEffectModule();
+ if (effectModule == nullptr) {
+ RETURN(INVALID_OPERATION);
+ }
+ audio_config_base_t inputCfg = AUDIO_CONFIG_BASE_INITIALIZER;
+ audio_config_base_t outputCfg = AUDIO_CONFIG_BASE_INITIALIZER;
+ bool isOutput;
+ status_t status = effectModule->getConfigs(&inputCfg, &outputCfg, &isOutput);
+ if (status == NO_ERROR) {
+ constexpr bool isInput = false; // effects always use 'OUT' channel masks.
+ _config->inputCfg = VALUE_OR_RETURN_STATUS_AS_OUT(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(inputCfg, isInput));
+ _config->outputCfg = VALUE_OR_RETURN_STATUS_AS_OUT(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(outputCfg, isInput));
+ _config->isOnInputStream = !isOutput;
+ }
+ RETURN(status);
+}
+
Status AudioFlinger::EffectHandle::command(int32_t cmdCode,
const std::vector<uint8_t>& cmdData,
int32_t maxResponseSize,
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 42614cc..78788df 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -283,6 +283,10 @@
status_t setHapticIntensity(int id, int intensity);
status_t setVibratorInfo(const media::AudioVibratorInfo& vibratorInfo);
+ status_t getConfigs(audio_config_base_t* inputCfg,
+ audio_config_base_t* outputCfg,
+ bool* isOutput) const;
+
void dump(int fd, const Vector<String16>& args);
private:
@@ -302,6 +306,8 @@
? EFFECT_BUFFER_ACCESS_WRITE : EFFECT_BUFFER_ACCESS_ACCUMULATE;
}
+ status_t setVolumeInternal(uint32_t *left, uint32_t *right, bool controller);
+
effect_config_t mConfig; // input and output audio configuration
sp<EffectHalInterface> mEffectInterface; // Effect module HAL
@@ -314,6 +320,7 @@
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mOffloaded; // effect is currently offloaded to the audio DSP
bool mAddedToHal; // effect has been added to the audio HAL
+ bool mIsOutput; // direction of the AF thread
#ifdef FLOAT_EFFECT_CHAIN
bool mSupportsFloat; // effect supports float processing
@@ -370,6 +377,8 @@
int32_t* _aidl_return) override;
android::binder::Status disconnect() override;
android::binder::Status getCblk(media::SharedFileRegion* _aidl_return) override;
+ android::binder::Status getConfig(media::EffectConfig* _config,
+ int32_t* _aidl_return) override;
sp<Client> client() const { return mClient; }
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index eb640bb..cb46c52 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -54,6 +54,14 @@
bool getAndSetSilencedNotified_l() { bool silencedNotified = mSilencedNotified;
mSilencedNotified = true;
return silencedNotified; }
+
+ /**
+ * Updates the mute state and notifies the audio service. Call this only when holding player
+ * thread lock.
+ */
+ void processMuteEvent_l(const sp<IAudioManager>& audioManager,
+ mute_state_t muteState)
+ REQUIRES(AudioFlinger::MmapPlaybackThread::mLock);
private:
friend class MmapThread;
@@ -71,5 +79,12 @@
pid_t mPid;
bool mSilenced; // protected by MMapThread::mLock
bool mSilencedNotified; // protected by MMapThread::mLock
+
+ // TODO: replace PersistableBundle with own struct
+ // access these two variables only when holding player thread lock.
+ std::unique_ptr<os::PersistableBundle> mMuteEventExtras
+ GUARDED_BY(AudioFlinger::MmapPlaybackThread::mLock);
+ mute_state_t mMuteState
+ GUARDED_BY(AudioFlinger::MmapPlaybackThread::mLock);
}; // end of Track
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 93593a3..68a3800 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -31,7 +31,6 @@
mPlaybackThreadHandle(playbackThreadHandle),
mRecordThreadHandle(recordThreadHandle) {}
SoftwarePatch(const SoftwarePatch&) = default;
- SoftwarePatch& operator=(const SoftwarePatch&) = default;
// Must be called under AudioFlinger::mLock
status_t getLatencyMs_l(double *latencyMs) const;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 6a138bb..e52d1ee 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -204,6 +204,12 @@
float getSpeed() const { return mSpeed; }
bool isSpatialized() const override { return mIsSpatialized; }
+ /**
+ * Updates the mute state and notifies the audio service. Call this only when holding player
+ * thread lock.
+ */
+ void processMuteEvent_l(const sp<IAudioManager>& audioManager, mute_state_t muteState);
+
protected:
// for numerous
friend class PlaybackThread;
@@ -354,6 +360,11 @@
TeePatches mTeePatches;
const float mSpeed;
const bool mIsSpatialized;
+
+ // TODO: replace PersistableBundle with own struct
+ // access these two variables only when holding player thread lock.
+ std::unique_ptr<os::PersistableBundle> mMuteEventExtras;
+ mute_state_t mMuteState;
}; // end of Track
diff --git a/services/audioflinger/TEST_MAPPING b/services/audioflinger/TEST_MAPPING
index 3de5a9f..9aff137 100644
--- a/services/audioflinger/TEST_MAPPING
+++ b/services/audioflinger/TEST_MAPPING
@@ -1,10 +1,19 @@
{
- "presubmit": [
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 07e82a8..71c24f3 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -31,6 +31,7 @@
#include <sys/syscall.h>
#include <cutils/bitops.h>
#include <cutils/properties.h>
+#include <binder/PersistableBundle.h>
#include <media/AudioContainers.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioParameter.h>
@@ -178,6 +179,11 @@
// Direct output thread minimum sleep time in idle or active(underrun) state
static const nsecs_t kDirectMinSleepTimeUs = 10000;
+// Minimum amount of time between checking to see if the timestamp is advancing
+// for underrun detection. If we check too frequently, we may not detect a
+// timestamp update and will falsely detect underrun.
+static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000;
+
// The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good
// balance between power consumption and latency, and allows threads to be scheduled reliably
// by the CFS scheduler.
@@ -741,6 +747,12 @@
sendConfigEvent_l(configEvent);
}
+void AudioFlinger::ThreadBase::sendHalLatencyModesChangedEvent_l()
+{
+ sp<ConfigEvent> configEvent = sp<HalLatencyModesChangedEvent>::make();
+ sendConfigEvent_l(configEvent);
+}
+
// post condition: mConfigEvents.isEmpty()
void AudioFlinger::ThreadBase::processConfigEvents_l()
{
@@ -808,6 +820,10 @@
setCheckOutputStageEffects();
} break;
+ case CFG_EVENT_HAL_LATENCY_MODES_CHANGED: {
+ onHalLatencyModesChanged_l();
+ } break;
+
default:
ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
break;
@@ -2030,7 +2046,8 @@
mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1),
mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
mLeftVolFloat(-1.0), mRightVolFloat(-1.0),
- mDownStreamPatch{}
+ mDownStreamPatch{},
+ mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs)
{
snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -3136,10 +3153,7 @@
auto backInserter = std::back_inserter(metadata.tracks);
for (const sp<Track> &track : mActiveTracks) {
// No track is invalid as this is called after prepareTrack_l in the same critical section
- // Do not forward metadata for PatchTrack with unspecified stream type
- if (track->streamType() != AUDIO_STREAM_PATCH) {
- track->copyMetadataTo(backInserter);
- }
+ track->copyMetadataTo(backInserter);
}
sendMetadataToBackend_l(metadata);
}
@@ -3922,6 +3936,8 @@
// stop(), pause(), etc.), but the threadLoop is entitled to call audio
// data / buffer methods on tracks from activeTracks without the ThreadBase lock.
activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end());
+
+ setHalLatencyMode_l();
} // mLock scope ends
if (mBytesRemaining == 0) {
@@ -3967,19 +3983,24 @@
void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
- // mono blend occurs for mixer threads only (not direct or offloaded)
- // and is handled here if we're going directly to the sink.
- if (requireMonoBlend() && !mEffectBufferValid) {
- mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount, mNormalFrameCount,
- true /*limit*/);
- }
+ // Apply mono blending and balancing if the effect buffer is not valid. Otherwise,
+ // do these processes after effects are applied.
+ if (!mEffectBufferValid) {
+ // mono blend occurs for mixer threads only (not direct or offloaded)
+ // and is handled here if we're going directly to the sink.
+ if (requireMonoBlend()) {
+ mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount,
+ mNormalFrameCount, true /*limit*/);
+ }
- if (!hasFastMixer()) {
- // Balance must take effect after mono conversion.
- // We do it here if there is no FastMixer.
- // mBalance detects zero balance within the class for speed (not needed here).
- mBalance.setBalance(mMasterBalance.load());
- mBalance.process((float *)mMixerBuffer, mNormalFrameCount);
+ if (!hasFastMixer()) {
+ // Balance must take effect after mono conversion.
+ // We do it here if there is no FastMixer.
+ // mBalance detects zero balance within the class for speed
+ // (not needed here).
+ mBalance.setBalance(mMasterBalance.load());
+ mBalance.process((float *)mMixerBuffer, mNormalFrameCount);
+ }
}
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
@@ -4616,6 +4637,9 @@
if (configChanged) {
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -4646,6 +4670,9 @@
} else {
status = mOutput->stream->legacyReleaseAudioPatch();
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -5016,6 +5043,7 @@
mCallbackThread->setDraining(mDrainSequence);
}
mHwPaused = false;
+ setHalLatencyMode_l();
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
@@ -5357,8 +5385,19 @@
volume *= vh;
track->mCachedVolume = volume;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
- float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
- float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr));
+ float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+ float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+
+ vlf *= volume;
+ vrf *= volume;
track->setFinalVolume((vlf + vrf) / 2.f);
++fastTracks;
@@ -5531,6 +5570,15 @@
ALOGV("Track right volume out of range: %.3g", vrf);
vrf = GAIN_FLOAT_UNITY;
}
+
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+
// now apply the master volume and stream type volume and shaper volume
vlf *= v * vh;
vrf *= v * vh;
@@ -5889,18 +5937,35 @@
return trackCount;
}
-bool AudioFlinger::PlaybackThread::checkRunningTimestamp()
+bool AudioFlinger::PlaybackThread::IsTimestampAdvancing::check(AudioStreamOut * output)
{
+ // Check the timestamp to see if it's advancing once every 150ms. If we check too frequently, we
+ // could falsely detect that the frame position has stalled due to underrun because we haven't
+ // given the Audio HAL enough time to update.
+ const nsecs_t nowNs = systemTime();
+ if (nowNs - mPreviousNs < mMinimumTimeBetweenChecksNs) {
+ return mLatchedValue;
+ }
+ mPreviousNs = nowNs;
+ mLatchedValue = false;
+ // Determine if the presentation position is still advancing.
uint64_t position = 0;
struct timespec unused;
- const status_t ret = mOutput->getPresentationPosition(&position, &unused);
+ const status_t ret = output->getPresentationPosition(&position, &unused);
if (ret == NO_ERROR) {
- if (position != mLastCheckedTimestampPosition) {
- mLastCheckedTimestampPosition = position;
- return true;
+ if (position != mPreviousPosition) {
+ mPreviousPosition = position;
+ mLatchedValue = true;
}
}
- return false;
+ return mLatchedValue;
+}
+
+void AudioFlinger::PlaybackThread::IsTimestampAdvancing::clear()
+{
+ mLatchedValue = true;
+ mPreviousPosition = 0;
+ mPreviousNs = 0;
}
// isTrackAllowed_l() must be called with ThreadBase::mLock held
@@ -6100,31 +6165,44 @@
{
float left, right;
+
// Ensure volumeshaper state always advances even when muted.
const sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
const auto [shaperVolume, shaperActive] = track->getVolumeHandler()->getVolume(
proxy->framesReleased());
mVolumeShaperActive = shaperActive;
+ gain_minifloat_packed_t vlr = proxy->getVolumeLR();
+ left = float_from_gain(gain_minifloat_unpack_left(vlr));
+ right = float_from_gain(gain_minifloat_unpack_right(vlr));
+
+ const bool clientVolumeMute = (left == 0.f && right == 0.f);
+
if (mMasterMute || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
left = right = 0;
} else {
float typeVolume = mStreamTypes[track->streamType()].volume;
const float v = mMasterVolume * typeVolume * shaperVolume;
- gain_minifloat_packed_t vlr = proxy->getVolumeLR();
- left = float_from_gain(gain_minifloat_unpack_left(vlr));
if (left > GAIN_FLOAT_UNITY) {
left = GAIN_FLOAT_UNITY;
}
- left *= v * mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
- right = float_from_gain(gain_minifloat_unpack_right(vlr));
if (right > GAIN_FLOAT_UNITY) {
right = GAIN_FLOAT_UNITY;
}
+
+ left *= v * mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
right *= v * mMasterBalanceRight;
}
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ clientVolumeMute,
+ shaperVolume == 0.f});
+
if (lastTrack) {
track->setFinalVolume((left + right) / 2.f);
if (left != mLeftVolFloat || right != mRightVolFloat) {
@@ -6335,9 +6413,9 @@
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
// Only consider last track started for mixer state control
+ bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
if (--(track->mRetryCount) <= 0) {
- const bool running = checkRunningTimestamp();
- if (running) { // still running, give us more time.
+ if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
track->mRetryCount = kMaxTrackRetriesOffload;
} else {
ALOGV("BUFFER TIMEOUT: remove track(%d) from active list", trackId);
@@ -6918,9 +6996,9 @@
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
+ bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
if (--(track->mRetryCount) <= 0) {
- const bool running = checkRunningTimestamp();
- if (running) { // still running, give us more time.
+ if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
track->mRetryCount = kMaxTrackRetriesOffload;
} else {
ALOGV("OffloadThread: BUFFER TIMEOUT: remove track(%d) from active list",
@@ -7259,6 +7337,94 @@
{
}
+void AudioFlinger::SpatializerThread::onFirstRef() {
+ PlaybackThread::onFirstRef();
+
+ Mutex::Autolock _l(mLock);
+ status_t status = mOutput->stream->setLatencyModeCallback(this);
+ if (status != INVALID_OPERATION) {
+ updateHalSupportedLatencyModes_l();
+ }
+}
+
+status_t AudioFlinger::SpatializerThread::createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ status_t status = MixerThread::createAudioPatch_l(patch, handle);
+ updateHalSupportedLatencyModes_l();
+ return status;
+}
+
+void AudioFlinger::SpatializerThread::updateHalSupportedLatencyModes_l() {
+ std::vector<audio_latency_mode_t> latencyModes;
+ if (mOutput->stream->getRecommendedLatencyModes(&latencyModes) != NO_ERROR) {
+ latencyModes.clear();
+ }
+ if (latencyModes != mSupportedLatencyModes) {
+ mSupportedLatencyModes.swap(latencyModes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
+
+void AudioFlinger::SpatializerThread::onHalLatencyModesChanged_l() {
+ mAudioFlinger->onSupportedLatencyModesChanged(mId, mSupportedLatencyModes);
+}
+
+void AudioFlinger::SpatializerThread::setHalLatencyMode_l() {
+ // if mSupportedLatencyModes is empty, the HAL stream does not support
+ // latency mode control and we can exit.
+ if (mSupportedLatencyModes.empty()) {
+ return;
+ }
+ audio_latency_mode_t latencyMode = AUDIO_LATENCY_MODE_FREE;
+ if (mSupportedLatencyModes.size() == 1) {
+ // If the HAL only support one latency mode currently, confirm the choice
+ latencyMode = mSupportedLatencyModes[0];
+ } else if (mSupportedLatencyModes.size() > 1) {
+ // Request low latency if:
+ // - The low latency mode is requested by the spatializer controller
+ // (mRequestedLatencyMode = AUDIO_LATENCY_MODE_LOW)
+ // AND
+ // - At least one active track is spatialized
+ bool hasSpatializedActiveTrack = false;
+ for (const auto& track : mActiveTracks) {
+ if (track->isSpatialized()) {
+ hasSpatializedActiveTrack = true;
+ break;
+ }
+ }
+ if (hasSpatializedActiveTrack && mRequestedLatencyMode == AUDIO_LATENCY_MODE_LOW) {
+ latencyMode = AUDIO_LATENCY_MODE_LOW;
+ }
+ }
+
+ if (latencyMode != mSetLatencyMode) {
+ status_t status = mOutput->stream->setLatencyMode(latencyMode);
+ if (status == NO_ERROR) {
+ mSetLatencyMode = latencyMode;
+ }
+ }
+}
+
+status_t AudioFlinger::SpatializerThread::setRequestedLatencyMode(audio_latency_mode_t mode) {
+ if (mode != AUDIO_LATENCY_MODE_LOW && mode != AUDIO_LATENCY_MODE_FREE) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ mRequestedLatencyMode = mode;
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::SpatializerThread::getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes) {
+ if (modes == nullptr) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ *modes = mSupportedLatencyModes;
+ return NO_ERROR;
+}
+
void AudioFlinger::SpatializerThread::checkOutputStageEffects()
{
bool hasVirtualizer = false;
@@ -7311,6 +7477,14 @@
}
}
+void AudioFlinger::SpatializerThread::onRecommendedLatencyModeChanged(
+ std::vector<audio_latency_mode_t> modes) {
+ Mutex::Autolock _l(mLock);
+ if (modes != mSupportedLatencyModes) {
+ mSupportedLatencyModes.swap(modes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
// ----------------------------------------------------------------------------
// Record
@@ -9160,6 +9334,9 @@
track->logEndInterval();
track->logBeginInterval(pathSourcesAsString);
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -9176,6 +9353,9 @@
} else {
status = mInput->stream->legacyReleaseAudioPatch();
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -9472,8 +9652,10 @@
return mHalStream->getMmapPosition(position);
}
-status_t AudioFlinger::MmapThread::exitStandby()
+status_t AudioFlinger::MmapThread::exitStandby_l()
{
+ // The HAL must receive track metadata before starting the stream
+ updateMetadata_l();
status_t ret = mHalStream->start();
if (ret != NO_ERROR) {
ALOGE("%s: error mHalStream->start() = %d for first track", __FUNCTION__, ret);
@@ -9499,13 +9681,10 @@
status_t ret;
+ // For the first track, reuse portId and session allocated when the stream was opened.
if (*handle == mPortId) {
- // For the first track, reuse portId and session allocated when the stream was opened.
- ret = exitStandby();
- if (ret == NO_ERROR) {
- acquireWakeLock();
- }
- return ret;
+ acquireWakeLock();
+ return NO_ERROR;
}
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
@@ -9607,7 +9786,6 @@
}
}
-
mActiveTracks.add(track);
sp<EffectChain> chain = getEffectChain_l(mSessionId);
if (chain != 0) {
@@ -9618,11 +9796,16 @@
track->logBeginInterval(patchSinksToString(&mPatch)); // log to MediaMetrics
*handle = portId;
+
+ if (mActiveTracks.size() == 1) {
+ ret = exitStandby_l();
+ }
+
broadcast_l();
- ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());
+ ALOGV("%s DONE status %d handle %d stream %p", __FUNCTION__, ret, *handle, mHalStream.get());
- return NO_ERROR;
+ return ret;
}
status_t AudioFlinger::MmapThread::stop(audio_port_handle_t handle)
@@ -9634,7 +9817,6 @@
}
if (handle == mPortId) {
- mHalStream->stop();
releaseWakeLock();
return NO_ERROR;
}
@@ -9671,6 +9853,10 @@
chain->decTrackCnt();
}
+ if (mActiveTracks.isEmpty()) {
+ mHalStream->stop();
+ }
+
broadcast_l();
return NO_ERROR;
@@ -9941,6 +10127,9 @@
mPatch = *patch;
mDeviceId = deviceId;
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -9960,6 +10149,9 @@
} else {
status = mHalStream->legacyReleaseAudioPatch();
}
+ // Force meteadata update after a route change
+ mActiveTracks.setHasChanged();
+
return status;
}
@@ -10259,20 +10451,10 @@
} else {
sp<MmapStreamCallback> callback = mCallback.promote();
if (callback != 0) {
- int channelCount;
- if (isOutput()) {
- channelCount = audio_channel_count_from_out_mask(mChannelMask);
- } else {
- channelCount = audio_channel_count_from_in_mask(mChannelMask);
- }
- Vector<float> values;
- for (int i = 0; i < channelCount; i++) {
- values.add(volume);
- }
mHalVolFloat = volume; // SW volume control worked, so update value.
mNoCallbackWarningCount = 0;
mLock.unlock();
- callback->onVolumeChanged(mChannelMask, values);
+ callback->onVolumeChanged(volume);
mLock.lock();
} else {
if (mNoCallbackWarningCount < kMaxNoCallbackWarnings) {
@@ -10283,6 +10465,14 @@
}
for (const sp<MmapTrack> &track : mActiveTracks) {
track->setMetadataHasChanged();
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ mStreamVolume == 0.f,
+ mStreamMute,
+ // TODO(b/241533526): adjust logic to include mute from AppOps
+ false /*muteFromPlaybackRestricted*/,
+ false /*muteFromClientVolume*/,
+ false /*muteFromVolumeShaper*/});
}
}
}
@@ -10367,16 +10557,15 @@
mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
}
-status_t AudioFlinger::MmapCaptureThread::exitStandby()
+status_t AudioFlinger::MmapCaptureThread::exitStandby_l()
{
{
// mInput might have been cleared by clearInput()
- Mutex::Autolock _l(mLock);
if (mInput != nullptr && mInput->stream != nullptr) {
mInput->stream->setGain(1.0f);
}
}
- return MmapThread::exitStandby();
+ return MmapThread::exitStandby_l();
}
AudioFlinger::AudioStreamIn* AudioFlinger::MmapCaptureThread::clearInput()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 074ae8f..b477d65 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -55,7 +55,8 @@
CFG_EVENT_RELEASE_AUDIO_PATCH,
CFG_EVENT_UPDATE_OUT_DEVICE,
CFG_EVENT_RESIZE_BUFFER,
- CFG_EVENT_CHECK_OUTPUT_STAGE_EFFECTS
+ CFG_EVENT_CHECK_OUTPUT_STAGE_EFFECTS,
+ CFG_EVENT_HAL_LATENCY_MODES_CHANGED,
};
class ConfigEventData: public RefBase {
@@ -282,6 +283,15 @@
virtual ~CheckOutputStageEffectsEvent() {}
};
+ class HalLatencyModesChangedEvent : public ConfigEvent {
+ public:
+ HalLatencyModesChangedEvent() :
+ ConfigEvent(CFG_EVENT_HAL_LATENCY_MODES_CHANGED) {
+ }
+
+ virtual ~HalLatencyModesChangedEvent() {}
+ };
+
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -353,6 +363,7 @@
void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs);
void sendCheckOutputStageEffectsEvent();
void sendCheckOutputStageEffectsEvent_l();
+ void sendHalLatencyModesChangedEvent_l();
void processConfigEvents_l();
virtual void setCheckOutputStageEffects() {}
@@ -364,7 +375,7 @@
virtual void toAudioPortConfig(struct audio_port_config *config) = 0;
virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs);
-
+ virtual void onHalLatencyModesChanged_l() {}
// see note at declaration of mStandby, mOutDevice and mInDevice
@@ -613,7 +624,7 @@
virtual void dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
- friend class AudioFlinger; // for mEffectChains
+ friend class AudioFlinger; // for mEffectChains and mAudioManager
const type_t mType;
@@ -786,6 +797,11 @@
*/
bool readAndClearHasChanged();
+ /** Force updating track metadata to audio HAL stream next time
+ * readAndClearHasChanged() is called.
+ */
+ void setHasChanged() { mHasChanged = true; }
+
private:
void logTrack(const char *funcName, const sp<T> &track) const;
@@ -921,6 +937,8 @@
}
virtual void checkOutputStageEffects() {}
+ virtual void setHalLatencyMode_l() {}
+
void dumpInternals_l(int fd, const Vector<String16>& args) override;
void dumpTracks_l(int fd, const Vector<String16>& args) override;
@@ -1064,6 +1082,15 @@
bool hasMixer() const {
return mType == MIXER || mType == DUPLICATING || mType == SPATIALIZER;
}
+
+ virtual status_t setRequestedLatencyMode(
+ audio_latency_mode_t mode __unused) { return INVALID_OPERATION; }
+
+ virtual status_t getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes __unused) {
+ return INVALID_OPERATION;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -1150,7 +1177,7 @@
volatile int32_t mSuspended;
int64_t mBytesWritten;
- int64_t mFramesWritten; // not reset on standby
+ std::atomic<int64_t> mFramesWritten; // not reset on standby
int64_t mLastFramesWritten = -1; // track changes in timestamp
// server frames written.
int64_t mSuspendedFrames; // not reset on standby
@@ -1364,6 +1391,7 @@
virtual bool hasFastMixer() const = 0;
virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
{ FastTrackUnderruns dummy; return dummy; }
+ const std::atomic<int64_t>& framesWritten() const { return mFramesWritten; }
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
@@ -1381,13 +1409,38 @@
std::atomic_bool mCheckOutputStageEffects{};
- // A differential check on the timestamps to see if there is a change in the
- // timestamp frame position between the last call to checkRunningTimestamp.
- uint64_t mLastCheckedTimestampPosition = ~0LL;
- bool checkRunningTimestamp();
+ // Provides periodic checking for timestamp advancement for underrun detection.
+ class IsTimestampAdvancing {
+ public:
+ // The timestamp will not be checked any faster than the specified time.
+ IsTimestampAdvancing(nsecs_t minimumTimeBetweenChecksNs)
+ : mMinimumTimeBetweenChecksNs(minimumTimeBetweenChecksNs)
+ {
+ clear();
+ }
+ // Check if the presentation position has advanced in the last periodic time.
+ bool check(AudioStreamOut * output);
+ // Clear the internal state when the playback state changes for the output
+ // stream.
+ void clear();
+ private:
+ // The minimum time between timestamp checks.
+ const nsecs_t mMinimumTimeBetweenChecksNs;
+ // Add differential check on the timestamps to see if there is a change in the
+ // timestamp frame position between the last call to check.
+ uint64_t mPreviousPosition;
+ // The time at which the last check occurred, to ensure we don't check too
+ // frequently, giving the Audio HAL enough time to update its timestamps.
+ nsecs_t mPreviousNs;
+ // The valued is latched so we don't check timestamps too frequently.
+ bool mLatchedValue;
+ };
+ IsTimestampAdvancing mIsTimestampAdvancing;
- virtual void flushHw_l() { mLastCheckedTimestampPosition = ~0LL; }
+ virtual void flushHw_l() {
+ mIsTimestampAdvancing.clear();
+ }
};
class MixerThread : public PlaybackThread {
@@ -1682,7 +1735,8 @@
}
};
-class SpatializerThread : public MixerThread {
+class SpatializerThread : public MixerThread,
+ public StreamOutHalInterfaceLatencyModeCallback {
public:
SpatializerThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
@@ -1693,10 +1747,35 @@
bool hasFastMixer() const override { return false; }
+ status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) override;
+
+ // RefBase
+ virtual void onFirstRef();
+
+ // StreamOutHalInterfaceLatencyModeCallback
+ void onRecommendedLatencyModeChanged(std::vector<audio_latency_mode_t> modes) override;
+
+ status_t setRequestedLatencyMode(audio_latency_mode_t mode) override;
+ status_t getSupportedLatencyModes(std::vector<audio_latency_mode_t>* modes) override;
+
protected:
void checkOutputStageEffects() override;
+ void onHalLatencyModesChanged_l() override;
+ void setHalLatencyMode_l() override;
private:
+ void updateHalSupportedLatencyModes_l();
+
+ // Support low latency mode by default as unless explicitly indicated by the audio HAL
+ // we assume the audio path is compatible with the head tracking latency requirements
+ std::vector<audio_latency_mode_t> mSupportedLatencyModes = {AUDIO_LATENCY_MODE_LOW};
+ // default to invalid value to force first update to the audio HAL
+ audio_latency_mode_t mSetLatencyMode =
+ (audio_latency_mode_t)AUDIO_LATENCY_MODE_INVALID;
+ // Do not request a specific mode by default
+ audio_latency_mode_t mRequestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
+
sp<EffectHandle> mFinalDownMixer;
};
@@ -2014,7 +2093,7 @@
virtual void threadLoop_exit();
virtual void threadLoop_standby();
virtual bool shouldStandby_l() { return false; }
- virtual status_t exitStandby();
+ virtual status_t exitStandby_l() REQUIRES(mLock);
virtual status_t initCheck() const { return (mHalStream == 0) ? NO_INIT : NO_ERROR; }
virtual size_t frameCount() const { return mFrameCount; }
@@ -2165,7 +2244,7 @@
AudioStreamIn* clearInput();
- status_t exitStandby() override;
+ status_t exitStandby_l() REQUIRES(mLock) override;
void updateMetadata_l() override;
void processVolume_l() override;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 2677ab3..20bfbb0 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -397,6 +397,8 @@
int64_t mLogStartFrames = 0; // Timestamp frames at start()
double mLogLatencyMs = 0.; // Track the last log latency
+ bool mLogForceVolumeUpdate = true; // force volume update to TrackMetrics.
+
TrackMetrics mTrackMetrics;
bool mServerLatencySupported = false;
diff --git a/services/audioflinger/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index 30d69ab..6fc70d6 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -64,7 +64,6 @@
AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str());
}
++mIntervalCount;
- mIntervalStartTimeNs = systemTime();
}
void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
@@ -90,11 +89,9 @@
// Called when we are removed from the Thread.
void logEndInterval() {
std::lock_guard l(mLock);
- if (mIntervalStartTimeNs != 0) {
- const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
- mIntervalStartTimeNs = 0;
- mCumulativeTimeNs += elapsedTimeNs;
- mDeviceTimeNs += elapsedTimeNs;
+ if (mLastVolumeChangeTimeNs != 0) {
+ logVolume_l(mVolume); // flush out the last volume.
+ mLastVolumeChangeTimeNs = 0;
}
}
@@ -133,20 +130,8 @@
// may be called multiple times during an interval
void logVolume(float volume) {
- const int64_t timeNs = systemTime();
std::lock_guard l(mLock);
- if (mStartVolumeTimeNs == 0) {
- mDeviceVolume = mVolume = volume;
- mLastVolumeChangeTimeNs = mStartVolumeTimeNs = timeNs;
- updateMinMaxVolume(0, mVolume);
- return;
- }
- const int64_t durationNs = timeNs - mLastVolumeChangeTimeNs;
- updateMinMaxVolume(durationNs, mVolume);
- mDeviceVolume = (mDeviceVolume * (mLastVolumeChangeTimeNs - mStartVolumeTimeNs) +
- mVolume * durationNs) / (timeNs - mStartVolumeTimeNs);
- mVolume = volume;
- mLastVolumeChangeTimeNs = timeNs;
+ logVolume_l(volume);
}
// Use absolute numbers returned by AudioTrackShared.
@@ -158,6 +143,7 @@
}
private:
+
// no lock required - all arguments and constants.
void deliverDeviceMetrics(const char *eventName, const char *devices) const {
mediametrics::LogItem(mMetricsId)
@@ -167,6 +153,23 @@
.record();
}
+ void logVolume_l(float volume) REQUIRES(mLock) {
+ const int64_t timeNs = systemTime();
+ const int64_t durationNs = mLastVolumeChangeTimeNs == 0
+ ? 0 : timeNs - mLastVolumeChangeTimeNs;
+ if (durationNs > 0) {
+ // See West's algorithm for weighted averages
+ // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
+ / (durationNs + mDeviceTimeNs);
+ mDeviceTimeNs += durationNs;
+ mCumulativeTimeNs += durationNs;
+ }
+ updateMinMaxVolume(durationNs, mVolume); // always update.
+ mVolume = volume;
+ mLastVolumeChangeTimeNs = timeNs;
+ }
+
void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
if (mIntervalCount > 0) {
mediametrics::LogItem item(mMetricsId);
@@ -199,14 +202,12 @@
// mDevices is not reset by resetIntervalGroupMetrics.
mIntervalCount = 0;
- mIntervalStartTimeNs = 0;
// mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
mDeviceTimeNs = 0;
mVolume = 0.f;
mDeviceVolume = 0.f;
- mStartVolumeTimeNs = 0;
- mLastVolumeChangeTimeNs = 0;
+ mLastVolumeChangeTimeNs = 0; // last time volume logged, cleared on endInterval
mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
mMinVolumeDurationNs = 0;
@@ -230,14 +231,12 @@
// Number of intervals and playing time
int32_t mIntervalCount GUARDED_BY(mLock) = 0;
- int64_t mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
- int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0;
- int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
+ int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
+ int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0; // time on device.
// Average volume
- double mVolume GUARDED_BY(mLock) = 0.f;
- double mDeviceVolume GUARDED_BY(mLock) = 0.f;
- int64_t mStartVolumeTimeNs GUARDED_BY(mLock) = 0;
+ double mVolume GUARDED_BY(mLock) = 0.f; // last set volume.
+ double mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
int64_t mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
// Min/Max volume
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6135020..44a93c1 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1094,12 +1094,22 @@
__func__, mId, (int)mThreadIoHandle);
}
- // states to reset position info for non-offloaded/direct tracks
- if (!isOffloaded() && !isDirect()
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+
+ // states to reset position info for pcm tracks
+ if (audio_is_linear_pcm(mFormat)
&& (state == IDLE || state == STOPPED || state == FLUSHED)) {
mFrameMap.reset();
+
+ if (!isFastTrack() && (isDirect() || isOffloaded())) {
+ // Start point of track -> sink frame map. If the HAL returns a
+ // frame position smaller than the first written frame in
+ // updateTrackFrameInfo, the timestamp can be interpolated
+ // instead of using a larger value.
+ mFrameMap.push(mAudioTrackServerProxy->framesReleased(),
+ playbackThread->framesWritten());
+ }
}
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if (isFastTrack()) {
// refresh fast track underruns on start because that field is never cleared
// by the fast mixer; furthermore, the same track can be recycled, i.e. start
@@ -1123,6 +1133,7 @@
.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
mLogLatencyMs = 0.;
}
+ mLogForceVolumeUpdate = true; // at least one volume logged for metrics when starting.
if (status == NO_ERROR || status == ALREADY_EXISTS) {
// for streaming tracks, remove the buffer read stop limit.
@@ -1394,12 +1405,21 @@
if (mFinalVolume != volume) { // Compare to an epsilon if too many meaningless updates
mFinalVolume = volume;
setMetadataHasChanged();
- mTrackMetrics.logVolume(volume);
+ mLogForceVolumeUpdate = true;
+ }
+ if (mLogForceVolumeUpdate) {
+ mLogForceVolumeUpdate = false;
+ mTrackMetrics.logVolume(mFinalVolume);
}
}
void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backInserter) const
{
+ // Do not forward metadata for PatchTrack with unspecified stream type
+ if (mStreamType == AUDIO_STREAM_PATCH) {
+ return;
+ }
+
playback_track_metadata_v7_t metadata;
metadata.base = {
.usage = mAttr.usage,
@@ -1474,6 +1494,39 @@
}
}
+// must be called with player thread lock held
+void AudioFlinger::PlaybackThread::Track::processMuteEvent_l(const sp<
+ IAudioManager>& audioManager, mute_state_t muteState)
+{
+ if (mMuteState == muteState) {
+ // mute state did not change, do nothing
+ return;
+ }
+
+ status_t result = UNKNOWN_ERROR;
+ if (audioManager && mPortId != AUDIO_PORT_HANDLE_NONE) {
+ if (mMuteEventExtras == nullptr) {
+ mMuteEventExtras = std::make_unique<os::PersistableBundle>();
+ }
+ mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey),
+ static_cast<int>(muteState));
+
+ result = audioManager->portEvent(mPortId,
+ PLAYER_UPDATE_MUTED,
+ mMuteEventExtras);
+ }
+
+ if (result == OK) {
+ mMuteState = muteState;
+ } else {
+ ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d",
+ __func__,
+ id(),
+ mPortId,
+ result);
+ }
+}
+
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
if (!isOffloaded() && !isDirect()) {
@@ -1908,6 +1961,7 @@
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
+ ALOGD("%s, haptic playback was muted for track %d", __func__, mTrack->id());
mTrack->setHapticPlaybackEnabled(false);
*ret = true;
}
@@ -1925,6 +1979,7 @@
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
+ ALOGD("%s, haptic playback was unmuted for track %d", __func__, mTrack->id());
mTrack->setHapticPlaybackEnabled(true);
*ret = true;
}
@@ -3085,6 +3140,38 @@
{
}
+void AudioFlinger::MmapThread::MmapTrack::processMuteEvent_l(const sp<
+ IAudioManager>& audioManager, mute_state_t muteState)
+{
+ if (mMuteState == muteState) {
+ // mute state did not change, do nothing
+ return;
+ }
+
+ status_t result = UNKNOWN_ERROR;
+ if (audioManager && mPortId != AUDIO_PORT_HANDLE_NONE) {
+ if (mMuteEventExtras == nullptr) {
+ mMuteEventExtras = std::make_unique<os::PersistableBundle>();
+ }
+ mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey),
+ static_cast<int>(muteState));
+
+ result = audioManager->portEvent(mPortId,
+ PLAYER_UPDATE_MUTED,
+ mMuteEventExtras);
+ }
+
+ if (result == OK) {
+ mMuteState = muteState;
+ } else {
+ ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d",
+ __func__,
+ id(),
+ mPortId,
+ result);
+ }
+}
+
void AudioFlinger::MmapThread::MmapTrack::appendDumpHeader(String8& result)
{
result.appendFormat("Client Session Port Id Format Chn mask SRate Flags %s\n",
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 496591a..c4c27e8 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -134,17 +134,17 @@
// request an output appropriate for playback of the supplied stream type and parameters
virtual audio_io_handle_t getOutput(audio_stream_type_t stream) = 0;
virtual status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- const AttributionSourceState& attributionSouce,
- const audio_config_t *config,
- audio_output_flags_t *flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs,
- output_type_t *outputType,
- bool *isSpatialized) = 0;
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ const AttributionSourceState& attributionSouce,
+ audio_config_t *config,
+ audio_output_flags_t *flags,
+ audio_port_handle_t *selectedDeviceId,
+ audio_port_handle_t *portId,
+ std::vector<audio_io_handle_t> *secondaryOutputs,
+ output_type_t *outputType,
+ bool *isSpatialized) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding
// stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
@@ -160,7 +160,7 @@
audio_unique_id_t riid,
audio_session_t session,
const AttributionSourceState& attributionSouce,
- const audio_config_base_t *config,
+ audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
diff --git a/services/audiopolicy/TEST_MAPPING b/services/audiopolicy/TEST_MAPPING
index f130f7c..4d43eb0 100644
--- a/services/audiopolicy/TEST_MAPPING
+++ b/services/audiopolicy/TEST_MAPPING
@@ -11,12 +11,23 @@
"include-filter": "com.google.android.gts.audio.AudioHostTest#testTwoChannelCapturing"
}
]
- },
+ }
+ ],
+ "postsubmit": [
{
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_DEFAULT__OUTPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__INPUT"
+ },
+ {
+ "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic_TestAAudioBasic_TestBasic_LOW_LATENCY__OUTPUT"
}
]
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index c2a20c6..bb1699e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -70,8 +70,9 @@
* @return OK if the request is valid
* otherwise if the request is not supported
*/
- status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid,
- audio_output_flags_t flags,
+ status_t getOutputForAttr(const audio_attributes_t& attributes,
+ const audio_config_base_t& config,
+ uid_t uid, audio_output_flags_t flags,
sp<AudioPolicyMix> &primaryMix,
std::vector<sp<AudioPolicyMix>> *secondaryMixes);
@@ -126,6 +127,7 @@
enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
const audio_attributes_t& attributes,
+ const audio_config_base_t& config,
uid_t uid);
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 0431619..7119b85 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -239,12 +239,13 @@
}
void setUseSwBridge() { mUseSwBridge = true; }
bool useSwBridge() const { return mUseSwBridge; }
+ bool canCloseOutput() const { return mCloseOutput; }
bool isConnected() const { return mPatchHandle != AUDIO_PATCH_HANDLE_NONE; }
audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }
sp<DeviceDescriptor> srcDevice() const { return mSrcDevice; }
sp<DeviceDescriptor> sinkDevice() const { return mSinkDevice; }
wp<SwAudioOutputDescriptor> swOutput() const { return mSwOutput; }
- void setSwOutput(const sp<SwAudioOutputDescriptor>& swOutput);
+ void setSwOutput(const sp<SwAudioOutputDescriptor>& swOutput, bool closeOutput = false);
wp<HwAudioOutputDescriptor> hwOutput() const { return mHwOutput; }
void setHwOutput(const sp<HwAudioOutputDescriptor>& hwOutput);
@@ -258,6 +259,15 @@
wp<SwAudioOutputDescriptor> mSwOutput;
wp<HwAudioOutputDescriptor> mHwOutput;
bool mUseSwBridge = false;
+ /**
+ * For either HW bridge associated to a SwOutput for activity / volume or SwBridge for also
+ * sample rendering / activity & volume, an existing playback thread may be reused (e.g.
+ * not already opened at APM startup or Direct Output).
+ * If reusing an already opened output, when this output is not used anymore, the AudioFlinger
+ * patch must be updated to refine the output device(s) information and ensure the right
+ * behavior of AudioDeviceCallback.
+ */
+ bool mCloseOutput = false;
};
/**
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 90b812d..37443ab 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -97,6 +97,25 @@
uint32_t flags,
bool exactMatchRequiredForInputFlags = false) const;
+ /**
+ * @brief areAllDevicesSupported: Checks if the given devices are supported by the IO profile.
+ *
+ * @param devices vector of devices to be checked for compatibility
+ * @return true if all devices are supported, false otherwise.
+ */
+ bool areAllDevicesSupported(const DeviceVector &devices) const;
+
+ /**
+ * @brief isCompatibleProfileForFlags: Checks if the IO profile is compatible with
+ * specified flags.
+ *
+ * @param flags to be checked for compatibility
+ * @param exactMatchRequiredForInputFlags true if exact match is required on flags
+ * @return true if the profile is compatible, false otherwise.
+ */
+ bool isCompatibleProfileForFlags(uint32_t flags,
+ bool exactMatchRequiredForInputFlags = false) const;
+
void dump(String8 *dst, int spaces) const;
void log();
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 546f56b..551eab6 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -151,7 +151,7 @@
}
status_t AudioPolicyMixCollection::getOutputForAttr(
- const audio_attributes_t& attributes, uid_t uid,
+ const audio_attributes_t& attributes, const audio_config_base_t& config, uid_t uid,
audio_output_flags_t flags,
sp<AudioPolicyMix> &primaryMix,
std::vector<sp<AudioPolicyMix>> *secondaryMixes)
@@ -177,7 +177,7 @@
continue; // Primary output already found
}
- switch (mixMatch(policyMix.get(), i, attributes, uid)) {
+ switch (mixMatch(policyMix.get(), i, attributes, config, uid)) {
case MixMatchStatus::INVALID_MIX:
// The mix has contradictory rules, ignore it
// TODO: reject invalid mix at registration
@@ -202,7 +202,8 @@
}
AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
- const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) {
+ const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes,
+ const audio_config_base_t& config, uid_t uid) {
if (mix->mMixType == MIX_TYPE_PLAYERS) {
// Loopback render mixes are created from a public API and thus restricted
@@ -229,6 +230,14 @@
}
}
+ // Permit match only if requested format and mix format are PCM and can be format
+ // adapted by the mixer, or are the same (compressed) format.
+ if (!((audio_is_linear_pcm(config.format) && audio_is_linear_pcm(mix->mFormat.format)) ||
+ (config.format == mix->mFormat.format)) &&
+ config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
+ return MixMatchStatus::NO_MATCH;
+ }
+
int userId = (int) multiuser_get_user_id(uid);
// TODO if adding more player rules (currently only 2), make rule handling "generic"
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 713b0ac..8b6866e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -105,9 +105,11 @@
{
}
-void SourceClientDescriptor::setSwOutput(const sp<SwAudioOutputDescriptor>& swOutput)
+void SourceClientDescriptor::setSwOutput(
+ const sp<SwAudioOutputDescriptor>& swOutput, bool closeOutput)
{
mSwOutput = swOutput;
+ mCloseOutput = closeOutput;
}
void SourceClientDescriptor::setHwOutput(const sp<HwAudioOutputDescriptor>& hwOutput)
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index 21f2018..7b4cecf 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -40,11 +40,9 @@
const bool isRecordThread =
getType() == AUDIO_PORT_TYPE_MIX && getRole() == AUDIO_PORT_ROLE_SINK;
ALOG_ASSERT(isPlaybackThread != isRecordThread);
-
- if (!devices.isEmpty()) {
- if (!mSupportedDevices.containsAllDevices(devices)) {
- return false;
- }
+ if (!areAllDevicesSupported(devices) ||
+ !isCompatibleProfileForFlags(flags, exactMatchRequiredForInputFlags)) {
+ return false;
}
if (!audio_is_valid_format(format) ||
@@ -78,6 +76,33 @@
}
}
+ if (updatedSamplingRate != NULL) {
+ *updatedSamplingRate = myUpdatedSamplingRate;
+ }
+ if (updatedFormat != NULL) {
+ *updatedFormat = myUpdatedFormat;
+ }
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = myUpdatedChannelMask;
+ }
+ return true;
+}
+
+bool IOProfile::areAllDevicesSupported(const DeviceVector &devices) const {
+ if (devices.empty()) {
+ return true;
+ }
+ return mSupportedDevices.containsAllDevices(devices);
+}
+
+bool IOProfile::isCompatibleProfileForFlags(uint32_t flags,
+ bool exactMatchRequiredForInputFlags) const {
+ const bool isPlaybackThread =
+ getType() == AUDIO_PORT_TYPE_MIX && getRole() == AUDIO_PORT_ROLE_SOURCE;
+ const bool isRecordThread =
+ getType() == AUDIO_PORT_TYPE_MIX && getRole() == AUDIO_PORT_ROLE_SINK;
+ ALOG_ASSERT(isPlaybackThread != isRecordThread);
+
const uint32_t mustMatchOutputFlags =
AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
if (isPlaybackThread && (((getFlags() ^ flags) & mustMatchOutputFlags)
@@ -93,15 +118,6 @@
return false;
}
- if (updatedSamplingRate != NULL) {
- *updatedSamplingRate = myUpdatedSamplingRate;
- }
- if (updatedFormat != NULL) {
- *updatedFormat = myUpdatedFormat;
- }
- if (updatedChannelMask != NULL) {
- *updatedChannelMask = myUpdatedChannelMask;
- }
return true;
}
diff --git a/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml b/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
index e7908eb..c453dea 100644
--- a/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
+++ b/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
@@ -12,6 +12,7 @@
</mixPort>
<!-- Le Audio Audio Ports -->
<mixPort name="le audio output" role="source" />
+ <mixPort name="le audio broadcast output" role="source" />
<mixPort name="le audio input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000 24000 32000 44100 48000"
@@ -47,6 +48,7 @@
<devicePort tagName="BLE Headset Out" type="AUDIO_DEVICE_OUT_BLE_HEADSET" role="sink"/>
<devicePort tagName="BLE Speaker Out" type="AUDIO_DEVICE_OUT_BLE_SPEAKER" role="sink"/>
<devicePort tagName="BLE Headset In" type="AUDIO_DEVICE_IN_BLE_HEADSET" role="source"/>
+ <devicePort tagName="BLE Broadcast Out" type="AUDIO_DEVICE_OUT_BLE_BROADCAST" role="sink"/>
</devicePorts>
<routes>
<route type="mix" sink="BT A2DP Out"
@@ -63,5 +65,7 @@
sources="BLE Headset In"/>
<route type="mix" sink="BLE Speaker Out"
sources="le audio output"/>
+ <route type="mix" sink="BLE Broadcast Out"
+ sources="le audio broadcast output"/>
</routes>
</module>
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index b036e12..60b16ac 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -25,11 +25,11 @@
const engineConfig::ProductStrategies gOrderedStrategies = {
{"STRATEGY_PHONE",
{
- {"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
+ {AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}},
},
- {"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
+ {AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,
""}},
}
@@ -37,11 +37,11 @@
},
{"STRATEGY_SONIFICATION",
{
- {"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
+ {AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
},
- {"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
+ {AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}},
}
@@ -49,7 +49,7 @@
},
{"STRATEGY_ENFORCED_AUDIBLE",
{
- {"", AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
+ {AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}}
}
@@ -57,7 +57,7 @@
},
{"STRATEGY_ACCESSIBILITY",
{
- {"", AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
+ {AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
}
@@ -65,7 +65,7 @@
},
{"STRATEGY_SONIFICATION_RESPECTFUL",
{
- {"", AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
+ {AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""},
@@ -77,11 +77,11 @@
},
{"STRATEGY_MEDIA",
{
- {"assistant", AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
+ {AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
{{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
},
- {"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
+ {AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""},
@@ -95,7 +95,7 @@
AUDIO_FLAG_NONE, ""}
},
},
- {"system", AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
+ {AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
}
@@ -103,7 +103,7 @@
},
{"STRATEGY_DTMF",
{
- {"", AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
+ {AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}
@@ -113,7 +113,7 @@
},
{"STRATEGY_CALL_ASSISTANT",
{
- {"", AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
+ {AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
@@ -121,7 +121,7 @@
},
{"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
{
- {"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
+ {AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_BEACON, ""},
@@ -140,7 +140,7 @@
const engineConfig::ProductStrategies gOrderedSystemStrategies = {
{"rerouting",
{
- {"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
+ {AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VIRTUAL_SOURCE, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
@@ -148,7 +148,7 @@
},
{"patch",
{
- {"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
+ {AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 2ebb7df..8036eea 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -35,7 +35,6 @@
using StreamVector = std::vector<audio_stream_type_t>;
struct AttributesGroup {
- std::string name;
audio_stream_type_t stream;
std::string volumeGroup;
AttributesVector attributesVect;
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 6f560d5..ac117f0 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -57,7 +57,6 @@
static constexpr const char *collectionTag = "AttributesGroups";
struct Attributes {
- static constexpr const char *name = "name";
static constexpr const char *streamType = "streamType";
static constexpr const char *volumeGroup = "volumeGroup";
};
@@ -313,12 +312,6 @@
status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
Collection &attributesGroup)
{
- std::string name = getXmlAttribute(child, Attributes::name);
- if (name.empty()) {
- ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name);
- }
- ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
-
std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup);
if (volumeGroup.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup);
@@ -339,7 +332,7 @@
AttributesVector attributesVect;
deserializeAttributesCollection(doc, child, attributesVect);
- attributesGroup.push_back({name, streamType, volumeGroup, attributesVect});
+ attributesGroup.push_back({streamType, volumeGroup, attributesVect});
return NO_ERROR;
}
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index d4d514d..7534984 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -78,7 +78,7 @@
case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO &&
config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+ ALOGW("setForceUse() invalid config %d for COMMUNICATION", config);
return BAD_VALUE;
}
break;
@@ -88,14 +88,14 @@
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_NO_BT_A2DP && config != AUDIO_POLICY_FORCE_SPEAKER ) {
- ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+ ALOGW("setForceUse() invalid config %d for MEDIA", config);
return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_RECORD:
if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+ ALOGW("setForceUse() invalid config %d for RECORD", config);
return BAD_VALUE;
}
break;
@@ -105,19 +105,22 @@
config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) {
- ALOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+ ALOGW("setForceUse() invalid config %d for DOCK", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_SYSTEM:
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
- ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config);
+ ALOGW("setForceUse() invalid config %d for SYSTEM", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) {
ALOGW("setForceUse() invalid config %d for HDMI_SYSTEM_AUDIO", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
@@ -131,13 +134,13 @@
break;
case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_VIBRATE_RINGING", config);
+ ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
return BAD_VALUE;
}
break;
default:
ALOGW("setForceUse() invalid usage %d", usage);
- break; // TODO return BAD_VALUE?
+ return BAD_VALUE;
}
return EngineBase::setForceUse(usage, config);
}
@@ -195,6 +198,12 @@
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, }));
}
+ // If connected to a dock, never use the device speaker for calls
+ if (!availableOutputDevices.getDevicesFromTypes({AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET})
+ .isEmpty()) {
+ availableOutputDevices.remove(
+ availableOutputDevices.getDevicesFromTypes({AUDIO_DEVICE_OUT_SPEAKER}));
+ }
} break;
case STRATEGY_ACCESSIBILITY: {
// do not route accessibility prompts to a digital output currently configured with a
@@ -267,10 +276,15 @@
break;
case STRATEGY_PHONE: {
- devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
- if (!devices.isEmpty()) break;
+ // TODO(b/243670205): remove this logic that gives preference to last removable devices
+ // once a UX decision has been made
devices = availableOutputDevices.getFirstDevicesFromTypes(
- getLastRemovableMediaDevices(GROUP_NONE, {AUDIO_DEVICE_OUT_BLE_HEADSET}));
+ getLastRemovableMediaDevices(GROUP_NONE, {
+ // excluding HEARING_AID and BLE_HEADSET because Dialer uses
+ // setCommunicationDevice to select them explicitly
+ AUDIO_DEVICE_OUT_HEARING_AID,
+ AUDIO_DEVICE_OUT_BLE_HEADSET
+ }));
if (!devices.isEmpty()) break;
devices = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, AUDIO_DEVICE_OUT_EARPIECE});
diff --git a/services/audiopolicy/managerdefault/Android.bp b/services/audiopolicy/managerdefault/Android.bp
index 4b4817e..6e34eb0 100644
--- a/services/audiopolicy/managerdefault/Android.bp
+++ b/services/audiopolicy/managerdefault/Android.bp
@@ -10,6 +10,10 @@
cc_library_shared {
name: "libaudiopolicymanagerdefault",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
srcs: [
"AudioPolicyManager.cpp",
"EngineLibrary.cpp",
@@ -36,7 +40,6 @@
"libaudiopolicyenginedefault",
"framework-permission-aidl-cpp",
"libaudioclient_aidl_conversion",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
],
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 744609f..8a3cd63 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -140,8 +140,6 @@
status_t AudioPolicyManager::setDeviceConnectionStateInt(
audio_policy_dev_state_t state, const android::media::audio::common::AudioPort& port,
audio_format_t encodedFormat) {
- // TODO: b/211601178 Forward 'port' to Audio HAL via mHwModules. For now, only device_type,
- // device_address and device_name are forwarded.
if (port.ext.getTag() != AudioPortExt::device) {
return BAD_VALUE;
}
@@ -160,7 +158,13 @@
sp<DeviceDescriptor> device = mHwModules.getDeviceDescriptor(
device_type, device_address.c_str(), device_name, encodedFormat,
state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
- return device ? setDeviceConnectionStateInt(device, state) : INVALID_OPERATION;
+ if (device == nullptr) {
+ return INVALID_OPERATION;
+ }
+ if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+ device->setExtraAudioDescriptors(port.extraAudioDescriptors);
+ }
+ return setDeviceConnectionStateInt(device, state);
}
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceType,
@@ -1054,12 +1058,12 @@
if (curProfile->getFlags() != AUDIO_OUTPUT_FLAG_SPATIALIZER) {
continue;
}
- // reject profiles not corresponding to a device currently available
- DeviceVector supportedDevices = curProfile->getSupportedDevices();
- if (!mAvailableOutputDevices.containsAtLeastOne(supportedDevices)) {
- continue;
- }
if (!devices.empty()) {
+ // reject profiles not corresponding to a device currently available
+ DeviceVector supportedDevices = curProfile->getSupportedDevices();
+ if (!mAvailableOutputDevices.containsAtLeastOne(supportedDevices)) {
+ continue;
+ }
if (supportedDevices.getDevicesFromDeviceTypeAddrVec(devices).size()
!= devices.size()) {
continue;
@@ -1128,7 +1132,7 @@
const audio_attributes_t *attr,
audio_stream_type_t *stream,
uid_t uid,
- const audio_config_t *config,
+ audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
@@ -1161,7 +1165,12 @@
// otherwise, fallback to the dynamic policies, if none match, query the engine.
// Secondary outputs are always found by dynamic policies as the engine do not support them
sp<AudioPolicyMix> primaryMix;
- status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, primaryMix, secondaryMixes);
+ const audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
+ .channel_mask = config->channel_mask,
+ .format = config->format,
+ };
+ status = mPolicyMixes.getOutputForAttr(*resultAttr, clientConfig, uid, *flags, primaryMix,
+ secondaryMixes);
if (status != OK) {
return status;
}
@@ -1170,10 +1179,9 @@
bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && primaryMix != nullptr;
// FIXME: in case of RENDER policy, the output capabilities should be checked
- if ((usePrimaryOutputFromPolicyMixes
- || (secondaryMixes != nullptr && !secondaryMixes->empty()))
- && !audio_is_linear_pcm(config->format)) {
- ALOGD("%s: rejecting request as dynamic audio policy only support pcm", __func__);
+ if ((secondaryMixes != nullptr && !secondaryMixes->empty())
+ && !audio_is_linear_pcm(config->format)) {
+ ALOGD("%s: rejecting request as secondary mixes only support pcm", __func__);
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
@@ -1182,19 +1190,27 @@
primaryMix->mDeviceAddress,
AUDIO_FORMAT_DEFAULT);
sp<SwAudioOutputDescriptor> policyDesc = primaryMix->getOutput();
- if (deviceDesc != nullptr
- && (policyDesc == nullptr || (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT))) {
+ bool tryDirectForFlags = policyDesc == nullptr ||
+ (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT);
+ // if a direct output can be opened to deliver the track's multi-channel content to the
+ // output rather than being downmixed by the primary output, then use this direct
+ // output by by-passing the primary mix if possible, otherwise fall-through to primary
+ // mix.
+ bool tryDirectForChannelMask = policyDesc != nullptr
+ && (audio_channel_count_from_out_mask(policyDesc->getConfig().channel_mask) <
+ audio_channel_count_from_out_mask(config->channel_mask));
+ if (deviceDesc != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
audio_io_handle_t newOutput;
status = openDirectOutput(
*stream, session, config,
(audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT),
DeviceVector(deviceDesc), &newOutput);
- if (status != NO_ERROR) {
- policyDesc = nullptr;
- } else {
+ if (status == NO_ERROR) {
policyDesc = mOutputs.valueFor(newOutput);
primaryMix->setOutput(policyDesc);
- }
+ } else if (tryDirectForFlags) {
+ policyDesc = nullptr;
+ } // otherwise use primary if available.
}
if (policyDesc != nullptr) {
policyDesc->mPolicyMix = primaryMix;
@@ -1257,6 +1273,15 @@
flags, isSpatialized, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC);
}
if (*output == AUDIO_IO_HANDLE_NONE) {
+ AudioProfileVector profiles;
+ status_t ret = getProfilesForDevices(outputDevices, profiles, *flags, false /*isInput*/);
+ if (ret == NO_ERROR && !profiles.empty()) {
+ config->channel_mask = profiles[0]->getChannels().empty() ? config->channel_mask
+ : *profiles[0]->getChannels().begin();
+ config->sample_rate = profiles[0]->getSampleRates().empty() ? config->sample_rate
+ : *profiles[0]->getSampleRates().begin();
+ config->format = profiles[0]->getFormat();
+ }
return INVALID_OPERATION;
}
@@ -1284,7 +1309,7 @@
audio_session_t session,
audio_stream_type_t *stream,
const AttributionSourceState& attributionSource,
- const audio_config_t *config,
+ audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
@@ -1868,7 +1893,8 @@
// (see b/200293124, the incorrect selection of AUDIO_OUTPUT_FLAG_VOIP_RX).
// 3: the output supporting the exact channel mask
// 4: the output with a higher channel count than requested
- // 5: the output with a higher sampling rate than requested
+ // 5: the output with the highest sampling rate if the requested sample rate is
+ // greater than default sampling rate
// 6: the output with the highest number of requested performance flags
// 7: the output with the bit depth the closest to the requested one
// 8: the primary output
@@ -1928,8 +1954,7 @@
}
// sampling rate match
- if (samplingRate > SAMPLE_RATE_HZ_DEFAULT &&
- samplingRate <= outputDesc->getSamplingRate()) {
+ if (samplingRate > SAMPLE_RATE_HZ_DEFAULT) {
currentMatchCriteria[4] = outputDesc->getSamplingRate();
}
@@ -2396,7 +2421,7 @@
audio_unique_id_t riid,
audio_session_t session,
const AttributionSourceState& attributionSource,
- const audio_config_base_t *config,
+ audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
@@ -2537,6 +2562,16 @@
*input = getInputForDevice(device, session, attributes, config, flags, policyMix);
if (*input == AUDIO_IO_HANDLE_NONE) {
status = INVALID_OPERATION;
+ AudioProfileVector profiles;
+ status_t ret = getProfilesForDevices(
+ DeviceVector(device), profiles, flags, true /*isInput*/);
+ if (ret == NO_ERROR && !profiles.empty()) {
+ config->channel_mask = profiles[0]->getChannels().empty() ? config->channel_mask
+ : *profiles[0]->getChannels().begin();
+ config->sample_rate = profiles[0]->getSampleRates().empty() ? config->sample_rate
+ : *profiles[0]->getSampleRates().begin();
+ config->format = profiles[0]->getFormat();
+ }
goto error;
}
@@ -2568,7 +2603,7 @@
audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp<DeviceDescriptor> &device,
audio_session_t session,
const audio_attributes_t &attributes,
- const audio_config_base_t *config,
+ audio_config_base_t *config,
audio_input_flags_t flags,
const sp<AudioPolicyMix> &policyMix)
{
@@ -2595,31 +2630,19 @@
flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_ULTRASOUND);
}
- // find a compatible input profile (not necessarily identical in parameters)
- sp<IOProfile> profile;
// sampling rate and flags may be updated by getInputProfile
uint32_t profileSamplingRate = (config->sample_rate == 0) ?
SAMPLE_RATE_HZ_DEFAULT : config->sample_rate;
- audio_format_t profileFormat;
+ audio_format_t profileFormat = config->format;
audio_channel_mask_t profileChannelMask = config->channel_mask;
audio_input_flags_t profileFlags = flags;
- for (;;) {
- profileFormat = config->format; // reset each time through loop, in case it is updated
- profile = getInputProfile(device, profileSamplingRate, profileFormat, profileChannelMask,
- profileFlags);
- if (profile != 0) {
- break; // success
- } else if (profileFlags & AUDIO_INPUT_FLAG_RAW) {
- profileFlags = (audio_input_flags_t) (profileFlags & ~AUDIO_INPUT_FLAG_RAW); // retry
- } else if (profileFlags != AUDIO_INPUT_FLAG_NONE && audio_is_linear_pcm(config->format)) {
- profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
- } else { // fail
- ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, "
- "channel mask 0x%X, flags %#x", __func__, device->toString().c_str(),
- config->sample_rate, config->format, config->channel_mask, flags);
- return input;
- }
+ // find a compatible input profile (not necessarily identical in parameters)
+ sp<IOProfile> profile = getInputProfile(
+ device, profileSamplingRate, profileFormat, profileChannelMask, profileFlags);
+ if (profile == nullptr) {
+ return input;
}
+
// Pick input sampling rate if not specified by client
uint32_t samplingRate = config->sample_rate;
if (samplingRate == 0) {
@@ -3141,7 +3164,7 @@
// stream by the engine.
DeviceTypeSet deviceTypes = {device};
if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
- DeviceTypeSet deviceTypes = mEngine->getOutputDevicesForAttributes(
+ deviceTypes = mEngine->getOutputDevicesForAttributes(
attr, nullptr, true /*fromCache*/).types();
}
return getVolumeIndex(getVolumeCurves(attr), index, deviceTypes);
@@ -3151,7 +3174,7 @@
int &index,
const DeviceTypeSet& deviceTypes) const
{
- if (isSingleDeviceType(deviceTypes, audio_is_output_device)) {
+ if (!isSingleDeviceType(deviceTypes, audio_is_output_device)) {
return BAD_VALUE;
}
index = curves.getVolumeIndex(deviceTypes);
@@ -4081,8 +4104,8 @@
status_t AudioPolicyManager::getDirectProfilesForAttributes(const audio_attributes_t* attr,
AudioProfileVector& audioProfilesVector) {
- AudioDeviceTypeAddrVector devices;
- status_t status = getDevicesForAttributes(*attr, &devices, false /* forVolume */);
+ DeviceVector devices;
+ status_t status = getDevicesForAttributes(*attr, devices, false /* forVolume */);
if (status != OK) {
return status;
}
@@ -4090,44 +4113,8 @@
if (devices.empty()) {
return OK; // no output devices for the attributes
}
-
- for (const auto& hwModule : mHwModules) {
- // the MSD module checks for different conditions
- if (strcmp(hwModule->getName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0) {
- continue;
- }
- for (const auto& outputProfile : hwModule->getOutputProfiles()) {
- if (!outputProfile->asAudioPort()->isDirectOutput()) {
- continue;
- }
- // allow only profiles that support all the available and routed devices
- if (outputProfile->getSupportedDevices().getDevicesFromDeviceTypeAddrVec(devices).size()
- != devices.size()) {
- continue;
- }
- audioProfilesVector.addAllValidProfiles(
- outputProfile->asAudioPort()->getAudioProfiles());
- }
- }
-
- // add the direct profiles from MSD if present and has audio patches to all the output(s)
- const auto& msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
- if (msdModule != nullptr) {
- if (msdHasPatchesToAllDevices(devices)) {
- ALOGV("%s: MSD audio patches set to all output devices.", __func__);
- for (const auto& outputProfile : msdModule->getOutputProfiles()) {
- if (!outputProfile->asAudioPort()->isDirectOutput()) {
- continue;
- }
- audioProfilesVector.addAllValidProfiles(
- outputProfile->asAudioPort()->getAudioProfiles());
- }
- } else {
- ALOGV("%s: MSD audio patches NOT set to all output devices.", __func__);
- }
- }
-
- return NO_ERROR;
+ return getProfilesForDevices(devices, audioProfilesVector,
+ AUDIO_OUTPUT_FLAG_DIRECT /*flags*/, false /*isInput*/);
}
status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
@@ -4527,7 +4514,11 @@
audio_attributes_t resultAttr;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = sourceDesc->config().sample_rate;
- config.channel_mask = sourceDesc->config().channel_mask;
+ audio_channel_mask_t sourceMask = sourceDesc->config().channel_mask;
+ config.channel_mask =
+ (audio_channel_mask_get_representation(sourceMask)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX) ? sourceMask
+ : audio_channel_mask_in_to_out(sourceMask);
config.format = sourceDesc->config().format;
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
@@ -4548,7 +4539,8 @@
ALOGE("%s output is duplicated", __func__);
return INVALID_OPERATION;
}
- sourceDesc->setSwOutput(outputDesc);
+ bool closeOutput = outputDesc->mDirectOpenCount != 0;
+ sourceDesc->setSwOutput(outputDesc, closeOutput);
} else {
// Same for "raw patches" aka created from createAudioPatch API
SortedVector<audio_io_handle_t> outputs =
@@ -4567,7 +4559,7 @@
__func__, sinkDevice->toString().c_str());
return INVALID_OPERATION;
}
- sourceDesc->setSwOutput(outputDesc);
+ sourceDesc->setSwOutput(outputDesc, /* closeOutput= */ false);
}
// create a software bridge in PatchPanel if:
// - source and sink devices are on different HW modules OR
@@ -4696,17 +4688,29 @@
// releaseOutput has already called closeOutput in case of direct output
return NO_ERROR;
}
- if (!outputDesc->isActive() && !sourceDesc->useSwBridge()) {
- resetOutputDevice(outputDesc);
- } else {
- // Reuse patch handle if still valid / do not force rerouting if still routed
- patchHandle = outputDesc->getPatchHandle();
- setOutputDevices(outputDesc,
- getNewOutputDevices(outputDesc, true /*fromCache*/),
- patchHandle == AUDIO_PATCH_HANDLE_NONE, /*force*/
- 0,
- patchHandle == AUDIO_PATCH_HANDLE_NONE ? nullptr : &patchHandle);
- }
+ patchHandle = outputDesc->getPatchHandle();
+ // When a Sw bridge is released, the mixer used by this bridge will release its
+ // patch at AudioFlinger side. Hence, the mixer audio patch must be recreated
+ // Reuse patch handle to force audio flinger removing initial mixer patch removal
+ // updating hal patch handle (prevent leaks).
+ // While using a HwBridge, force reconsidering device only if not reusing an existing
+ // output and no more activity on output (will force to close).
+ bool force = sourceDesc->useSwBridge() ||
+ (sourceDesc->canCloseOutput() && !outputDesc->isActive());
+ // APM pattern is to have always outputs opened / patch realized for reachable devices.
+ // Update device may result to NONE (empty), coupled with force, it releases the patch.
+ // Reconsider device only for cases:
+ // 1 / Active Output
+ // 2 / Inactive Output previously hosting HwBridge
+ // 3 / Inactive Output previously hosting SwBridge that can be closed.
+ bool updateDevice = outputDesc->isActive() || !sourceDesc->useSwBridge() ||
+ sourceDesc->canCloseOutput();
+ setOutputDevices(outputDesc,
+ updateDevice ? getNewOutputDevices(outputDesc, true /*fromCache*/) :
+ outputDesc->devices(),
+ force,
+ 0,
+ patchHandle == AUDIO_PATCH_HANDLE_NONE ? nullptr : &patchHandle);
} else {
return BAD_VALUE;
}
@@ -5353,10 +5357,6 @@
}
}
- // The caller can have the devices criteria ignored by passing and empty vector, and
- // getSpatializerOutputProfile() will ignore the devices when looking for a match.
- // Otherwise an output profile supporting a spatializer effect that can be routed
- // to the specified devices must exist.
sp<IOProfile> profile =
getSpatializerOutputProfile(config, devices);
if (profile == nullptr) {
@@ -6330,8 +6330,8 @@
continue;
}
sp<AudioPolicyMix> primaryMix;
- status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
- client->flags(), primaryMix, nullptr);
+ status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
+ client->uid(), client->flags(), primaryMix, nullptr);
if (status != OK) {
continue;
}
@@ -6443,8 +6443,8 @@
for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
sp<AudioPolicyMix> primaryMix;
std::vector<sp<AudioPolicyMix>> secondaryMixes;
- status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
- client->flags(), primaryMix, &secondaryMixes);
+ status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
+ client->uid(), client->flags(), primaryMix, &secondaryMixes);
std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
for (auto &secondaryMix : secondaryMixes) {
sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
@@ -6676,70 +6676,15 @@
return (stream1 == stream2);
}
-// TODO - consider MSD routes b/214971780
status_t AudioPolicyManager::getDevicesForAttributes(
const audio_attributes_t &attr, AudioDeviceTypeAddrVector *devices, bool forVolume) {
if (devices == nullptr) {
return BAD_VALUE;
}
- // Devices are determined in the following precedence:
- //
- // 1) Devices associated with a dynamic policy matching the attributes. This is often
- // a remote submix from MIX_ROUTE_FLAG_LOOP_BACK.
- //
- // If no such dynamic policy then
- // 2) Devices containing an active client using setPreferredDevice
- // with same strategy as the attributes.
- // (from the default Engine::getOutputDevicesForAttributes() implementation).
- //
- // If no corresponding active client with setPreferredDevice then
- // 3) Devices associated with the strategy determined by the attributes
- // (from the default Engine::getOutputDevicesForAttributes() implementation).
- //
- // See related getOutputForAttrInt().
-
- // check dynamic policies but only for primary descriptors (secondary not used for audible
- // audio routing, only used for duplication for playback capture)
- sp<AudioPolicyMix> policyMix;
- status_t status = mPolicyMixes.getOutputForAttr(attr, 0 /*uid unknown here*/,
- AUDIO_OUTPUT_FLAG_NONE, policyMix, nullptr /* secondaryMixes */);
- if (status != OK) {
- return status;
- }
-
DeviceVector curDevices;
- if (policyMix != nullptr && policyMix->getOutput() != nullptr &&
- // For volume control, skip LOOPBACK mixes which use AUDIO_DEVICE_OUT_REMOTE_SUBMIX
- // as they are unaffected by device/stream volume
- // (per SwAudioOutputDescriptor::isFixedVolume()).
- (!forVolume || policyMix->mDeviceType != AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
- ) {
- sp<DeviceDescriptor> deviceDesc = mAvailableOutputDevices.getDevice(
- policyMix->mDeviceType, policyMix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
- curDevices.add(deviceDesc);
- } else {
- // The default Engine::getOutputDevicesForAttributes() uses findPreferredDevice()
- // which selects setPreferredDevice if active. This means forVolume call
- // will take an active setPreferredDevice, if such exists.
-
- curDevices = mEngine->getOutputDevicesForAttributes(
- attr, nullptr /* preferredDevice */, false /* fromCache */);
- }
-
- if (forVolume) {
- // We alias the device AUDIO_DEVICE_OUT_SPEAKER_SAFE to AUDIO_DEVICE_OUT_SPEAKER
- // for single volume control in AudioService (such relationship should exist if
- // SPEAKER_SAFE is present).
- //
- // (This is unrelated to a different device grouping as Volume::getDeviceCategory)
- DeviceVector speakerSafeDevices =
- curDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER_SAFE);
- if (!speakerSafeDevices.isEmpty()) {
- curDevices.merge(
- mAvailableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
- curDevices.remove(speakerSafeDevices);
- }
+ if (status_t status = getDevicesForAttributes(attr, curDevices, forVolume); status != OK) {
+ return status;
}
for (const auto& device : curDevices) {
devices->push_back(device->getDeviceTypeAddr());
@@ -7094,51 +7039,68 @@
{
// Choose an input profile based on the requested capture parameters: select the first available
// profile supporting all requested parameters.
+ // The flags can be ignored if it doesn't contain a much match flag.
//
// TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
// the best matching profile, not the first one.
- sp<IOProfile> firstInexact;
- uint32_t updatedSamplingRate = 0;
- audio_format_t updatedFormat = AUDIO_FORMAT_INVALID;
- audio_channel_mask_t updatedChannelMask = AUDIO_CHANNEL_INVALID;
- for (const auto& hwModule : mHwModules) {
- for (const auto& profile : hwModule->getInputProfiles()) {
- // profile->log();
- //updatedFormat = format;
- if (profile->isCompatibleProfile(DeviceVector(device), samplingRate,
- &samplingRate /*updatedSamplingRate*/,
- format,
- &format, /*updatedFormat*/
- channelMask,
- &channelMask /*updatedChannelMask*/,
- // FIXME ugly cast
- (audio_output_flags_t) flags,
- true /*exactMatchRequiredForInputFlags*/)) {
- return profile;
- }
- if (firstInexact == nullptr && profile->isCompatibleProfile(DeviceVector(device),
- samplingRate,
- &updatedSamplingRate,
- format,
- &updatedFormat,
- channelMask,
- &updatedChannelMask,
- // FIXME ugly cast
- (audio_output_flags_t) flags,
- false /*exactMatchRequiredForInputFlags*/)) {
- firstInexact = profile;
- }
+ const audio_input_flags_t mustMatchFlag = AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+ const audio_input_flags_t oriFlags = flags;
+ for (;;) {
+ sp<IOProfile> firstInexact = nullptr;
+ uint32_t updatedSamplingRate = 0;
+ audio_format_t updatedFormat = AUDIO_FORMAT_INVALID;
+ audio_channel_mask_t updatedChannelMask = AUDIO_CHANNEL_INVALID;
+ for (const auto& hwModule : mHwModules) {
+ for (const auto& profile : hwModule->getInputProfiles()) {
+ // profile->log();
+ //updatedFormat = format;
+ if (profile->isCompatibleProfile(DeviceVector(device), samplingRate,
+ &samplingRate /*updatedSamplingRate*/,
+ format,
+ &format, /*updatedFormat*/
+ channelMask,
+ &channelMask /*updatedChannelMask*/,
+ // FIXME ugly cast
+ (audio_output_flags_t) flags,
+ true /*exactMatchRequiredForInputFlags*/)) {
+ return profile;
+ }
+ if (firstInexact == nullptr && profile->isCompatibleProfile(DeviceVector(device),
+ samplingRate,
+ &updatedSamplingRate,
+ format,
+ &updatedFormat,
+ channelMask,
+ &updatedChannelMask,
+ // FIXME ugly cast
+ (audio_output_flags_t) flags,
+ false /*exactMatchRequiredForInputFlags*/)) {
+ firstInexact = profile;
+ }
+ }
+ }
+
+ if (firstInexact != nullptr) {
+ samplingRate = updatedSamplingRate;
+ format = updatedFormat;
+ channelMask = updatedChannelMask;
+ return firstInexact;
+ } else if (flags & AUDIO_INPUT_FLAG_RAW) {
+ flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_RAW); // retry
+ } else if ((flags & mustMatchFlag) == AUDIO_INPUT_FLAG_NONE &&
+ flags != AUDIO_INPUT_FLAG_NONE && audio_is_linear_pcm(format)) {
+ flags = AUDIO_INPUT_FLAG_NONE;
+ } else { // fail
+ ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, "
+ "channel mask 0x%X, flags %#x", __func__, device->toString().c_str(),
+ samplingRate, format, channelMask, oriFlags);
+ break;
}
}
- if (firstInexact != nullptr) {
- samplingRate = updatedSamplingRate;
- format = updatedFormat;
- channelMask = updatedChannelMask;
- return firstInexact;
- }
- return NULL;
+
+ return nullptr;
}
float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
@@ -7829,6 +7791,9 @@
addOutput(output, desc);
+ sp<DeviceDescriptor> speaker = mAvailableOutputDevices.getDevice(
+ AUDIO_DEVICE_OUT_SPEAKER, String8(""), AUDIO_FORMAT_DEFAULT);
+
if (audio_is_remote_submix_device(deviceType) && address != "0") {
sp<AudioPolicyMix> policyMix;
if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) == NO_ERROR) {
@@ -7839,13 +7804,13 @@
address.string());
}
- } else if (hasPrimaryOutput() && profile->getModule()
- != mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)
+ } else if (hasPrimaryOutput() && speaker != nullptr
+ && mPrimaryOutput->supportsDevice(speaker) && !desc->supportsDevice(speaker)
&& ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
// no duplicated output for:
// - direct outputs
// - outputs used by dynamic policy mixes
- // - outputs opened on the primary HW module
+ // - outputs that supports SPEAKER while the primary output does not.
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
//TODO: configure audio effect output stage here
@@ -7873,4 +7838,109 @@
return desc;
}
+status_t AudioPolicyManager::getDevicesForAttributes(
+ const audio_attributes_t &attr, DeviceVector &devices, bool forVolume) {
+ // Devices are determined in the following precedence:
+ //
+ // 1) Devices associated with a dynamic policy matching the attributes. This is often
+ // a remote submix from MIX_ROUTE_FLAG_LOOP_BACK.
+ //
+ // If no such dynamic policy then
+ // 2) Devices containing an active client using setPreferredDevice
+ // with same strategy as the attributes.
+ // (from the default Engine::getOutputDevicesForAttributes() implementation).
+ //
+ // If no corresponding active client with setPreferredDevice then
+ // 3) Devices associated with the strategy determined by the attributes
+ // (from the default Engine::getOutputDevicesForAttributes() implementation).
+ //
+ // See related getOutputForAttrInt().
+
+ // check dynamic policies but only for primary descriptors (secondary not used for audible
+ // audio routing, only used for duplication for playback capture)
+ sp<AudioPolicyMix> policyMix;
+ status_t status = mPolicyMixes.getOutputForAttr(attr, AUDIO_CONFIG_BASE_INITIALIZER,
+ 0 /*uid unknown here*/, AUDIO_OUTPUT_FLAG_NONE, policyMix,
+ nullptr /* secondaryMixes */);
+ if (status != OK) {
+ return status;
+ }
+
+ if (policyMix != nullptr && policyMix->getOutput() != nullptr &&
+ // For volume control, skip LOOPBACK mixes which use AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+ // as they are unaffected by device/stream volume
+ // (per SwAudioOutputDescriptor::isFixedVolume()).
+ (!forVolume || policyMix->mDeviceType != AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+ ) {
+ sp<DeviceDescriptor> deviceDesc = mAvailableOutputDevices.getDevice(
+ policyMix->mDeviceType, policyMix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
+ devices.add(deviceDesc);
+ } else {
+ // The default Engine::getOutputDevicesForAttributes() uses findPreferredDevice()
+ // which selects setPreferredDevice if active. This means forVolume call
+ // will take an active setPreferredDevice, if such exists.
+
+ devices = mEngine->getOutputDevicesForAttributes(
+ attr, nullptr /* preferredDevice */, false /* fromCache */);
+ }
+
+ if (forVolume) {
+ // We alias the device AUDIO_DEVICE_OUT_SPEAKER_SAFE to AUDIO_DEVICE_OUT_SPEAKER
+ // for single volume control in AudioService (such relationship should exist if
+ // SPEAKER_SAFE is present).
+ //
+ // (This is unrelated to a different device grouping as Volume::getDeviceCategory)
+ DeviceVector speakerSafeDevices =
+ devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER_SAFE);
+ if (!speakerSafeDevices.isEmpty()) {
+ devices.merge(mAvailableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
+ devices.remove(speakerSafeDevices);
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getProfilesForDevices(const DeviceVector& devices,
+ AudioProfileVector& audioProfiles,
+ uint32_t flags,
+ bool isInput) {
+ for (const auto& hwModule : mHwModules) {
+ // the MSD module checks for different conditions
+ if (strcmp(hwModule->getName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0) {
+ continue;
+ }
+ IOProfileCollection ioProfiles = isInput ? hwModule->getInputProfiles()
+ : hwModule->getOutputProfiles();
+ for (const auto& profile : ioProfiles) {
+ if (!profile->areAllDevicesSupported(devices) ||
+ !profile->isCompatibleProfileForFlags(
+ flags, false /*exactMatchRequiredForInputFlags*/)) {
+ continue;
+ }
+ audioProfiles.addAllValidProfiles(profile->asAudioPort()->getAudioProfiles());
+ }
+ }
+
+ if (!isInput) {
+ // add the direct profiles from MSD if present and has audio patches to all the output(s)
+ const auto &msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+ if (msdModule != nullptr) {
+ if (msdHasPatchesToAllDevices(devices.toTypeAddrVector())) {
+ ALOGV("%s: MSD audio patches set to all output devices.", __func__);
+ for (const auto &profile: msdModule->getOutputProfiles()) {
+ if (!profile->asAudioPort()->isDirectOutput()) {
+ continue;
+ }
+ audioProfiles.addAllValidProfiles(profile->asAudioPort()->getAudioProfiles());
+ }
+ } else {
+ ALOGV("%s: MSD audio patches NOT set to all output devices.", __func__);
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index db0ee15..74460c7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -117,7 +117,7 @@
audio_session_t session,
audio_stream_type_t *stream,
const AttributionSourceState& attributionSource,
- const audio_config_t *config,
+ audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
@@ -132,7 +132,7 @@
audio_unique_id_t riid,
audio_session_t session,
const AttributionSourceState& attributionSource,
- const audio_config_base_t *config,
+ audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
@@ -1052,7 +1052,7 @@
const audio_attributes_t *attr,
audio_stream_type_t *stream,
uid_t uid,
- const audio_config_t *config,
+ audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
@@ -1099,6 +1099,18 @@
const audio_config_t *config,
const AudioDeviceTypeAddrVector &devices) const;
+
+ /**
+ * @brief Gets an IOProfile for a spatializer output with the best match with
+ * provided arguments.
+ * The caller can have the devices criteria ignored by passing and empty vector, and
+ * getSpatializerOutputProfile() will ignore the devices when looking for a match.
+ * Otherwise an output profile supporting a spatializer effect that can be routed
+ * to the specified devices must exist.
+ * @param config audio configuration describing the audio format, channels, sample rate...
+ * @param devices the sink audio device selected for playback
+ * @return an IOProfile that canbe used to open a spatializer output.
+ */
sp<IOProfile> getSpatializerOutputProfile(const audio_config_t *config,
const AudioDeviceTypeAddrVector &devices) const;
@@ -1121,7 +1133,9 @@
* @param session requester session id
* @param uid requester uid
* @param attributes requester audio attributes (e.g. input source and tags matter)
- * @param config requester audio configuration (e.g. sample rate, format, channel mask).
+ * @param config requested audio configuration (e.g. sample rate, format, channel mask),
+ * will be updated if current configuration doesn't support but another
+ * one does
* @param flags requester input flags
* @param policyMix may be null, policy rules to be followed by the requester
* @return input io handle aka unique input identifier selected for this device.
@@ -1129,7 +1143,7 @@
audio_io_handle_t getInputForDevice(const sp<DeviceDescriptor> &device,
audio_session_t session,
const audio_attributes_t &attributes,
- const audio_config_base_t *config,
+ audio_config_base_t *config,
audio_input_flags_t flags,
const sp<AudioPolicyMix> &policyMix);
@@ -1248,6 +1262,15 @@
// Filters only the relevant flags for getProfileForOutput
audio_output_flags_t getRelevantFlags (audio_output_flags_t flags, bool directOnly);
+
+ status_t getDevicesForAttributes(const audio_attributes_t &attr,
+ DeviceVector &devices,
+ bool forVolume);
+
+ status_t getProfilesForDevices(const DeviceVector& devices,
+ AudioProfileVector& audioProfiles,
+ uint32_t flags,
+ bool isInput);
};
};
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index cdad9a6..4c19d40 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -10,6 +10,10 @@
cc_library_shared {
name: "libaudiopolicyservice",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
srcs: [
"AudioPolicyClientImpl.cpp",
"AudioPolicyEffects.cpp",
@@ -49,7 +53,6 @@
"libshmemcompat",
"libutils",
"libstagefright_foundation",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 70fdfcb..c7a60c2 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -127,7 +127,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
+ fx->set(nullptr /*type */, &effect->mUuid, -1 /* priority */, nullptr /* callback */,
+ audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -279,7 +280,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
+ fx->set(nullptr /* type */, &effect->mUuid, 0 /* priority */, nullptr /* callback */,
+ audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d",
@@ -984,8 +986,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
- nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
+ fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0 /* priority */, nullptr /* callback */,
+ AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
deviceEffects->getDeviceAddress()});
status_t status = fx->initCheck();
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index df49bba..b15b61d 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -443,6 +443,13 @@
convertContainer<std::vector<int32_t>>(secondaryOutputs,
legacy2aidl_audio_io_handle_t_int32_t));
_aidl_return->isSpatialized = isSpatialized;
+ } else {
+ _aidl_return->configBase.format = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_format_t_AudioFormatDescription(config.format));
+ _aidl_return->configBase.channelMask = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ config.channel_mask, false /*isInput*/));
+ _aidl_return->configBase.sampleRate = config.sample_rate;
}
return binderStatusFromStatusT(result);
}
@@ -755,6 +762,9 @@
if (status == PERMISSION_DENIED) {
AutoCallerClear acc;
mAudioPolicyManager->releaseInput(portId);
+ } else {
+ _aidl_return->config = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/));
}
return binderStatusFromStatusT(status);
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index e7d945f..50a536b 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -516,9 +516,8 @@
void AudioPolicyService::doOnCheckSpatializer()
{
- Mutex::Autolock _l(mLock);
-
- ALOGI("%s mSpatializer %p level %d", __func__, mSpatializer.get(), (int)mSpatializer->getLevel());
+ ALOGV("%s mSpatializer %p level %d",
+ __func__, mSpatializer.get(), (int)mSpatializer->getLevel());
if (mSpatializer != nullptr) {
// Note: mSpatializer != nullptr => mAudioPolicyManager != nullptr
@@ -527,6 +526,8 @@
audio_io_handle_t newOutput;
const audio_attributes_t attr = attributes_initializer(AUDIO_USAGE_MEDIA);
audio_config_base_t config = mSpatializer->getAudioInConfig();
+
+ Mutex::Autolock _l(mLock);
status_t status =
mAudioPolicyManager->getSpatializerOutput(&config, &attr, &newOutput);
ALOGV("%s currentOutput %d newOutput %d channel_mask %#x",
@@ -538,21 +539,19 @@
mLock.unlock();
// It is OK to call detachOutput() is none is already attached.
mSpatializer->detachOutput();
- if (status != NO_ERROR || newOutput == AUDIO_IO_HANDLE_NONE) {
- mLock.lock();
- return;
+ if (status == NO_ERROR && newOutput != AUDIO_IO_HANDLE_NONE) {
+ status = mSpatializer->attachOutput(newOutput, numActiveTracks);
}
- status = mSpatializer->attachOutput(newOutput, numActiveTracks);
mLock.lock();
if (status != NO_ERROR) {
mAudioPolicyManager->releaseSpatializerOutput(newOutput);
}
} else if (mSpatializer->getLevel() == media::SpatializationLevel::NONE
&& mSpatializer->getOutput() != AUDIO_IO_HANDLE_NONE) {
- mLock.unlock();
audio_io_handle_t output = mSpatializer->detachOutput();
- mLock.lock();
+
if (output != AUDIO_IO_HANDLE_NONE) {
+ Mutex::Autolock _l(mLock);
mAudioPolicyManager->releaseSpatializerOutput(output);
}
}
@@ -581,19 +580,16 @@
void AudioPolicyService::doOnUpdateActiveSpatializerTracks()
{
- sp<Spatializer> spatializer;
+ if (mSpatializer == nullptr) {
+ return;
+ }
+ audio_io_handle_t output = mSpatializer->getOutput();
size_t activeClients;
{
Mutex::Autolock _l(mLock);
- if (mSpatializer == nullptr) {
- return;
- }
- spatializer = mSpatializer;
- activeClients = countActiveClientsOnOutput_l(mSpatializer->getOutput());
+ activeClients = countActiveClientsOnOutput_l(output);
}
- if (spatializer != nullptr) {
- spatializer->updateActiveTracks(activeClients);
- }
+ mSpatializer->updateActiveTracks(activeClients);
}
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -1215,6 +1211,14 @@
dumpReleaseLock(mLock, locked);
+ if (mSpatializer != nullptr) {
+ std::string dumpString = mSpatializer->toString(1 /* level */);
+ write(fd, dumpString.c_str(), dumpString.size());
+ } else {
+ String8 spatializerPtr = String8::format("Spatializer no supportted on this device\n");
+ write(fd, spatializerPtr.c_str(), spatializerPtr.size());
+ }
+
{
std::string timeCheckStats = getIAudioPolicyServiceStatistics().dump();
dprintf(fd, "\nIAudioPolicyService binder call profile\n");
@@ -1721,6 +1725,7 @@
}
bool AudioPolicyService::UidPolicy::isA11yOnTop() {
+ Mutex::Autolock _l(mLock);
for (const auto &uid : mCachedUids) {
if (!isA11yUid(uid.first)) {
continue;
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 3a08cf8..5c37f99 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -475,8 +475,8 @@
Mutex mLock;
ActivityManager mAm;
bool mObserverRegistered = false;
- std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids;
- std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids;
+ std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids GUARDED_BY(mLock);
+ std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids GUARDED_BY(mLock);
std::vector<uid_t> mAssistantUids;
std::vector<uid_t> mActiveAssistantUids;
std::vector<uid_t> mA11yUids;
@@ -1060,6 +1060,7 @@
CaptureStateNotifier mCaptureStateNotifier;
+ // created in onFirstRef() and never cleared: does not need to be guarded by mLock
sp<Spatializer> mSpatializer;
void *mLibraryHandle = nullptr;
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index c199a76..d411b8e 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -15,11 +15,12 @@
** limitations under the License.
*/
-
+#include <string>
#define LOG_TAG "Spatializer"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
@@ -58,11 +59,13 @@
if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
std::move(_tmp.value()); })
-audio_channel_mask_t getMaxChannelMask(std::vector<audio_channel_mask_t> masks) {
+static audio_channel_mask_t getMaxChannelMask(
+ const std::vector<audio_channel_mask_t>& masks, size_t channelLimit = SIZE_MAX) {
uint32_t maxCount = 0;
audio_channel_mask_t maxMask = AUDIO_CHANNEL_NONE;
for (auto mask : masks) {
const size_t count = audio_channel_count_from_out_mask(mask);
+ if (count > channelLimit) continue; // ignore masks greater than channelLimit
if (count > maxCount) {
maxMask = mask;
maxCount = count;
@@ -84,6 +87,7 @@
kWhatOnFramesProcessed, // AudioEffect::EVENT_FRAMES_PROCESSED
kWhatOnHeadToStagePose, // SpatializerPoseController::Listener::onHeadToStagePose
kWhatOnActualModeChange, // SpatializerPoseController::Listener::onActualModeChange
+ kWhatOnLatencyModesChanged, // Spatializer::onSupportedLatencyModesChanged
};
static constexpr const char *kNumFramesKey = "numFrames";
static constexpr const char *kModeKey = "mode";
@@ -93,15 +97,27 @@
static constexpr const char *kRotation0Key = "rotation0";
static constexpr const char *kRotation1Key = "rotation1";
static constexpr const char *kRotation2Key = "rotation2";
+ static constexpr const char *kLatencyModesKey = "latencyModes";
+
+ class LatencyModes : public RefBase {
+ public:
+ LatencyModes(audio_io_handle_t output,
+ const std::vector<audio_latency_mode_t>& latencyModes)
+ : mOutput(output), mLatencyModes(latencyModes) {}
+ ~LatencyModes() = default;
+
+ audio_io_handle_t mOutput;
+ std::vector<audio_latency_mode_t> mLatencyModes;
+ };
void onMessageReceived(const sp<AMessage> &msg) override {
+ sp<Spatializer> spatializer = mSpatializer.promote();
+ if (spatializer == nullptr) {
+ ALOGW("%s: Cannot promote spatializer", __func__);
+ return;
+ }
switch (msg->what()) {
case kWhatOnFramesProcessed: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
int numFrames;
if (!msg->findInt32(kNumFramesKey, &numFrames)) {
ALOGE("%s: Cannot find num frames!", __func__);
@@ -112,11 +128,6 @@
}
} break;
case kWhatOnHeadToStagePose: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
std::vector<float> headToStage(sHeadPoseKeys.size());
for (size_t i = 0 ; i < sHeadPoseKeys.size(); i++) {
if (!msg->findFloat(sHeadPoseKeys[i], &headToStage[i])) {
@@ -127,18 +138,25 @@
spatializer->onHeadToStagePoseMsg(headToStage);
} break;
case kWhatOnActualModeChange: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
int mode;
- if (!msg->findInt32(EngineCallbackHandler::kModeKey, &mode)) {
+ if (!msg->findInt32(kModeKey, &mode)) {
ALOGE("%s: Cannot find actualMode!", __func__);
return;
}
spatializer->onActualModeChangeMsg(static_cast<HeadTrackingMode>(mode));
} break;
+
+ case kWhatOnLatencyModesChanged: {
+ sp<RefBase> object;
+ if (!msg->findObject(kLatencyModesKey, &object)) {
+ ALOGE("%s: Cannot find latency modes!", __func__);
+ return;
+ }
+ sp<LatencyModes> latencyModes = static_cast<LatencyModes*>(object.get());
+ spatializer->onSupportedLatencyModesChangedMsg(
+ latencyModes->mOutput, std::move(latencyModes->mLatencyModes));
+ } break;
+
default:
LOG_ALWAYS_FATAL("Invalid callback message %d", msg->what());
}
@@ -157,6 +175,41 @@
};
// ---------------------------------------------------------------------------
+
+// Convert recorded sensor data to string with level indentation.
+std::string Spatializer::HeadToStagePoseRecorder::toString(unsigned level) const {
+ std::string prefixSpace(level, ' ');
+ return mPoseRecordLog.dumpToString((prefixSpace + " ").c_str(), Spatializer::mMaxLocalLogLine);
+}
+
+// Compute sensor data, record into local log when it is time.
+void Spatializer::HeadToStagePoseRecorder::record(const std::vector<float>& headToStage) {
+ if (headToStage.size() != mPoseVectorSize) return;
+
+ if (mNumOfSampleSinceLastRecord++ == 0) {
+ mFirstSampleTimestamp = std::chrono::steady_clock::now();
+ }
+ // if it's time, do record and reset.
+ if (shouldRecordLog()) {
+ poseSumToAverage();
+ mPoseRecordLog.log(
+ "mean: %s, min: %s, max %s, calculated %d samples in %0.4f second(s)",
+ Spatializer::toString<double>(mPoseRadianSum, true /* radianToDegree */).c_str(),
+ Spatializer::toString<float>(mMinPoseAngle, true /* radianToDegree */).c_str(),
+ Spatializer::toString<float>(mMaxPoseAngle, true /* radianToDegree */).c_str(),
+ mNumOfSampleSinceLastRecord, mNumOfSecondsSinceLastRecord.count());
+ resetRecord();
+ }
+ // update stream average.
+ for (int i = 0; i < mPoseVectorSize; i++) {
+ mPoseRadianSum[i] += headToStage[i];
+ mMaxPoseAngle[i] = std::max(mMaxPoseAngle[i], headToStage[i]);
+ mMinPoseAngle[i] = std::min(mMinPoseAngle[i], headToStage[i]);
+ }
+ return;
+}
+
+// ---------------------------------------------------------------------------
sp<Spatializer> Spatializer::create(SpatializerPolicyCallback *callback) {
sp<Spatializer> spatializer;
@@ -176,17 +229,22 @@
ALOG_ASSERT(!descriptors.empty(),
"%s getDescriptors() returned no error but empty list", __func__);
- //TODO: get supported spatialization modes from FX engine or descriptor
-
+ // TODO: get supported spatialization modes from FX engine or descriptor
sp<EffectHalInterface> effect;
status = effectsFactoryHal->createEffect(&descriptors[0].uuid, AUDIO_SESSION_OUTPUT_STAGE,
AUDIO_IO_HANDLE_NONE, AUDIO_PORT_HANDLE_NONE, &effect);
- ALOGI("%s FX create status %d effect %p", __func__, status, effect.get());
+ ALOGI("%s FX create status %d effect ID %" PRId64, __func__, status,
+ effect ? effect->effectId() : 0);
if (status == NO_ERROR && effect != nullptr) {
spatializer = new Spatializer(descriptors[0], callback);
if (spatializer->loadEngineConfiguration(effect) != NO_ERROR) {
spatializer.clear();
+ ALOGW("%s loadEngine error: %d effect Id %" PRId64,
+ __func__, status, effect ? effect->effectId() : 0);
+ } else {
+ spatializer->mLocalLog.log("%s with effect Id %" PRId64, __func__,
+ effect ? effect->effectId() : 0);
}
}
@@ -221,6 +279,16 @@
mHandler.clear();
}
+static std::string channelMaskVectorToString(
+ const std::vector<audio_channel_mask_t>& masks) {
+ std::stringstream ss;
+ for (const auto &mask : masks) {
+ if (ss.tellp() != 0) ss << "|";
+ ss << mask;
+ }
+ return ss.str();
+}
+
status_t Spatializer::loadEngineConfiguration(sp<EffectHalInterface> effect) {
ALOGV("%s", __func__);
@@ -268,6 +336,7 @@
ALOGW("%s: cannot get SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES", __func__);
return status;
}
+
for (const auto spatializationMode : spatializationModes) {
if (!aidl_utils::isValidEnum(spatializationMode)) {
ALOGW("%s: ignoring spatializationMode:%d", __func__, (int)spatializationMode);
@@ -309,7 +378,7 @@
}
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
- .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)getMaxChannelMask(mChannelMasks))
+ .set(AMEDIAMETRICS_PROP_CHANNELMASKS, channelMaskVectorToString(mChannelMasks))
.set(AMEDIAMETRICS_PROP_LEVELS, aidl_utils::enumsToString(mLevels))
.set(AMEDIAMETRICS_PROP_MODES, aidl_utils::enumsToString(mSpatializationModes))
.set(AMEDIAMETRICS_PROP_HEADTRACKINGMODES, aidl_utils::enumsToString(mHeadTrackingModes))
@@ -323,7 +392,7 @@
std::lock_guard lock(mLock);
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
// For now use highest supported channel count
- config.channel_mask = getMaxChannelMask(mChannelMasks);
+ config.channel_mask = getMaxChannelMask(mChannelMasks, FCC_LIMIT);
return config;
}
@@ -334,6 +403,17 @@
return BAD_VALUE;
}
+ if (mSpatializerCallback != nullptr) {
+ if (IInterface::asBinder(callback) == IInterface::asBinder(mSpatializerCallback)) {
+ ALOGW("%s: Registering callback %p again",
+ __func__, mSpatializerCallback.get());
+ return NO_ERROR;
+ }
+ ALOGE("%s: Already one client registered with callback %p",
+ __func__, mSpatializerCallback.get());
+ return INVALID_OPERATION;
+ }
+
sp<IBinder> binder = IInterface::asBinder(callback);
status_t status = binder->linkToDeath(this);
if (status == NO_ERROR) {
@@ -366,7 +446,8 @@
}
Status Spatializer::setLevel(SpatializationLevel level) {
- ALOGV("%s level %d", __func__, (int)level);
+ ALOGV("%s level %s", __func__, media::toString(level).c_str());
+ mLocalLog.log("%s with %s", __func__, media::toString(level).c_str());
if (level != SpatializationLevel::NONE
&& std::find(mLevels.begin(), mLevels.end(), level) == mLevels.end()) {
return binderStatusFromStatusT(BAD_VALUE);
@@ -426,11 +507,12 @@
}
Status Spatializer::setDesiredHeadTrackingMode(SpatializerHeadTrackingMode mode) {
- ALOGV("%s mode %d", __func__, (int)mode);
+ ALOGV("%s mode %s", __func__, media::toString(mode).c_str());
if (!mSupportsHeadTracking) {
return binderStatusFromStatusT(INVALID_OPERATION);
}
+ mLocalLog.log("%s with %s", __func__, media::toString(mode).c_str());
std::lock_guard lock(mLock);
switch (mode) {
case SpatializerHeadTrackingMode::OTHER:
@@ -485,6 +567,7 @@
}
std::lock_guard lock(mLock);
if (mPoseController != nullptr) {
+ mLocalLog.log("%s with screenToStage %s", __func__, toString<float>(screenToStage).c_str());
mPoseController->setScreenToStagePose(maybePose.value());
}
return Status::ok();
@@ -520,6 +603,7 @@
}
std::lock_guard lock(mLock);
if (mHeadSensor != sensorHandle) {
+ mLocalLog.log("%s with 0x%08x", __func__, sensorHandle);
mHeadSensor = sensorHandle;
checkPoseController_l();
checkSensorsState_l();
@@ -534,6 +618,7 @@
}
std::lock_guard lock(mLock);
if (mScreenSensor != sensorHandle) {
+ mLocalLog.log("%s with 0x%08x", __func__, sensorHandle);
mScreenSensor = sensorHandle;
// TODO: consider a new method setHeadAndScreenSensor()
// because we generally set both at the same time.
@@ -550,6 +635,7 @@
}
std::lock_guard lock(mLock);
mDisplayOrientation = physicalToLogicalAngle;
+ mLocalLog.log("%s with %f", __func__, physicalToLogicalAngle);
if (mPoseController != nullptr) {
mPoseController->setDisplayOrientation(mDisplayOrientation);
}
@@ -564,6 +650,7 @@
std::lock_guard lock(mLock);
ALOGV("%s hingeAngle %f", __func__, hingeAngle);
if (mEngine != nullptr) {
+ mLocalLog.log("%s with %f", __func__, hingeAngle);
setEffectParameter_l(SPATIALIZER_PARAM_HINGE_ANGLE, std::vector<float>{hingeAngle});
}
return Status::ok();
@@ -650,6 +737,8 @@
callback = mHeadTrackingCallback;
if (mEngine != nullptr) {
setEffectParameter_l(SPATIALIZER_PARAM_HEAD_TO_STAGE, headToStage);
+ mPoseRecorder.record(headToStage);
+ mPoseDurableRecorder.record(headToStage);
}
}
@@ -659,7 +748,9 @@
}
void Spatializer::onActualModeChange(HeadTrackingMode mode) {
- ALOGV("%s(%d)", __func__, (int)mode);
+ std::string modeStr = SpatializerPoseController::toString(mode);
+ ALOGV("%s(%s)", __func__, modeStr.c_str());
+ mLocalLog.log("%s with %s", __func__, modeStr.c_str());
sp<AMessage> msg =
new AMessage(EngineCallbackHandler::kWhatOnActualModeChange, mHandler);
msg->setInt32(EngineCallbackHandler::kModeKey, static_cast<int>(mode));
@@ -695,6 +786,9 @@
std::vector<SpatializerHeadTrackingMode>{spatializerMode});
}
callback = mHeadTrackingCallback;
+ mLocalLog.log("%s: %s, spatializerMode %s", __func__,
+ SpatializerPoseController::toString(mode).c_str(),
+ media::toString(spatializerMode).c_str());
}
if (callback != nullptr) {
callback->onHeadTrackingModeChanged(spatializerMode);
@@ -708,19 +802,24 @@
{
std::lock_guard lock(mLock);
ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
+ mLocalLog.log("%s with output %d tracks %zu (mOutput %d)", __func__, (int)output,
+ numActiveTracks, (int)mOutput);
if (mOutput != AUDIO_IO_HANDLE_NONE) {
LOG_ALWAYS_FATAL_IF(mEngine == nullptr, "%s output set without FX engine", __func__);
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
mPoseController.reset();
+ AudioSystem::removeSupportedLatencyModesCallback(this);
}
+
// create FX instance on output
AttributionSourceState attributionSource = AttributionSourceState();
mEngine = new AudioEffect(attributionSource);
- mEngine->set(nullptr, &mEngineDescriptor.uuid, 0, Spatializer::engineCallback /* cbf */,
- this /* user */, AUDIO_SESSION_OUTPUT_STAGE, output, {} /* device */,
- false /* probe */, true /* notifyFramesProcessed */);
+ mEngine->set(nullptr /* type */, &mEngineDescriptor.uuid, 0 /* priority */,
+ wp<AudioEffect::IAudioEffectCallback>::fromExisting(this),
+ AUDIO_SESSION_OUTPUT_STAGE, output, {} /* device */, false /* probe */,
+ true /* notifyFramesProcessed */);
status_t status = mEngine->initCheck();
ALOGV("%s mEngine create status %d", __func__, (int)status);
if (status != NO_ERROR) {
@@ -730,6 +829,13 @@
outputChanged = mOutput != output;
mOutput = output;
mNumActiveTracks = numActiveTracks;
+ AudioSystem::addSupportedLatencyModesCallback(this);
+
+ std::vector<audio_latency_mode_t> latencyModes;
+ status = AudioSystem::getSupportedLatencyModes(mOutput, &latencyModes);
+ if (status == OK) {
+ mSupportedLatencyModes = latencyModes;
+ }
checkEngineState_l();
if (mSupportsHeadTracking) {
@@ -752,6 +858,7 @@
{
std::lock_guard lock(mLock);
+ mLocalLog.log("%s with output %d tracks %zu", __func__, (int)mOutput, mNumActiveTracks);
ALOGV("%s mOutput %d", __func__, (int)mOutput);
if (mOutput == AUDIO_IO_HANDLE_NONE) {
return output;
@@ -759,6 +866,7 @@
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
+ AudioSystem::removeSupportedLatencyModesCallback(this);
output = mOutput;
mOutput = AUDIO_IO_HANDLE_NONE;
mPoseController.reset();
@@ -771,9 +879,31 @@
return output;
}
+void Spatializer::onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) {
+ ALOGV("%s output %d num modes %zu", __func__, (int)output, modes.size());
+ sp<AMessage> msg =
+ new AMessage(EngineCallbackHandler::kWhatOnLatencyModesChanged, mHandler);
+ msg->setObject(EngineCallbackHandler::kLatencyModesKey,
+ sp<EngineCallbackHandler::LatencyModes>::make(output, modes));
+ msg->post();
+}
+
+void Spatializer::onSupportedLatencyModesChangedMsg(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes) {
+ std::lock_guard lock(mLock);
+ ALOGV("%s output %d mOutput %d num modes %zu",
+ __func__, (int)output, (int)mOutput, modes.size());
+ if (output == mOutput) {
+ mSupportedLatencyModes = std::move(modes);
+ checkSensorsState_l();
+ }
+}
+
void Spatializer::updateActiveTracks(size_t numActiveTracks) {
std::lock_guard lock(mLock);
if (mNumActiveTracks != numActiveTracks) {
+ mLocalLog.log("%s from %zu to %zu", __func__, mNumActiveTracks, numActiveTracks);
mNumActiveTracks = numActiveTracks;
checkEngineState_l();
checkSensorsState_l();
@@ -781,17 +911,25 @@
}
void Spatializer::checkSensorsState_l() {
+ audio_latency_mode_t requestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
+ bool lowLatencySupported = mSupportedLatencyModes.empty()
+ || (std::find(mSupportedLatencyModes.begin(), mSupportedLatencyModes.end(),
+ AUDIO_LATENCY_MODE_LOW) != mSupportedLatencyModes.end());
if (mSupportsHeadTracking && mPoseController != nullptr) {
- if (mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
+ if (lowLatencySupported && mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
&& mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
&& mHeadSensor != SpatializerPoseController::INVALID_SENSOR) {
mPoseController->setHeadSensor(mHeadSensor);
mPoseController->setScreenSensor(mScreenSensor);
+ requestedLatencyMode = AUDIO_LATENCY_MODE_LOW;
} else {
mPoseController->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
mPoseController->setScreenSensor(SpatializerPoseController::INVALID_SENSOR);
}
}
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::setRequestedLatencyMode(mOutput, requestedLatencyMode);
+ }
}
void Spatializer::checkEngineState_l() {
@@ -837,28 +975,87 @@
}
}
-void Spatializer::engineCallback(int32_t event, void *user, void *info) {
- if (user == nullptr) {
- return;
- }
- Spatializer* const me = reinterpret_cast<Spatializer *>(user);
- switch (event) {
- case AudioEffect::EVENT_FRAMES_PROCESSED: {
- int frames = info == nullptr ? 0 : *(int*)info;
- ALOGV("%s frames processed %d for me %p", __func__, frames, me);
- me->postFramesProcessedMsg(frames);
- } break;
- default:
- ALOGV("%s event %d", __func__, event);
- break;
- }
-}
-
-void Spatializer::postFramesProcessedMsg(int frames) {
+void Spatializer::onFramesProcessed(int32_t framesProcessed) {
sp<AMessage> msg =
new AMessage(EngineCallbackHandler::kWhatOnFramesProcessed, mHandler);
- msg->setInt32(EngineCallbackHandler::kNumFramesKey, frames);
+ msg->setInt32(EngineCallbackHandler::kNumFramesKey, framesProcessed);
msg->post();
}
+std::string Spatializer::toString(unsigned level) const {
+ std::string prefixSpace;
+ prefixSpace.append(level, ' ');
+ std::string ss = prefixSpace + "Spatializer:\n";
+ bool needUnlock = false;
+
+ prefixSpace += ' ';
+ if (!mLock.try_lock()) {
+ // dumpsys even try_lock failed, information dump can be useful although may not accurate
+ ss.append(prefixSpace).append("try_lock failed, dumpsys below maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ // Spatializer class information.
+ // 1. Capabilities (mLevels, mHeadTrackingModes, mSpatializationModes, mChannelMasks, etc)
+ ss.append(prefixSpace).append("Supported levels: [");
+ for (auto& level : mLevels) {
+ base::StringAppendF(&ss, " %s", media::toString(level).c_str());
+ }
+ base::StringAppendF(&ss, "], mLevel: %s", media::toString(mLevel).c_str());
+
+ base::StringAppendF(&ss, "\n%smHeadTrackingModes: [", prefixSpace.c_str());
+ for (auto& mode : mHeadTrackingModes) {
+ base::StringAppendF(&ss, " %s", media::toString(mode).c_str());
+ }
+ base::StringAppendF(&ss, "], Desired: %s, Actual %s\n",
+ SpatializerPoseController::toString(mDesiredHeadTrackingMode).c_str(),
+ media::toString(mActualHeadTrackingMode).c_str());
+
+ base::StringAppendF(&ss, "%smSpatializationModes: [", prefixSpace.c_str());
+ for (auto& mode : mSpatializationModes) {
+ base::StringAppendF(&ss, " %s", media::toString(mode).c_str());
+ }
+ ss += "]\n";
+
+ base::StringAppendF(&ss, "%smChannelMasks: ", prefixSpace.c_str());
+ for (auto& mask : mChannelMasks) {
+ base::StringAppendF(&ss, "%s", audio_channel_out_mask_to_string(mask));
+ }
+ base::StringAppendF(&ss, "\n%smSupportsHeadTracking: %s\n", prefixSpace.c_str(),
+ mSupportsHeadTracking ? "true" : "false");
+ // 2. Settings (Output, tracks)
+ base::StringAppendF(&ss, "%smNumActiveTracks: %zu\n", prefixSpace.c_str(), mNumActiveTracks);
+ base::StringAppendF(&ss, "%sOutputStreamHandle: %d\n", prefixSpace.c_str(), (int)mOutput);
+
+ // 3. Sensors, Effect information.
+ base::StringAppendF(&ss, "%sHeadSensorHandle: 0x%08x\n", prefixSpace.c_str(), mHeadSensor);
+ base::StringAppendF(&ss, "%sScreenSensorHandle: 0x%08x\n", prefixSpace.c_str(), mScreenSensor);
+ base::StringAppendF(&ss, "%sEffectHandle: %p\n", prefixSpace.c_str(), mEngine.get());
+ base::StringAppendF(&ss, "%sDisplayOrientation: %f\n", prefixSpace.c_str(),
+ mDisplayOrientation);
+
+ ss.append(prefixSpace + "CommandLog:\n");
+ ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
+
+ // PostController dump.
+ if (mPoseController != nullptr) {
+ ss += mPoseController->toString(level + 1);
+ ss.append(prefixSpace +
+ "Sensor data format - [rx, ry, rz, vx, vy, vz] (units-degree, "
+ "r-transform, v-angular velocity, x-pitch, y-roll, z-yaw):\n");
+ ss.append(prefixSpace + "PerMinuteHistory:\n");
+ ss += mPoseDurableRecorder.toString(level + 1);
+ ss.append(prefixSpace + "PerSecondHistory:\n");
+ ss += mPoseRecorder.toString(level + 1);
+ } else {
+ ss.append(prefixSpace).append("SpatializerPoseController not exist\n");
+ }
+
+ if (needUnlock) {
+ mLock.unlock();
+ }
+ return ss;
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 29f4b08..586fc1f 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -17,15 +17,19 @@
#ifndef ANDROID_MEDIA_SPATIALIZER_H
#define ANDROID_MEDIA_SPATIALIZER_H
+#include <android-base/stringprintf.h>
#include <android/media/BnEffect.h>
#include <android/media/BnSpatializer.h>
#include <android/media/SpatializationLevel.h>
#include <android/media/SpatializationMode.h>
#include <android/media/SpatializerHeadTrackingMode.h>
+#include <audio_utils/SimpleLog.h>
+#include <math.h>
+#include <media/AudioEffect.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/stagefright/foundation/ALooper.h>
-#include <media/AudioEffect.h>
#include <system/audio_effects/effect_spatializer.h>
+#include <string>
#include "SpatializerPoseController.h"
@@ -84,8 +88,10 @@
* spatializer mixer thread is destroyed.
*/
class Spatializer : public media::BnSpatializer,
+ public AudioEffect::IAudioEffectCallback,
public IBinder::DeathRecipient,
- private SpatializerPoseController::Listener {
+ private SpatializerPoseController::Listener,
+ public virtual AudioSystem::SupportedLatencyModesCallback {
public:
static sp<Spatializer> create(SpatializerPolicyCallback *callback);
@@ -122,6 +128,10 @@
/** IBinder::DeathRecipient. Listen to the death of the INativeSpatializerCallback. */
virtual void binderDied(const wp<IBinder>& who);
+ /** SupportedLatencyModesCallback */
+ void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) override;
+
/** Registers a INativeSpatializerCallback when a client is attached to this Spatializer
* by audio policy service.
*/
@@ -150,6 +160,45 @@
void calculateHeadPose();
+ /** Convert fields in Spatializer and sub-modules to a string. Disable thread-safety-analysis
+ * here because we want to dump mutex guarded members even try_lock failed to provide as much
+ * information as possible for debugging purpose. */
+ std::string toString(unsigned level) const NO_THREAD_SAFETY_ANALYSIS;
+
+ static std::string toString(audio_latency_mode_t mode) {
+ switch (mode) {
+ case AUDIO_LATENCY_MODE_FREE:
+ return "LATENCY_MODE_FREE";
+ case AUDIO_LATENCY_MODE_LOW:
+ return "LATENCY_MODE_LOW";
+ }
+ return "EnumNotImplemented";
+ };
+
+ /**
+ * Format head to stage vector to a string, [0.00, 0.00, 0.00, -1.29, -0.50, 15.27].
+ */
+ template <typename T>
+ static std::string toString(const std::vector<T>& vec, bool radianToDegree = false) {
+ if (vec.size() == 0) {
+ return "[]";
+ }
+
+ std::string ss = "[";
+ for (auto f = vec.begin(); f != vec.end(); ++f) {
+ if (f != vec.begin()) {
+ ss .append(", ");
+ }
+ if (radianToDegree) {
+ base::StringAppendF(&ss, "%0.2f", HeadToStagePoseRecorder::getDegreeWithRadian(*f));
+ } else {
+ base::StringAppendF(&ss, "%f", *f);
+ }
+ }
+ ss.append("]");
+ return ss;
+ };
+
private:
Spatializer(effect_descriptor_t engineDescriptor,
SpatializerPolicyCallback *callback);
@@ -162,6 +211,8 @@
void onHeadToStagePoseMsg(const std::vector<float>& headToStage);
void onActualModeChangeMsg(media::HeadTrackingMode mode);
+ void onSupportedLatencyModesChangedMsg(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes);
static constexpr int kMaxEffectParamValues = 10;
/**
@@ -274,7 +325,7 @@
return NO_ERROR;
}
- void postFramesProcessedMsg(int frames);
+ virtual void onFramesProcessed(int32_t framesProcessed) override;
/**
* Checks if head and screen sensors must be actively monitored based on
@@ -354,10 +405,105 @@
sp<EngineCallbackHandler> mHandler;
size_t mNumActiveTracks GUARDED_BY(mLock) = 0;
+ std::vector<audio_latency_mode_t> mSupportedLatencyModes GUARDED_BY(mLock);
- static const std::vector<const char *> sHeadPoseKeys;
-};
+ static const std::vector<const char*> sHeadPoseKeys;
+ // Local log for command messages.
+ static constexpr int mMaxLocalLogLine = 10;
+ SimpleLog mLocalLog{mMaxLocalLogLine};
+
+ /**
+ * @brief Calculate and record sensor data.
+ * Dump to local log with max/average pose angle every mPoseRecordThreshold.
+ */
+ class HeadToStagePoseRecorder {
+ public:
+ HeadToStagePoseRecorder(std::chrono::duration<double> threshold, int maxLogLine)
+ : mPoseRecordThreshold(threshold), mPoseRecordLog(maxLogLine) {
+ resetRecord();
+ }
+
+ /** Convert recorded sensor data to string with level indentation */
+ std::string toString(unsigned level) const;
+
+ /**
+ * @brief Calculate sensor data, record into local log when it is time.
+ *
+ * @param headToStage The vector from Pose3f::toVector().
+ */
+ void record(const std::vector<float>& headToStage);
+
+ static constexpr float getDegreeWithRadian(const float radian) {
+ float radianToDegreeRatio = (180 / PI);
+ return (radian * radianToDegreeRatio);
+ }
+
+ private:
+ static constexpr float PI = M_PI;
+ /**
+ * Pose recorder time threshold to record sensor data in local log.
+ * Sensor data will be recorded into log at least every mPoseRecordThreshold.
+ */
+ std::chrono::duration<double> mPoseRecordThreshold;
+ // Number of seconds pass since last record.
+ std::chrono::duration<double> mNumOfSecondsSinceLastRecord;
+ /**
+ * According to frameworks/av/media/libheadtracking/include/media/Pose.h
+ * "The vector will have exactly 6 elements, where the first three are a translation vector
+ * and the last three are a rotation vector."
+ */
+ static constexpr size_t mPoseVectorSize = 6;
+ /**
+ * Timestamp of last sensor data record in local log.
+ */
+ std::chrono::time_point<std::chrono::steady_clock> mFirstSampleTimestamp;
+ /**
+ * Number of sensor samples received since last record, sample rate is ~100Hz which produce
+ * ~6k samples/minute.
+ */
+ uint32_t mNumOfSampleSinceLastRecord = 0;
+ /* The sum of pose angle represented by radian since last dump, div
+ * mNumOfSampleSinceLastRecord to get arithmetic mean. Largest possible value: 2PI * 100Hz *
+ * mPoseRecordThreshold.
+ */
+ std::vector<double> mPoseRadianSum;
+ std::vector<float> mMaxPoseAngle;
+ std::vector<float> mMinPoseAngle;
+ // Local log for history sensor data.
+ SimpleLog mPoseRecordLog{mMaxLocalLogLine};
+
+ bool shouldRecordLog() {
+ mNumOfSecondsSinceLastRecord = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::steady_clock::now() - mFirstSampleTimestamp);
+ return mNumOfSecondsSinceLastRecord >= mPoseRecordThreshold;
+ }
+
+ void resetRecord() {
+ mPoseRadianSum.assign(mPoseVectorSize, 0);
+ mMaxPoseAngle.assign(mPoseVectorSize, -PI);
+ mMinPoseAngle.assign(mPoseVectorSize, PI);
+ mNumOfSampleSinceLastRecord = 0;
+ mNumOfSecondsSinceLastRecord = std::chrono::seconds(0);
+ }
+
+ // Add each sample to sum and only calculate when record.
+ void poseSumToAverage() {
+ if (mNumOfSampleSinceLastRecord == 0) return;
+ for (auto& p : mPoseRadianSum) {
+ const float reciprocal = 1.f / mNumOfSampleSinceLastRecord;
+ p *= reciprocal;
+ }
+ }
+ }; // HeadToStagePoseRecorder
+
+ // Record one log line per second (up to mMaxLocalLogLine) to capture most recent sensor data.
+ HeadToStagePoseRecorder mPoseRecorder GUARDED_BY(mLock) =
+ HeadToStagePoseRecorder(std::chrono::seconds(1), mMaxLocalLogLine);
+ // Record one log line per minute (up to mMaxLocalLogLine) to capture durable sensor data.
+ HeadToStagePoseRecorder mPoseDurableRecorder GUARDED_BY(mLock) =
+ HeadToStagePoseRecorder(std::chrono::minutes(1), mMaxLocalLogLine);
+}; // Spatializer
}; // namespace android
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index 304d44a..97ddd4e 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
#include "SpatializerPoseController.h"
+#include <android-base/stringprintf.h>
+#include <chrono>
+#include <cstdint>
+#include <string>
#define LOG_TAG "SpatializerPoseController"
//#define LOG_NDEBUG 0
@@ -61,13 +65,13 @@
// Screen is considered to be unstable (not still) if it has moved significantly within the last
// time window of this duration.
-constexpr auto kScreenStillnessWindowDuration = 3s;
+constexpr auto kScreenStillnessWindowDuration = 750ms;
// Screen is considered to have moved significantly if translated by this much (in meter, approx).
constexpr float kScreenStillnessTranslationThreshold = 0.1f;
// Screen is considered to have moved significantly if rotated by this much (in radians, approx).
-constexpr float kScreenStillnessRotationThreshold = 7.0f / 180 * M_PI;
+constexpr float kScreenStillnessRotationThreshold = 15.0f / 180 * M_PI;
// Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
// what we use for pose filtering.
@@ -100,7 +104,10 @@
.screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
})),
mPoseProvider(SensorPoseProvider::create("headtracker", this)),
- mThread([this, maxUpdatePeriod] {
+ mThread([this, maxUpdatePeriod] { // It's important that mThread is initialized after
+ // everything else because it runs a member
+ // function that may use any member
+ // of this class.
while (true) {
Pose3f headToStage;
std::optional<HeadTrackingMode> modeIfChanged;
@@ -288,4 +295,58 @@
}
}
+std::string SpatializerPoseController::toString(unsigned level) const {
+ std::string prefixSpace;
+ prefixSpace.append(level, ' ');
+ std::string ss = prefixSpace + "SpatializerPoseController:\n";
+ bool needUnlock = false;
+
+ prefixSpace += ' ';
+ auto now = std::chrono::steady_clock::now();
+ if (!mMutex.try_lock_until(now + media::kSpatializerDumpSysTimeOutInSecond)) {
+ ss.append(prefixSpace).append("try_lock failed, dumpsys maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ ss += prefixSpace;
+ if (mHeadSensor == media::SensorPoseProvider::INVALID_HANDLE) {
+ ss.append("HeadSensor: INVALID\n");
+ } else {
+ base::StringAppendF(&ss, "HeadSensor: 0x%08x\n", mHeadSensor);
+ }
+
+ ss += prefixSpace;
+ if (mScreenSensor == media::SensorPoseProvider::INVALID_HANDLE) {
+ ss += "ScreenSensor: INVALID\n";
+ } else {
+ base::StringAppendF(&ss, "ScreenSensor: 0x%08x\n", mScreenSensor);
+ }
+
+ ss += prefixSpace;
+ if (mActualMode.has_value()) {
+ base::StringAppendF(&ss, "ActualMode: %s", toString(mActualMode.value()).c_str());
+ } else {
+ ss += "ActualMode NOTEXIST\n";
+ }
+
+ if (mProcessor) {
+ ss += mProcessor->toString_l(level + 1);
+ } else {
+ ss.append(prefixSpace.c_str()).append("HeadTrackingProcessor not exist\n");
+ }
+
+ if (mPoseProvider) {
+ ss += mPoseProvider->toString(level + 1);
+ } else {
+ ss.append(prefixSpace.c_str()).append("SensorPoseProvider not exist\n");
+ }
+
+ if (needUnlock) {
+ mMutex.unlock();
+ }
+ // TODO: 233092747 add history sensor info with SimpleLog.
+ return ss;
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/SpatializerPoseController.h b/services/audiopolicy/service/SpatializerPoseController.h
index 2c6d79a..546eba0 100644
--- a/services/audiopolicy/service/SpatializerPoseController.h
+++ b/services/audiopolicy/service/SpatializerPoseController.h
@@ -113,8 +113,23 @@
*/
void waitUntilCalculated();
+ // convert fields to a printable string
+ std::string toString(unsigned level) const;
+
+ static std::string toString(media::HeadTrackingMode mode) {
+ switch (mode) {
+ case media::HeadTrackingMode::STATIC:
+ return "STATIC";
+ case media::HeadTrackingMode::WORLD_RELATIVE:
+ return "WORLD_RELATIVE";
+ case media::HeadTrackingMode::SCREEN_RELATIVE:
+ return "SCREEN_RELATIVE";
+ }
+ return "EnumNotImplemented";
+ };
+
private:
- mutable std::mutex mMutex;
+ mutable std::timed_mutex mMutex;
Listener* const mListener;
const std::chrono::microseconds mSensorPeriod;
// Order matters for the following two members to ensure correct destruction.
@@ -123,12 +138,15 @@
int32_t mHeadSensor = media::SensorPoseProvider::INVALID_HANDLE;
int32_t mScreenSensor = media::SensorPoseProvider::INVALID_HANDLE;
std::optional<media::HeadTrackingMode> mActualMode;
- std::thread mThread;
- std::condition_variable mCondVar;
+ std::condition_variable_any mCondVar;
bool mShouldCalculate = true;
bool mShouldExit = false;
bool mCalculated = false;
+ // It's important that mThread is the last variable in this class
+ // since we starts mThread in initializer list
+ std::thread mThread;
+
void onPose(int64_t timestamp, int32_t sensor, const media::Pose3f& pose,
const std::optional<media::Twist3f>& twist, bool isNewReference) override;
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index 2e220bc..6813587 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -10,6 +10,10 @@
cc_test {
name: "audiopolicy_tests",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
+
include_dirs: [
"frameworks/av/services/audiopolicy",
],
@@ -30,6 +34,7 @@
],
static_libs: [
+ "audioclient-types-aidl-cpp",
"libaudiopolicycomponents",
"libgmock",
],
@@ -56,6 +61,11 @@
cc_test {
name: "audio_health_tests",
+
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
require_root: true,
shared_libs: [
@@ -65,7 +75,6 @@
"liblog",
"libmedia_helper",
"libutils",
- "android.media.audio.common.types-V1-cpp",
"libaudioclient_aidl_conversion",
"libstagefright_foundation",
"libshmemcompat",
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index 057fa58..96f58d2 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -103,8 +103,12 @@
++mAudioPortListUpdateCount;
}
- status_t setDeviceConnectedState(
- const struct audio_port_v7 *port __unused, bool connected __unused) override {
+ status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) override {
+ if (connected) {
+ mConnectedDevicePorts.push_back(*port);
+ } else {
+ mDisconnectedDevicePorts.push_back(*port);
+ }
return NO_ERROR;
}
@@ -150,6 +154,30 @@
return NO_ERROR;
}
+ size_t getConnectedDevicePortCount() const {
+ return mConnectedDevicePorts.size();
+ }
+
+ const struct audio_port_v7 *getLastConnectedDevicePort() const {
+ if (mConnectedDevicePorts.empty()) {
+ return nullptr;
+ }
+ auto it = --mConnectedDevicePorts.end();
+ return &(*it);
+ }
+
+ size_t getDisconnectedDevicePortCount() const {
+ return mDisconnectedDevicePorts.size();
+ }
+
+ const struct audio_port_v7 *getLastDisconnectedDevicePort() const {
+ if (mDisconnectedDevicePorts.empty()) {
+ return nullptr;
+ }
+ auto it = --mDisconnectedDevicePorts.end();
+ return &(*it);
+ }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
@@ -158,6 +186,8 @@
std::set<std::string> mAllowedModuleNames;
size_t mAudioPortListUpdateCount = 0;
size_t mRoutingUpdatedUpdateCount = 0;
+ std::vector<struct audio_port_v7> mConnectedDevicePorts;
+ std::vector<struct audio_port_v7> mDisconnectedDevicePorts;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index 7441f20..2a7a060 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -37,6 +37,7 @@
using AudioPolicyManager::getDirectProfilesForAttributes;
using AudioPolicyManager::setDeviceConnectionState;
using AudioPolicyManager::deviceToAudioPort;
+ using AudioPolicyManager::handleDeviceConfigChange;
uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
};
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 5429176..bb00c48 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -148,6 +148,8 @@
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
+
+ const uint32_t k48000SamplingRate = 48000;
};
void AudioPolicyManagerTest::SetUp() {
@@ -414,11 +416,11 @@
AudioPolicyConfig& config = mManager->getConfig();
mMsdOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_BUS);
sp<AudioProfile> pcmOutputProfile = new AudioProfile(
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
sp<AudioProfile> ac3OutputProfile = new AudioProfile(
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000);
+ AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, k48000SamplingRate);
sp<AudioProfile> iec958OutputProfile = new AudioProfile(
- AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
mMsdOutputDevice->addAudioProfile(pcmOutputProfile);
mMsdOutputDevice->addAudioProfile(ac3OutputProfile);
mMsdOutputDevice->addAudioProfile(iec958OutputProfile);
@@ -473,7 +475,7 @@
// Add a profile with another encoding to the default device to test routing
// of streams that are not supported by MSD.
sp<AudioProfile> dtsOutputProfile = new AudioProfile(
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000);
+ AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, k48000SamplingRate);
config.getDefaultOutputDevice()->addAudioProfile(dtsOutputProfile);
sp<OutputProfile> primaryEncodedOutputProfile = new OutputProfile("encoded");
primaryEncodedOutputProfile->addAudioProfile(dtsOutputProfile);
@@ -491,7 +493,7 @@
// Add HDMI input device with IEC60958 profile for HDMI in -> MSD patching.
mHdmiInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_HDMI);
sp<AudioProfile> iec958InputProfile = new AudioProfile(
- AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_IN_STEREO, 48000);
+ AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate);
mHdmiInputDevice->addAudioProfile(iec958InputProfile);
config.addDevice(mHdmiInputDevice);
sp<InputProfile> hdmiInputProfile = new InputProfile("hdmi input");
@@ -556,8 +558,8 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -566,7 +568,7 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -574,13 +576,13 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -588,8 +590,8 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
@@ -600,9 +602,8 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- nullptr /*output*/, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, nullptr /*output*/, &portId);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -612,9 +613,8 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- nullptr /*output*/, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, nullptr /*output*/, &portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(-static_cast<int>(mExpectedAudioPatchCount), patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -623,8 +623,8 @@
{
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
@@ -653,8 +653,8 @@
ASSERT_EQ(AUDIO_FORMAT_IEC60958, patch->mPatch.sinks[0].format);
ASSERT_EQ(AUDIO_CHANNEL_IN_STEREO, patch->mPatch.sources[0].channel_mask);
ASSERT_EQ(AUDIO_CHANNEL_OUT_STEREO, patch->mPatch.sinks[0].channel_mask);
- ASSERT_EQ(48000, patch->mPatch.sources[0].sample_rate);
- ASSERT_EQ(48000, patch->mPatch.sinks[0].sample_rate);
+ ASSERT_EQ(k48000SamplingRate, patch->mPatch.sources[0].sample_rate);
+ ASSERT_EQ(k48000SamplingRate, patch->mPatch.sinks[0].sample_rate);
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
@@ -919,6 +919,30 @@
EXPECT_TRUE(foundVoipTx);
}
+TEST_F(AudioPolicyManagerTestWithConfigurationFile, HandleDeviceConfigChange) {
+ {
+ const auto prevCounter = mClient->getRoutingUpdatedCounter();
+
+ EXPECT_EQ(NO_ERROR, mManager->setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "", "", AUDIO_FORMAT_LDAC));
+ const auto currCounter = mClient->getRoutingUpdatedCounter();
+ EXPECT_GT(currCounter, prevCounter);
+ }
+ {
+ const auto prevCounter = mClient->getRoutingUpdatedCounter();
+ // Update device configuration
+ EXPECT_EQ(NO_ERROR, mManager->handleDeviceConfigChange(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+ "" /*address*/, "" /*name*/,
+ AUDIO_FORMAT_AAC));
+
+ // As mClient marks isReconfigA2dpSupported to false, device state needs to be toggled for
+ // config changes to take effect
+ const auto currCounter = mClient->getRoutingUpdatedCounter();
+ EXPECT_GT(currCounter, prevCounter);
+ }
+}
+
using PolicyMixTuple = std::tuple<audio_usage_t, audio_source_t, uint32_t>;
class AudioPolicyManagerTestDynamicPolicy : public AudioPolicyManagerTestWithConfigurationFile {
@@ -996,7 +1020,7 @@
clearPolicyMix();
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig,
std::vector<PolicyMixTuple>());
@@ -1035,7 +1059,7 @@
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig,
std::vector<PolicyMixTuple>());
@@ -1273,7 +1297,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "", audioConfig, std::vector<PolicyMixTuple>());
ASSERT_EQ(INVALID_OPERATION, ret);
@@ -1307,7 +1331,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, mUsageRules);
ASSERT_EQ(NO_ERROR, ret);
@@ -1323,7 +1347,7 @@
std::string tags = "addr=" + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getInputForAttr(attr, mTracker->getRiid(), &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &mPortId);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
ASSERT_EQ(extractionPort.id, selectedDeviceId);
@@ -1350,8 +1374,8 @@
audio_port_handle_t playbackRoutedPortId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&playbackRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE,
- nullptr /*output*/, nullptr /*portId*/, attr);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, nullptr /*portId*/,
+ attr);
if (std::find_if(begin(mUsageRules), end(mUsageRules), [&usage](const auto &usageRule) {
return (std::get<0>(usageRule) == usage) &&
(std::get<2>(usageRule) == RULE_MATCH_ATTRIBUTE_USAGE);}) != end(mUsageRules) ||
@@ -1493,7 +1517,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
status_t ret = addPolicyMix(MIX_TYPE_RECORDERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_IN_REMOTE_SUBMIX, mMixAddress, audioConfig, mSourceRules);
ASSERT_EQ(NO_ERROR, ret);
@@ -1509,7 +1533,7 @@
std::string tags = std::string("addr=") + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
ASSERT_EQ(NO_ERROR, mManager->startOutput(mPortId));
ASSERT_EQ(injectionPort.id, getDeviceIdFromPatch(mClient->getLastAddedPatch()));
@@ -1537,7 +1561,7 @@
audio_port_handle_t captureRoutedPortId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
getInputForAttr(attr, mTracker->getRiid(), &captureRoutedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &portId);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &portId);
if (std::find_if(begin(mSourceRules), end(mSourceRules), [&source](const auto &sourceRule) {
return (std::get<1>(sourceRule) == source) &&
(std::get<2>(sourceRule) == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET);})
@@ -1687,11 +1711,11 @@
// Try start input or output according to the device type
if (audio_is_output_devices(type)) {
getOutputForAttr(&routedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
getInputForAttr({}, tracker.getRiid(), &routedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -1700,6 +1724,45 @@
address.c_str(), name.c_str(), AUDIO_FORMAT_DEFAULT));
}
+android::media::audio::common::ExtraAudioDescriptor make_ExtraAudioDescriptor(
+ android::media::audio::common::AudioStandard audioStandard,
+ android::media::audio::common::AudioEncapsulationType audioEncapsulationType) {
+ android::media::audio::common::ExtraAudioDescriptor result;
+ result.standard = audioStandard;
+ result.audioDescriptor = {0xb4, 0xaf, 0x98, 0x1a};
+ result.encapsulationType = audioEncapsulationType;
+ return result;
+}
+
+TEST_P(AudioPolicyManagerTestDeviceConnection, PassingExtraAudioDescriptors) {
+ const audio_devices_t type = std::get<0>(GetParam());
+ if (!audio_device_is_digital(type)) {
+ // EADs are used only for HDMI devices.
+ GTEST_SKIP() << "Not a digital device type: " << audio_device_to_string(type);
+ }
+ const std::string name = std::get<1>(GetParam());
+ const std::string address = std::get<2>(GetParam());
+ android::media::AudioPort audioPort;
+ ASSERT_EQ(NO_ERROR,
+ mManager->deviceToAudioPort(type, address.c_str(), name.c_str(), &audioPort));
+ android::media::audio::common::AudioPort& port = audioPort.hal;
+ port.extraAudioDescriptors.push_back(make_ExtraAudioDescriptor(
+ android::media::audio::common::AudioStandard::EDID,
+ android::media::audio::common::AudioEncapsulationType::IEC61937));
+ const size_t lastConnectedDevicePortCount = mClient->getConnectedDevicePortCount();
+ const size_t lastDisconnectedDevicePortCount = mClient->getDisconnectedDevicePortCount();
+ EXPECT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE, port, AUDIO_FORMAT_DEFAULT));
+ EXPECT_EQ(lastConnectedDevicePortCount + 1, mClient->getConnectedDevicePortCount());
+ EXPECT_EQ(lastDisconnectedDevicePortCount, mClient->getDisconnectedDevicePortCount());
+ const audio_port_v7* devicePort = mClient->getLastConnectedDevicePort();
+ EXPECT_EQ(port.extraAudioDescriptors.size(), devicePort->num_extra_audio_descriptors);
+ EXPECT_EQ(AUDIO_STANDARD_EDID, devicePort->extra_audio_descriptors[0].standard);
+ EXPECT_EQ(AUDIO_ENCAPSULATION_TYPE_IEC61937,
+ devicePort->extra_audio_descriptors[0].encapsulation_type);
+ EXPECT_NE(0, devicePort->extra_audio_descriptors[0].descriptor[0]);
+}
+
INSTANTIATE_TEST_CASE_P(
DeviceConnectionState,
AudioPolicyManagerTestDeviceConnection,
@@ -1715,6 +1778,57 @@
)
);
+class AudioPolicyManagerCarTest : public AudioPolicyManagerTestDynamicPolicy {
+protected:
+ std::string getConfigFile() override { return sCarConfig; }
+
+ static const std::string sCarConfig;
+};
+
+const std::string AudioPolicyManagerCarTest::sCarConfig =
+ AudioPolicyManagerCarTest::sExecutableDir + "test_car_ap_atmos_offload_configuration.xml";
+
+TEST_F(AudioPolicyManagerCarTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerCarTest, Dump) {
+ dumpToLog();
+}
+
+TEST_F(AudioPolicyManagerCarTest, GetOutputForAttrAtmosOutputAfterRegisteringPolicyMix) {
+ status_t ret;
+ audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
+ const std::string kTestBusMediaOutput = "bus0_media_out";
+ ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_BUS, kTestBusMediaOutput, audioConfig, std::vector<PolicyMixTuple>());
+ ASSERT_EQ(NO_ERROR, ret);
+
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_E_AC3_JOC, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, &output, &portId);
+ ASSERT_NE(AUDIO_PORT_HANDLE_NONE, selectedDeviceId);
+ sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ ASSERT_EQ(AUDIO_FORMAT_E_AC3_JOC, outDesc->getFormat());
+ ASSERT_EQ(AUDIO_CHANNEL_OUT_5POINT1, outDesc->getChannelMask());
+ ASSERT_EQ(k48000SamplingRate, outDesc->getSamplingRate());
+
+ selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ output = AUDIO_IO_HANDLE_NONE;
+ portId = AUDIO_PORT_HANDLE_NONE;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_7POINT1POINT4,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, &output, &portId);
+ ASSERT_NE(AUDIO_PORT_HANDLE_NONE, selectedDeviceId);
+ outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ ASSERT_EQ(AUDIO_FORMAT_PCM_16_BIT, outDesc->getFormat());
+ ASSERT_EQ(AUDIO_CHANNEL_OUT_7POINT1POINT4, outDesc->getChannelMask());
+ ASSERT_EQ(k48000SamplingRate, outDesc->getSamplingRate());
+}
+
class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
protected:
std::string getConfigFile() override { return sTvConfig; }
@@ -1735,8 +1849,8 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t output;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000,
- flags, &output, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ k48000SamplingRate, flags, &output, &portId);
sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
ASSERT_NE(nullptr, outDesc.get());
audio_port_v7 port = {};
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index ff4d568..5e71210 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -12,6 +12,7 @@
srcs: [
"test_audio_policy_configuration.xml",
"test_audio_policy_primary_only_configuration.xml",
+ "test_car_ap_atmos_offload_configuration.xml",
"test_invalid_audio_policy_configuration.xml",
"test_tv_apm_configuration.xml",
"test_settop_box_surround_configuration.xml",
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 5e1822a..d342aea 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -71,6 +71,9 @@
<devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"
role="source" address="hfp_client_in">
</devicePort>
+ <devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink"
+ encodedFormats="AUDIO_FORMAT_LDAC AUDIO_FORMAT_APTX AUDIO_FORMAT_APTX_HD AUDIO_FORMAT_AAC AUDIO_FORMAT_SBC">
+ </devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
@@ -85,6 +88,8 @@
sources="mixport_bt_hfp_output,voip_rx"/>
<route type="mix" sink="mixport_bt_hfp_input"
sources="BT SCO Headset Mic"/>
+ <route type="mix" sink="BT A2DP Out"
+ sources="primary output"/>
</routes>
</module>
diff --git a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
new file mode 100644
index 0000000..d131ed8
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
@@ -0,0 +1,308 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <modules>
+ <!-- Primary Audio HAL -->
+ <module name="primary" halVersion="3.0">
+ <attachedDevices>
+ <!-- One bus per context -->
+ <item>bus0_media_out</item>
+ <item>bus1_navigation_out</item>
+ <item>bus2_voice_command_out</item>
+ <item>bus3_call_ring_out</item>
+ <item>bus4_call_out</item>
+ <item>bus5_alarm_out</item>
+ <item>bus6_notification_out</item>
+ <item>bus7_system_sound_out</item>
+ <!-- names with _audio_zone_# are used for defined an emulator rear seat audio zone
+ where each number # is the zone id number -->
+ <item>bus100_audio_zone_1</item>
+ <item>bus200_audio_zone_2</item>
+ <item>Built-In Mic</item>
+ <item>Built-In Back Mic</item>
+ <item>Echo-Reference Mic</item>
+ <item>FM Tuner</item>
+ <item>Tone Generator 0</item>
+ <item>Tone Generator 1</item>
+ </attachedDevices>
+ <defaultOutputDevice>bus0_media_out</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="mixport_bus0_media_out" role="source"
+ flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus0_media_out_atmos" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1"/>
+ </mixPort>
+ <mixPort name="mixport_bus0_media_out_atmos_pcm" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ </mixPort>
+ <mixPort name="mixport_bus1_navigation_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus2_voice_command_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus3_call_ring_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus4_call_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus5_alarm_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus6_notification_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus7_system_sound_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus100_audio_zone_1" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus200_audio_zone_2" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </mixPort>
+ <mixPort name="mixport_tuner0" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_input_bus_tone_zone_0" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_input_bus_tone_zone_1" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus0_media_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus1_navigation_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus1_navigation_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus2_voice_command_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus2_voice_command_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus3_call_ring_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus3_call_ring_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus4_call_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus4_call_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus5_alarm_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus5_alarm_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus6_notification_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus6_notification_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus7_system_sound_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus7_system_sound_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus100_audio_zone_1" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus100_audio_zone_1">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus200_audio_zone_2" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus200_audio_zone_2">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC"
+ role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Echo-Reference Mic" type="AUDIO_DEVICE_IN_ECHO_REFERENCE"
+ role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source"
+ address="tuner0">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Tone Generator 0" type="AUDIO_DEVICE_IN_BUS" role="source"
+ address="input_bus_tone_zone_0">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Tone Generator 1" type="AUDIO_DEVICE_IN_BUS" role="source"
+ address="input_bus_tone_zone_1">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ </devicePorts>
+ <!-- route declaration, i.e. list all available sources for a given sink -->
+ <routes>
+ <route type="mix" sink="bus0_media_out"
+ sources="mixport_bus0_media_out,mixport_bus0_media_out_atmos,mixport_bus0_media_out_atmos_pcm"/>
+ <route type="mix" sink="bus1_navigation_out" sources="mixport_bus1_navigation_out"/>
+ <route type="mix" sink="bus2_voice_command_out"
+ sources="mixport_bus2_voice_command_out"/>
+ <route type="mix" sink="bus3_call_ring_out" sources="mixport_bus3_call_ring_out"/>
+ <route type="mix" sink="bus4_call_out" sources="mixport_bus4_call_out"/>
+ <route type="mix" sink="bus5_alarm_out" sources="mixport_bus5_alarm_out"/>
+ <route type="mix" sink="bus6_notification_out"
+ sources="mixport_bus6_notification_out"/>
+ <route type="mix" sink="bus7_system_sound_out"
+ sources="mixport_bus7_system_sound_out"/>
+ <route type="mix" sink="bus100_audio_zone_1" sources="mixport_bus100_audio_zone_1"/>
+ <route type="mix" sink="bus200_audio_zone_2" sources="mixport_bus200_audio_zone_2"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic,Built-In Back Mic,Echo-Reference Mic"/>
+ <route type="mix" sink="mixport_tuner0" sources="FM Tuner"/>
+ <route type="mix" sink="mixport_input_bus_tone_zone_0" sources="Tone Generator 0"/>
+ <route type="mix" sink="mixport_input_bus_tone_zone_1" sources="Tone Generator 1"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 80410ab..bc8981e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -142,7 +142,10 @@
// Set to keep track of logged service error events.
static std::set<String8> sServiceErrorEventSet;
-CameraService::CameraService() :
+CameraService::CameraService(
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper) :
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper == nullptr ?
+ std::make_shared<CameraServiceProxyWrapper>() : cameraServiceProxyWrapper),
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mNumberOfCameras(0),
mNumberOfCamerasWithoutSystemCamera(0),
@@ -195,7 +198,7 @@
// This needs to be last call in this function, so that it's as close to
// ServiceManager::addService() as possible.
- CameraServiceProxyWrapper::pingCameraServiceProxy();
+ mCameraServiceProxyWrapper->pingCameraServiceProxy();
ALOGI("CameraService pinged cameraservice proxy");
}
@@ -269,7 +272,10 @@
cameraId.c_str());
continue;
}
- i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+ auto ret = i->getListener()->onTorchStatusChanged(mapToInterface(status),
+ String16{cameraId});
+ i->handleBinderStatus(ret, "%s: Failed to trigger onTorchStatusChanged for %d:%d: %d",
+ __FUNCTION__, i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
}
}
@@ -538,8 +544,12 @@
id.c_str());
continue;
}
- listener->getListener()->onPhysicalCameraStatusChanged(mapToInterface(newStatus),
- id16, physicalId16);
+ auto ret = listener->getListener()->onPhysicalCameraStatusChanged(
+ mapToInterface(newStatus), id16, physicalId16);
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onPhysicalCameraStatusChanged for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
}
}
@@ -580,8 +590,11 @@
int32_t newStrengthLevel) {
Mutex::Autolock lock(mStatusListenerLock);
for (auto& i : mListenerList) {
- i->getListener()->onTorchStrengthLevelChanged(String16{cameraId},
+ auto ret = i->getListener()->onTorchStrengthLevelChanged(String16{cameraId},
newStrengthLevel);
+ i->handleBinderStatus(ret,
+ "%s: Failed to trigger onTorchStrengthLevelChanged for %d:%d: %d", __FUNCTION__,
+ i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
}
}
@@ -973,15 +986,16 @@
}
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new Camera2Client(cameraService, tmp, packageName, featureId,
- cameraId, api1CameraId, facing, sensorOrientation, clientPid, clientUid,
- servicePid, overrideForPerfClass);
+ *client = new Camera2Client(cameraService, tmp, cameraService->mCameraServiceProxyWrapper,
+ packageName, featureId, cameraId, api1CameraId, facing, sensorOrientation,
+ clientPid, clientUid, servicePid, overrideForPerfClass);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
- *client = new CameraDeviceClient(cameraService, tmp, packageName,
- systemNativeClient, featureId, cameraId, facing, sensorOrientation,
- clientPid, clientUid, servicePid, overrideForPerfClass);
+ *client = new CameraDeviceClient(cameraService, tmp,
+ cameraService->mCameraServiceProxyWrapper, packageName, systemNativeClient,
+ featureId, cameraId, facing, sensorOrientation, clientPid, clientUid, servicePid,
+ overrideForPerfClass);
}
return Status::ok();
}
@@ -1698,7 +1712,13 @@
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- if (CameraServiceProxyWrapper::isCameraDisabled()) {
+ userid_t clientUserId = multiuser_get_user_id(clientUid);
+ int callingUid = CameraThreadState::getCallingUid();
+ if (clientUid == USE_CALLING_UID) {
+ clientUserId = multiuser_get_user_id(callingUid);
+ }
+
+ if (mCameraServiceProxyWrapper->isCameraDisabled(clientUserId)) {
String8 msg =
String8::format("Camera disabled by device policy");
ALOGE("%s: %s", __FUNCTION__, msg.string());
@@ -1707,7 +1727,8 @@
// enforce system camera permissions
if (oomScoreOffset > 0 &&
- !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid())) {
+ !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid()) &&
+ !isTrustedCallingUid(CameraThreadState::getCallingUid())) {
String8 msg =
String8::format("Cannot change the priority of a client %s pid %d for "
"camera id %s without SYSTEM_CAMERA permissions",
@@ -1901,12 +1922,15 @@
}
}
+ // Enable/disable camera service watchdog
+ client->setCameraServiceWatchdog(mCameraServiceWatchdogEnabled);
+
// Set rotate-and-crop override behavior
if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
} else {
client->setRotateAndCropOverride(
- CameraServiceProxyWrapper::getRotateAndCropOverride(
+ mCameraServiceProxyWrapper->getRotateAndCropOverride(
clientPackageName, facing, multiuser_get_user_id(clientUid)));
}
@@ -1956,7 +1980,7 @@
device = client;
int32_t openLatencyMs = ns2ms(systemTime() - openTimeNs);
- CameraServiceProxyWrapper::logOpen(cameraId, facing, clientPackageName,
+ mCameraServiceProxyWrapper->logOpen(cameraId, facing, clientPackageName,
effectiveApiLevel, isNonSystemNdk, openLatencyMs);
{
@@ -2382,10 +2406,8 @@
for (const auto& it : mListenerList) {
auto ret = it->getListener()->onCameraAccessPrioritiesChanged();
- if (!ret.isOk()) {
- ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__,
- ret.exceptionCode());
- }
+ it->handleBinderStatus(ret, "%s: Failed to trigger permission callback for %d:%d: %d",
+ __FUNCTION__, it->getListenerUid(), it->getListenerPid(), ret.exceptionCode());
}
}
@@ -2444,7 +2466,7 @@
const auto basicClient = current->getValue();
if (basicClient.get() != nullptr) {
basicClient->setRotateAndCropOverride(
- CameraServiceProxyWrapper::getRotateAndCropOverride(
+ mCameraServiceProxyWrapper->getRotateAndCropOverride(
basicClient->getPackageName(),
basicClient->getCameraFacing(),
multiuser_get_user_id(basicClient->getClientUid())));
@@ -4647,8 +4669,12 @@
cameraId.c_str());
continue;
}
- listener->getListener()->onStatusChanged(mapToInterface(status),
+ auto ret = listener->getListener()->onStatusChanged(mapToInterface(status),
String16(cameraId));
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onStatusChanged callback for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
});
}
@@ -4681,10 +4707,10 @@
} else {
ret = it->getListener()->onCameraClosed(cameraId64);
}
- if (!ret.isOk()) {
- ALOGE("%s: Failed to trigger onCameraOpened/onCameraClosed callback: %d", __FUNCTION__,
- ret.exceptionCode());
- }
+
+ it->handleBinderStatus(ret,
+ "%s: Failed to trigger onCameraOpened/onCameraClosed callback for %d:%d: %d",
+ __FUNCTION__, it->getListenerUid(), it->getListenerPid(), ret.exceptionCode());
}
}
@@ -4785,8 +4811,12 @@
String8(physicalCameraId).c_str());
continue;
}
- listener->getListener()->onPhysicalCameraStatusChanged(status,
+ auto ret = listener->getListener()->onPhysicalCameraStatusChanged(status,
logicalCameraId, physicalCameraId);
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onPhysicalCameraStatusChanged for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
}
}
@@ -4842,6 +4872,8 @@
return handleSetCameraMute(args);
} else if (args.size() >= 2 && args[0] == String16("watch")) {
return handleWatchCommand(args, in, out);
+ } else if (args.size() >= 2 && args[0] == String16("set-watchdog")) {
+ return handleSetCameraServiceWatchdog(args);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return OK;
@@ -4935,6 +4967,28 @@
return OK;
}
+status_t CameraService::handleSetCameraServiceWatchdog(const Vector<String16>& args) {
+ int enableWatchdog = atoi(String8(args[1]));
+
+ if (enableWatchdog < 0 || enableWatchdog > 1) return BAD_VALUE;
+
+ Mutex::Autolock lock(mServiceLock);
+
+ mCameraServiceWatchdogEnabled = enableWatchdog;
+
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ basicClient->setCameraServiceWatchdog(enableWatchdog);
+ }
+ }
+ }
+
+ return OK;
+}
+
status_t CameraService::handleGetRotateAndCrop(int out) {
Mutex::Autolock lock(mServiceLock);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index d96ea00..0395475 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -48,6 +48,7 @@
#include "utils/AutoConditionLock.h"
#include "utils/ClientManager.h"
#include "utils/IPCTransport.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <set>
#include <string>
@@ -100,7 +101,10 @@
// Implementation of BinderService<T>
static char const* getServiceName() { return "media.camera"; }
- CameraService();
+ // Non-null arguments for cameraServiceProxyWrapper should be provided for
+ // testing purposes only.
+ CameraService(std::shared_ptr<CameraServiceProxyWrapper>
+ cameraServiceProxyWrapper = nullptr);
virtual ~CameraService();
/////////////////////////////////////////////////////////////////////
@@ -339,6 +343,9 @@
// Set/reset camera mute
virtual status_t setCameraMute(bool enabled) = 0;
+ // Set Camera service watchdog
+ virtual status_t setCameraServiceWatchdog(bool enabled) = 0;
+
// The injection camera session to replace the internal camera
// session.
virtual status_t injectCamera(const String8& injectedCamId,
@@ -769,6 +776,8 @@
sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
+
// Delay-load the Camera HAL module
virtual void onFirstRef();
@@ -1055,6 +1064,29 @@
return IInterface::asBinder(mListener)->linkToDeath(this);
}
+ template<typename... args_t>
+ void handleBinderStatus(const binder::Status &ret, const char *logOnError,
+ args_t... args) {
+ if (!ret.isOk() &&
+ (ret.exceptionCode() != binder::Status::Exception::EX_TRANSACTION_FAILED
+ || !mLastTransactFailed)) {
+ ALOGE(logOnError, args...);
+ }
+
+ // If the transaction failed, the process may have died (or other things, see
+ // b/28321379). Mute consecutive errors from this listener to avoid log spam.
+ if (ret.exceptionCode() == binder::Status::Exception::EX_TRANSACTION_FAILED) {
+ if (!mLastTransactFailed) {
+ ALOGE("%s: Muting similar errors from listener %d:%d", __FUNCTION__,
+ mListenerUid, mListenerPid);
+ }
+ mLastTransactFailed = true;
+ } else {
+ // Reset mLastTransactFailed when binder becomes healthy again.
+ mLastTransactFailed = false;
+ }
+ }
+
virtual void binderDied(const wp<IBinder> &/*who*/) {
auto parent = mParent.promote();
if (parent.get() != nullptr) {
@@ -1075,6 +1107,9 @@
int mListenerPid = -1;
bool mIsVendorListener = false;
bool mOpenCloseCallbackAllowed = false;
+
+ // Flag for preventing log spam when binder becomes unhealthy
+ bool mLastTransactFailed = false;
};
// Guarded by mStatusListenerMutex
@@ -1198,6 +1233,9 @@
// Handle 'watch' command as passed through 'cmd'
status_t handleWatchCommand(const Vector<String16> &args, int inFd, int outFd);
+ // Set the camera service watchdog
+ status_t handleSetCameraServiceWatchdog(const Vector<String16>& args);
+
// Enable tag monitoring of the given tags in provided clients
status_t startWatchingTags(const Vector<String16> &args, int outFd);
@@ -1284,6 +1322,9 @@
// Current camera mute mode
bool mOverrideCameraMuteMode = false;
+ // Camera Service watchdog flag
+ bool mCameraServiceWatchdogEnabled = true;
+
/**
* A listener class that implements the IBinder::DeathRecipient interface
* for use to call back the error state injected by the external camera, and
diff --git a/services/camera/libcameraservice/CameraServiceWatchdog.cpp b/services/camera/libcameraservice/CameraServiceWatchdog.cpp
index a169667..e101dd3 100644
--- a/services/camera/libcameraservice/CameraServiceWatchdog.cpp
+++ b/services/camera/libcameraservice/CameraServiceWatchdog.cpp
@@ -66,6 +66,17 @@
}
}
+void CameraServiceWatchdog::setEnabled(bool enable)
+{
+ AutoMutex _l(mEnabledLock);
+
+ if (enable) {
+ mEnabled = true;
+ } else {
+ mEnabled = false;
+ }
+}
+
void CameraServiceWatchdog::stop(uint32_t tid)
{
AutoMutex _l(mWatchdogLock);
diff --git a/services/camera/libcameraservice/CameraServiceWatchdog.h b/services/camera/libcameraservice/CameraServiceWatchdog.h
index f4955e2..e35d69e 100644
--- a/services/camera/libcameraservice/CameraServiceWatchdog.h
+++ b/services/camera/libcameraservice/CameraServiceWatchdog.h
@@ -21,12 +21,14 @@
* expected duration has exceeded.
* Notes on multi-threaded behaviors:
* - The threadloop is blocked/paused when there are no calls being
- * monitored.
+ * monitored (when the TID cycle to counter map is empty).
* - The start and stop functions handle simultaneous call monitoring
* and single call monitoring differently. See function documentation for
* more details.
+ * To disable/enable:
+ * - adb shell cmd media.camera set-cameraservice-watchdog [0/1]
*/
-
+#pragma once
#include <chrono>
#include <thread>
#include <time.h>
@@ -49,26 +51,41 @@
public:
explicit CameraServiceWatchdog() : mPause(true), mMaxCycles(kMaxCycles),
- mCycleLengthMs(kCycleLengthMs) {};
+ mCycleLengthMs(kCycleLengthMs), mEnabled(true) {};
- explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs) :
- mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs) {};
+ explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs, bool enabled) :
+ mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs), mEnabled(enabled)
+ {};
virtual ~CameraServiceWatchdog() {};
virtual void requestExit();
+ /** Enables/disables the watchdog */
+ void setEnabled(bool enable);
+
/** Used to wrap monitored calls in start and stop functions using custom timer values */
template<typename T>
auto watchThread(T func, uint32_t tid, uint32_t cycles, uint32_t cycleLength) {
- auto res = NULL;
+ decltype(func()) res;
if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) {
// Create another instance of the watchdog to prevent disruption
// of timer for current monitored calls
+
+ // Lock for mEnabled
+ mEnabledLock.lock();
sp<CameraServiceWatchdog> tempWatchdog =
- new CameraServiceWatchdog(cycles, cycleLength);
- tempWatchdog->run("CameraServiceWatchdog");
+ new CameraServiceWatchdog(cycles, cycleLength, mEnabled);
+ mEnabledLock.unlock();
+
+ status_t status = tempWatchdog->run("CameraServiceWatchdog");
+ if (status != OK) {
+ ALOGE("Unable to watch thread: %s (%d)", strerror(-status), status);
+ res = watchThread(func, tid);
+ return res;
+ }
+
res = tempWatchdog->watchThread(func, tid);
tempWatchdog->requestExit();
tempWatchdog.clear();
@@ -84,11 +101,16 @@
/** Used to wrap monitored calls in start and stop functions using class timer values */
template<typename T>
auto watchThread(T func, uint32_t tid) {
- auto res = NULL;
+ decltype(func()) res;
+ AutoMutex _l(mEnabledLock);
- start(tid);
- res = func();
- stop(tid);
+ if (mEnabled) {
+ start(tid);
+ res = func();
+ stop(tid);
+ } else {
+ res = func();
+ }
return res;
}
@@ -109,11 +131,13 @@
virtual bool threadLoop();
- Mutex mWatchdogLock; // Lock for condition variable
- Condition mWatchdogCondition; // Condition variable for stop/start
- bool mPause; // True if thread is currently paused
- uint32_t mMaxCycles; // Max cycles
- uint32_t mCycleLengthMs; // Length of time elapsed per cycle
+ Mutex mWatchdogLock; // Lock for condition variable
+ Mutex mEnabledLock; // Lock for enabled status
+ Condition mWatchdogCondition; // Condition variable for stop/start
+ bool mPause; // True if tid map is empty
+ uint32_t mMaxCycles; // Max cycles
+ uint32_t mCycleLengthMs; // Length of time elapsed per cycle
+ bool mEnabled; // True if watchdog is enabled
std::unordered_map<uint32_t, uint32_t> tidToCycleCounterMap; // Thread Id to cycle counter map
};
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 68dc7f8..88f27de 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -52,6 +52,7 @@
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
const String8& cameraDeviceId,
@@ -62,7 +63,7 @@
uid_t clientUid,
int servicePid,
bool overrideForPerfClass):
- Camera2ClientBase(cameraService, cameraClient, clientPackageName,
+ Camera2ClientBase(cameraService, cameraClient, cameraServiceProxyWrapper, clientPackageName,
false/*systemNativeClient - since no ndk for api1*/, clientFeatureId,
cameraDeviceId, api1CameraId, cameraFacing, sensorOrientation, clientPid,
clientUid, servicePid, overrideForPerfClass, /*legacyClient*/ true),
@@ -139,28 +140,53 @@
mFrameProcessor = new FrameProcessor(mDevice, this);
threadName = String8::format("C2-%d-FrameProc",
mCameraId);
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mCaptureSequencer = new CaptureSequencer(this);
threadName = String8::format("C2-%d-CaptureSeq",
mCameraId);
- mCaptureSequencer->run(threadName.string());
+ res = mCaptureSequencer->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start capture sequencer thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mJpegProcessor = new JpegProcessor(this, mCaptureSequencer);
threadName = String8::format("C2-%d-JpegProc",
mCameraId);
- mJpegProcessor->run(threadName.string());
+ res = mJpegProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start jpeg processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mZslProcessor = new ZslProcessor(this, mCaptureSequencer);
threadName = String8::format("C2-%d-ZslProc",
mCameraId);
- mZslProcessor->run(threadName.string());
+ res = mZslProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start zsl processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mCallbackProcessor = new CallbackProcessor(this);
threadName = String8::format("C2-%d-CallbkProc",
mCameraId);
- mCallbackProcessor->run(threadName.string());
+ res = mCallbackProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start callback processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
if (gLogLevel >= 1) {
SharedParameters::Lock l(mParameters);
@@ -478,7 +504,7 @@
CameraService::Client::disconnect();
int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
- CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
+ mCameraServiceProxyWrapper->logClose(mCameraIdStr, closeLatencyMs);
return res;
}
@@ -2316,6 +2342,10 @@
return INVALID_OPERATION;
}
+status_t Camera2Client::setCameraServiceWatchdog(bool enabled) {
+ return mDevice->setCameraServiceWatchdog(enabled);
+}
+
status_t Camera2Client::setRotateAndCropOverride(uint8_t rotateAndCrop) {
if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index c8dfc46..fe91cba 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -90,12 +90,15 @@
virtual bool supportsCameraMute();
virtual status_t setCameraMute(bool enabled);
+ virtual status_t setCameraServiceWatchdog(bool enabled);
+
/**
* Interface used by CameraService
*/
Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
const String8& cameraDeviceId,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 5e91501..ba26ac4 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -31,7 +31,6 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "api2/CameraDeviceClient.h"
-#include "utils/CameraServiceProxyWrapper.h"
#include <camera_metadata_hidden.h>
@@ -87,6 +86,7 @@
CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -97,9 +97,9 @@
uid_t clientUid,
int servicePid,
bool overrideForPerfClass) :
- Camera2ClientBase(cameraService, remoteCallback, clientPackageName, systemNativeClient,
- clientFeatureId, cameraId, /*API1 camera ID*/ -1, cameraFacing, sensorOrientation,
- clientPid, clientUid, servicePid, overrideForPerfClass),
+ Camera2ClientBase(cameraService, remoteCallback, cameraServiceProxyWrapper, clientPackageName,
+ systemNativeClient, clientFeatureId, cameraId, /*API1 camera ID*/ -1, cameraFacing,
+ sensorOrientation, clientPid, clientUid, servicePid, overrideForPerfClass),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0),
@@ -127,7 +127,12 @@
String8 threadName;
mFrameProcessor = new FrameProcessorBase(mDevice);
threadName = String8::format("CDU-%s-FrameProc", mCameraIdStr.string());
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
@@ -692,7 +697,7 @@
nsecs_t configureEnd = systemTime();
int32_t configureDurationMs = ns2ms(configureEnd) - startTimeMs;
- CameraServiceProxyWrapper::logStreamConfigured(mCameraIdStr, operatingMode,
+ mCameraServiceProxyWrapper->logStreamConfigured(mCameraIdStr, operatingMode,
false /*internalReconfig*/, configureDurationMs);
}
@@ -1730,6 +1735,10 @@
return binder::Status::ok();
}
+status_t CameraDeviceClient::setCameraServiceWatchdog(bool enabled) {
+ return mDevice->setCameraServiceWatchdog(enabled);
+}
+
status_t CameraDeviceClient::setRotateAndCropOverride(uint8_t rotateAndCrop) {
if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
@@ -2057,7 +2066,7 @@
Camera2ClientBase::detachDevice();
int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
- CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
+ mCameraServiceProxyWrapper->logClose(mCameraIdStr, closeLatencyMs);
}
/** Device-related methods */
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index c5aad6b..06844c6 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -29,6 +29,7 @@
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
#include "CompositeStream.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include "utils/SessionConfigurationUtils.h"
using android::camera3::OutputStreamInfo;
@@ -178,6 +179,7 @@
CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool clientPackageOverride,
const std::optional<String16>& clientFeatureId,
@@ -206,6 +208,8 @@
virtual status_t stopWatchingTags(int out);
virtual status_t dumpWatchedEventsToVector(std::vector<std::string> &out);
+ virtual status_t setCameraServiceWatchdog(bool enabled);
+
/**
* Device listener interface
*/
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 9303fd2..4d4d481 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -44,7 +44,12 @@
String8 threadName;
mFrameProcessor = new camera2::FrameProcessorBase(mOfflineSession);
threadName = String8::format("Offline-%s-FrameProc", mCameraIdStr.string());
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
@@ -66,6 +71,10 @@
return OK;
}
+status_t CameraOfflineSessionClient::setCameraServiceWatchdog(bool) {
+ return OK;
+}
+
status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
// Since we're not submitting more capture requests, changes to rotateAndCrop override
// make no difference.
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index f2c42d8..9ea1093 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -84,6 +84,8 @@
bool supportsCameraMute() override;
status_t setCameraMute(bool enabled) override;
+ status_t setCameraServiceWatchdog(bool enabled) override;
+
// permissions management
status_t startCameraOps() override;
status_t finishCameraOps() override;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index dc5002b..f926b88 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -37,9 +37,11 @@
#include "device3/aidl/AidlCamera3Device.h"
#include "device3/hidl/HidlCamera3Device.h"
#include "utils/CameraThreadState.h"
-#include "utils/CameraServiceProxyWrapper.h"
namespace android {
+
+const static size_t kDisconnectTimeoutMs = 2500;
+
using namespace camera2;
// Interface used by CameraService
@@ -48,6 +50,7 @@
Camera2ClientBase<TClientBase>::Camera2ClientBase(
const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -64,6 +67,7 @@
clientFeatureId, cameraId, api1CameraId, cameraFacing, sensorOrientation, clientPid,
clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper),
mDeviceActive(false), mApi1CameraId(api1CameraId)
{
ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
@@ -101,11 +105,6 @@
TClientBase::mCameraIdStr.string());
status_t res;
- // Verify ops permissions
- res = TClientBase::startCameraOps();
- if (res != OK) {
- return res;
- }
IPCTransport providerTransport = IPCTransport::INVALID;
res = providerPtr->getCameraIdIPCTransport(TClientBase::mCameraIdStr.string(),
&providerTransport);
@@ -115,13 +114,13 @@
switch (providerTransport) {
case IPCTransport::HIDL:
mDevice =
- new HidlCamera3Device(TClientBase::mCameraIdStr, mOverrideForPerfClass,
- mLegacyClient);
+ new HidlCamera3Device(mCameraServiceProxyWrapper,
+ TClientBase::mCameraIdStr, mOverrideForPerfClass, mLegacyClient);
break;
case IPCTransport::AIDL:
mDevice =
- new AidlCamera3Device(TClientBase::mCameraIdStr, mOverrideForPerfClass,
- mLegacyClient);
+ new AidlCamera3Device(mCameraServiceProxyWrapper,
+ TClientBase::mCameraIdStr, mOverrideForPerfClass, mLegacyClient);
break;
default:
ALOGE("%s Invalid transport for camera id %s", __FUNCTION__,
@@ -141,8 +140,30 @@
return res;
}
+ // Verify ops permissions
+ res = TClientBase::startCameraOps();
+ if (res != OK) {
+ TClientBase::finishCameraOps();
+ mDevice.clear();
+ return res;
+ }
+
wp<NotificationListener> weakThis(this);
res = mDevice->setNotifyCallback(weakThis);
+ if (res != OK) {
+ ALOGE("%s: Camera %s: Unable to set notify callback: %s (%d)",
+ __FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
+ return res;
+ }
+
+ /** Start watchdog thread */
+ mCameraServiceWatchdog = new CameraServiceWatchdog();
+ res = mCameraServiceWatchdog->run("Camera2ClientBaseWatchdog");
+ if (res != OK) {
+ ALOGE("%s: Unable to start camera service watchdog thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
return OK;
}
@@ -155,8 +176,13 @@
disconnect();
- ALOGI("Closed Camera %s. Client was: %s (PID %d, UID %u)",
- TClientBase::mCameraIdStr.string(),
+ if (mCameraServiceWatchdog != NULL) {
+ mCameraServiceWatchdog->requestExit();
+ mCameraServiceWatchdog.clear();
+ }
+
+ ALOGI("%s: Client object's dtor for Camera Id %s completed. Client was: %s (PID %d, UID %u)",
+ __FUNCTION__, TClientBase::mCameraIdStr.string(),
String8(TClientBase::mClientPackageName).string(),
mInitialClientPid, TClientBase::mClientUid);
}
@@ -238,9 +264,18 @@
// ICameraClient2BaseUser interface
-
template <typename TClientBase>
binder::Status Camera2ClientBase<TClientBase>::disconnect() {
+ if (mCameraServiceWatchdog != nullptr) {
+ // Initialization from hal succeeded, time disconnect.
+ return mCameraServiceWatchdog->WATCH_CUSTOM_TIMER(disconnectImpl(),
+ kDisconnectTimeoutMs / kCycleLengthMs, kCycleLengthMs);
+ }
+ return disconnectImpl();
+}
+
+template <typename TClientBase>
+binder::Status Camera2ClientBase<TClientBase>::disconnectImpl() {
ATRACE_CALL();
ALOGD("Camera %s: start to disconnect", TClientBase::mCameraIdStr.string());
Mutex::Autolock icl(mBinderSerializationLock);
@@ -325,7 +360,7 @@
TClientBase::mCameraIdStr.string(), res);
return res;
}
- CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr, maxPreviewFps);
+ mCameraServiceProxyWrapper->logActive(TClientBase::mCameraIdStr, maxPreviewFps);
}
mDeviceActive = true;
@@ -344,7 +379,7 @@
ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
TClientBase::mCameraIdStr.string(), res);
}
- CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
+ mCameraServiceProxyWrapper->logIdle(TClientBase::mCameraIdStr,
requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode,
streamStats);
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index b0d1c3f..6e37589 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -19,6 +19,8 @@
#include "common/CameraDeviceBase.h"
#include "camera/CaptureResult.h"
+#include "utils/CameraServiceProxyWrapper.h"
+#include "CameraServiceWatchdog.h"
namespace android {
@@ -47,6 +49,7 @@
// TODO: too many params, move into a ClientArgs<T>
Camera2ClientBase(const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -135,6 +138,7 @@
pid_t mInitialClientPid;
bool mOverrideForPerfClass = false;
bool mLegacyClient = false;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
virtual sp<IBinder> asBinderWrapper() {
return IInterface::asBinder(this);
@@ -173,6 +177,12 @@
private:
template<typename TProviderPtr>
status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
+
+ binder::Status disconnectImpl();
+
+ // Watchdog thread
+ sp<CameraServiceWatchdog> mCameraServiceWatchdog;
+
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7e2f93c..69514f3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -449,6 +449,11 @@
virtual status_t setCameraMute(bool enabled) = 0;
/**
+ * Enable/disable camera service watchdog
+ */
+ virtual status_t setCameraServiceWatchdog(bool enabled) = 0;
+
+ /**
* Get the status tracker of the camera device
*/
virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index abaea66..cd23250 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -579,7 +579,11 @@
std::lock_guard<std::mutex> lock(mInterfaceMutex);
mDeviceState = newState;
status_t res = OK;
- for (auto& provider : mProviders) {
+ // Make a copy of mProviders because we unlock mInterfaceMutex temporarily
+ // within the loop. It's possible that during the time mInterfaceMutex is
+ // unlocked, mProviders has changed.
+ auto providers = mProviders;
+ for (auto& provider : providers) {
ALOGV("%s: Notifying %s for new state 0x%" PRIx64,
__FUNCTION__, provider->mProviderName.c_str(), newState);
// b/199240726 Camera providers can for example try to add/remove
@@ -1408,6 +1412,27 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addReadoutTimestampTag(
+ bool readoutTimestampSupported) {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+
+ auto entry = c.find(ANDROID_SENSOR_READOUT_TIMESTAMP);
+ if (entry.count != 0) {
+ ALOGE("%s: CameraCharacteristics must not contain ANDROID_SENSOR_READOUT_TIMESTAMP!",
+ __FUNCTION__);
+ }
+
+ uint8_t readoutTimestamp = ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED;
+ if (readoutTimestampSupported) {
+ readoutTimestamp = ANDROID_SENSOR_READOUT_TIMESTAMP_HARDWARE;
+ }
+
+ res = c.update(ANDROID_SENSOR_READOUT_TIMESTAMP, &readoutTimestamp, 1);
+
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
status_t res = OK;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index a66598d..d049aff 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -663,6 +663,7 @@
status_t deriveHeicTags(bool maxResolution = false);
status_t addRotateCropTags();
status_t addPreCorrectionActiveArraySize();
+ status_t addReadoutTimestampTag(bool readoutTimestampSupported = true);
static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
android_pixel_format_t format,
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
index 81b4779..ef68f28 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
@@ -532,6 +532,11 @@
ALOGE("%s: Unable to override zoomRatio related tags: %s (%d)",
__FUNCTION__, strerror(-res), res);
}
+ res = addReadoutTimestampTag();
+ if (OK != res) {
+ ALOGE("%s: Unable to add sensorReadoutTimestamp tag: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
camera_metadata_entry flashAvailable =
mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
diff --git a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
index bded9aa..d60565f 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
@@ -655,6 +655,11 @@
ALOGE("%s: Unable to override zoomRatio related tags: %s (%d)",
__FUNCTION__, strerror(-res), res);
}
+ res = addReadoutTimestampTag(/*readoutTimestampSupported*/false);
+ if (OK != res) {
+ ALOGE("%s: Unable to add sensorReadoutTimestamp tag: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
camera_metadata_entry flashAvailable =
mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 7c2f34f..644f682 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -63,7 +63,6 @@
#include "utils/CameraThreadState.h"
#include "utils/SessionConfigurationUtils.h"
#include "utils/TraceHFR.h"
-#include "utils/CameraServiceProxyWrapper.h"
#include <algorithm>
#include <tuple>
@@ -73,7 +72,9 @@
namespace android {
-Camera3Device::Camera3Device(const String8 &id, bool overrideForPerfClass, bool legacyClient):
+Camera3Device::Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8 &id, bool overrideForPerfClass, bool legacyClient):
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper),
mId(id),
mLegacyClient(legacyClient),
mOperatingMode(NO_MODE),
@@ -113,10 +114,6 @@
status_t Camera3Device::initializeCommonLocked() {
- /** Start watchdog thread */
- mCameraServiceWatchdog = new CameraServiceWatchdog();
- mCameraServiceWatchdog->run("CameraServiceWatchdog");
-
/** Start up status tracker thread */
mStatusTracker = new StatusTracker(this);
status_t res = mStatusTracker->run(String8::format("C3Dev-%s-Status", mId.string()).string());
@@ -230,6 +227,15 @@
// Hidl/AidlCamera3DeviceInjectionMethods
mInjectionMethods = createCamera3DeviceInjectionMethods(this);
+ /** Start watchdog thread */
+ mCameraServiceWatchdog = new CameraServiceWatchdog();
+ res = mCameraServiceWatchdog->run("CameraServiceWatchdog");
+ if (res != OK) {
+ SET_ERR_L("Unable to start camera service watchdog thread: %s (%d)",
+ strerror(-res), res);
+ return res;
+ }
+
return OK;
}
@@ -1506,6 +1512,7 @@
maxExpectedDuration);
status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
+ mStatusTracker->dumpActiveComponents();
SET_ERR_L("Can't idle device in %f seconds!",
maxExpectedDuration/1e9);
}
@@ -2225,7 +2232,6 @@
if (mStatus == STATUS_ACTIVE) {
markClientActive = true;
mPauseStateNotify = true;
- mStatusTracker->markComponentIdle(clientStatusId, Fence::NO_FENCE);
rc = internalPauseAndWaitLocked(maxExpectedDuration);
}
@@ -2257,7 +2263,7 @@
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
- CameraServiceProxyWrapper::logStreamConfigured(mId, mOperatingMode, true /*internalReconfig*/,
+ mCameraServiceProxyWrapper->logStreamConfigured(mId, mOperatingMode, true /*internalReconfig*/,
ns2ms(systemTime() - startTime));
if (markClientActive) {
@@ -3442,6 +3448,10 @@
if (res == OK) {
sp<Camera3Device> parent = mParent.promote();
if (parent != nullptr) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ }
mReconfigured |= parent->reconfigureCamera(mLatestSessionParams, mStatusId);
}
setPaused(false);
@@ -4094,6 +4104,17 @@
}
}
+status_t Camera3Device::setCameraServiceWatchdog(bool enabled) {
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (mCameraServiceWatchdog != NULL) {
+ mCameraServiceWatchdog->setEnabled(enabled);
+ }
+
+ return OK;
+}
+
void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
if (mNextRequests.empty()) {
return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index f927b4d..d757eb9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -49,6 +49,7 @@
#include "utils/TagMonitor.h"
#include "utils/IPCTransport.h"
#include "utils/LatencyHistogram.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <camera_metadata_hidden.h>
using android::camera3::camera_capture_request_t;
@@ -82,7 +83,8 @@
friend class AidlCamera3Device;
public:
- explicit Camera3Device(const String8& id, bool overrideForPerfClass, bool legacyClient = false);
+ explicit Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false);
virtual ~Camera3Device();
// Delete and optionally close native handles and clear the input vector afterward
@@ -281,6 +283,11 @@
*/
status_t setCameraMute(bool enabled);
+ /**
+ * Enables/disables camera service watchdog
+ */
+ status_t setCameraServiceWatchdog(bool enabled);
+
// Get the status trackeer for the camera device
wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
@@ -320,6 +327,8 @@
// Constant to use for stream ID when one doesn't exist
static const int NO_STREAM = -1;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
+
// A lock to enforce serialization on the input/configure side
// of the public interface.
// Not locked by methods guarded by mOutputLock, since they may act
@@ -453,6 +462,28 @@
// Verify buffer caches
std::vector<uint64_t> bufIds(offlineStream.circulatingBufferIds.begin(),
offlineStream.circulatingBufferIds.end());
+ {
+ // Due to timing it is possible that we may not have any remaining pending
+ // capture requests that can update the caches on Hal side. This can result in
+ // buffer cache mismatch between the service and the Hal and must be accounted
+ // for.
+ std::lock_guard<std::mutex> l(mFreedBuffersLock);
+ for (const auto& it : mFreedBuffers) {
+ if (it.first == id) {
+ ALOGV("%s: stream ID %d buffer id %" PRIu64 " cache removal still "
+ "pending", __FUNCTION__, id, it.second);
+ const auto& cachedEntry = std::find(bufIds.begin(), bufIds.end(),
+ it.second);
+ if (cachedEntry != bufIds.end()) {
+ bufIds.erase(cachedEntry);
+ } else {
+ ALOGE("%s: stream ID %d buffer id %" PRIu64 " cache removal still "
+ "pending however buffer is no longer in the offline stream "
+ "info!", __FUNCTION__, id, it.second);
+ }
+ }
+ }
+ }
if (!verifyBufferIds(id, bufIds)) {
ALOGE("%s: stream ID %d buffer cache records mismatch!", __FUNCTION__, id);
return UNKNOWN_ERROR;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 1e20ee0..5a1ee9b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -67,6 +67,7 @@
mTraceFirstBuffer(true),
mUseBufferManager(false),
mTimestampOffset(timestampOffset),
+ mUseReadoutTime(false),
mConsumerUsage(0),
mDropBuffers(false),
mMirrorMode(mirrorMode),
@@ -100,6 +101,7 @@
mTraceFirstBuffer(true),
mUseBufferManager(false),
mTimestampOffset(timestampOffset),
+ mUseReadoutTime(false),
mConsumerUsage(0),
mDropBuffers(false),
mMirrorMode(mirrorMode),
@@ -140,6 +142,7 @@
mTraceFirstBuffer(true),
mUseBufferManager(false),
mTimestampOffset(timestampOffset),
+ mUseReadoutTime(false),
mConsumerUsage(consumerUsage),
mDropBuffers(false),
mMirrorMode(mirrorMode),
@@ -188,6 +191,7 @@
mTraceFirstBuffer(true),
mUseBufferManager(false),
mTimestampOffset(timestampOffset),
+ mUseReadoutTime(false),
mConsumerUsage(consumerUsage),
mDropBuffers(false),
mMirrorMode(mirrorMode),
@@ -327,7 +331,7 @@
status_t res =
gbLocker.lockAsync(
GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_RARELY,
- &mapped, fenceFd.get());
+ &mapped, fenceFd.release());
if (res != OK) {
ALOGE("%s: Failed to lock the buffer: %s (%d)", __FUNCTION__, strerror(-res), res);
return res;
@@ -462,17 +466,19 @@
}
}
+ nsecs_t captureTime = (mUseReadoutTime && readoutTimestamp != 0 ?
+ readoutTimestamp : timestamp) - mTimestampOffset;
if (mPreviewFrameSpacer != nullptr) {
- res = mPreviewFrameSpacer->queuePreviewBuffer(timestamp - mTimestampOffset,
- readoutTimestamp - mTimestampOffset, transform, anwBuffer, anwReleaseFence);
+ nsecs_t readoutTime = (readoutTimestamp != 0 ? readoutTimestamp : timestamp)
+ - mTimestampOffset;
+ res = mPreviewFrameSpacer->queuePreviewBuffer(captureTime, readoutTime,
+ transform, anwBuffer, anwReleaseFence);
if (res != OK) {
ALOGE("%s: Stream %d: Error queuing buffer to preview buffer spacer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
return res;
}
} else {
- nsecs_t captureTime = (mSyncToDisplay ? readoutTimestamp : timestamp)
- - mTimestampOffset;
nsecs_t presentTime = mSyncToDisplay ?
syncTimestampToDisplayLocked(captureTime) : captureTime;
@@ -501,10 +507,6 @@
mStreamUnpreparable = true;
}
- if (res != OK) {
- close(anwReleaseFence);
- }
-
*releaseFenceOut = releaseFence;
return res;
@@ -696,7 +698,8 @@
mTotalBufferCount ++;
res = mPreviewFrameSpacer->run(String8::format("PreviewSpacer-%d", mId).string());
if (res != OK) {
- ALOGE("%s: Unable to start preview spacer", __FUNCTION__);
+ ALOGE("%s: Unable to start preview spacer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
return res;
}
}
@@ -705,12 +708,16 @@
mFrameCount = 0;
mLastTimestamp = 0;
+ mUseReadoutTime =
+ (timestampBase == OutputConfiguration::TIMESTAMP_BASE_READOUT_SENSOR || mSyncToDisplay);
+
if (isDeviceTimeBaseRealtime()) {
if (isDefaultTimeBase && !isConsumedByHWComposer() && !isVideoStream()) {
// Default time base, but not hardware composer or video encoder
mTimestampOffset = 0;
} else if (timestampBase == OutputConfiguration::TIMESTAMP_BASE_REALTIME ||
- timestampBase == OutputConfiguration::TIMESTAMP_BASE_SENSOR) {
+ timestampBase == OutputConfiguration::TIMESTAMP_BASE_SENSOR ||
+ timestampBase == OutputConfiguration::TIMESTAMP_BASE_READOUT_SENSOR) {
mTimestampOffset = 0;
}
// If timestampBase is CHOREOGRAPHER SYNCED or MONOTONIC, leave
@@ -720,7 +727,7 @@
// Reverse offset for monotonicTime -> bootTime
mTimestampOffset = -mTimestampOffset;
} else {
- // If timestampBase is DEFAULT, MONOTONIC, SENSOR, or
+ // If timestampBase is DEFAULT, MONOTONIC, SENSOR, READOUT_SENSOR or
// CHOREOGRAPHER_SYNCED, timestamp offset is 0.
mTimestampOffset = 0;
}
@@ -1298,7 +1305,7 @@
void* mapped = nullptr;
base::unique_fd fenceFd(dup(fence));
status_t res = graphicBuffer->lockAsync(GraphicBuffer::USAGE_SW_READ_OFTEN, &mapped,
- fenceFd.get());
+ fenceFd.release());
if (res != OK) {
ALOGE("%s: Failed to lock the buffer: %s (%d)", __FUNCTION__, strerror(-res), res);
return;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index e8065ce..3587af4 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -341,6 +341,11 @@
nsecs_t mTimestampOffset;
/**
+ * If camera readout time is used rather than the start-of-exposure time.
+ */
+ bool mUseReadoutTime;
+
+ /**
* Consumer end point usage flag set by the constructor for the deferred
* consumer case.
*/
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index ed66df0..f4e3fad 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -787,10 +787,12 @@
SessionStatsBuilder& sessionStatsBuilder) {
bool timestampIncreasing =
!((request.zslCapture && request.stillCapture) || request.hasInputBuffer);
+ nsecs_t readoutTimestamp = request.resultExtras.hasReadoutTimestamp ?
+ request.resultExtras.readoutTimestamp : 0;
returnOutputBuffers(useHalBufManager, listener,
request.pendingOutputBuffers.array(),
request.pendingOutputBuffers.size(),
- request.shutterTimestamp, request.shutterReadoutTimestamp,
+ request.shutterTimestamp, readoutTimestamp,
/*requested*/true, request.requestTimeNs, sessionStatsBuilder, timestampIncreasing,
request.outputSurfaces, request.resultExtras,
request.errorBufStrategy, request.transform);
@@ -852,7 +854,10 @@
}
r.shutterTimestamp = msg.timestamp;
- r.shutterReadoutTimestamp = msg.readout_timestamp;
+ if (msg.readout_timestamp_valid) {
+ r.resultExtras.hasReadoutTimestamp = true;
+ r.resultExtras.readoutTimestamp = msg.readout_timestamp;
+ }
if (r.minExpectedDuration != states.minFrameDuration) {
for (size_t i = 0; i < states.outputStreams.size(); i++) {
auto outputStream = states.outputStreams[i];
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index 493a9e2..fa00495 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -65,6 +65,7 @@
typedef struct camera_shutter_msg {
uint32_t frame_number;
uint64_t timestamp;
+ bool readout_timestamp_valid;
uint64_t readout_timestamp;
} camera_shutter_msg_t;
@@ -104,8 +105,6 @@
struct InFlightRequest {
// Set by notify() SHUTTER call.
nsecs_t shutterTimestamp;
- // Set by notify() SHUTTER call with readout time.
- nsecs_t shutterReadoutTimestamp;
// Set by process_capture_result().
nsecs_t sensorTimestamp;
int requestStatus;
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
index f05520f..973bc04 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
@@ -161,9 +161,11 @@
return (uint64_t)usage;
}
-AidlCamera3Device::AidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient) : Camera3Device(id, overrideForPerfClass, legacyClient) {
- mCallbacks = ndk::SharedRefBase::make<AidlCameraDeviceCallbacks>(this);
+AidlCamera3Device::AidlCamera3Device(
+ std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient) :
+ Camera3Device(cameraServiceProxyWrapper, id, overrideForPerfClass, legacyClient) {
+ mCallbacks = ndk::SharedRefBase::make<AidlCameraDeviceCallbacks>(this);
}
status_t AidlCamera3Device::initialize(sp<CameraProviderManager> manager,
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
index d20a7eb..ecf42b4 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
@@ -39,8 +39,9 @@
using AidlRequestMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
class AidlCameraDeviceCallbacks;
friend class AidlCameraDeviceCallbacks;
- explicit AidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient = false);
+ explicit AidlCamera3Device(
+ std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false);
virtual ~AidlCamera3Device() { }
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3OutputUtils.cpp b/services/camera/libcameraservice/device3/aidl/AidlCamera3OutputUtils.cpp
index 02eebd2..b2accc1 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3OutputUtils.cpp
@@ -110,6 +110,7 @@
m.type = CAMERA_MSG_SHUTTER;
m.message.shutter.frame_number = msg.get<Tag::shutter>().frameNumber;
m.message.shutter.timestamp = msg.get<Tag::shutter>().timestamp;
+ m.message.shutter.readout_timestamp_valid = true;
m.message.shutter.readout_timestamp = msg.get<Tag::shutter>().readoutTimestamp;
break;
}
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
index 2e98fe0..faac83f 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
@@ -31,8 +31,9 @@
public Camera3Device {
public:
- explicit HidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient = false) : Camera3Device(id, overrideForPerfClass, legacyClient) { }
+ explicit HidlCamera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false) :
+ Camera3Device(cameraServiceProxyWrapper, id, overrideForPerfClass, legacyClient) { }
virtual ~HidlCamera3Device() {}
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp b/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
index 8b0cd65..ff6fc17 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
@@ -105,6 +105,7 @@
m.type = CAMERA_MSG_SHUTTER;
m.message.shutter.frame_number = msg.msg.shutter.frameNumber;
m.message.shutter.timestamp = msg.msg.shutter.timestamp;
+ m.message.shutter.readout_timestamp_valid = false;
m.message.shutter.readout_timestamp = 0LL;
break;
}
diff --git a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
index d3377f4..74b3700 100644
--- a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
@@ -31,47 +31,47 @@
std::map<int, std::vector<camera_metadata_tag>> static_api_level_to_keys{
{30, {
ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_MAX_SIZES,
+ ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_ZOOM_RATIO_RANGES,
ANDROID_CONTROL_ZOOM_RATIO_RANGE,
ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
- ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_ZOOM_RATIO_RANGES,
} },
{31, {
- ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_DEFAULT_SECURE_IMAGE_SIZE,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION,
+ ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_DEFAULT_SECURE_IMAGE_SIZE,
+ ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED,
+ ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
ANDROID_SENSOR_INFO_BINNING_FACTOR,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
} },
{32, {
ANDROID_INFO_DEVICE_STATE_ORIENTATIONS,
} },
{33, {
- ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL,
ANDROID_AUTOMOTIVE_LENS_FACING,
ANDROID_AUTOMOTIVE_LOCATION,
+ ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL,
+ ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL,
+ ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP,
ANDROID_REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE,
ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES,
- ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP,
- ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL,
} },
};
@@ -81,9 +81,9 @@
*/
std::map<int, std::vector<camera_metadata_tag>> dynamic_api_level_to_keys{
{30, {
+ ANDROID_CONTROL_EXTENDED_SCENE_MODE,
ANDROID_CONTROL_ZOOM_RATIO,
ANDROID_SCALER_ROTATE_AND_CROP,
- ANDROID_CONTROL_EXTENDED_SCENE_MODE,
} },
{31, {
ANDROID_SENSOR_PIXEL_MODE,
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
index e43b91f..d909624 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
@@ -64,6 +64,7 @@
fuzz_config: {
cc: [
"android-media-fuzzing-reports@google.com",
+ "android-camera-fwk-eng@google.com",
],
componentid: 155276,
libfuzzer_options: [
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
index 97d7bf4..71c26ea 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
@@ -111,12 +111,15 @@
size_t mPreviewBufferCount = 0;
bool mAutoFocusMessage = false;
bool mSnapshotNotification = false;
+ bool mRecordingNotification = false;
mutable Mutex mPreviewLock;
mutable Condition mPreviewCondition;
mutable Mutex mAutoFocusLock;
mutable Condition mAutoFocusCondition;
mutable Mutex mSnapshotLock;
mutable Condition mSnapshotCondition;
+ mutable Mutex mRecordingLock;
+ mutable Condition mRecordingCondition;
void getNumCameras();
void getCameraInformation(int32_t cameraId);
@@ -125,6 +128,7 @@
void invokeDump();
void invokeShellCommand();
void invokeNotifyCalls();
+ void invokeTorchAPIs(int32_t cameraId);
// CameraClient interface
void notifyCallback(int32_t msgType, int32_t, int32_t) override;
@@ -152,6 +156,8 @@
Mutex::Autolock l(mPreviewLock);
++mPreviewBufferCount;
mPreviewCondition.broadcast();
+ mRecordingNotification = true;
+ mRecordingCondition.broadcast();
break;
}
case CAMERA_MSG_COMPRESSED_IMAGE: {
@@ -311,114 +317,153 @@
mCameraService->notifySystemEvent(eventId, args);
}
+void CameraFuzzer::invokeTorchAPIs(int32_t cameraId) {
+ String16 cameraIdStr = String16(String8::format("%d", cameraId));
+ sp<IBinder> binder = new BBinder;
+
+ mCameraService->setTorchMode(cameraIdStr, true, binder);
+ ALOGV("Turned torch on.");
+ int32_t torchStrength = rand() % 5 + 1;
+ ALOGV("Changing torch strength level to %d", torchStrength);
+ mCameraService->turnOnTorchWithStrengthLevel(cameraIdStr, torchStrength, binder);
+ mCameraService->setTorchMode(cameraIdStr, false, binder);
+ ALOGV("Turned torch off.");
+}
+
void CameraFuzzer::invokeCameraAPIs() {
- for (int32_t cameraId = 0; cameraId < mNumCameras; ++cameraId) {
- getCameraInformation(cameraId);
+ /** In order to avoid the timeout issue caused due to multiple iteration of loops, the 'for'
+ * loops are removed and the 'cameraId', 'pictureSize' and 'videoSize' are derived using the
+ * FuzzedDataProvider from the available cameras and vectors of 'pictureSizes' and 'videoSizes'
+ */
+ int32_t cameraId = mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(0, mNumCameras - 1);
+ getCameraInformation(cameraId);
+ invokeTorchAPIs(cameraId);
- ::android::binder::Status rc;
- sp<ICamera> cameraDevice;
+ ::android::binder::Status rc;
+ sp<ICamera> cameraDevice;
- rc = mCameraService->connect(this, cameraId, String16(),
- android::CameraService::USE_CALLING_UID, android::CameraService::USE_CALLING_PID,
- /*targetSdkVersion*/__ANDROID_API_FUTURE__, &cameraDevice);
- if (!rc.isOk()) {
- // camera not connected
- return;
+ rc = mCameraService->connect(this, cameraId, String16(),
+ android::CameraService::USE_CALLING_UID,
+ android::CameraService::USE_CALLING_PID,
+ /*targetSdkVersion*/ __ANDROID_API_FUTURE__, &cameraDevice);
+ if (!rc.isOk()) {
+ // camera not connected
+ return;
+ }
+ if (cameraDevice) {
+ sp<Surface> previewSurface;
+ sp<SurfaceControl> surfaceControl;
+ CameraParameters params(cameraDevice->getParameters());
+ String8 focusModes(params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
+ bool isAFSupported = false;
+ const char* focusMode = nullptr;
+
+ if (focusModes.contains(CameraParameters::FOCUS_MODE_AUTO)) {
+ isAFSupported = true;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_MACRO)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_MACRO;
}
- if (cameraDevice) {
- sp<Surface> previewSurface;
- sp<SurfaceControl> surfaceControl;
- CameraParameters params(cameraDevice->getParameters());
- String8 focusModes(params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
- bool isAFSupported = false;
- const char *focusMode = nullptr;
+ if (nullptr != focusMode) {
+ params.set(CameraParameters::KEY_FOCUS_MODE, focusMode);
+ cameraDevice->setParameters(params.flatten());
+ }
+ int previewWidth, previewHeight;
+ params.getPreviewSize(&previewWidth, &previewHeight);
- if (focusModes.contains(CameraParameters::FOCUS_MODE_AUTO)) {
- isAFSupported = true;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_MACRO)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_MACRO;
- }
- if (nullptr != focusMode) {
- params.set(CameraParameters::KEY_FOCUS_MODE, focusMode);
- cameraDevice->setParameters(params.flatten());
- }
- int previewWidth, previewHeight;
- params.getPreviewSize(&previewWidth, &previewHeight);
+ mComposerClient = new SurfaceComposerClient;
+ mComposerClient->initCheck();
- mComposerClient = new SurfaceComposerClient;
- mComposerClient->initCheck();
-
- bool shouldPassInvalidLayerMetaData = mFuzzedDataProvider->ConsumeBool();
- int layerMetaData;
- if (shouldPassInvalidLayerMetaData) {
- layerMetaData = mFuzzedDataProvider->ConsumeIntegral<int>();
- } else {
- layerMetaData = kLayerMetadata[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ bool shouldPassInvalidLayerMetaData = mFuzzedDataProvider->ConsumeBool();
+ int layerMetaData;
+ if (shouldPassInvalidLayerMetaData) {
+ layerMetaData = mFuzzedDataProvider->ConsumeIntegral<int>();
+ } else {
+ layerMetaData = kLayerMetadata[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
0, kNumLayerMetaData - 1)];
- }
- surfaceControl = mComposerClient->createSurface(
+ }
+ surfaceControl = mComposerClient->createSurface(
String8("Test Surface"), previewWidth, previewHeight,
CameraParameters::previewFormatToEnum(params.getPreviewFormat()), layerMetaData);
- if (surfaceControl.get() != nullptr) {
- SurfaceComposerClient::Transaction{}
+ if (surfaceControl.get()) {
+ SurfaceComposerClient::Transaction{}
.setLayer(surfaceControl, 0x7fffffff)
.show(surfaceControl)
.apply();
- previewSurface = surfaceControl->getSurface();
+ previewSurface = surfaceControl->getSurface();
+ if (previewSurface.get()) {
cameraDevice->setPreviewTarget(previewSurface->getIGraphicBufferProducer());
}
- cameraDevice->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER);
+ }
+ cameraDevice->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER);
- Vector<Size> pictureSizes;
- params.getSupportedPictureSizes(pictureSizes);
+ Vector<Size> pictureSizes;
+ params.getSupportedPictureSizes(pictureSizes);
- for (size_t i = 0; i < pictureSizes.size(); ++i) {
- params.setPictureSize(pictureSizes[i].width, pictureSizes[i].height);
- cameraDevice->setParameters(params.flatten());
- cameraDevice->startPreview();
- waitForPreviewStart();
- cameraDevice->autoFocus();
- waitForEvent(mAutoFocusLock, mAutoFocusCondition, mAutoFocusMessage);
- bool shouldPassInvalidCameraMsg = mFuzzedDataProvider->ConsumeBool();
- int msgType;
- if (shouldPassInvalidCameraMsg) {
- msgType = mFuzzedDataProvider->ConsumeIntegral<int>();
- } else {
- msgType = kCameraMsg[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ if (pictureSizes.size()) {
+ Size pictureSize = pictureSizes[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ 0, pictureSizes.size() - 1)];
+ params.setPictureSize(pictureSize.width, pictureSize.height);
+ cameraDevice->setParameters(params.flatten());
+ cameraDevice->startPreview();
+ waitForPreviewStart();
+ cameraDevice->autoFocus();
+ waitForEvent(mAutoFocusLock, mAutoFocusCondition, mAutoFocusMessage);
+ bool shouldPassInvalidCameraMsg = mFuzzedDataProvider->ConsumeBool();
+ int msgType;
+ if (shouldPassInvalidCameraMsg) {
+ msgType = mFuzzedDataProvider->ConsumeIntegral<int>();
+ } else {
+ msgType = kCameraMsg[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
0, kNumCameraMsg - 1)];
- }
- cameraDevice->takePicture(msgType);
-
- waitForEvent(mSnapshotLock, mSnapshotCondition, mSnapshotNotification);
}
+ cameraDevice->takePicture(msgType);
- Vector<Size> videoSizes;
- params.getSupportedVideoSizes(videoSizes);
+ waitForEvent(mSnapshotLock, mSnapshotCondition, mSnapshotNotification);
+ cameraDevice->stopPreview();
+ }
- for (size_t i = 0; i < videoSizes.size(); ++i) {
- params.setVideoSize(videoSizes[i].width, videoSizes[i].height);
+ Vector<Size> videoSizes;
+ params.getSupportedVideoSizes(videoSizes);
- cameraDevice->setParameters(params.flatten());
- cameraDevice->startPreview();
- waitForPreviewStart();
- cameraDevice->setVideoBufferMode(
+ if (videoSizes.size()) {
+ Size videoSize = videoSizes[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ 0, videoSizes.size() - 1)];
+ params.setVideoSize(videoSize.width, videoSize.height);
+
+ cameraDevice->setParameters(params.flatten());
+ cameraDevice->startPreview();
+ waitForPreviewStart();
+ cameraDevice->setVideoBufferMode(
android::hardware::BnCamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE);
- cameraDevice->setVideoTarget(previewSurface->getIGraphicBufferProducer());
- cameraDevice->startRecording();
- cameraDevice->stopRecording();
+ sp<SurfaceControl> surfaceControlVideo = mComposerClient->createSurface(
+ String8("Test Surface Video"), previewWidth, previewHeight,
+ CameraParameters::previewFormatToEnum(params.getPreviewFormat()),
+ layerMetaData);
+ if (surfaceControlVideo.get()) {
+ SurfaceComposerClient::Transaction{}
+ .setLayer(surfaceControlVideo, 0x7fffffff)
+ .show(surfaceControlVideo)
+ .apply();
+ sp<Surface> previewSurfaceVideo = surfaceControlVideo->getSurface();
+ if (previewSurfaceVideo.get()) {
+ cameraDevice->setVideoTarget(previewSurfaceVideo->getIGraphicBufferProducer());
+ }
}
cameraDevice->stopPreview();
- cameraDevice->disconnect();
+ cameraDevice->startRecording();
+ waitForEvent(mRecordingLock, mRecordingCondition, mRecordingNotification);
+ cameraDevice->stopRecording();
}
+ cameraDevice->disconnect();
}
}
diff --git a/services/camera/libcameraservice/tests/Android.bp b/services/camera/libcameraservice/tests/Android.bp
index 3616572..4d7798c 100644
--- a/services/camera/libcameraservice/tests/Android.bp
+++ b/services/camera/libcameraservice/tests/Android.bp
@@ -27,8 +27,13 @@
"external/dynamic_depth/internal",
],
+ header_libs: [
+ "libmedia_headers",
+ ],
+
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"libcameraservice",
"libhidlbase",
@@ -57,6 +62,7 @@
],
srcs: [
+ "CameraPermissionsTest.cpp",
"CameraProviderManagerTest.cpp",
"ClientManagerTest.cpp",
"DepthProcessorTest.cpp",
diff --git a/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
new file mode 100644
index 0000000..42d99e8
--- /dev/null
+++ b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <android/hardware/BnCameraServiceListener.h>
+#include <android/hardware/BnCameraServiceProxy.h>
+#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
+#include <android/hardware/ICameraService.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "../CameraService.h"
+#include "../utils/CameraServiceProxyWrapper.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <vector>
+
+using namespace android;
+using namespace android::hardware::camera;
+
+// Empty service listener.
+class TestCameraServiceListener : public hardware::BnCameraServiceListener {
+public:
+ virtual ~TestCameraServiceListener() {};
+
+ virtual binder::Status onStatusChanged(int32_t , const String16&) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
+ const String16& /*cameraId*/, const String16& /*physicalCameraId*/) {
+ // No op
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onTorchStatusChanged(int32_t /*status*/, const String16& /*cameraId*/) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onCameraAccessPrioritiesChanged() {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraOpened(const String16& /*cameraId*/,
+ const String16& /*clientPackageName*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraClosed(const String16& /*cameraId*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/,
+ int32_t /*torchStrength*/) {
+ // No op
+ return binder::Status::ok();
+ }
+};
+
+// Empty device callback.
+class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
+public:
+ TestCameraDeviceCallbacks() {}
+
+ virtual ~TestCameraDeviceCallbacks() {}
+
+ virtual binder::Status onDeviceError(int /*errorCode*/,
+ const CaptureResultExtras& /*resultExtras*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onDeviceIdle() {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCaptureStarted(const CaptureResultExtras& /*resultExtras*/,
+ int64_t /*timestamp*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onResultReceived(const CameraMetadata& /*metadata*/,
+ const CaptureResultExtras& /*resultExtras*/,
+ const std::vector<PhysicalCaptureResultInfo>& /*physicalResultInfos*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onPrepared(int /*streamId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRepeatingRequestError(
+ int64_t /*lastFrameNumber*/, int32_t /*stoppedSequenceId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRequestQueueEmpty() {
+ return binder::Status::ok();
+ }
+};
+
+// Override isCameraDisabled from the CameraServiceProxy with a flag.
+class CameraServiceProxyOverride : public ::android::hardware::BnCameraServiceProxy {
+public:
+ CameraServiceProxyOverride() :
+ mCameraServiceProxy(CameraServiceProxyWrapper::getDefaultCameraServiceProxy()),
+ mCameraDisabled(false), mOverrideCameraDisabled(false)
+ { }
+
+ virtual binder::Status getRotateAndCropOverride(const String16& packageName, int lensFacing,
+ int userId, int *ret) override {
+ return mCameraServiceProxy->getRotateAndCropOverride(packageName, lensFacing,
+ userId, ret);
+ }
+
+ virtual binder::Status pingForUserUpdate() override {
+ return mCameraServiceProxy->pingForUserUpdate();
+ }
+
+ virtual binder::Status notifyCameraState(
+ const hardware::CameraSessionStats& cameraSessionStats) override {
+ return mCameraServiceProxy->notifyCameraState(cameraSessionStats);
+ }
+
+ virtual binder::Status isCameraDisabled(int userId, bool *ret) override {
+ if (mOverrideCameraDisabled) {
+ *ret = mCameraDisabled;
+ return binder::Status::ok();
+ }
+ return mCameraServiceProxy->isCameraDisabled(userId, ret);
+ }
+
+ void setCameraDisabled(bool cameraDisabled) {
+ mCameraDisabled = cameraDisabled;
+ }
+
+ void setOverrideCameraDisabled(bool overrideCameraDisabled) {
+ mOverrideCameraDisabled = overrideCameraDisabled;
+ }
+
+protected:
+ sp<hardware::ICameraServiceProxy> mCameraServiceProxy;
+ bool mCameraDisabled;
+ bool mOverrideCameraDisabled;
+};
+
+class AutoDisconnectDevice {
+public:
+ AutoDisconnectDevice(sp<hardware::camera2::ICameraDeviceUser> device) :
+ mDevice(device)
+ { }
+
+ ~AutoDisconnectDevice() {
+ if (mDevice != nullptr) {
+ mDevice->disconnect();
+ }
+ }
+
+private:
+ sp<hardware::camera2::ICameraDeviceUser> mDevice;
+};
+
+class CameraPermissionsTest : public ::testing::Test {
+protected:
+ static sp<CameraService> sCameraService;
+ static sp<CameraServiceProxyOverride> sCameraServiceProxy;
+ static std::shared_ptr<CameraServiceProxyWrapper> sCameraServiceProxyWrapper;
+ static uid_t sOldUid;
+
+ static void SetUpTestSuite() {
+ sOldUid = getuid();
+ setuid(AID_CAMERASERVER);
+ sCameraServiceProxy = new CameraServiceProxyOverride();
+ sCameraServiceProxyWrapper =
+ std::make_shared<CameraServiceProxyWrapper>(sCameraServiceProxy);
+ sCameraService = new CameraService(sCameraServiceProxyWrapper);
+ sCameraService->clearCachedVariables();
+ }
+
+ static void TearDownTestSuite() {
+ sCameraServiceProxyWrapper = nullptr;
+ sCameraServiceProxy = nullptr;
+ sCameraService = nullptr;
+ setuid(sOldUid);
+ }
+};
+
+sp<CameraService> CameraPermissionsTest::sCameraService = nullptr;
+sp<CameraServiceProxyOverride> CameraPermissionsTest::sCameraServiceProxy = nullptr;
+std::shared_ptr<CameraServiceProxyWrapper>
+CameraPermissionsTest::sCameraServiceProxyWrapper = nullptr;
+uid_t CameraPermissionsTest::sOldUid = 0;
+
+// Test that camera connections fail with ERROR_DISABLED when the camera is disabled via device
+// policy, and succeed when it isn't.
+TEST_F(CameraPermissionsTest, TestCameraDisabled) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(true);
+
+ sCameraServiceProxy->setCameraDisabled(true);
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> device;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
+ AutoDisconnectDevice autoDisconnect(device);
+ ASSERT_TRUE(!status.isOk()) << "connectDevice returned OK status";
+ ASSERT_EQ(status.serviceSpecificErrorCode(), hardware::ICameraService::ERROR_DISABLED)
+ << "connectDevice returned exception code " << status.exceptionCode();
+ }
+
+ sCameraServiceProxy->setCameraDisabled(false);
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> device;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
+ AutoDisconnectDevice autoDisconnect(device);
+ ASSERT_TRUE(status.isOk());
+ }
+}
+
+// Test that consecutive camera connections succeed.
+TEST_F(CameraPermissionsTest, TestConsecutiveConnections) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(false);
+
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
+ AutoDisconnectDevice autoDisconnectA(deviceA);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
+ AutoDisconnectDevice autoDisconnectB(deviceB);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ }
+}
+
+// Test that consecutive camera connections succeed even when a nonzero oomScoreOffset is provided
+// in the second call.
+TEST_F(CameraPermissionsTest, TestConflictingOomScoreOffset) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(false);
+
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
+ AutoDisconnectDevice autoDisconnectA(deviceA);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 1/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
+ AutoDisconnectDevice autoDisconnectB(deviceB);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ }
+}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 69175cc..885c6aa 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -29,29 +29,29 @@
using hardware::ICameraServiceProxy;
using hardware::CameraSessionStats;
-Mutex CameraServiceProxyWrapper::sProxyMutex;
-sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::sCameraServiceProxy;
-
-Mutex CameraServiceProxyWrapper::mLock;
-std::map<String8, std::shared_ptr<CameraServiceProxyWrapper::CameraSessionStatsWrapper>>
- CameraServiceProxyWrapper::mSessionStatsMap;
-
/**
* CameraSessionStatsWrapper functions
*/
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen() {
- Mutex::Autolock l(mLock);
-
- updateProxyDeviceState(mSessionStats);
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateProxyDeviceState(
+ sp<hardware::ICameraServiceProxy>& proxyBinder) {
+ if (proxyBinder == nullptr) return;
+ proxyBinder->notifyCameraState(mSessionStats);
}
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(int32_t latencyMs) {
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen(
+ sp<hardware::ICameraServiceProxy>& proxyBinder) {
+ Mutex::Autolock l(mLock);
+ updateProxyDeviceState(proxyBinder);
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(
+ sp<hardware::ICameraServiceProxy>& proxyBinder, int32_t latencyMs) {
Mutex::Autolock l(mLock);
mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_CLOSED;
mSessionStats.mLatencyMs = latencyMs;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
}
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onStreamConfigured(
@@ -66,12 +66,13 @@
}
}
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive(float maxPreviewFps) {
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive(
+ sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps) {
Mutex::Autolock l(mLock);
mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_ACTIVE;
mSessionStats.mMaxPreviewFps = maxPreviewFps;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
// Reset mCreationDuration to -1 to distinguish between 1st session
// after configuration, and all other sessions after configuration.
@@ -79,6 +80,7 @@
}
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
+ sp<hardware::ICameraServiceProxy>& proxyBinder,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats) {
@@ -91,7 +93,7 @@
mSessionStats.mUserTag = String16(userTag.c_str());
mSessionStats.mVideoStabilizationMode = videoStabilizationMode;
mSessionStats.mStreamStats = streamStats;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
mSessionStats.mInternalReconfigure = 0;
mSessionStats.mStreamStats.clear();
@@ -103,19 +105,26 @@
sp<ICameraServiceProxy> CameraServiceProxyWrapper::getCameraServiceProxy() {
#ifndef __BRILLO__
- Mutex::Autolock al(sProxyMutex);
- if (sCameraServiceProxy == nullptr) {
- sp<IServiceManager> sm = defaultServiceManager();
- // Use checkService because cameraserver normally starts before the
- // system server and the proxy service. So the long timeout that getService
- // has before giving up is inappropriate.
- sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
- if (binder != nullptr) {
- sCameraServiceProxy = interface_cast<ICameraServiceProxy>(binder);
- }
+ Mutex::Autolock al(mProxyMutex);
+ if (mCameraServiceProxy == nullptr) {
+ mCameraServiceProxy = getDefaultCameraServiceProxy();
}
#endif
- return sCameraServiceProxy;
+ return mCameraServiceProxy;
+}
+
+sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::getDefaultCameraServiceProxy() {
+#ifndef __BRILLO__
+ sp<IServiceManager> sm = defaultServiceManager();
+ // Use checkService because cameraserver normally starts before the
+ // system server and the proxy service. So the long timeout that getService
+ // has before giving up is inappropriate.
+ sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
+ if (binder != nullptr) {
+ return interface_cast<ICameraServiceProxy>(binder);
+ }
+#endif
+ return nullptr;
}
void CameraServiceProxyWrapper::pingCameraServiceProxy() {
@@ -138,12 +147,6 @@
return ret;
}
-void CameraServiceProxyWrapper::updateProxyDeviceState(const CameraSessionStats& sessionStats) {
- sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
- if (proxyBinder == nullptr) return;
- proxyBinder->notifyCameraState(sessionStats);
-}
-
void CameraServiceProxyWrapper::logStreamConfigured(const String8& id,
int operatingMode, bool internalConfig, int32_t latencyMs) {
std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
@@ -175,7 +178,8 @@
}
ALOGV("%s: id %s", __FUNCTION__, id.c_str());
- sessionStats->onActive(maxPreviewFps);
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onActive(proxyBinder, maxPreviewFps);
}
void CameraServiceProxyWrapper::logIdle(const String8& id,
@@ -205,7 +209,8 @@
streamStats[i].mStartLatencyMs);
}
- sessionStats->onIdle(requestCount, resultErrorCount, deviceError, userTag,
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag,
videoStabilizationMode, streamStats);
}
@@ -235,7 +240,8 @@
ALOGV("%s: id %s, facing %d, effectiveApiLevel %d, isNdk %d, latencyMs %d",
__FUNCTION__, id.c_str(), facing, effectiveApiLevel, isNdk, latencyMs);
- sessionStats->onOpen();
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onOpen(proxyBinder);
}
void CameraServiceProxyWrapper::logClose(const String8& id, int32_t latencyMs) {
@@ -259,14 +265,15 @@
}
ALOGV("%s: id %s, latencyMs %d", __FUNCTION__, id.c_str(), latencyMs);
- sessionStats->onClose(latencyMs);
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onClose(proxyBinder, latencyMs);
}
-bool CameraServiceProxyWrapper::isCameraDisabled() {
+bool CameraServiceProxyWrapper::isCameraDisabled(int userId) {
sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
if (proxyBinder == nullptr) return true;
bool ret = false;
- auto status = proxyBinder->isCameraDisabled(&ret);
+ auto status = proxyBinder->isCameraDisabled(userId, &ret);
if (!status.isOk()) {
ALOGE("%s: Failed during camera disabled query: %s", __FUNCTION__,
status.exceptionMessage().c_str());
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index e34a8f0..c00c691 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -32,72 +32,80 @@
class CameraServiceProxyWrapper {
private:
// Guard mCameraServiceProxy
- static Mutex sProxyMutex;
+ Mutex mProxyMutex;
// Cached interface to the camera service proxy in system service
- static sp<hardware::ICameraServiceProxy> sCameraServiceProxy;
+ sp<hardware::ICameraServiceProxy> mCameraServiceProxy;
- struct CameraSessionStatsWrapper {
+ class CameraSessionStatsWrapper {
+ private:
hardware::CameraSessionStats mSessionStats;
Mutex mLock; // lock for per camera session stats
+ /**
+ * Update the session stats of a given camera device (open/close/active/idle) with
+ * the camera proxy service in the system service
+ */
+ void updateProxyDeviceState(sp<hardware::ICameraServiceProxy>& proxyBinder);
+
+ public:
CameraSessionStatsWrapper(const String16& cameraId, int facing, int newCameraState,
const String16& clientName, int apiLevel, bool isNdk, int32_t latencyMs) :
mSessionStats(cameraId, facing, newCameraState, clientName, apiLevel, isNdk, latencyMs)
- {}
+ { }
- void onOpen();
- void onClose(int32_t latencyMs);
+ void onOpen(sp<hardware::ICameraServiceProxy>& proxyBinder);
+ void onClose(sp<hardware::ICameraServiceProxy>& proxyBinder, int32_t latencyMs);
void onStreamConfigured(int operatingMode, bool internalReconfig, int32_t latencyMs);
- void onActive(float maxPreviewFps);
- void onIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ void onActive(sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps);
+ void onIdle(sp<hardware::ICameraServiceProxy>& proxyBinder,
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats);
};
// Lock for camera session stats map
- static Mutex mLock;
+ Mutex mLock;
// Map from camera id to the camera's session statistics
- static std::map<String8, std::shared_ptr<CameraSessionStatsWrapper>> mSessionStatsMap;
+ std::map<String8, std::shared_ptr<CameraSessionStatsWrapper>> mSessionStatsMap;
- /**
- * Update the session stats of a given camera device (open/close/active/idle) with
- * the camera proxy service in the system service
- */
- static void updateProxyDeviceState(
- const hardware::CameraSessionStats& sessionStats);
-
- static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
+ sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
public:
+ CameraServiceProxyWrapper(sp<hardware::ICameraServiceProxy> serviceProxy = nullptr) :
+ mCameraServiceProxy(serviceProxy)
+ { }
+
+ static sp<hardware::ICameraServiceProxy> getDefaultCameraServiceProxy();
+
// Open
- static void logOpen(const String8& id, int facing,
+ void logOpen(const String8& id, int facing,
const String16& clientPackageName, int apiLevel, bool isNdk,
int32_t latencyMs);
// Close
- static void logClose(const String8& id, int32_t latencyMs);
+ void logClose(const String8& id, int32_t latencyMs);
// Stream configuration
- static void logStreamConfigured(const String8& id, int operatingMode, bool internalReconfig,
+ void logStreamConfigured(const String8& id, int operatingMode, bool internalReconfig,
int32_t latencyMs);
// Session state becomes active
- static void logActive(const String8& id, float maxPreviewFps);
+ void logActive(const String8& id, float maxPreviewFps);
// Session state becomes idle
- static void logIdle(const String8& id,
+ void logIdle(const String8& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats);
// Ping camera service proxy for user update
- static void pingCameraServiceProxy();
+ void pingCameraServiceProxy();
// Return the current top activity rotate and crop override.
- static int getRotateAndCropOverride(String16 packageName, int lensFacing, int userId);
+ int getRotateAndCropOverride(String16 packageName, int lensFacing, int userId);
// Detect if the camera is disabled by device policy.
- static bool isCameraDisabled();
+ bool isCameraDisabled(int userId);
};
} // android
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 2eb2d55..ed490a8 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -127,7 +127,7 @@
size_t getUHRMaxJpegBufferSize(camera3::Size uhrMaxJpegSize,
camera3::Size defaultMaxJpegSize, size_t defaultMaxJpegBufferSize) {
- return (uhrMaxJpegSize.width * uhrMaxJpegSize.height) /
+ return ((float)(uhrMaxJpegSize.width * uhrMaxJpegSize.height)) /
(defaultMaxJpegSize.width * defaultMaxJpegSize.height) * defaultMaxJpegBufferSize;
}
@@ -458,7 +458,7 @@
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
if (timestampBase < OutputConfiguration::TIMESTAMP_BASE_DEFAULT ||
- timestampBase > OutputConfiguration::TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED) {
+ timestampBase > OutputConfiguration::TIMESTAMP_BASE_MAX) {
String8 msg = String8::format("Camera %s: invalid timestamp base %d",
logicalCameraId.string(), timestampBase);
ALOGE("%s: %s", __FUNCTION__, msg.string());
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
index 4e6f832..5444f2a 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
@@ -50,7 +50,7 @@
for (const auto &stream : aidl.streams) {
if (static_cast<int>(stream.dynamicRangeProfile) !=
ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD) {
- ALOGE("%s Dynamic range profile %" PRId64 " not supported by HIDL", __FUNCTION__,
+ ALOGE("%s Dynamic range profile %" PRId64 " not supported by HIDL", __FUNCTION__,
stream.dynamicRangeProfile);
return BAD_VALUE;
}
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index 461f5e9..fe87ed6 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -169,6 +169,22 @@
camera_metadata_entry lastEntry = lastValues.find(tag);
+ // Monitor when the stream ids change, this helps visually see what
+ // monitored metadata values are for capture requests with different
+ // stream ids.
+ if (source == REQUEST) {
+ if (inputStreamId != mLastInputStreamId) {
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, camera_metadata_ro_entry_t{},
+ cameraId, std::unordered_set<int>(), inputStreamId);
+ mLastInputStreamId = inputStreamId;
+ }
+
+ if (outputStreamIds != mLastStreamIds) {
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, camera_metadata_ro_entry_t{},
+ cameraId, outputStreamIds, -1);
+ mLastStreamIds = outputStreamIds;
+ }
+ }
if (entry.count > 0) {
bool isDifferent = false;
if (lastEntry.count > 0) {
@@ -190,22 +206,14 @@
// No last entry, so always consider to be different
isDifferent = true;
}
- // Also monitor when the stream ids change, this helps visually see what
- // monitored metadata values are for capture requests with different
- // stream ids.
- if (source == REQUEST &&
- (inputStreamId != mLastInputStreamId || outputStreamIds != mLastStreamIds)) {
- mLastInputStreamId = inputStreamId;
- mLastStreamIds = outputStreamIds;
- isDifferent = true;
- }
+
if (isDifferent) {
ALOGV("%s: Tag %s changed", __FUNCTION__,
get_local_camera_metadata_tag_name_vendor_id(
tag, mVendorTagId));
lastValues.update(entry);
mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId,
- outputStreamIds, inputStreamId);
+ std::unordered_set<int>(), -1);
}
} else if (lastEntry.count > 0) {
// Value has been removed
@@ -219,8 +227,8 @@
entry.count = 0;
mLastInputStreamId = inputStreamId;
mLastStreamIds = outputStreamIds;
- mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId, outputStreamIds,
- inputStreamId);
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId,
+ std::unordered_set<int>(), -1);
}
}
@@ -261,23 +269,39 @@
for (const auto& event : mMonitoringEvents) {
int indentation = (event.source == REQUEST) ? 15 : 30;
- String8 eventString = String8::format("f%d:%" PRId64 "ns:%*s%*s%s.%s: ",
+ String8 eventString = String8::format("f%d:%" PRId64 "ns:%*s%*s",
event.frameNumber, event.timestamp,
2, event.cameraId.c_str(),
indentation,
- event.source == REQUEST ? "REQ:" : "RES:",
+ event.source == REQUEST ? "REQ:" : "RES:");
+
+ if (!event.outputStreamIds.empty()) {
+ eventString += " output stream ids:";
+ for (const auto& id : event.outputStreamIds) {
+ eventString.appendFormat(" %d", id);
+ }
+ eventString += "\n";
+ vec.emplace_back(eventString.string());
+ continue;
+ }
+
+ if (event.inputStreamId != -1) {
+ eventString.appendFormat(" input stream id: %d\n", event.inputStreamId);
+ vec.emplace_back(eventString.string());
+ continue;
+ }
+
+ eventString += String8::format(
+ "%s.%s: ",
get_local_camera_metadata_section_name_vendor_id(event.tag, mVendorTagId),
get_local_camera_metadata_tag_name_vendor_id(event.tag, mVendorTagId));
- if (event.newData.size() == 0) {
- eventString += " (Removed)";
+
+ if (event.newData.empty()) {
+ eventString += " (Removed)\n";
} else {
- eventString += getEventDataString(event.newData.data(),
- event.tag,
- event.type,
- event.newData.size() / camera_metadata_type_size[event.type],
- indentation + 18,
- event.outputStreamIds,
- event.inputStreamId);
+ eventString += getEventDataString(
+ event.newData.data(), event.tag, event.type,
+ event.newData.size() / camera_metadata_type_size[event.type], indentation + 18);
}
vec.emplace_back(eventString.string());
}
@@ -285,13 +309,8 @@
#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29
-String8 TagMonitor::getEventDataString(const uint8_t* data_ptr,
- uint32_t tag,
- int type,
- int count,
- int indentation,
- const std::unordered_set<int32_t>& outputStreamIds,
- int32_t inputStreamId) {
+String8 TagMonitor::getEventDataString(const uint8_t* data_ptr, uint32_t tag, int type, int count,
+ int indentation) {
static int values_per_line[NUM_TYPES] = {
[TYPE_BYTE] = 16,
[TYPE_INT32] = 8,
@@ -362,17 +381,7 @@
returnStr += "??? ";
}
}
- returnStr += "] ";
- if (!outputStreamIds.empty()) {
- returnStr += "output stream ids: ";
- for (const auto &id : outputStreamIds) {
- returnStr.appendFormat(" %d ", id);
- }
- }
- if (inputStreamId != -1) {
- returnStr.appendFormat("input stream id: %d", inputStreamId);
- }
- returnStr += "\n";
+ returnStr += "]\n";
}
return returnStr;
}
@@ -385,11 +394,12 @@
source(src),
frameNumber(frameNumber),
timestamp(timestamp),
+ cameraId(cameraId),
tag(value.tag),
type(value.type),
newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count),
- cameraId(cameraId), outputStreamIds(outputStreamIds), inputStreamId(inputStreamId) {
-}
+ outputStreamIds(outputStreamIds),
+ inputStreamId(inputStreamId) {}
TagMonitor::MonitorEvent::~MonitorEvent() {
}
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 088d6fe..9ded15d 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -85,12 +85,8 @@
// function.
void dumpMonitoredTagEventsToVectorLocked(std::vector<std::string> &out);
- static String8 getEventDataString(const uint8_t *data_ptr,
- uint32_t tag, int type,
- int count,
- int indentation,
- const std::unordered_set<int32_t> &outputStreamIds,
- int32_t inputStreamId);
+ static String8 getEventDataString(const uint8_t* data_ptr, uint32_t tag, int type, int count,
+ int indentation);
void monitorSingleMetadata(TagMonitor::eventSource source, int64_t frameNumber,
nsecs_t timestamp, const std::string& cameraId, uint32_t tag,
@@ -128,12 +124,15 @@
eventSource source;
uint32_t frameNumber;
nsecs_t timestamp;
+ std::string cameraId;
uint32_t tag;
uint8_t type;
std::vector<uint8_t> newData;
- std::string cameraId;
+ // NOTE: We want to print changes to outputStreamIds and inputStreamId in their own lines.
+ // So any MonitorEvent where these fields are not the default value will have garbage
+ // values for all fields other than source, frameNumber, timestamp, and cameraId.
std::unordered_set<int32_t> outputStreamIds;
- int32_t inputStreamId = 1;
+ int32_t inputStreamId = -1;
};
// A ring buffer for tracking the last kMaxMonitorEvents metadata changes
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 99e3691..12593ff 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -240,6 +240,35 @@
"sharing_requested",
};
+static constexpr const char * HeadTrackerDeviceEnabledFields[] {
+ "mediametrics_headtrackerdeviceenabled_reported",
+ "type",
+ "event",
+ "enabled",
+};
+
+static constexpr const char * HeadTrackerDeviceSupportedFields[] {
+ "mediametrics_headtrackerdevicesupported_reported",
+ "type",
+ "event",
+ "supported",
+};
+
+static constexpr const char * SpatializerCapabilitiesFields[] {
+ "mediametrics_spatializer_reported",
+ "head_tracking_modes",
+ "spatializer_levels",
+ "spatializer_modes",
+ "channel_masks",
+};
+
+static constexpr const char * SpatializerDeviceEnabledFields[] {
+ "mediametrics_spatializerdeviceenabled_reported",
+ "type",
+ "event",
+ "enabled",
+};
+
/**
* printFields is a helper method that prints the fields and corresponding values
* in a human readable style.
@@ -459,6 +488,15 @@
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mAudioPowerUsage.checkCreatePatch(item);
}));
+
+ // Handle Spatializer - these keys are prefixed by "audio.spatializer."
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER "*." AMEDIAMETRICS_PROP_EVENT,
+ std::monostate{}, /* match any event */
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mSpatializer.onEvent(item);
+ }));
}
AudioAnalytics::~AudioAnalytics()
@@ -1525,5 +1563,173 @@
return { s, n };
}
+// Classifies the setting event for statsd (use generated statsd enums.proto constants).
+static int32_t classifySettingEvent(bool isSetAlready, bool withinBoot) {
+ if (isSetAlready) {
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_NORMAL;
+ }
+ if (withinBoot) {
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_BOOT;
+ }
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_FIRST;
+}
+
+void AudioAnalytics::Spatializer::onEvent(
+ const std::shared_ptr<const android::mediametrics::Item> &item)
+{
+ const auto key = item->getKey();
+
+ if (!startsWith(key, AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER)) return;
+
+ const std::string suffix =
+ key.substr(std::size(AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER) - 1);
+
+ std::string eventStr; // optional - find the actual event string.
+ (void)item->get(AMEDIAMETRICS_PROP_EVENT, &eventStr);
+
+ const size_t delim = suffix.find('.'); // note could use split.
+ if (delim == suffix.npos) {
+ // on create with suffix == "0" for the first spatializer effect.
+
+ std::string headTrackingModes;
+ (void)item->get(AMEDIAMETRICS_PROP_HEADTRACKINGMODES, &headTrackingModes);
+
+ std::string levels;
+ (void)item->get(AMEDIAMETRICS_PROP_LEVELS, &levels);
+
+ std::string modes;
+ (void)item->get(AMEDIAMETRICS_PROP_MODES, &modes);
+
+ std::string channelMasks;
+ (void)item->get(AMEDIAMETRICS_PROP_CHANNELMASKS, &channelMasks);
+
+ LOG(LOG_LEVEL) << "key:" << key
+ << " headTrackingModes:" << headTrackingModes
+ << " levels:" << levels
+ << " modes:" << modes
+ << " channelMasks:" << channelMasks
+ ;
+
+ const std::vector<int32_t> headTrackingModesVector =
+ types::vectorFromMap(headTrackingModes, types::getHeadTrackingModeMap());
+ const std::vector<int32_t> levelsVector =
+ types::vectorFromMap(levels, types::getSpatializerLevelMap());
+ const std::vector<int32_t> modesVector =
+ types::vectorFromMap(modes, types::getSpatializerModeMap());
+ const std::vector<int64_t> channelMasksVector =
+ types::channelMaskVectorFromString(channelMasks);
+
+ const auto [ result, str ] = sendToStatsd(SpatializerCapabilitiesFields,
+ CONDITION(android::util::MEDIAMETRICS_SPATIALIZERCAPABILITIES_REPORTED)
+ , headTrackingModesVector
+ , levelsVector
+ , modesVector
+ , channelMasksVector
+ );
+
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_SPATIALIZERCAPABILITIES_REPORTED, str);
+
+ std::lock_guard lg(mLock);
+ if (mFirstCreateTimeNs == 0) {
+ // Only update the create time once to prevent audioserver restart
+ // from looking like a boot.
+ mFirstCreateTimeNs = item->getTimestamp();
+ }
+ mSimpleLog.log("%s suffix: %s item: %s",
+ __func__, suffix.c_str(), item->toString().c_str());
+ } else {
+ std::string subtype = suffix.substr(0, delim);
+ if (subtype != "device") return; // not a device.
+
+ const std::string deviceType = suffix.substr(std::size("device.") - 1);
+
+ const int32_t deviceTypeStatsd =
+ types::lookup<types::AUDIO_DEVICE_INFO_TYPE, int32_t>(deviceType);
+
+ std::string address;
+ (void)item->get(AMEDIAMETRICS_PROP_ADDRESS, &address);
+ std::string enabled;
+ (void)item->get(AMEDIAMETRICS_PROP_ENABLED, &enabled);
+ std::string hasHeadTracker;
+ (void)item->get(AMEDIAMETRICS_PROP_HASHEADTRACKER, &hasHeadTracker);
+ std::string headTrackerEnabled;
+ (void)item->get(AMEDIAMETRICS_PROP_HEADTRACKERENABLED, &headTrackerEnabled);
+
+ std::lock_guard lg(mLock);
+
+ // Validate from our cached state
+
+ // Our deviceKey takes the device type and appends the address if any.
+ // This distinguishes different wireless devices for the purposes of tracking.
+ std::string deviceKey(deviceType);
+ deviceKey.append("_").append(address);
+ DeviceState& deviceState = mDeviceStateMap[deviceKey];
+
+ // check whether the settings event is within a certain time of spatializer creation.
+ const bool withinBoot =
+ item->getTimestamp() - mFirstCreateTimeNs < kBootDurationThreshold;
+
+ if (!enabled.empty()) {
+ if (enabled != deviceState.enabled) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.enabled.empty(), withinBoot);
+ deviceState.enabled = enabled;
+ const bool enabledStatsd = enabled == "true";
+ const auto [ result, str ] = sendToStatsd(SpatializerDeviceEnabledFields,
+ CONDITION(android::util::MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , enabledStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED, str);
+ }
+ }
+ if (!hasHeadTracker.empty()) {
+ if (hasHeadTracker != deviceState.hasHeadTracker) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.hasHeadTracker.empty(), withinBoot);
+ deviceState.hasHeadTracker = hasHeadTracker;
+ const bool supportedStatsd = hasHeadTracker == "true";
+ const auto [ result, str ] = sendToStatsd(HeadTrackerDeviceSupportedFields,
+ CONDITION(android::util::MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , supportedStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED, str);
+ }
+ }
+ if (!headTrackerEnabled.empty()) {
+ if (headTrackerEnabled != deviceState.headTrackerEnabled) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.headTrackerEnabled.empty(), withinBoot);
+ deviceState.headTrackerEnabled = headTrackerEnabled;
+ const bool enabledStatsd = headTrackerEnabled == "true";
+ const auto [ result, str ] = sendToStatsd(HeadTrackerDeviceEnabledFields,
+ CONDITION(android::util::MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , enabledStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED, str);
+ }
+ }
+ mSimpleLog.log("%s deviceKey: %s item: %s",
+ __func__, deviceKey.c_str(), item->toString().c_str());
+ }
+}
+
+std::pair<std::string, int32_t> AudioAnalytics::Spatializer::dump(
+ int32_t lines, const char *prefix) const
+{
+ std::lock_guard lg(mLock);
+ std::string s = mSimpleLog.dumpToString(prefix == nullptr ? "" : prefix, lines);
+ size_t n = std::count(s.begin(), s.end(), '\n');
+ return { s, n };
+}
} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index 6e24a58..d2b4aab 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -135,6 +135,94 @@
return map;
}
+// A map for the Java AudioDeviceInfo types to internal (native) output devices.
+const std::unordered_map<std::string, int32_t>& getAudioDeviceOutCompactMap() {
+ // DO NOT MODIFY VALUES (OK to add new ones).
+ static std::unordered_map<std::string, int32_t> map{
+ // should "unknown" go to AUDIO_DEVICE_NONE?
+ {"earpiece", AUDIO_DEVICE_OUT_EARPIECE},
+ {"speaker", AUDIO_DEVICE_OUT_SPEAKER},
+ {"headset", AUDIO_DEVICE_OUT_WIRED_HEADSET},
+ {"headphone", AUDIO_DEVICE_OUT_WIRED_HEADPHONE},
+ {"bt_sco", AUDIO_DEVICE_OUT_BLUETOOTH_SCO},
+ {"bt_sco_hs", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET},
+ {"bt_sco_carkit", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT},
+ {"bt_a2dp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP},
+ {"bt_a2dp_hp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES},
+ {"bt_a2dp_spk", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER},
+ {"aux_digital", AUDIO_DEVICE_OUT_AUX_DIGITAL},
+ {"hdmi", AUDIO_DEVICE_OUT_HDMI},
+ {"analog_dock", AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET},
+ {"digital_dock", AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET},
+ {"usb_accessory", AUDIO_DEVICE_OUT_USB_ACCESSORY},
+ {"usb_device", AUDIO_DEVICE_OUT_USB_DEVICE},
+ {"remote_submix", AUDIO_DEVICE_OUT_REMOTE_SUBMIX},
+ {"telephony_tx", AUDIO_DEVICE_OUT_TELEPHONY_TX},
+ {"line", AUDIO_DEVICE_OUT_LINE},
+ {"hdmi_arc", AUDIO_DEVICE_OUT_HDMI_ARC},
+ {"hdmi_earc", AUDIO_DEVICE_OUT_HDMI_EARC},
+ {"spdif", AUDIO_DEVICE_OUT_SPDIF},
+ {"fm_transmitter", AUDIO_DEVICE_OUT_FM},
+ {"aux_line", AUDIO_DEVICE_OUT_AUX_LINE},
+ {"speaker_safe", AUDIO_DEVICE_OUT_SPEAKER_SAFE},
+ {"ip", AUDIO_DEVICE_OUT_IP},
+ {"bus", AUDIO_DEVICE_OUT_BUS},
+ {"proxy", AUDIO_DEVICE_OUT_PROXY},
+ {"usb_headset", AUDIO_DEVICE_OUT_USB_HEADSET},
+ {"hearing_aid_out", AUDIO_DEVICE_OUT_HEARING_AID},
+ {"echo_canceller", AUDIO_DEVICE_OUT_ECHO_CANCELLER},
+ // default does not exist
+ {"ble_headset", AUDIO_DEVICE_OUT_BLE_HEADSET},
+ {"ble_speaker", AUDIO_DEVICE_OUT_BLE_SPEAKER},
+ {"ble_broadcast", AUDIO_DEVICE_OUT_BLE_BROADCAST},
+ };
+ return map;
+}
+
+// A map for the Java AudioDeviceInfo types.
+// This uses generated statsd enums.proto constants.
+const std::unordered_map<std::string, int32_t>& getAudioDeviceInfoTypeMap() {
+ // DO NOT MODIFY VALUES (OK to add new ones).
+ static std::unordered_map<std::string, int32_t> map{
+ {"unknown", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_UNKNOWN},
+ {"earpiece", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_EARPIECE},
+ {"speaker", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_SPEAKER},
+ {"headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_WIRED_HEADSET},
+ {"headphone", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_WIRED_HEADPHONES}, // sic
+ {"bt_sco", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_sco_hs", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_sco_carkit", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_a2dp", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"bt_a2dp_hp", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"bt_a2dp_spk", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"aux_digital", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI},
+ {"hdmi", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI},
+ {"analog_dock", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_DOCK},
+ {"digital_dock", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_DOCK},
+ {"usb_accessory", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_ACCESSORY},
+ {"usb_device", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_DEVICE},
+ {"usb_headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_HEADSET},
+ {"remote_submix", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_REMOTE_SUBMIX},
+ {"telephony_tx", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_TELEPHONY},
+ {"line", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_LINE_ANALOG},
+ {"hdmi_arc", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI_ARC},
+ {"hdmi_earc", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI_EARC},
+ {"spdif", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_LINE_DIGITAL},
+ {"fm_transmitter", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_FM},
+ {"aux_line", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_AUX_LINE},
+ {"speaker_safe", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_SPEAKER_SAFE},
+ {"ip", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_IP},
+ {"bus", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUS},
+ {"proxy", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_UNKNOWN /* AUDIO_DEVICE_INFO_TYPE_PROXY */},
+ {"hearing_aid_out", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HEARING_AID},
+ {"echo_canceller", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_ECHO_REFERENCE}, // sic
+ {"ble_headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_HEADSET},
+ {"ble_speaker", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_SPEAKER},
+ {"ble_broadcast", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_BROADCAST},
+ };
+ return map;
+}
+
const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap() {
// DO NOT MODIFY VALUES (OK to add new ones).
// This may be found in frameworks/av/services/audioflinger/Threads.h
@@ -197,6 +285,41 @@
return map;
}
+const std::unordered_map<std::string, int32_t>& getHeadTrackingModeMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/base/media/java/android/media/Spatializer.java
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializerHeadTrackingMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"OTHER", 0},
+ {"DISABLED", -1},
+ {"RELATIVE_WORLD", 1},
+ {"RELATIVE_SCREEN", 2},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getSpatializerLevelMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/base/media/java/android/media/Spatializer.java
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializerHeadTrackingMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"NONE", 0},
+ {"SPATIALIZER_MULTICHANNEL", 1},
+ {"SPATIALIZER_MCHAN_BED_PLUS_OBJECTS", 2},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getSpatializerModeMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializationMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"SPATIALIZER_BINAURAL", 0},
+ {"SPATIALIZER_TRANSAURAL", 1},
+ };
+ return map;
+}
+
const std::unordered_map<std::string, int32_t>& getStatusMap() {
// DO NOT MODIFY VALUES(OK to add new ones).
static std::unordered_map<std::string, int32_t> map {
@@ -286,6 +409,35 @@
return value;
}
+std::vector<int32_t> vectorFromMap(
+ const std::string &str, const std::unordered_map<std::string, int32_t>& map)
+{
+ std::vector<int32_t> v;
+
+ if (str.empty()) return v;
+
+ const auto result = stringutils::split(str, "|");
+ for (const auto &s : result) {
+ auto it = map.find(s);
+ if (it == map.end()) continue;
+ v.push_back(it->second);
+ }
+ return v;
+}
+
+std::vector<int64_t> channelMaskVectorFromString(const std::string &s)
+{
+ std::vector<int64_t> v;
+
+ const auto result = stringutils::split(s, "|");
+ for (const auto &mask : result) {
+ // 0 if undetected or if actually 0.
+ int64_t int64Mask = strtoll(mask.c_str(), nullptr, 0);
+ v.push_back(int64Mask);
+ }
+ return v;
+}
+
template <>
int32_t lookup<CONTENT_TYPE>(const std::string &contentType)
{
@@ -441,6 +593,17 @@
}
template <>
+int32_t lookup<AUDIO_DEVICE_INFO_TYPE>(const std::string& audioDeviceInfoType)
+{
+ auto& map = getAudioDeviceInfoTypeMap();
+ auto it = map.find(audioDeviceInfoType);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
int32_t lookup<CALLER_NAME>(const std::string &callerName)
{
auto& map = getAudioCallerNameMap();
@@ -463,6 +626,39 @@
}
template <>
+int32_t lookup<HEAD_TRACKING_MODE>(const std::string& headTrackingMode)
+{
+ auto& map = getHeadTrackingModeMap();
+ auto it = map.find(headTrackingMode);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
+int32_t lookup<SPATIALIZER_LEVEL>(const std::string& spatializerLevel)
+{
+ auto& map = getSpatializerLevelMap();
+ auto it = map.find(spatializerLevel);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
+int32_t lookup<SPATIALIZER_MODE>(const std::string& spatializerMode)
+{
+ auto& map = getSpatializerModeMap();
+ auto it = map.find(spatializerMode);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
int32_t lookup<STATUS>(const std::string &status)
{
auto& map = getStatusMap();
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index ff16b9e..ceb3e6a 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -72,6 +72,7 @@
bool MediaMetricsService::useUidForPackage(
const std::string& package, const std::string& installer)
{
+ // NOLINTBEGIN(bugprone-branch-clone)
if (strchr(package.c_str(), '.') == nullptr) {
return false; // not of form 'com.whatever...'; assume internal and ok
} else if (strncmp(package.c_str(), "android.", 8) == 0) {
@@ -85,6 +86,7 @@
} else {
return true; // we're not sure where it came from, use uid only.
}
+ // NOLINTEND(bugprone-branch-clone)
}
/* static */
@@ -336,6 +338,15 @@
result << "-- some lines may be truncated --\n";
}
+ const int32_t spatializerLinesToDump = all ? INT32_MAX : 15;
+ result << "\nSpatializer Message Log:";
+ const auto [ spatializerDumpString, spatializerLines ] =
+ mAudioAnalytics.dumpSpatializer(spatializerLinesToDump);
+ result << "\n" << spatializerDumpString;
+ if (spatializerLines == spatializerLinesToDump) {
+ result << "-- some lines may be truncated --\n";
+ }
+
result << "\nLogSessionId:\n"
<< mediametrics::ValidateId::get()->dump();
diff --git a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
index 5ee8c30..7000ba8 100644
--- a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
+++ b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
@@ -92,6 +92,15 @@
return mHealth.dump(lines);
}
+ /**
+ * Returns a pair consisting of the dump string and the number of lines in the string.
+ *
+ * Spatializer dump.
+ */
+ std::pair<std::string, int32_t> dumpSpatializer(int32_t lines = INT32_MAX) const {
+ return mSpatializer.dump(lines);
+ }
+
void clear() {
// underlying state is locked.
mPreviousAnalyticsState->clear();
@@ -317,6 +326,36 @@
SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
} mHealth{*this};
+ // Spatializer is a nested class that tracks related messages.
+ class Spatializer {
+ public:
+ explicit Spatializer(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics(audioAnalytics) {}
+
+ // an item that starts with "audio.spatializer"
+ void onEvent(const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ std::pair<std::string, int32_t> dump(
+ int32_t lines = INT32_MAX, const char *prefix = nullptr) const;
+
+ private:
+
+ // Current device state as strings:
+ // "" means unknown, "true" or "false".
+ struct DeviceState {
+ std::string enabled;
+ std::string hasHeadTracker;
+ std::string headTrackerEnabled;
+ };
+
+ AudioAnalytics& mAudioAnalytics;
+ static constexpr int64_t kBootDurationThreshold = 120 /* seconds */ * 1e9;
+ mutable std::mutex mLock;
+ int64_t mFirstCreateTimeNs GUARDED_BY(mLock) = 0;
+ std::map<std::string, DeviceState> mDeviceStateMap GUARDED_BY(mLock);
+ SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
+ } mSpatializer{*this};
+
AudioPowerUsage mAudioPowerUsage;
};
diff --git a/services/mediametrics/include/mediametricsservice/AudioPowerUsage.h b/services/mediametrics/include/mediametricsservice/AudioPowerUsage.h
index b7215e6..6e5a5cf 100644
--- a/services/mediametrics/include/mediametricsservice/AudioPowerUsage.h
+++ b/services/mediametrics/include/mediametricsservice/AudioPowerUsage.h
@@ -50,7 +50,7 @@
*/
std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
- // align with message AudioUsageDataReported in frameworks/base/cmds/statsd/src/atoms.proto
+ // align with message AudioPowerUsageDataReported in frameworks/proto_logging/stats/atoms.proto
enum AudioType {
UNKNOWN_TYPE = 0,
VOICE_CALL_TYPE = 1, // voice call
diff --git a/services/mediametrics/include/mediametricsservice/AudioTypes.h b/services/mediametrics/include/mediametricsservice/AudioTypes.h
index 5dbff9b..b5fe28b 100644
--- a/services/mediametrics/include/mediametricsservice/AudioTypes.h
+++ b/services/mediametrics/include/mediametricsservice/AudioTypes.h
@@ -29,6 +29,14 @@
const std::unordered_map<std::string, int64_t>& getAudioDeviceOutMap();
const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap();
const std::unordered_map<std::string, int32_t>& getAudioTrackTraitsMap();
+const std::unordered_map<std::string, int32_t>& getHeadTrackingModeMap();
+const std::unordered_map<std::string, int32_t>& getSpatializerLevelMap();
+const std::unordered_map<std::string, int32_t>& getSpatializerModeMap();
+
+std::vector<int32_t> vectorFromMap(
+ const std::string &str, const std::unordered_map<std::string, int32_t>& map);
+
+std::vector<int64_t> channelMaskVectorFromString(const std::string &s);
// Enumeration for the device connection results.
enum DeviceConnectionResult : int32_t {
@@ -47,14 +55,18 @@
AAUDIO_DIRECTION,
AAUDIO_PERFORMANCE_MODE,
AAUDIO_SHARING_MODE,
+ AUDIO_DEVICE_INFO_TYPE,
CALLER_NAME,
CONTENT_TYPE,
ENCODING,
+ HEAD_TRACKING_MODE,
INPUT_DEVICE, // int64_t
INPUT_FLAG,
OUTPUT_DEVICE, // int64_t
OUTPUT_FLAG,
SOURCE_TYPE,
+ SPATIALIZER_LEVEL,
+ SPATIALIZER_MODE,
STATUS,
STREAM_TYPE,
THREAD_TYPE,
diff --git a/services/mediametrics/include/mediametricsservice/MediaMetricsService.h b/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
index 8d0b1cf..3ec5ac7 100644
--- a/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
+++ b/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
@@ -125,7 +125,7 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
// mStatsdLog is locked internally (thread-safe) and shows the last atoms logged
- static constexpr size_t STATSD_LOG_LINES_MAX = 30; // recent log lines to keep
+ static constexpr size_t STATSD_LOG_LINES_MAX = 48; // recent log lines to keep
static constexpr size_t STATSD_LOG_LINES_DUMP = 4; // normal amount of lines to dump
const std::shared_ptr<mediametrics::StatsdLog> mStatsdLog{
std::make_shared<mediametrics::StatsdLog>(STATSD_LOG_LINES_MAX)};
diff --git a/services/mediametrics/include/mediametricsservice/StringUtils.h b/services/mediametrics/include/mediametricsservice/StringUtils.h
index a56f5b8..a91d37b 100644
--- a/services/mediametrics/include/mediametricsservice/StringUtils.h
+++ b/services/mediametrics/include/mediametricsservice/StringUtils.h
@@ -23,6 +23,19 @@
namespace android::mediametrics::stringutils {
+// Define a way of printing a vector - this
+// is used for proto repeated arguments.
+template <typename T>
+inline std::ostream & operator<< (std::ostream& s,
+ std::vector<T> const& v) {
+ s << "{ ";
+ for (const auto& e : v) {
+ s << e << " ";
+ }
+ s << "}";
+ return s;
+}
+
/**
* fieldPrint is a helper method that logs to a stringstream a sequence of
* field names (in a fixed size array) together with a variable number of arg parameters.
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index a7b045e..01adf7f 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -99,16 +99,14 @@
}
int32_t error_code = -1;
- if (item->getInt32("android.media.audiorecord.errcode", &error_code)) {
- metrics_proto.set_error_code(error_code);
- } else if (item->getInt32("android.media.audiorecord.lastError.code", &error_code)) {
+ if (item->getInt32("android.media.audiorecord.errcode", &error_code) ||
+ item->getInt32("android.media.audiorecord.lastError.code", &error_code)) {
metrics_proto.set_error_code(error_code);
}
std::string error_function;
- if (item->getString("android.media.audiorecord.errfunc", &error_function)) {
- metrics_proto.set_error_function(error_function);
- } else if (item->getString("android.media.audiorecord.lastError.at", &error_function)) {
+ if (item->getString("android.media.audiorecord.errfunc", &error_function) ||
+ item->getString("android.media.audiorecord.lastError.at", &error_function)) {
metrics_proto.set_error_function(error_function);
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index b4610bc..4d18876 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -271,10 +271,9 @@
snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId);
result.append(buffer);
- std::string clientName;
- Status status = infos[j].client->getName(&clientName);
- if (!status.isOk()) {
- clientName = "<unknown client>";
+ std::string clientName = "<unknown client>";
+ if (infos[j].client != nullptr) {
+ Status status = infos[j].client->getName(&clientName);
}
snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
result.append(buffer);
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 3f18b95..290e9ff 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -22,6 +22,7 @@
#include <assert.h>
#include <map>
#include <mutex>
+#include <set>
#include <sstream>
#include <thread>
#include <utils/Singleton.h>
@@ -68,9 +69,29 @@
return result.str();
}
+namespace {
+
+const static std::map<audio_format_t, audio_format_t> NEXT_FORMAT_TO_TRY = {
+ {AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_32_BIT},
+ {AUDIO_FORMAT_PCM_32_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED},
+ {AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_16_BIT}
+};
+
+audio_format_t getNextFormatToTry(audio_format_t curFormat, audio_format_t returnedFromAPM) {
+ if (returnedFromAPM != AUDIO_FORMAT_DEFAULT) {
+ return returnedFromAPM;
+ }
+ const auto it = NEXT_FORMAT_TO_TRY.find(curFormat);
+ return it != NEXT_FORMAT_TO_TRY.end() ? it->second : AUDIO_FORMAT_DEFAULT;
+}
+
+}
+
aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
aaudio_result_t result = AAUDIO_OK;
copyFrom(request.getConstantConfiguration());
+ mRequestedDeviceId = getDeviceId();
+
mMmapClient.attributionSource = request.getAttributionSource();
// TODO b/182392769: use attribution source util
mMmapClient.attributionSource.uid = VALUE_OR_FATAL(
@@ -79,43 +100,45 @@
legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
audio_format_t audioFormat = getFormat();
+ std::set<audio_format_t> formatsTried;
+ while (true) {
+ if (formatsTried.find(audioFormat) != formatsTried.end()) {
+ // APM returning something that has already tried.
+ ALOGW("Have already tried to open #x, but failed before");
+ break;
+ }
+ formatsTried.insert(audioFormat);
- result = openWithFormat(audioFormat);
- if (result == AAUDIO_OK) return result;
+ audio_format_t nextFormatToTry = AUDIO_FORMAT_DEFAULT;
+ result = openWithFormat(audioFormat, &nextFormatToTry);
+ if (result == AAUDIO_OK || result != AAUDIO_ERROR_UNAVAILABLE) {
+ // Return if it is successful or there is an error that is not
+ // AAUDIO_ERROR_UNAVAILABLE happens.
+ ALOGI("Opened format=%#x with result=%d", audioFormat, result);
+ break;
+ }
- if (result == AAUDIO_ERROR_UNAVAILABLE && audioFormat == AUDIO_FORMAT_PCM_FLOAT) {
- ALOGD("%s() FLOAT failed, perhaps due to format. Try again with 32_BIT", __func__);
- audioFormat = AUDIO_FORMAT_PCM_32_BIT;
- result = openWithFormat(audioFormat);
- }
- if (result == AAUDIO_OK) return result;
-
- if (result == AAUDIO_ERROR_UNAVAILABLE && audioFormat == AUDIO_FORMAT_PCM_32_BIT) {
- ALOGD("%s() 32_BIT failed, perhaps due to format. Try again with 24_BIT_PACKED", __func__);
- audioFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
- result = openWithFormat(audioFormat);
- }
- if (result == AAUDIO_OK) return result;
-
- // TODO The HAL and AudioFlinger should be recommending a format if the open fails.
- // But that recommendation is not propagating back from the HAL.
- // So for now just try something very likely to work.
- if (result == AAUDIO_ERROR_UNAVAILABLE && audioFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
- ALOGD("%s() 24_BIT failed, perhaps due to format. Try again with 16_BIT", __func__);
- audioFormat = AUDIO_FORMAT_PCM_16_BIT;
- result = openWithFormat(audioFormat);
+ nextFormatToTry = getNextFormatToTry(audioFormat, nextFormatToTry);
+ ALOGD("%s() %#x failed, perhaps due to format. Try again with %#x",
+ __func__, audioFormat, nextFormatToTry);
+ audioFormat = nextFormatToTry;
+ if (audioFormat == AUDIO_FORMAT_DEFAULT) {
+ // Nothing else to try
+ break;
+ }
}
return result;
}
-aaudio_result_t AAudioServiceEndpointMMAP::openWithFormat(audio_format_t audioFormat) {
+aaudio_result_t AAudioServiceEndpointMMAP::openWithFormat(
+ audio_format_t audioFormat, audio_format_t* nextFormatToTry) {
aaudio_result_t result = AAUDIO_OK;
audio_config_base_t config;
audio_port_handle_t deviceId;
const audio_attributes_t attributes = getAudioAttributesFrom(this);
- mRequestedDeviceId = deviceId = getDeviceId();
+ deviceId = mRequestedDeviceId;
// Fill in config
config.format = audioFormat;
@@ -151,6 +174,10 @@
audio_session_t sessionId = AAudioConvert_aaudioToAndroidSessionId(requestedSessionId);
// Open HAL stream. Set mMmapStream
+ ALOGD("%s trying to open MMAP stream with format=%#x, "
+ "sample_rate=%u, channel_mask=%#x, device=%d",
+ __func__, config.format, config.sample_rate,
+ config.channel_mask, deviceId);
status_t status = MmapStreamInterface::openMmapStream(streamDirection,
&attributes,
&config,
@@ -165,7 +192,11 @@
if (status != OK) {
// This can happen if the resource is busy or the config does
// not match the hardware.
- ALOGD("%s() - openMmapStream() returned status %d", __func__, status);
+ ALOGD("%s() - openMmapStream() returned status=%d, suggested format=%#x, sample_rate=%u, "
+ "channel_mask=%#x",
+ __func__, status, config.format, config.sample_rate, config.format);
+ *nextFormatToTry = config.format != audioFormat ? config.format
+ : *nextFormatToTry;
return AAUDIO_ERROR_UNAVAILABLE;
}
@@ -221,6 +252,9 @@
error:
close();
+ // restore original requests
+ setDeviceId(mRequestedDeviceId);
+ setSessionId(requestedSessionId);
return result;
}
@@ -228,9 +262,6 @@
if (mMmapStream != nullptr) {
// Needs to be explicitly cleared or CTS will fail but it is not clear why.
mMmapStream.clear();
- // Apparently the above close is asynchronous. An attempt to open a new device
- // right after a close can fail. Also some callbacks may still be in flight!
- // FIXME Make closing synchronous.
AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
}
}
@@ -362,12 +393,8 @@
asyncTask.detach();
}
-void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) {
- // TODO Do we really need a different volume for each channel?
- // We get called with an array filled with a single value!
- float volume = values[0];
- ALOGD("%s() volume[0] = %f", __func__, volume);
+void AAudioServiceEndpointMMAP::onVolumeChanged(float volume) {
+ ALOGD("%s() volume = %f", __func__, volume);
std::lock_guard<std::mutex> lock(mLockStreams);
for(const auto& stream : mRegisteredStreams) {
stream->onVolumeChanged(volume);
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index 3e7f2c7..73e0f61 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -77,8 +77,7 @@
// -------------- Callback functions for MmapStreamCallback ---------------------
void onTearDown(audio_port_handle_t portHandle) override;
- void onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) override;
+ void onVolumeChanged(float volume) override;
void onRoutingChanged(audio_port_handle_t portHandle) override;
// ------------------------------------------------------------------------------
@@ -93,7 +92,7 @@
private:
- aaudio_result_t openWithFormat(audio_format_t audioFormat);
+ aaudio_result_t openWithFormat(audio_format_t audioFormat, audio_format_t* nextFormatToTry);
aaudio_result_t createMmapBuffer(android::base::unique_fd* fileDescriptor);
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 9f48f80..d395c25 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -278,7 +278,7 @@
if (result != AAUDIO_OK) goto error;
// This should happen at the end of the start.
- sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED, static_cast<int64_t>(mClientHandle));
setState(AAUDIO_STREAM_STATE_STARTED);
return result;
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 80e4296..5076239 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -25,6 +25,10 @@
name: "libaaudioservice",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
srcs: [
"AAudioClientTracker.cpp",
"AAudioCommandQueue.cpp",
@@ -70,7 +74,6 @@
"framework-permission-aidl-cpp",
"libaudioclient_aidl_conversion",
"packagemanager_aidl-cpp",
- "android.media.audio.common.types-V1-cpp",
],
export_shared_lib_headers: [
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
index 5c1dda1..9c4f9a5 100644
--- a/services/tuner/Android.bp
+++ b/services/tuner/Android.bp
@@ -17,7 +17,6 @@
"android.hardware.common.fmq-V1",
"android.hardware.tv.tuner-V1",
],
-
backend: {
java: {
enabled: false,
diff --git a/services/tuner/TunerDemux.cpp b/services/tuner/TunerDemux.cpp
index a6f3a2c..7e07da1 100644
--- a/services/tuner/TunerDemux.cpp
+++ b/services/tuner/TunerDemux.cpp
@@ -26,6 +26,7 @@
#include <aidl/android/hardware/tv/tuner/Result.h>
#include "TunerDvr.h"
+#include "TunerService.h"
#include "TunerTimeFilter.h"
using ::aidl::android::hardware::tv::tuner::IDvr;
@@ -41,23 +42,20 @@
namespace tv {
namespace tuner {
-TunerDemux::TunerDemux(shared_ptr<IDemux> demux, int id) {
+TunerDemux::TunerDemux(const shared_ptr<IDemux> demux, const int id,
+ const shared_ptr<TunerService> tuner) {
mDemux = demux;
mDemuxId = id;
+ mTunerService = tuner;
}
TunerDemux::~TunerDemux() {
mDemux = nullptr;
+ mTunerService = nullptr;
}
::ndk::ScopedAStatus TunerDemux::setFrontendDataSource(
const shared_ptr<ITunerFrontend>& in_frontend) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
int frontendId;
in_frontend->getFrontendId(&frontendId);
@@ -65,43 +63,26 @@
}
::ndk::ScopedAStatus TunerDemux::setFrontendDataSourceById(int frontendId) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDemux->setFrontendDataSource(frontendId);
}
::ndk::ScopedAStatus TunerDemux::openFilter(const DemuxFilterType& in_type, int32_t in_bufferSize,
const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IFilter> filter;
shared_ptr<TunerFilter::FilterCallback> filterCb =
::ndk::SharedRefBase::make<TunerFilter::FilterCallback>(in_cb);
shared_ptr<IFilterCallback> cb = filterCb;
auto status = mDemux->openFilter(in_type, in_bufferSize, cb, &filter);
if (status.isOk()) {
- *_aidl_return = ::ndk::SharedRefBase::make<TunerFilter>(filter, filterCb, in_type);
+ *_aidl_return =
+ ::ndk::SharedRefBase::make<TunerFilter>(filter, filterCb, in_type, mTunerService);
}
return status;
}
::ndk::ScopedAStatus TunerDemux::openTimeFilter(shared_ptr<ITunerTimeFilter>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<ITimeFilter> filter;
auto status = mDemux->openTimeFilter(&filter);
if (status.isOk()) {
@@ -113,35 +94,17 @@
::ndk::ScopedAStatus TunerDemux::getAvSyncHwId(const shared_ptr<ITunerFilter>& tunerFilter,
int32_t* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IFilter> halFilter = (static_cast<TunerFilter*>(tunerFilter.get()))->getHalFilter();
return mDemux->getAvSyncHwId(halFilter, _aidl_return);
}
::ndk::ScopedAStatus TunerDemux::getAvSyncTime(int32_t avSyncHwId, int64_t* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDemux->getAvSyncTime(avSyncHwId, _aidl_return);
}
::ndk::ScopedAStatus TunerDemux::openDvr(DvrType in_dvbType, int32_t in_bufferSize,
const shared_ptr<ITunerDvrCallback>& in_cb,
shared_ptr<ITunerDvr>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IDvrCallback> callback = ::ndk::SharedRefBase::make<TunerDvr::DvrCallback>(in_cb);
shared_ptr<IDvr> halDvr;
auto res = mDemux->openDvr(in_dvbType, in_bufferSize, callback, &halDvr);
@@ -153,36 +116,15 @@
}
::ndk::ScopedAStatus TunerDemux::connectCiCam(int32_t ciCamId) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDemux->connectCiCam(ciCamId);
}
::ndk::ScopedAStatus TunerDemux::disconnectCiCam() {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDemux->disconnectCiCam();
}
::ndk::ScopedAStatus TunerDemux::close() {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto res = mDemux->close();
- mDemux = nullptr;
-
- return res;
+ return mDemux->close();
}
} // namespace tuner
diff --git a/services/tuner/TunerDemux.h b/services/tuner/TunerDemux.h
index cdb3aa0..0c71987 100644
--- a/services/tuner/TunerDemux.h
+++ b/services/tuner/TunerDemux.h
@@ -32,10 +32,13 @@
namespace tv {
namespace tuner {
+class TunerService;
+
class TunerDemux : public BnTunerDemux {
public:
- TunerDemux(shared_ptr<IDemux> demux, int demuxId);
+ TunerDemux(const shared_ptr<IDemux> demux, const int demuxId,
+ const shared_ptr<TunerService> tuner);
virtual ~TunerDemux();
::ndk::ScopedAStatus setFrontendDataSource(
@@ -60,6 +63,7 @@
private:
shared_ptr<IDemux> mDemux;
int mDemuxId;
+ shared_ptr<TunerService> mTunerService;
};
} // namespace tuner
diff --git a/services/tuner/TunerDescrambler.cpp b/services/tuner/TunerDescrambler.cpp
index 70aee20..8cc5fe0 100644
--- a/services/tuner/TunerDescrambler.cpp
+++ b/services/tuner/TunerDescrambler.cpp
@@ -46,33 +46,15 @@
::ndk::ScopedAStatus TunerDescrambler::setDemuxSource(
const shared_ptr<ITunerDemux>& in_tunerDemux) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDescrambler->setDemuxSource((static_cast<TunerDemux*>(in_tunerDemux.get()))->getId());
}
::ndk::ScopedAStatus TunerDescrambler::setKeyToken(const vector<uint8_t>& in_keyToken) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDescrambler->setKeyToken(in_keyToken);
}
::ndk::ScopedAStatus TunerDescrambler::addPid(
const DemuxPid& in_pid, const shared_ptr<ITunerFilter>& in_optionalSourceFilter) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IFilter> halFilter =
(in_optionalSourceFilter == nullptr)
? nullptr
@@ -83,12 +65,6 @@
::ndk::ScopedAStatus TunerDescrambler::removePid(
const DemuxPid& in_pid, const shared_ptr<ITunerFilter>& in_optionalSourceFilter) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IFilter> halFilter =
(in_optionalSourceFilter == nullptr)
? nullptr
@@ -98,16 +74,7 @@
}
::ndk::ScopedAStatus TunerDescrambler::close() {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto res = mDescrambler->close();
- mDescrambler = nullptr;
-
- return res;
+ return mDescrambler->close();
}
} // namespace tuner
diff --git a/services/tuner/TunerDvr.cpp b/services/tuner/TunerDvr.cpp
index 8776f7e..9a35db8 100644
--- a/services/tuner/TunerDvr.cpp
+++ b/services/tuner/TunerDvr.cpp
@@ -41,32 +41,14 @@
}
::ndk::ScopedAStatus TunerDvr::getQueueDesc(AidlMQDesc* _aidl_return) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDvr->getQueueDesc(_aidl_return);
}
::ndk::ScopedAStatus TunerDvr::configure(const DvrSettings& in_settings) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDvr->configure(in_settings);
}
::ndk::ScopedAStatus TunerDvr::attachFilter(const shared_ptr<ITunerFilter>& in_filter) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (in_filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -82,12 +64,6 @@
}
::ndk::ScopedAStatus TunerDvr::detachFilter(const shared_ptr<ITunerFilter>& in_filter) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (in_filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -103,46 +79,19 @@
}
::ndk::ScopedAStatus TunerDvr::start() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDvr->start();
}
::ndk::ScopedAStatus TunerDvr::stop() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDvr->stop();
}
::ndk::ScopedAStatus TunerDvr::flush() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mDvr->flush();
}
::ndk::ScopedAStatus TunerDvr::close() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto status = mDvr->close();
- mDvr = nullptr;
-
- return status;
+ return mDvr->close();
}
/////////////// IDvrCallback ///////////////////////
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
index e8c7767..fd1f886 100644
--- a/services/tuner/TunerFilter.cpp
+++ b/services/tuner/TunerFilter.cpp
@@ -36,28 +36,28 @@
using namespace std;
-TunerFilter::TunerFilter(shared_ptr<IFilter> filter, shared_ptr<FilterCallback> cb,
- DemuxFilterType type)
+TunerFilter::TunerFilter(const shared_ptr<IFilter> filter, const shared_ptr<FilterCallback> cb,
+ const DemuxFilterType type, const shared_ptr<TunerService> tuner)
: mFilter(filter),
mType(type),
mStarted(false),
mShared(false),
mClientPid(-1),
- mFilterCallback(cb) {}
+ mFilterCallback(cb),
+ mTunerService(tuner) {}
TunerFilter::~TunerFilter() {
- Mutex::Autolock _l(mLock);
- mFilter = nullptr;
+ freeSharedFilterToken("");
+
+ {
+ Mutex::Autolock _l(mLock);
+ mFilter = nullptr;
+ mTunerService = nullptr;
+ }
}
::ndk::ScopedAStatus TunerFilter::getQueueDesc(AidlMQDesc* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -73,12 +73,6 @@
::ndk::ScopedAStatus TunerFilter::getId(int32_t* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -94,12 +88,6 @@
::ndk::ScopedAStatus TunerFilter::getId64Bit(int64_t* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -115,12 +103,6 @@
::ndk::ScopedAStatus TunerFilter::configure(const DemuxFilterSettings& in_settings) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -132,12 +114,6 @@
::ndk::ScopedAStatus TunerFilter::configureMonitorEvent(int32_t monitorEventType) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -149,12 +125,6 @@
::ndk::ScopedAStatus TunerFilter::configureIpFilterContextId(int32_t cid) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -166,12 +136,6 @@
::ndk::ScopedAStatus TunerFilter::configureAvStreamType(const AvStreamType& in_avStreamType) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -183,12 +147,6 @@
::ndk::ScopedAStatus TunerFilter::setDataSource(const shared_ptr<ITunerFilter>& filter) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -207,12 +165,6 @@
::ndk::ScopedAStatus TunerFilter::getAvSharedHandle(NativeHandle* out_avMemory,
int64_t* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -225,12 +177,6 @@
::ndk::ScopedAStatus TunerFilter::releaseAvHandle(const NativeHandle& in_handle,
int64_t in_avDataId) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -242,12 +188,6 @@
::ndk::ScopedAStatus TunerFilter::start() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -267,12 +207,6 @@
::ndk::ScopedAStatus TunerFilter::stop() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -291,12 +225,6 @@
::ndk::ScopedAStatus TunerFilter::flush() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -312,12 +240,6 @@
::ndk::ScopedAStatus TunerFilter::close() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -326,7 +248,7 @@
mFilterCallback->sendSharedFilterStatus(STATUS_INACCESSIBLE);
mFilterCallback->detachSharedFilterCallback();
}
- TunerService::getTunerService()->removeSharedFilter(this->ref<TunerFilter>());
+ mTunerService->removeSharedFilter(this->ref<TunerFilter>());
} else {
// Calling from shared process, do not really close this filter.
if (mFilterCallback != nullptr) {
@@ -341,7 +263,6 @@
mFilterCallback->detachCallbacks();
}
auto res = mFilter->close();
- mFilter = nullptr;
mStarted = false;
mShared = false;
mClientPid = -1;
@@ -351,12 +272,6 @@
::ndk::ScopedAStatus TunerFilter::acquireSharedFilterToken(string* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared || mStarted) {
ALOGD("create SharedFilter in wrong state");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -365,7 +280,7 @@
IPCThreadState* ipc = IPCThreadState::self();
mClientPid = ipc->getCallingPid();
- string token = TunerService::getTunerService()->addFilterToShared(this->ref<TunerFilter>());
+ string token = mTunerService->addFilterToShared(this->ref<TunerFilter>());
_aidl_return->assign(token);
mShared = true;
@@ -374,12 +289,6 @@
::ndk::ScopedAStatus TunerFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (!mShared) {
// The filter is not shared or the shared filter has been closed.
return ::ndk::ScopedAStatus::ok();
@@ -390,7 +299,7 @@
mFilterCallback->detachSharedFilterCallback();
}
- TunerService::getTunerService()->removeSharedFilter(this->ref<TunerFilter>());
+ mTunerService->removeSharedFilter(this->ref<TunerFilter>());
mShared = false;
return ::ndk::ScopedAStatus::ok();
@@ -398,24 +307,12 @@
::ndk::ScopedAStatus TunerFilter::getFilterType(DemuxFilterType* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
*_aidl_return = mType;
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus TunerFilter::setDelayHint(const FilterDelayHint& in_hint) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFilter->setDelayHint(in_hint);
}
diff --git a/services/tuner/TunerFilter.h b/services/tuner/TunerFilter.h
index 93d8898..f6178c4 100644
--- a/services/tuner/TunerFilter.h
+++ b/services/tuner/TunerFilter.h
@@ -53,8 +53,9 @@
using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
-class TunerFilter : public BnTunerFilter {
+class TunerService;
+class TunerFilter : public BnTunerFilter {
public:
class FilterCallback : public BnFilterCallback {
public:
@@ -75,7 +76,8 @@
Mutex mCallbackLock;
};
- TunerFilter(shared_ptr<IFilter> filter, shared_ptr<FilterCallback> cb, DemuxFilterType type);
+ TunerFilter(const shared_ptr<IFilter> filter, const shared_ptr<FilterCallback> cb,
+ const DemuxFilterType type, const shared_ptr<TunerService> tuner);
virtual ~TunerFilter();
::ndk::ScopedAStatus getId(int32_t* _aidl_return) override;
@@ -113,6 +115,7 @@
int32_t mClientPid;
shared_ptr<FilterCallback> mFilterCallback;
Mutex mLock;
+ shared_ptr<TunerService> mTunerService;
};
} // namespace tuner
diff --git a/services/tuner/TunerFrontend.cpp b/services/tuner/TunerFrontend.cpp
index 5116305..b52a740 100644
--- a/services/tuner/TunerFrontend.cpp
+++ b/services/tuner/TunerFrontend.cpp
@@ -43,12 +43,6 @@
::ndk::ScopedAStatus TunerFrontend::setCallback(
const shared_ptr<ITunerFrontendCallback>& tunerFrontendCallback) {
- if (mFrontend == nullptr) {
- ALOGE("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (tunerFrontendCallback == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -60,53 +54,23 @@
}
::ndk::ScopedAStatus TunerFrontend::tune(const FrontendSettings& settings) {
- if (mFrontend == nullptr) {
- ALOGE("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->tune(settings);
}
::ndk::ScopedAStatus TunerFrontend::stopTune() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->stopTune();
}
::ndk::ScopedAStatus TunerFrontend::scan(const FrontendSettings& settings,
FrontendScanType frontendScanType) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->scan(settings, frontendScanType);
}
::ndk::ScopedAStatus TunerFrontend::stopScan() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->stopScan();
}
::ndk::ScopedAStatus TunerFrontend::setLnb(const shared_ptr<ITunerLnb>& lnb) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (lnb == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -116,46 +80,19 @@
}
::ndk::ScopedAStatus TunerFrontend::linkCiCamToFrontend(int32_t ciCamId, int32_t* _aidl_return) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->linkCiCam(ciCamId, _aidl_return);
}
::ndk::ScopedAStatus TunerFrontend::unlinkCiCamToFrontend(int32_t ciCamId) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->unlinkCiCam(ciCamId);
}
::ndk::ScopedAStatus TunerFrontend::close() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto res = mFrontend->close();
- mFrontend = nullptr;
-
- return res;
+ return mFrontend->close();
}
::ndk::ScopedAStatus TunerFrontend::getStatus(const vector<FrontendStatusType>& in_statusTypes,
vector<FrontendStatus>* _aidl_return) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->getStatus(in_statusTypes, _aidl_return);
}
@@ -165,34 +102,16 @@
}
::ndk::ScopedAStatus TunerFrontend::getHardwareInfo(std::string* _aidl_return) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->getHardwareInfo(_aidl_return);
}
::ndk::ScopedAStatus TunerFrontend::removeOutputPid(int32_t in_pid) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->removeOutputPid(in_pid);
}
::ndk::ScopedAStatus TunerFrontend::getFrontendStatusReadiness(
const std::vector<FrontendStatusType>& in_statusTypes,
std::vector<FrontendStatusReadiness>* _aidl_return) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mFrontend->getFrontendStatusReadiness(in_statusTypes, _aidl_return);
}
diff --git a/services/tuner/TunerLnb.cpp b/services/tuner/TunerLnb.cpp
index 1e143c3..22306e1 100644
--- a/services/tuner/TunerLnb.cpp
+++ b/services/tuner/TunerLnb.cpp
@@ -42,12 +42,6 @@
::ndk::ScopedAStatus TunerLnb::setCallback(
const shared_ptr<ITunerLnbCallback>& in_tunerLnbCallback) {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (in_tunerLnbCallback == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -59,56 +53,23 @@
}
::ndk::ScopedAStatus TunerLnb::setVoltage(LnbVoltage in_voltage) {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mLnb->setVoltage(in_voltage);
}
::ndk::ScopedAStatus TunerLnb::setTone(LnbTone in_tone) {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mLnb->setTone(in_tone);
}
::ndk::ScopedAStatus TunerLnb::setSatellitePosition(LnbPosition in_position) {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mLnb->setSatellitePosition(in_position);
}
::ndk::ScopedAStatus TunerLnb::sendDiseqcMessage(const vector<uint8_t>& in_diseqcMessage) {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mLnb->sendDiseqcMessage(in_diseqcMessage);
}
::ndk::ScopedAStatus TunerLnb::close() {
- if (mLnb == nullptr) {
- ALOGE("ILnb is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto res = mLnb->close();
- mLnb = nullptr;
-
- return res;
+ return mLnb->close();
}
/////////////// ILnbCallback ///////////////////////
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
index 4833aaf..514a636 100644
--- a/services/tuner/TunerService.cpp
+++ b/services/tuner/TunerService.cpp
@@ -27,6 +27,7 @@
#include <android/binder_manager.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
+#include <cutils/properties.h>
#include <utils/Log.h>
#include <string>
@@ -51,65 +52,45 @@
namespace tv {
namespace tuner {
-shared_ptr<TunerService> TunerService::sTunerService = nullptr;
-
TunerService::TunerService() {
- if (!TunerHelper::checkTunerFeature()) {
- ALOGD("Device doesn't have tuner hardware.");
- return;
+ const string statsServiceName = string() + ITuner::descriptor + "/default";
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(statsServiceName.c_str()));
+ mTuner = ITuner::fromBinder(binder);
+ ALOGE_IF(mTuner == nullptr, "Failed to get Tuner HAL Service");
+
+ mTunerVersion = TUNER_HAL_VERSION_2_0;
+ if (mTuner->getInterfaceVersion(&mTunerVersion).isOk()) {
+ // Tuner AIDL HAL version 1 will be Tuner HAL 2.0
+ mTunerVersion = (mTunerVersion + 1) << 16;
}
+ // Register the tuner resources to TRM.
updateTunerResources();
}
-TunerService::~TunerService() {}
+TunerService::~TunerService() {
+ mTuner = nullptr;
+}
binder_status_t TunerService::instantiate() {
- sTunerService = ::ndk::SharedRefBase::make<TunerService>();
- return AServiceManager_addService(sTunerService->asBinder().get(), getServiceName());
-}
-
-shared_ptr<TunerService> TunerService::getTunerService() {
- return sTunerService;
-}
-
-bool TunerService::hasITuner() {
- ALOGV("hasITuner");
- if (mTuner != nullptr) {
- return true;
+ shared_ptr<TunerService> tunerService = ::ndk::SharedRefBase::make<TunerService>();
+ bool lazyHal = property_get_bool("ro.tuner.lazyhal", false);
+ if (lazyHal) {
+ return AServiceManager_registerLazyService(tunerService->asBinder().get(),
+ getServiceName());
}
- const string statsServiceName = string() + ITuner::descriptor + "/default";
- if (AServiceManager_isDeclared(statsServiceName.c_str())) {
- ::ndk::SpAIBinder binder(AServiceManager_waitForService(statsServiceName.c_str()));
- mTuner = ITuner::fromBinder(binder);
- } else {
- mTuner = nullptr;
- ALOGE("Failed to get Tuner HAL Service");
- return false;
- }
-
- mTunerVersion = TUNER_HAL_VERSION_2_0;
- // TODO: Enable this after Tuner HAL is frozen.
- // if (mTuner->getInterfaceVersion(&mTunerVersion).isOk()) {
- // // Tuner AIDL HAL version 1 will be Tuner HAL 2.0
- // mTunerVersion = (mTunerVersion + 1) << 16;
- //}
-
- return true;
+ return AServiceManager_addService(tunerService->asBinder().get(), getServiceName());
}
::ndk::ScopedAStatus TunerService::openDemux(int32_t /* in_demuxHandle */,
shared_ptr<ITunerDemux>* _aidl_return) {
ALOGV("openDemux");
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
vector<int32_t> id;
shared_ptr<IDemux> demux;
auto status = mTuner->openDemux(&id, &demux);
if (status.isOk()) {
- *_aidl_return = ::ndk::SharedRefBase::make<TunerDemux>(demux, id[0]);
+ *_aidl_return =
+ ::ndk::SharedRefBase::make<TunerDemux>(demux, id[0], this->ref<TunerService>());
}
return status;
@@ -117,41 +98,19 @@
::ndk::ScopedAStatus TunerService::getDemuxCaps(DemuxCapabilities* _aidl_return) {
ALOGV("getDemuxCaps");
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->getDemuxCaps(_aidl_return);
}
::ndk::ScopedAStatus TunerService::getFrontendIds(vector<int32_t>* ids) {
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->getFrontendIds(ids);
}
::ndk::ScopedAStatus TunerService::getFrontendInfo(int32_t id, FrontendInfo* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("ITuner service is not init.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->getFrontendInfo(id, _aidl_return);
}
::ndk::ScopedAStatus TunerService::openFrontend(int32_t frontendHandle,
shared_ptr<ITunerFrontend>* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("ITuner service is not init.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
int id = TunerHelper::getResourceIdFromHandle(frontendHandle, FRONTEND);
shared_ptr<IFrontend> frontend;
auto status = mTuner->openFrontendById(id, &frontend);
@@ -163,12 +122,6 @@
}
::ndk::ScopedAStatus TunerService::openLnb(int lnbHandle, shared_ptr<ITunerLnb>* _aidl_return) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<ILnb> lnb;
int id = TunerHelper::getResourceIdFromHandle(lnbHandle, LNB);
auto status = mTuner->openLnbById(id, &lnb);
@@ -181,12 +134,6 @@
::ndk::ScopedAStatus TunerService::openLnbByName(const string& lnbName,
shared_ptr<ITunerLnb>* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
vector<int32_t> id;
shared_ptr<ILnb> lnb;
auto status = mTuner->openLnbByName(lnbName, &id, &lnb);
@@ -199,12 +146,6 @@
::ndk::ScopedAStatus TunerService::openDescrambler(int32_t /*descramblerHandle*/,
shared_ptr<ITunerDescrambler>* _aidl_return) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
shared_ptr<IDescrambler> descrambler;
// int id = TunerHelper::getResourceIdFromHandle(descramblerHandle, DESCRAMBLER);
auto status = mTuner->openDescrambler(&descrambler);
@@ -216,7 +157,6 @@
}
::ndk::ScopedAStatus TunerService::getTunerHalVersion(int* _aidl_return) {
- hasITuner();
*_aidl_return = mTunerVersion;
return ::ndk::ScopedAStatus::ok();
}
@@ -224,12 +164,6 @@
::ndk::ScopedAStatus TunerService::openSharedFilter(const string& in_filterToken,
const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (!PermissionCache::checkCallingPermission(sSharedFilterPermission)) {
ALOGE("Request requires android.permission.ACCESS_TV_SHARED_FILTER");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -261,34 +195,16 @@
}
::ndk::ScopedAStatus TunerService::setLna(bool bEnable) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->setLna(bEnable);
}
::ndk::ScopedAStatus TunerService::setMaxNumberOfFrontends(FrontendType in_frontendType,
int32_t in_maxNumber) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->setMaxNumberOfFrontends(in_frontendType, in_maxNumber);
}
::ndk::ScopedAStatus TunerService::getMaxNumberOfFrontends(FrontendType in_frontendType,
int32_t* _aidl_return) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTuner->getMaxNumberOfFrontends(in_frontendType, _aidl_return);
}
@@ -309,11 +225,6 @@
}
void TunerService::updateTunerResources() {
- if (!hasITuner()) {
- ALOGE("Failed to updateTunerResources");
- return;
- }
-
TunerHelper::updateTunerResources(getTRMFrontendInfos(), getTRMLnbHandles());
}
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
index 7fc2aa4..6435e17 100644
--- a/services/tuner/TunerService.h
+++ b/services/tuner/TunerService.h
@@ -86,10 +86,7 @@
string addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter);
void removeSharedFilter(const shared_ptr<TunerFilter>& sharedFilter);
- static shared_ptr<TunerService> getTunerService();
-
private:
- bool hasITuner();
void updateTunerResources();
vector<TunerFrontendInfo> getTRMFrontendInfos();
vector<int32_t> getTRMLnbHandles();
@@ -98,8 +95,6 @@
int mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
Mutex mSharedFiltersLock;
map<string, shared_ptr<TunerFilter>> mSharedFilters;
-
- static shared_ptr<TunerService> sTunerService;
};
} // namespace tuner
diff --git a/services/tuner/TunerTimeFilter.cpp b/services/tuner/TunerTimeFilter.cpp
index 73cd6b4..f5094b9 100644
--- a/services/tuner/TunerTimeFilter.cpp
+++ b/services/tuner/TunerTimeFilter.cpp
@@ -39,33 +39,14 @@
}
::ndk::ScopedAStatus TunerTimeFilter::setTimeStamp(int64_t timeStamp) {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTimeFilter->setTimeStamp(timeStamp);
}
::ndk::ScopedAStatus TunerTimeFilter::clearTimeStamp() {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
return mTimeFilter->clearTimeStamp();
}
::ndk::ScopedAStatus TunerTimeFilter::getSourceTime(int64_t* _aidl_return) {
- if (mTimeFilter == nullptr) {
- *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
auto status = mTimeFilter->getSourceTime(_aidl_return);
if (!status.isOk()) {
*_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
@@ -74,13 +55,6 @@
}
::ndk::ScopedAStatus TunerTimeFilter::getTimeStamp(int64_t* _aidl_return) {
- if (mTimeFilter == nullptr) {
- *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
auto status = mTimeFilter->getTimeStamp(_aidl_return);
if (!status.isOk()) {
*_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
@@ -89,16 +63,7 @@
}
::ndk::ScopedAStatus TunerTimeFilter::close() {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- auto status = mTimeFilter->close();
- mTimeFilter = nullptr;
-
- return status;
+ return mTimeFilter->close();
}
} // namespace tuner
diff --git a/services/tuner/hidl/TunerHidlDemux.cpp b/services/tuner/hidl/TunerHidlDemux.cpp
index a8151d2..bbb7782 100644
--- a/services/tuner/hidl/TunerHidlDemux.cpp
+++ b/services/tuner/hidl/TunerHidlDemux.cpp
@@ -20,6 +20,7 @@
#include "TunerHidlDvr.h"
#include "TunerHidlFilter.h"
+#include "TunerHidlService.h"
#include "TunerHidlTimeFilter.h"
using ::aidl::android::hardware::tv::tuner::DemuxFilterSubType;
@@ -42,23 +43,20 @@
namespace tv {
namespace tuner {
-TunerHidlDemux::TunerHidlDemux(sp<IDemux> demux, int id) {
+TunerHidlDemux::TunerHidlDemux(const sp<IDemux> demux, const int id,
+ const shared_ptr<TunerHidlService> tuner) {
mDemux = demux;
mDemuxId = id;
+ mTunerService = tuner;
}
TunerHidlDemux::~TunerHidlDemux() {
mDemux = nullptr;
+ mTunerService = nullptr;
}
::ndk::ScopedAStatus TunerHidlDemux::setFrontendDataSource(
const shared_ptr<ITunerFrontend>& in_frontend) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
int frontendId;
in_frontend->getFrontendId(&frontendId);
HidlResult res = mDemux->setFrontendDataSource(frontendId);
@@ -69,12 +67,6 @@
}
::ndk::ScopedAStatus TunerHidlDemux::setFrontendDataSourceById(int frontendId) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult res = mDemux->setFrontendDataSource(frontendId);
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -86,12 +78,6 @@
int32_t in_bufferSize,
const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlDemuxFilterMainType mainType = static_cast<HidlDemuxFilterMainType>(in_type.mainType);
HidlDemuxFilterType filterType{
.mainType = mainType,
@@ -132,17 +118,12 @@
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
}
- *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlFilter>(filterSp, filterCb, in_type);
+ *_aidl_return =
+ ::ndk::SharedRefBase::make<TunerHidlFilter>(filterSp, filterCb, in_type, mTunerService);
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus TunerHidlDemux::openTimeFilter(shared_ptr<ITunerTimeFilter>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult status;
sp<HidlITimeFilter> filterSp;
mDemux->openTimeFilter([&](HidlResult r, const sp<HidlITimeFilter>& filter) {
@@ -159,12 +140,6 @@
::ndk::ScopedAStatus TunerHidlDemux::getAvSyncHwId(const shared_ptr<ITunerFilter>& tunerFilter,
int32_t* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
uint32_t avSyncHwId;
HidlResult res;
sp<HidlIFilter> halFilter = static_cast<TunerHidlFilter*>(tunerFilter.get())->getHalFilter();
@@ -181,12 +156,6 @@
}
::ndk::ScopedAStatus TunerHidlDemux::getAvSyncTime(int32_t avSyncHwId, int64_t* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
uint64_t time;
HidlResult res;
mDemux->getAvSyncTime(static_cast<uint32_t>(avSyncHwId), [&](HidlResult r, uint64_t ts) {
@@ -204,12 +173,6 @@
::ndk::ScopedAStatus TunerHidlDemux::openDvr(DvrType in_dvbType, int32_t in_bufferSize,
const shared_ptr<ITunerDvrCallback>& in_cb,
shared_ptr<ITunerDvr>* _aidl_return) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult res;
sp<HidlIDvrCallback> callback = new TunerHidlDvr::DvrCallback(in_cb);
sp<HidlIDvr> hidlDvr;
@@ -228,12 +191,6 @@
}
::ndk::ScopedAStatus TunerHidlDemux::connectCiCam(int32_t ciCamId) {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult res = mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -242,12 +199,6 @@
}
::ndk::ScopedAStatus TunerHidlDemux::disconnectCiCam() {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult res = mDemux->disconnectCiCam();
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -256,15 +207,7 @@
}
::ndk::ScopedAStatus TunerHidlDemux::close() {
- if (mDemux == nullptr) {
- ALOGE("IDemux is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(HidlResult::UNAVAILABLE));
- }
-
HidlResult res = mDemux->close();
- mDemux = nullptr;
-
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
diff --git a/services/tuner/hidl/TunerHidlDemux.h b/services/tuner/hidl/TunerHidlDemux.h
index d535da6..94a715e 100644
--- a/services/tuner/hidl/TunerHidlDemux.h
+++ b/services/tuner/hidl/TunerHidlDemux.h
@@ -37,9 +37,12 @@
namespace tv {
namespace tuner {
+class TunerHidlService;
+
class TunerHidlDemux : public BnTunerDemux {
public:
- TunerHidlDemux(sp<HidlIDemux> demux, int demuxId);
+ TunerHidlDemux(const sp<HidlIDemux> demux, const int demuxId,
+ const shared_ptr<TunerHidlService> tuner);
virtual ~TunerHidlDemux();
::ndk::ScopedAStatus setFrontendDataSource(
@@ -64,6 +67,7 @@
private:
sp<HidlIDemux> mDemux;
int mDemuxId;
+ shared_ptr<TunerHidlService> mTunerService;
};
} // namespace tuner
diff --git a/services/tuner/hidl/TunerHidlDescrambler.cpp b/services/tuner/hidl/TunerHidlDescrambler.cpp
index dd8cd9c..51b7ede 100644
--- a/services/tuner/hidl/TunerHidlDescrambler.cpp
+++ b/services/tuner/hidl/TunerHidlDescrambler.cpp
@@ -45,12 +45,6 @@
::ndk::ScopedAStatus TunerHidlDescrambler::setDemuxSource(
const shared_ptr<ITunerDemux>& in_tunerDemux) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDescrambler->setDemuxSource(
static_cast<TunerHidlDemux*>(in_tunerDemux.get())->getId());
if (res != HidlResult::SUCCESS) {
@@ -60,12 +54,6 @@
}
::ndk::ScopedAStatus TunerHidlDescrambler::setKeyToken(const vector<uint8_t>& in_keyToken) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDescrambler->setKeyToken(in_keyToken);
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -75,12 +63,6 @@
::ndk::ScopedAStatus TunerHidlDescrambler::addPid(
const DemuxPid& in_pid, const shared_ptr<ITunerFilter>& in_optionalSourceFilter) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
sp<HidlIFilter> halFilter =
(in_optionalSourceFilter == nullptr)
? nullptr
@@ -94,12 +76,6 @@
::ndk::ScopedAStatus TunerHidlDescrambler::removePid(
const DemuxPid& in_pid, const shared_ptr<ITunerFilter>& in_optionalSourceFilter) {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
sp<HidlIFilter> halFilter =
(in_optionalSourceFilter == nullptr)
? nullptr
@@ -112,15 +88,7 @@
}
::ndk::ScopedAStatus TunerHidlDescrambler::close() {
- if (mDescrambler == nullptr) {
- ALOGE("IDescrambler is not initialized.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDescrambler->close();
- mDescrambler = nullptr;
-
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
diff --git a/services/tuner/hidl/TunerHidlDvr.cpp b/services/tuner/hidl/TunerHidlDvr.cpp
index 1a619d5..50d92de 100644
--- a/services/tuner/hidl/TunerHidlDvr.cpp
+++ b/services/tuner/hidl/TunerHidlDvr.cpp
@@ -54,12 +54,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::getQueueDesc(AidlMQDesc* _aidl_return) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
MQDesc dvrMQDesc;
HidlResult res;
mDvr->getQueueDesc([&](HidlResult r, const MQDesc& desc) {
@@ -77,12 +71,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::configure(const DvrSettings& in_settings) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDvr->configure(getHidlDvrSettings(in_settings));
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -91,12 +79,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::attachFilter(const shared_ptr<ITunerFilter>& in_filter) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (in_filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -116,12 +98,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::detachFilter(const shared_ptr<ITunerFilter>& in_filter) {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (in_filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -141,12 +117,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::start() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDvr->start();
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -155,12 +125,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::stop() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDvr->stop();
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -169,12 +133,6 @@
}
::ndk::ScopedAStatus TunerHidlDvr::flush() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDvr->flush();
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
@@ -183,15 +141,7 @@
}
::ndk::ScopedAStatus TunerHidlDvr::close() {
- if (mDvr == nullptr) {
- ALOGE("IDvr is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mDvr->close();
- mDvr = nullptr;
-
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index fe74a5c..d4a3db5 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -92,31 +92,32 @@
namespace tv {
namespace tuner {
-TunerHidlFilter::TunerHidlFilter(sp<HidlIFilter> filter, sp<FilterCallback> cb,
- DemuxFilterType type)
+TunerHidlFilter::TunerHidlFilter(const sp<HidlIFilter> filter, const sp<FilterCallback> cb,
+ const DemuxFilterType type,
+ const shared_ptr<TunerHidlService> tuner)
: mFilter(filter),
mType(type),
mStarted(false),
mShared(false),
mClientPid(-1),
- mFilterCallback(cb) {
+ mFilterCallback(cb),
+ mTunerService(tuner) {
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(filter);
}
TunerHidlFilter::~TunerHidlFilter() {
- Mutex::Autolock _l(mLock);
- mFilter = nullptr;
- mFilter_1_1 = nullptr;
+ freeSharedFilterToken("");
+
+ {
+ Mutex::Autolock _l(mLock);
+ mFilter = nullptr;
+ mFilter_1_1 = nullptr;
+ mTunerService = nullptr;
+ }
}
::ndk::ScopedAStatus TunerHidlFilter::getQueueDesc(AidlMQDesc* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -146,12 +147,6 @@
::ndk::ScopedAStatus TunerHidlFilter::getId(int32_t* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -200,12 +195,6 @@
::ndk::ScopedAStatus TunerHidlFilter::configure(const DemuxFilterSettings& in_settings) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -318,12 +307,6 @@
::ndk::ScopedAStatus TunerHidlFilter::setDataSource(const shared_ptr<ITunerFilter>& filter) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (filter == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -378,12 +361,6 @@
::ndk::ScopedAStatus TunerHidlFilter::releaseAvHandle(const NativeHandle& in_handle,
int64_t in_avDataId) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
ALOGD("%s is called on a shared filter", __FUNCTION__);
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -407,12 +384,6 @@
::ndk::ScopedAStatus TunerHidlFilter::start() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -434,12 +405,6 @@
::ndk::ScopedAStatus TunerHidlFilter::stop() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -461,12 +426,6 @@
::ndk::ScopedAStatus TunerHidlFilter::flush() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -487,12 +446,6 @@
::ndk::ScopedAStatus TunerHidlFilter::close() {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared) {
IPCThreadState* ipc = IPCThreadState::self();
int32_t callingPid = ipc->getCallingPid();
@@ -501,7 +454,7 @@
mFilterCallback->sendSharedFilterStatus(STATUS_INACCESSIBLE);
mFilterCallback->detachSharedFilterCallback();
}
- TunerHidlService::getTunerService()->removeSharedFilter(this->ref<TunerHidlFilter>());
+ mTunerService->removeSharedFilter(this->ref<TunerHidlFilter>());
} else {
// Calling from shared process, do not really close this filter.
if (mFilterCallback != nullptr) {
@@ -516,8 +469,6 @@
mFilterCallback->detachCallbacks();
}
HidlResult res = mFilter->close();
- mFilter = nullptr;
- mFilter_1_1 = nullptr;
mStarted = false;
mShared = false;
mClientPid = -1;
@@ -531,12 +482,6 @@
::ndk::ScopedAStatus TunerHidlFilter::acquireSharedFilterToken(string* _aidl_return) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (mShared || mStarted) {
ALOGD("create SharedFilter in wrong state");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -545,8 +490,7 @@
IPCThreadState* ipc = IPCThreadState::self();
mClientPid = ipc->getCallingPid();
- string token =
- TunerHidlService::getTunerService()->addFilterToShared(this->ref<TunerHidlFilter>());
+ string token = mTunerService->addFilterToShared(this->ref<TunerHidlFilter>());
_aidl_return->assign(token);
mShared = true;
@@ -555,12 +499,6 @@
::ndk::ScopedAStatus TunerHidlFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
Mutex::Autolock _l(mLock);
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (!mShared) {
// The filter is not shared or the shared filter has been closed.
return ::ndk::ScopedAStatus::ok();
@@ -571,19 +509,13 @@
mFilterCallback->detachSharedFilterCallback();
}
- TunerHidlService::getTunerService()->removeSharedFilter(this->ref<TunerHidlFilter>());
+ mTunerService->removeSharedFilter(this->ref<TunerHidlFilter>());
mShared = false;
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus TunerHidlFilter::getFilterType(DemuxFilterType* _aidl_return) {
- if (mFilter == nullptr) {
- ALOGE("IFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
*_aidl_return = mType;
return ::ndk::ScopedAStatus::ok();
}
diff --git a/services/tuner/hidl/TunerHidlFilter.h b/services/tuner/hidl/TunerHidlFilter.h
index 63c7a1b..a58eeca 100644
--- a/services/tuner/hidl/TunerHidlFilter.h
+++ b/services/tuner/hidl/TunerHidlFilter.h
@@ -114,6 +114,8 @@
const static int IP_V4_LENGTH = 4;
const static int IP_V6_LENGTH = 16;
+class TunerHidlService;
+
class TunerHidlFilter : public BnTunerFilter {
public:
class FilterCallback : public HidlIFilterCallback {
@@ -165,7 +167,8 @@
Mutex mCallbackLock;
};
- TunerHidlFilter(sp<HidlIFilter> filter, sp<FilterCallback> cb, DemuxFilterType type);
+ TunerHidlFilter(const sp<HidlIFilter> filter, const sp<FilterCallback> cb,
+ const DemuxFilterType type, const shared_ptr<TunerHidlService> tuner);
virtual ~TunerHidlFilter();
::ndk::ScopedAStatus getId(int32_t* _aidl_return) override;
@@ -230,6 +233,7 @@
int32_t mClientPid;
sp<FilterCallback> mFilterCallback;
Mutex mLock;
+ shared_ptr<TunerHidlService> mTunerService;
};
} // namespace tuner
diff --git a/services/tuner/hidl/TunerHidlFrontend.cpp b/services/tuner/hidl/TunerHidlFrontend.cpp
index 03957f3..7ffb2a4 100644
--- a/services/tuner/hidl/TunerHidlFrontend.cpp
+++ b/services/tuner/hidl/TunerHidlFrontend.cpp
@@ -176,26 +176,23 @@
namespace tv {
namespace tuner {
-TunerHidlFrontend::TunerHidlFrontend(sp<HidlIFrontend> frontend, int id) {
+TunerHidlFrontend::TunerHidlFrontend(const sp<HidlIFrontend> frontend, const int id,
+ const shared_ptr<TunerHidlService> tuner) {
mFrontend = frontend;
mFrontend_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFrontend);
mId = id;
+ mTunerService = tuner;
}
TunerHidlFrontend::~TunerHidlFrontend() {
mFrontend = nullptr;
mFrontend_1_1 = nullptr;
mId = -1;
+ mTunerService = nullptr;
}
::ndk::ScopedAStatus TunerHidlFrontend::setCallback(
const shared_ptr<ITunerFrontendCallback>& tunerFrontendCallback) {
- if (mFrontend == nullptr) {
- ALOGE("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (tunerFrontendCallback == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -211,12 +208,6 @@
}
::ndk::ScopedAStatus TunerHidlFrontend::tune(const FrontendSettings& settings) {
- if (mFrontend == nullptr) {
- ALOGE("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
HidlFrontendSettings frontendSettings;
HidlFrontendSettingsExt1_1 frontendSettingsExt;
@@ -234,12 +225,6 @@
}
::ndk::ScopedAStatus TunerHidlFrontend::stopTune() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status = mFrontend->stopTune();
if (status == HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::ok();
@@ -250,12 +235,6 @@
::ndk::ScopedAStatus TunerHidlFrontend::scan(const FrontendSettings& settings,
FrontendScanType frontendScanType) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
HidlFrontendSettings frontendSettings;
HidlFrontendSettingsExt1_1 frontendSettingsExt;
@@ -276,12 +255,6 @@
}
::ndk::ScopedAStatus TunerHidlFrontend::stopScan() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status = mFrontend->stopScan();
if (status == HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::ok();
@@ -291,12 +264,6 @@
}
::ndk::ScopedAStatus TunerHidlFrontend::setLnb(const shared_ptr<ITunerLnb>& lnb) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
if (lnb == nullptr) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::INVALID_ARGUMENT));
@@ -349,18 +316,8 @@
}
::ndk::ScopedAStatus TunerHidlFrontend::close() {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- TunerHidlService::getTunerService()->removeFrontend(this->ref<TunerHidlFrontend>());
-
+ mTunerService->removeFrontend(this->ref<TunerHidlFrontend>());
HidlResult status = mFrontend->close();
- mFrontend = nullptr;
- mFrontend_1_1 = nullptr;
-
if (status != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
}
@@ -370,12 +327,6 @@
::ndk::ScopedAStatus TunerHidlFrontend::getStatus(const vector<FrontendStatusType>& in_statusTypes,
vector<FrontendStatus>* _aidl_return) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res;
vector<HidlFrontendStatus> status;
vector<HidlFrontendStatusExt1_1> statusExt;
@@ -442,11 +393,6 @@
}
void TunerHidlFrontend::setLna(bool bEnable) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return;
- }
-
mFrontend->setLna(bEnable);
}
diff --git a/services/tuner/hidl/TunerHidlFrontend.h b/services/tuner/hidl/TunerHidlFrontend.h
index f698655..f54127b 100644
--- a/services/tuner/hidl/TunerHidlFrontend.h
+++ b/services/tuner/hidl/TunerHidlFrontend.h
@@ -64,9 +64,12 @@
namespace tv {
namespace tuner {
+class TunerHidlService;
+
class TunerHidlFrontend : public BnTunerFrontend {
public:
- TunerHidlFrontend(sp<HidlIFrontend> frontend, int id);
+ TunerHidlFrontend(const sp<HidlIFrontend> frontend, const int id,
+ const shared_ptr<TunerHidlService> tuner);
virtual ~TunerHidlFrontend();
::ndk::ScopedAStatus setCallback(
@@ -118,6 +121,7 @@
int mId;
sp<HidlIFrontend> mFrontend;
sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFrontend_1_1;
+ shared_ptr<TunerHidlService> mTunerService;
};
} // namespace tuner
diff --git a/services/tuner/hidl/TunerHidlService.cpp b/services/tuner/hidl/TunerHidlService.cpp
index 6f55f1e..aa03316 100644
--- a/services/tuner/hidl/TunerHidlService.cpp
+++ b/services/tuner/hidl/TunerHidlService.cpp
@@ -24,6 +24,7 @@
#include <android/binder_manager.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
+#include <cutils/properties.h>
#include <utils/Log.h>
#include "TunerHelper.h"
@@ -46,7 +47,6 @@
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode;
using ::aidl::android::hardware::tv::tuner::FrontendType;
using ::aidl::android::hardware::tv::tuner::Result;
-using ::aidl::android::media::tv::tunerresourcemanager::TunerFrontendInfo;
using ::android::IPCThreadState;
using ::android::PermissionCache;
using ::android::hardware::hidl_vec;
@@ -63,47 +63,9 @@
namespace tv {
namespace tuner {
-shared_ptr<TunerHidlService> TunerHidlService::sTunerService = nullptr;
-
TunerHidlService::TunerHidlService() {
- if (!TunerHelper::checkTunerFeature()) {
- ALOGD("Device doesn't have tuner hardware.");
- return;
- }
-
- updateTunerResources();
-}
-
-TunerHidlService::~TunerHidlService() {
- mOpenedFrontends.clear();
- mLnaStatus = -1;
-}
-
-binder_status_t TunerHidlService::instantiate() {
- if (HidlITuner::getService() == nullptr) {
- ALOGD("Failed to get ITuner HIDL HAL");
- return STATUS_NAME_NOT_FOUND;
- }
-
- sTunerService = ::ndk::SharedRefBase::make<TunerHidlService>();
- return AServiceManager_addService(sTunerService->asBinder().get(), getServiceName());
-}
-
-shared_ptr<TunerHidlService> TunerHidlService::getTunerService() {
- return sTunerService;
-}
-
-bool TunerHidlService::hasITuner() {
- ALOGV("hasITuner");
- if (mTuner != nullptr) {
- return true;
- }
-
mTuner = HidlITuner::getService();
- if (mTuner == nullptr) {
- ALOGE("Failed to get ITuner service");
- return false;
- }
+ ALOGE_IF(mTuner == nullptr, "Failed to get ITuner service");
mTunerVersion = TUNER_HAL_VERSION_1_0;
mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::castFrom(mTuner);
@@ -113,23 +75,35 @@
ALOGD("Failed to get ITuner_1_1 service");
}
- return true;
+ // Register tuner resources to TRM.
+ updateTunerResources();
}
-bool TunerHidlService::hasITuner_1_1() {
- ALOGV("hasITuner_1_1");
- hasITuner();
- return (mTunerVersion == TUNER_HAL_VERSION_1_1);
+TunerHidlService::~TunerHidlService() {
+ mOpenedFrontends.clear();
+ mLnaStatus = -1;
+ mTuner = nullptr;
+ mTuner_1_1 = nullptr;
+}
+
+binder_status_t TunerHidlService::instantiate() {
+ if (HidlITuner::getService() == nullptr) {
+ ALOGD("Failed to get ITuner HIDL HAL");
+ return STATUS_NAME_NOT_FOUND;
+ }
+
+ shared_ptr<TunerHidlService> tunerService = ::ndk::SharedRefBase::make<TunerHidlService>();
+ bool lazyHal = property_get_bool("ro.tuner.lazyhal", false);
+ if (lazyHal) {
+ return AServiceManager_registerLazyService(tunerService->asBinder().get(),
+ getServiceName());
+ }
+ return AServiceManager_addService(tunerService->asBinder().get(), getServiceName());
}
::ndk::ScopedAStatus TunerHidlService::openDemux(int32_t /* in_demuxHandle */,
shared_ptr<ITunerDemux>* _aidl_return) {
ALOGV("openDemux");
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res;
uint32_t id;
sp<IDemux> demuxSp = nullptr;
@@ -140,7 +114,8 @@
ALOGD("open demux, id = %d", demuxId);
});
if (res == HidlResult::SUCCESS) {
- *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlDemux>(demuxSp, id);
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlDemux>(demuxSp, id,
+ this->ref<TunerHidlService>());
return ::ndk::ScopedAStatus::ok();
}
@@ -150,11 +125,6 @@
::ndk::ScopedAStatus TunerHidlService::getDemuxCaps(DemuxCapabilities* _aidl_return) {
ALOGV("getDemuxCaps");
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res;
HidlDemuxCapabilities caps;
mTuner->getDemuxCaps([&](HidlResult r, const HidlDemuxCapabilities& demuxCaps) {
@@ -171,11 +141,6 @@
}
::ndk::ScopedAStatus TunerHidlService::getFrontendIds(vector<int32_t>* ids) {
- if (!hasITuner()) {
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
hidl_vec<HidlFrontendId> feIds;
HidlResult res = getHidlFrontendIds(feIds);
if (res != HidlResult::SUCCESS) {
@@ -188,12 +153,6 @@
}
::ndk::ScopedAStatus TunerHidlService::getFrontendInfo(int32_t id, FrontendInfo* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("ITuner service is not init.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlFrontendInfo info;
HidlResult res = getHidlFrontendInfo(id, info);
if (res != HidlResult::SUCCESS) {
@@ -202,7 +161,7 @@
HidlFrontendDtmbCapabilities dtmbCaps;
if (static_cast<HidlFrontendType>(info.type) == HidlFrontendType::DTMB) {
- if (!hasITuner_1_1()) {
+ if (mTuner_1_1 == nullptr) {
ALOGE("ITuner_1_1 service is not init.");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::UNAVAILABLE));
@@ -224,12 +183,6 @@
::ndk::ScopedAStatus TunerHidlService::openFrontend(int32_t frontendHandle,
shared_ptr<ITunerFrontend>* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("ITuner service is not init.");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
sp<HidlIFrontend> frontend;
int id = TunerHelper::getResourceIdFromHandle(frontendHandle, FRONTEND);
@@ -241,8 +194,8 @@
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
}
- shared_ptr<TunerHidlFrontend> tunerFrontend =
- ::ndk::SharedRefBase::make<TunerHidlFrontend>(frontend, id);
+ shared_ptr<TunerHidlFrontend> tunerFrontend = ::ndk::SharedRefBase::make<TunerHidlFrontend>(
+ frontend, id, this->ref<TunerHidlService>());
if (mLnaStatus != -1) {
tunerFrontend->setLna(mLnaStatus == 1);
}
@@ -255,12 +208,6 @@
}
::ndk::ScopedAStatus TunerHidlService::openLnb(int lnbHandle, shared_ptr<ITunerLnb>* _aidl_return) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
sp<HidlILnb> lnb;
int id = TunerHelper::getResourceIdFromHandle(lnbHandle, LNB);
@@ -278,12 +225,6 @@
::ndk::ScopedAStatus TunerHidlService::openLnbByName(const string& lnbName,
shared_ptr<ITunerLnb>* _aidl_return) {
- if (!hasITuner()) {
- ALOGE("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
int lnbId;
HidlResult status;
sp<HidlILnb> lnb;
@@ -302,12 +243,6 @@
::ndk::ScopedAStatus TunerHidlService::openDescrambler(
int32_t /*descramblerHandle*/, shared_ptr<ITunerDescrambler>* _aidl_return) {
- if (!hasITuner()) {
- ALOGD("get ITuner failed");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
sp<HidlIDescrambler> descrambler;
//int id = TunerHelper::getResourceIdFromHandle(descramblerHandle, DESCRAMBLER);
@@ -324,7 +259,6 @@
}
::ndk::ScopedAStatus TunerHidlService::getTunerHalVersion(int* _aidl_return) {
- hasITuner();
*_aidl_return = mTunerVersion;
return ::ndk::ScopedAStatus::ok();
}
@@ -332,7 +266,7 @@
::ndk::ScopedAStatus TunerHidlService::openSharedFilter(
const string& in_filterToken, const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) {
- if (!hasITuner()) {
+ if (mTuner == nullptr) {
ALOGE("get ITuner failed");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::UNAVAILABLE));
@@ -369,7 +303,7 @@
}
::ndk::ScopedAStatus TunerHidlService::setLna(bool bEnable) {
- if (!hasITuner()) {
+ if (mTuner == nullptr) {
ALOGE("get ITuner failed");
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::UNAVAILABLE));
@@ -428,11 +362,6 @@
}
void TunerHidlService::updateTunerResources() {
- if (!hasITuner()) {
- ALOGE("Failed to updateTunerResources");
- return;
- }
-
TunerHelper::updateTunerResources(getTRMFrontendInfos(), getTRMLnbHandles());
}
diff --git a/services/tuner/hidl/TunerHidlService.h b/services/tuner/hidl/TunerHidlService.h
index 2252d35..6f43d43 100644
--- a/services/tuner/hidl/TunerHidlService.h
+++ b/services/tuner/hidl/TunerHidlService.h
@@ -99,11 +99,7 @@
void removeSharedFilter(const shared_ptr<TunerHidlFilter>& sharedFilter);
void removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend);
- static shared_ptr<TunerHidlService> getTunerService();
-
private:
- bool hasITuner();
- bool hasITuner_1_1();
void updateTunerResources();
vector<TunerFrontendInfo> getTRMFrontendInfos();
vector<int32_t> getTRMLnbHandles();
@@ -121,8 +117,6 @@
Mutex mOpenedFrontendsLock;
unordered_set<shared_ptr<TunerHidlFrontend>> mOpenedFrontends;
int mLnaStatus = -1;
-
- static shared_ptr<TunerHidlService> sTunerService;
};
} // namespace tuner
diff --git a/services/tuner/hidl/TunerHidlTimeFilter.cpp b/services/tuner/hidl/TunerHidlTimeFilter.cpp
index d0606d6..06a71d0 100644
--- a/services/tuner/hidl/TunerHidlTimeFilter.cpp
+++ b/services/tuner/hidl/TunerHidlTimeFilter.cpp
@@ -43,12 +43,6 @@
}
::ndk::ScopedAStatus TunerHidlTimeFilter::setTimeStamp(int64_t timeStamp) {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status = mTimeFilter->setTimeStamp(static_cast<uint64_t>(timeStamp));
if (status != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
@@ -57,12 +51,6 @@
}
::ndk::ScopedAStatus TunerHidlTimeFilter::clearTimeStamp() {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status = mTimeFilter->clearTimeStamp();
if (status != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
@@ -71,13 +59,6 @@
}
::ndk::ScopedAStatus TunerHidlTimeFilter::getSourceTime(int64_t* _aidl_return) {
- if (mTimeFilter == nullptr) {
- *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
mTimeFilter->getSourceTime([&](HidlResult r, uint64_t t) {
status = r;
@@ -91,13 +72,6 @@
}
::ndk::ScopedAStatus TunerHidlTimeFilter::getTimeStamp(int64_t* _aidl_return) {
- if (mTimeFilter == nullptr) {
- *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult status;
mTimeFilter->getTimeStamp([&](HidlResult r, uint64_t t) {
status = r;
@@ -111,15 +85,7 @@
}
::ndk::ScopedAStatus TunerHidlTimeFilter::close() {
- if (mTimeFilter == nullptr) {
- ALOGE("ITimeFilter is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
HidlResult res = mTimeFilter->close();
- mTimeFilter = nullptr;
-
if (res != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
diff --git a/services/tuner/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
index a014dea..f8311ff 100644
--- a/services/tuner/main_tunerservice.cpp
+++ b/services/tuner/main_tunerservice.cpp
@@ -32,6 +32,7 @@
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
+ ProcessState::self()->setThreadPoolMaxThreadCount(8);
// Check legacy HIDL HAL first. If it's not existed, use AIDL HAL.
binder_status_t status = TunerHidlService::instantiate();
diff --git a/services/tuner/mediatuner.rc b/services/tuner/mediatuner.rc
index 6a3e199..90a0090 100644
--- a/services/tuner/mediatuner.rc
+++ b/services/tuner/mediatuner.rc
@@ -1,8 +1,14 @@
+# media.tuner service is not started by default unless tuner.server.enable is set
+# as "true" by TRM (Tuner Resource Manager). TRM checks ro.tuner.lazyhal, if it
+# isn't true , TRM sets tuner.server.enable as "true".
service media.tuner /system/bin/mediatuner
class main
group media
ioprio rt 4
- onrestart restart vendor.tuner-hal-1-0
- onrestart restart vendor.tuner-hal-1-1
- onrestart restart vendor.tuner-default
task_profiles ProcessCapacityHigh HighPerformance
+ interface aidl media.tuner
+ oneshot
+ disabled
+
+on property:tuner.server.enable=true
+ enable media.tuner