Merge "Added libaudioflinger_headers"
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/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/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/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index b277bed..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;
}
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
index 397e4c6..2732aa7 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -112,31 +112,11 @@
name: "android.hardware.drm-service.clearkey.aidl_fuzzer",
defaults: [
"fuzz_aidl_clearkey_service_defaults",
+ "service_fuzzer_defaults",
],
static_libs: [
- "libbase",
- "libbinder_random_parcel",
- "libcutils",
"liblog",
- "libutils",
],
- target: {
- android: {
- shared_libs: [
- "libbinder_ndk",
- "libbinder",
- ],
- },
- host: {
- static_libs: [
- "libbinder_ndk",
- "libbinder",
- ],
- },
- darwin: {
- enabled: false,
- },
- },
srcs: ["fuzzer.cpp"],
fuzz_config: {
cc: [
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 3f7cb3c..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",
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 0bd0d88..d62dd91 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -8,6 +8,7 @@
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
@@ -19,6 +20,7 @@
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
# See b/155364397. Need to have HAL service running for VTS.
@@ -26,12 +28,14 @@
# 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
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
@@ -40,10 +44,12 @@
# 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
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
# reset the property
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/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 5a660c5..a27c218 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -917,7 +917,8 @@
if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(ps_decode_op->u4_pic_wd), IVD_DECODE_FRAME);
+ mStride = ALIGN128(ps_decode_op->u4_pic_wd);
+ setParams(mStride, IVD_DECODE_FRAME);
}
if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) {
mWidth = ps_decode_op->u4_pic_wd;
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 5c24bd7..35a3b53 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -1447,6 +1447,10 @@
bool objcpy(C2BaseBlock* d, const BaseBlock& s) {
switch (s.getDiscriminator()) {
case BaseBlock::hidl_discriminator::nativeBlock: {
+ if (s.nativeBlock() == nullptr) {
+ LOG(ERROR) << "Null BaseBlock::nativeBlock handle";
+ return false;
+ }
native_handle_t* sHandle =
native_handle_clone(s.nativeBlock());
if (sHandle == nullptr) {
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/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/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 34ecd25..2de878d 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -807,7 +807,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/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/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 8dd5538..e36928d 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -410,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);
}
@@ -664,6 +664,8 @@
std::mutex mStreamLock;
+ const android::sp<MyPlayerBase> mPlayerBase;
+
private:
aaudio_result_t safeStop_l() REQUIRES(mStreamLock);
@@ -679,8 +681,6 @@
close_l();
}
- const android::sp<MyPlayerBase> mPlayerBase;
-
std::atomic<aaudio_stream_state_t> mState{AAUDIO_STREAM_STATE_UNINITIALIZED};
// These do not change after open().
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/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 09caa5c..fb3fcc1 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -406,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);
}
}
@@ -552,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 1f877b5..05609c4 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -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/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/Android.bp b/media/libaudioclient/Android.bp
index 79dae27..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",
@@ -307,7 +311,7 @@
"aidl/android/media/IEffectClient.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"shared-file-region-aidl",
],
backend: {
@@ -360,7 +364,7 @@
"aidl/android/media/TrackSecondaryOutputInfo.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
+ "android.media.audio.common.types-V2",
"framework-permission-aidl",
],
backend: {
@@ -404,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: {
@@ -447,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",
@@ -484,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/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 965c40f..66bd1c1 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1015,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,
@@ -1057,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));
@@ -1114,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) {
@@ -1151,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(
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 51080ef..1aecfc6 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -7,14 +7,6 @@
"name": "audio_aidl_status_tests"
},
{
- "name": "CtsNativeMediaAAudioTestCases",
- "options" : [
- {
- "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
- }
- ]
- },
- {
"name": "audiorecord_tests"
},
{
@@ -32,5 +24,24 @@
{
"name": "audiosystem_tests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsNativeMediaAAudioTestCases",
+ "options" : [
+ {
+ "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/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/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/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 9411f46..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);
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index e861932..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",
@@ -100,6 +102,9 @@
"-Wall",
"-Werror",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
shared_libs: [
"capture_state_listener-aidl-cpp",
"framework-permission-aidl-cpp",
@@ -125,7 +130,6 @@
],
static_libs: [
"android.hardware.audio.common@7.0-enums",
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
index 93fe306..e6149e4 100644
--- a/media/libaudioclient/tests/audioeffect_tests.cpp
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -19,19 +19,43 @@
#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 isEffectExistsOnAudioSession(const effect_uuid_t* type, int priority,
- audio_session_t sessionId) {
+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;
@@ -39,11 +63,19 @@
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, nullptr /* uid */, priority, nullptr /* callback */, sessionId);
- return effect->initCheck() == ALREADY_EXISTS;
+ effect->set(type, uuid, priority, callback, sessionId, AUDIO_IO_HANDLE_NONE, {}, false,
+ (callback != nullptr));
+ return effect;
}
-bool isEffectDefaultOnRecord(const effect_uuid_t* type, const sp<AudioRecord>& audioRecord) {
+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,
@@ -52,7 +84,8 @@
return false;
}
for (int i = 0; i < numEffects; i++) {
- if (memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) {
+ if ((memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) &&
+ (memcmp(&descriptors[i].uuid, uuid, sizeof(effect_uuid_t)) == 0)) {
return true;
}
}
@@ -61,11 +94,11 @@
void listEffectsAvailable(std::vector<effect_descriptor_t>& descriptors) {
uint32_t numEffects = 0;
- if (NO_ERROR == AudioEffect::queryNumberEffects(&numEffects)) {
- for (auto i = 0; i < numEffects; i++) {
- effect_descriptor_t des;
- if (NO_ERROR == AudioEffect::queryEffect(i, &des)) descriptors.push_back(des);
- }
+ 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);
}
}
@@ -81,11 +114,31 @@
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 = {
@@ -99,7 +152,7 @@
EFFECT_FLAG_TYPE_MASK, &descriptor));
std::vector<effect_descriptor_t> descriptors;
- listEffectsAvailable(descriptors);
+ ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
for (auto i = 0; i < descriptors.size(); i++) {
EXPECT_EQ(NO_ERROR,
@@ -124,15 +177,7 @@
}
TEST(AudioEffectTest, DISABLED_GetSetParameterForEffect) {
- 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> visualizer = new AudioEffect(attributionSource);
- ASSERT_NE(visualizer, nullptr) << "effect not created";
- visualizer->set(SL_IID_VISUALIZATION);
+ 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";
@@ -195,51 +240,58 @@
sp<AudioCapture> capture = nullptr;
std::vector<effect_descriptor_t> descriptors;
- listEffectsAvailable(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, capture->getAudioRecordHandle())) {
+ 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;
- char type[512];
- AudioEffect::guidToString(&selectedEffectType, type, sizeof(type));
-
+ 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, capture->getAudioRecordHandle()))
+ EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
- EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ 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, name, nullptr, kDefaultInputEffectPriority, AUDIO_SOURCE_MIC, &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, capture->getAudioRecordHandle()))
+ EXPECT_TRUE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
<< "Effect should have been default on record. " << type;
- EXPECT_TRUE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ 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());
@@ -250,86 +302,258 @@
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, capture->getAudioRecordHandle()))
+ EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
+ capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
- EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ 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, ManageStreamDefaultEffects) {
+TEST(AudioEffectTest, AuxEffectSanityTest) {
int32_t selectedEffect = -1;
-
std::vector<effect_descriptor_t> descriptors;
- listEffectsAvailable(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";
+ 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";
+ }
+}
- char type[512];
- AudioEffect::guidToString(selectedEffectType, type, sizeof(type));
+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(
- 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ 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_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
- << "Effect should not have been added. " << type;
+ 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(
- type, name, nullptr, kDefaultOutputEffectPriority, AUDIO_USAGE_MEDIA, &id);
- EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;
+ 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(
- 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ 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());
- float level = 0.2f, levelGot;
- playback->getAudioTrackHandle()->setAuxEffectSendLevel(level);
EXPECT_EQ(NO_ERROR, playback->start());
- EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
- << "Effect should have been added. " << type;
+ // 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());
- playback->getAudioTrackHandle()->getAuxEffectSendLevel(&levelGot);
- EXPECT_EQ(level, levelGot);
+ 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(
- 44100 /*sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ 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_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
- << "Effect should not have been added. " << type;
+ 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/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/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/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/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/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 e5e1521..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,7 +209,7 @@
sp<Looper> mLooper;
Listener* const mListener;
SensorManager* const mSensorManager;
- 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;
@@ -193,7 +228,6 @@
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(); }
@@ -346,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/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/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/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/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/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/tests/MtpFuzzer/Android.bp b/media/mtp/tests/MtpFuzzer/Android.bp
index 289b3ba..9e41680 100644
--- a/media/mtp/tests/MtpFuzzer/Android.bp
+++ b/media/mtp/tests/MtpFuzzer/Android.bp
@@ -57,3 +57,95 @@
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
index 7c6ff7a..7efaf67 100644
--- a/media/mtp/tests/MtpFuzzer/README.md
+++ b/media/mtp/tests/MtpFuzzer/README.md
@@ -2,6 +2,15 @@
## 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
@@ -22,3 +31,180 @@
$ 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/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_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/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 7495082..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;
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
index b2ae220..a3d6a96 100644
--- a/media/ndk/fuzzer/Android.bp
+++ b/media/ndk/fuzzer/Android.bp
@@ -64,3 +64,55 @@
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
index 4f78e4a..0fd08b0 100644
--- a/media/ndk/fuzzer/README.md
+++ b/media/ndk/fuzzer/README.md
@@ -2,6 +2,12 @@
## 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
@@ -22,3 +28,131 @@
$ 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_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/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/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/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/tests/Android.bp b/media/utils/tests/Android.bp
index 602cada..48d18b0 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -174,6 +174,16 @@
}
cc_test {
+ name: "static_string_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "static_string_view_tests.cpp",
+ ],
+}
+
+cc_test {
name: "timecheck_tests",
defaults: ["libmediautils_tests_host_unavail"],
@@ -192,3 +202,13 @@
"extended_accumulator_tests.cpp",
],
}
+
+cc_test {
+ name: "inplace_function_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "inplace_function_tests.cpp"
+ ],
+}
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/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 9cb30e2..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",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a68f63e..7f0fc1f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -597,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 {
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 72c378d..193e270 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1058,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.
@@ -1431,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;
}
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index a89a814..78788df 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -306,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
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/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 77e2421..745dbf2 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5376,12 +5376,6 @@
volume = masterVolume * mStreamTypes[track->streamType()].volume;
}
- track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
- /*muteState=*/{masterVolume == 0.f,
- mStreamTypes[track->streamType()].volume == 0.f,
- mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted()});
-
handleVoipVolume_l(&volume);
// cache the combined master volume and stream type volume for fast mixer; this
@@ -5391,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;
@@ -5546,12 +5551,6 @@
v = 0;
}
- track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
- /*muteState=*/{masterVolume == 0.f,
- mStreamTypes[track->streamType()].volume == 0.f,
- mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted()});
-
handleVoipVolume_l(&v);
if (track->isPausing()) {
@@ -5571,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;
@@ -6157,28 +6165,33 @@
{
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;
}
@@ -6186,7 +6199,9 @@
/*muteState=*/{mMasterMute,
mStreamTypes[track->streamType()].volume == 0.f,
mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted()});
+ track->isPlaybackRestricted(),
+ clientVolumeMute,
+ shaperVolume == 0.f});
if (lastTrack) {
track->setFinalVolume((left + right) / 2.f);
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/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/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 c3b19c2..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"
@@ -65,6 +66,6 @@
<route type="mix" sink="BLE Speaker Out"
sources="le audio output"/>
<route type="mix" sink="BLE Broadcast Out"
- sources="le audio output"/>
+ sources="le audio broadcast output"/>
</routes>
</module>
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index d67e6c4..7534984 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -276,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 c21c6a2..4a62eda 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1132,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,
@@ -1273,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;
}
@@ -1300,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,
@@ -2412,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,
@@ -2553,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;
}
@@ -2584,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)
{
@@ -4085,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;
}
@@ -4094,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,
@@ -6680,70 +6663,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, AUDIO_CONFIG_BASE_INITIALIZER,
- 0 /*uid unknown here*/, AUDIO_OUTPUT_FLAG_NONE, policyMix, nullptr);
- 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());
@@ -7897,4 +7825,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 87e6974..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,
@@ -1133,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.
@@ -1141,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);
@@ -1260,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/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 691f13c..50a536b 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -516,7 +516,8 @@
void AudioPolicyService::doOnCheckSpatializer()
{
- 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
@@ -1210,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");
@@ -1716,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 a87d871..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;
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index 20c7a5d..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;
@@ -172,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;
@@ -191,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);
}
}
@@ -236,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__);
@@ -283,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);
@@ -324,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))
@@ -338,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;
}
@@ -349,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) {
@@ -381,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);
@@ -441,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:
@@ -500,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();
@@ -535,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();
@@ -549,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.
@@ -565,6 +635,7 @@
}
std::lock_guard lock(mLock);
mDisplayOrientation = physicalToLogicalAngle;
+ mLocalLog.log("%s with %f", __func__, physicalToLogicalAngle);
if (mPoseController != nullptr) {
mPoseController->setDisplayOrientation(mDisplayOrientation);
}
@@ -579,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();
@@ -665,6 +737,8 @@
callback = mHeadTrackingCallback;
if (mEngine != nullptr) {
setEffectParameter_l(SPATIALIZER_PARAM_HEAD_TO_STAGE, headToStage);
+ mPoseRecorder.record(headToStage);
+ mPoseDurableRecorder.record(headToStage);
}
}
@@ -674,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));
@@ -710,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);
@@ -723,6 +802,8 @@
{
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
@@ -777,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;
@@ -821,6 +903,7 @@
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();
@@ -899,4 +982,80 @@
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 bc95fe4..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"
@@ -156,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);
@@ -364,9 +407,103 @@
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 23bcd77..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.
@@ -291,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 f199ecb..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,7 +138,7 @@
int32_t mHeadSensor = media::SensorPoseProvider::INVALID_HANDLE;
int32_t mScreenSensor = media::SensorPoseProvider::INVALID_HANDLE;
std::optional<media::HeadTrackingMode> mActualMode;
- std::condition_variable mCondVar;
+ std::condition_variable_any mCondVar;
bool mShouldCalculate = true;
bool mShouldExit = false;
bool mCalculated = false;
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index e887798..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,7 +34,6 @@
],
static_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"libaudiopolicycomponents",
"libgmock",
@@ -58,6 +61,11 @@
cc_test {
name: "audio_health_tests",
+
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
require_root: true,
shared_libs: [
@@ -67,7 +75,6 @@
"liblog",
"libmedia_helper",
"libutils",
- "android.media.audio.common.types-V1-cpp",
"libaudioclient_aidl_conversion",
"libstagefright_foundation",
"libshmemcompat",
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a2e7f7e..bc8981e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1712,7 +1712,13 @@
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- if (mCameraServiceProxyWrapper->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());
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index acee2ab..644f682 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1512,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);
}
@@ -2231,7 +2232,6 @@
if (mStatus == STATUS_ACTIVE) {
markClientActive = true;
mPauseStateNotify = true;
- mStatusTracker->markComponentIdle(clientStatusId, Fence::NO_FENCE);
rc = internalPauseAndWaitLocked(maxExpectedDuration);
}
@@ -3448,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);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e12d8be..5a1ee9b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -331,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;
@@ -507,10 +507,6 @@
mStreamUnpreparable = true;
}
- if (res != OK) {
- close(anwReleaseFence);
- }
-
*releaseFenceOut = releaseFence;
return res;
@@ -1309,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/tests/CameraPermissionsTest.cpp b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
index 4359f9b..42d99e8 100644
--- a/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
@@ -138,12 +138,12 @@
return mCameraServiceProxy->notifyCameraState(cameraSessionStats);
}
- virtual binder::Status isCameraDisabled(bool *ret) override {
+ virtual binder::Status isCameraDisabled(int userId, bool *ret) override {
if (mOverrideCameraDisabled) {
*ret = mCameraDisabled;
return binder::Status::ok();
}
- return mCameraServiceProxy->isCameraDisabled(ret);
+ return mCameraServiceProxy->isCameraDisabled(userId, ret);
}
void setCameraDisabled(bool cameraDisabled) {
@@ -167,7 +167,7 @@
{ }
~AutoDisconnectDevice() {
- if (mDevice != nullptr) {
+ if (mDevice != nullptr) {
mDevice->disconnect();
}
}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 733ecd9..885c6aa 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -269,11 +269,11 @@
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 6af56c3..c00c691 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -105,7 +105,7 @@
int getRotateAndCropOverride(String16 packageName, int lensFacing, int userId);
// Detect if the camera is disabled by device policy.
- bool isCameraDisabled();
+ bool isCameraDisabled(int userId);
};
} // android
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 9e2f896..ceb3e6a 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -338,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/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/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 bbfa0b7..133b9b4 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,6 +69,24 @@
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());
@@ -79,36 +98,38 @@
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;
@@ -151,6 +172,8 @@
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",
+ __func__, config.format, config.sample_rate, config.channel_mask);
status_t status = MmapStreamInterface::openMmapStream(streamDirection,
&attributes,
&config,
@@ -165,7 +188,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;
}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index 3658c58..73e0f61 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -92,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/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 43ff95c..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();
@@ -40,7 +41,6 @@
CHECK(status == STATUS_OK);
}
- ProcessState::self()->setThreadPoolMaxThreadCount(8);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return EXIT_FAILURE; // should not reached
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