Merge "cameraserver: Fix logic to skip callbacks for non HAL3 devices for vendor listeners."
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index d6f1412..68db233 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -33,7 +33,9 @@
dev->unlockDevice();
}
// Fire onClosed callback
- (*mUserSessionCallback.onClosed)(mUserSessionCallback.context, this);
+ if (mUserSessionCallback.onClosed != nullptr) {
+ (*mUserSessionCallback.onClosed)(mUserSessionCallback.context, this);
+ }
ALOGV("~ACameraCaptureSession: %p is deleted", this);
}
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
index 7ab0124..938b5f5 100644
--- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -253,21 +253,9 @@
return true;
}
- static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {}
-
- static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {}
-
- static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
- static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
- static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
-
private:
- ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected,
- onDeviceError};
- ACameraCaptureSession_stateCallbacks mSessionCb{
- this, onSessionClosed, onSessionReady, onSessionActive};
+ ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr};
+ ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr};
native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index cacd465..0d591d7 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -37,7 +37,7 @@
static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
- static constexpr size_t kMinBufferCountForEviction = 40;
+ static constexpr size_t kMinBufferCountForEviction = 25;
}
// Buffer structure in bufferpool process
@@ -718,8 +718,8 @@
mStats.mTotalFetches, mStats.mTotalTransfers);
}
for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
- if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
- && mBuffers.size() < kMinBufferCountForEviction) {
+ if (!clearCache && (mStats.mSizeCached < kMinAllocBytesForEviction
+ || mBuffers.size() < kMinBufferCountForEviction)) {
break;
}
auto it = mBuffers.find(*freeIt);
diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp
index 1dc676b..4db94f5 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.cpp
+++ b/media/codec2/components/aac/C2SoftAacEnc.cpp
@@ -155,11 +155,10 @@
mNumBytesPerInputFrame(0u),
mOutBufferSize(0u),
mSentCodecSpecificData(false),
- mInputTimeSet(false),
mInputSize(0),
- mNextFrameTimestampUs(0),
mSignalledError(false),
- mOutIndex(0u) {
+ mOutIndex(0u),
+ mRemainderLen(0u) {
}
C2SoftAacEnc::~C2SoftAacEnc() {
@@ -181,10 +180,11 @@
c2_status_t C2SoftAacEnc::onStop() {
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
- mNextFrameTimestampUs = 0;
+ mNextFrameTimestampUs.reset();
+ mLastFrameEndTimestampUs.reset();
mSignalledError = false;
+ mRemainderLen = 0;
return C2_OK;
}
@@ -199,9 +199,9 @@
c2_status_t C2SoftAacEnc::onFlush_sm() {
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
- mNextFrameTimestampUs = 0;
+ mNextFrameTimestampUs.reset();
+ mLastFrameEndTimestampUs.reset();
return C2_OK;
}
@@ -364,23 +364,35 @@
data = view.data();
capacity = view.capacity();
}
- if (!mInputTimeSet && capacity > 0) {
- mNextFrameTimestampUs = work->input.ordinal.timestamp;
- mInputTimeSet = true;
+ c2_cntr64_t inputTimestampUs = work->input.ordinal.timestamp;
+ if (inputTimestampUs < mLastFrameEndTimestampUs.value_or(inputTimestampUs)) {
+ ALOGW("Correcting overlapping timestamp: last frame ended at %lldus but "
+ "current frame is starting at %lldus. Using the last frame's end timestamp",
+ mLastFrameEndTimestampUs->peekll(), inputTimestampUs.peekll());
+ inputTimestampUs = *mLastFrameEndTimestampUs;
+ }
+ if (capacity > 0) {
+ if (!mNextFrameTimestampUs) {
+ mNextFrameTimestampUs = work->input.ordinal.timestamp;
+ }
+ mLastFrameEndTimestampUs = inputTimestampUs
+ + (capacity / sizeof(int16_t) * 1000000ll / channelCount / sampleRate);
}
- size_t numFrames = (capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
- / mNumBytesPerInputFrame;
+ size_t numFrames =
+ (mRemainderLen + capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
+ / mNumBytesPerInputFrame;
ALOGV("capacity = %zu; mInputSize = %zu; numFrames = %zu "
- "mNumBytesPerInputFrame = %u inputTS = %lld",
- capacity, mInputSize, numFrames,
- mNumBytesPerInputFrame, work->input.ordinal.timestamp.peekll());
+ "mNumBytesPerInputFrame = %u inputTS = %lld remaining = %zu",
+ capacity, mInputSize, numFrames, mNumBytesPerInputFrame, inputTimestampUs.peekll(),
+ mRemainderLen);
std::shared_ptr<C2LinearBlock> block;
std::unique_ptr<C2WriteView> wView;
uint8_t *outPtr = temp;
size_t outAvailable = 0u;
uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+ size_t bytesPerSample = channelCount * sizeof(int16_t);
AACENC_InArgs inargs;
AACENC_OutArgs outargs;
@@ -449,7 +461,25 @@
};
std::list<OutputBuffer> outputBuffers;
- while (encoderErr == AACENC_OK && inargs.numInSamples > 0) {
+ if (mRemainderLen > 0) {
+ size_t offset = 0;
+ for (; mRemainderLen < bytesPerSample && offset < capacity; ++offset) {
+ mRemainder[mRemainderLen++] = data[offset];
+ }
+ data += offset;
+ capacity -= offset;
+ if (mRemainderLen == bytesPerSample) {
+ inBuffer[0] = mRemainder;
+ inBufferSize[0] = bytesPerSample;
+ inargs.numInSamples = channelCount;
+ mRemainderLen = 0;
+ ALOGV("Processing remainder");
+ } else {
+ // We have exhausted the input already
+ inargs.numInSamples = 0;
+ }
+ }
+ while (encoderErr == AACENC_OK && inargs.numInSamples >= channelCount) {
if (numFrames && !block) {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
// TODO: error handling, proper usage, etc.
@@ -482,11 +512,13 @@
mInputSize = 0;
int consumed = (capacity / sizeof(int16_t)) - inargs.numInSamples
+ outargs.numInSamples;
- c2_cntr64_t currentFrameTimestampUs = mNextFrameTimestampUs;
- mNextFrameTimestampUs = work->input.ordinal.timestamp
+ ALOGV("consumed = %d, capacity = %zu, inSamples = %d, outSamples = %d",
+ consumed, capacity, inargs.numInSamples, outargs.numInSamples);
+ c2_cntr64_t currentFrameTimestampUs = *mNextFrameTimestampUs;
+ mNextFrameTimestampUs = inputTimestampUs
+ (consumed * 1000000ll / channelCount / sampleRate);
std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block, 0, outargs.numOutBytes);
-#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+#if 0
hexdump(outPtr, std::min(outargs.numOutBytes, 256));
#endif
outPtr = temp;
@@ -498,7 +530,11 @@
mInputSize += outargs.numInSamples * sizeof(int16_t);
}
- if (outargs.numInSamples > 0) {
+ if (inBuffer[0] == mRemainder) {
+ inBuffer[0] = const_cast<uint8_t *>(data);
+ inBufferSize[0] = capacity;
+ inargs.numInSamples = capacity / sizeof(int16_t);
+ } else if (outargs.numInSamples > 0) {
inBuffer[0] = (int16_t *)inBuffer[0] + outargs.numInSamples;
inBufferSize[0] -= outargs.numInSamples * sizeof(int16_t);
inargs.numInSamples -= outargs.numInSamples;
@@ -506,9 +542,8 @@
}
ALOGV("encoderErr = %d mInputSize = %zu "
"inargs.numInSamples = %d, mNextFrameTimestampUs = %lld",
- encoderErr, mInputSize, inargs.numInSamples, mNextFrameTimestampUs.peekll());
+ encoderErr, mInputSize, inargs.numInSamples, mNextFrameTimestampUs->peekll());
}
-
if (eos && inBufferSize[0] > 0) {
if (numFrames && !block) {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
@@ -539,6 +574,14 @@
&outBufDesc,
&inargs,
&outargs);
+ inBufferSize[0] = 0;
+ }
+
+ if (inBufferSize[0] > 0) {
+ for (size_t i = 0; i < inBufferSize[0]; ++i) {
+ mRemainder[i] = static_cast<uint8_t *>(inBuffer[0])[i];
+ }
+ mRemainderLen = inBufferSize[0];
}
while (outputBuffers.size() > 1) {
@@ -583,9 +626,9 @@
(void)pool;
mSentCodecSpecificData = false;
- mInputTimeSet = false;
mInputSize = 0u;
- mNextFrameTimestampUs = 0;
+ mNextFrameTimestampUs.reset();
+ mLastFrameEndTimestampUs.reset();
// TODO: we don't have any pending work at this time to drain.
return C2_OK;
diff --git a/media/codec2/components/aac/C2SoftAacEnc.h b/media/codec2/components/aac/C2SoftAacEnc.h
index 2655039..9a28280 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.h
+++ b/media/codec2/components/aac/C2SoftAacEnc.h
@@ -18,6 +18,7 @@
#define ANDROID_C2_SOFT_AAC_ENC_H_
#include <atomic>
+#include <optional>
#include <SimpleC2Component.h>
@@ -54,13 +55,17 @@
UINT mOutBufferSize;
bool mSentCodecSpecificData;
- bool mInputTimeSet;
size_t mInputSize;
- c2_cntr64_t mNextFrameTimestampUs;
+ std::optional<c2_cntr64_t> mNextFrameTimestampUs;
+ std::optional<c2_cntr64_t> mLastFrameEndTimestampUs;
bool mSignalledError;
std::atomic_uint64_t mOutIndex;
+ // We support max 6 channels
+ uint8_t mRemainder[6 * sizeof(int16_t)];
+ size_t mRemainderLen;
+
status_t initEncoder();
status_t setAudioParams();
diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
index 18e6db2..15564d9 100644
--- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
+++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
@@ -66,7 +66,7 @@
addParameter(
DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
.withDefault(new C2StreamSampleRateInfo::output(0u, 48000))
- .withFields({C2F(mSampleRate, value).inRange(8000, 96000)})
+ .withFields({C2F(mSampleRate, value).inRange(8000, 192000)})
.withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
.build());
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index a52ca15..a759e8f 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -593,12 +593,10 @@
}
}
- int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
-
if (inSize) {
uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
vpx_codec_err_t err = vpx_codec_decode(
- mCodecCtx, bitstream, inSize, &frameIndex, 0);
+ mCodecCtx, bitstream, inSize, &work->input.ordinal.frameIndex, 0);
if (err != VPX_CODEC_OK) {
ALOGE("on2 decoder failed to decode frame. err: %d", err);
mSignalledError = true;
@@ -608,7 +606,20 @@
}
}
- (void)outputBuffer(pool, work);
+ status_t err = outputBuffer(pool, work);
+ if (err == NOT_ENOUGH_DATA) {
+ if (inSize > 0) {
+ ALOGV("Maybe non-display frame at %lld.",
+ work->input.ordinal.frameIndex.peekll());
+ // send the work back with empty buffer.
+ inSize = 0;
+ }
+ } else if (err != OK) {
+ ALOGD("Error while getting the output frame out");
+ // work->result would be already filled; do fillEmptyWork() below to
+ // send the work back.
+ inSize = 0;
+ }
if (eos) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
@@ -742,16 +753,16 @@
}
return;
}
-bool C2SoftVpxDec::outputBuffer(
+status_t C2SoftVpxDec::outputBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work)
{
- if (!(work && pool)) return false;
+ if (!(work && pool)) return BAD_VALUE;
vpx_codec_iter_t iter = nullptr;
vpx_image_t *img = vpx_codec_get_frame(mCodecCtx, &iter);
- if (!img) return false;
+ if (!img) return NOT_ENOUGH_DATA;
if (img->d_w != mWidth || img->d_h != mHeight) {
mWidth = img->d_w;
@@ -768,7 +779,7 @@
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
- return false;
+ return UNKNOWN_ERROR;
}
}
@@ -791,18 +802,19 @@
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
work->result = err;
- return false;
+ return UNKNOWN_ERROR;
}
C2GraphicView wView = block->map().get();
if (wView.error()) {
ALOGE("graphic view map failed %d", wView.error());
work->result = C2_CORRUPTED;
- return false;
+ return UNKNOWN_ERROR;
}
- ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d",
- block->width(), block->height(), mWidth, mHeight, (int)*(int64_t *)img->user_priv);
+ ALOGV("provided (%dx%d) required (%dx%d), out frameindex %lld",
+ block->width(), block->height(), mWidth, mHeight,
+ ((c2_cntr64_t *)img->user_priv)->peekll());
uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
size_t srcYStride = img->stride[VPX_PLANE_Y];
@@ -858,8 +870,8 @@
dstYStride, dstUVStride,
mWidth, mHeight);
}
- finishWork(*(int64_t *)img->user_priv, work, std::move(block));
- return true;
+ finishWork(((c2_cntr64_t *)img->user_priv)->peekull(), work, std::move(block));
+ return OK;
}
c2_status_t C2SoftVpxDec::drainInternal(
@@ -875,7 +887,7 @@
return C2_OMITTED;
}
- while ((outputBuffer(pool, work))) {
+ while (outputBuffer(pool, work) == OK) {
}
if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.h b/media/codec2/components/vpx/C2SoftVpxDec.h
index e51bcee..2065165 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.h
+++ b/media/codec2/components/vpx/C2SoftVpxDec.h
@@ -85,7 +85,7 @@
status_t destroyDecoder();
void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2GraphicBlock> &block);
- bool outputBuffer(
+ status_t outputBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work);
c2_status_t drainInternal(
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 65d7a39..78ddd6d 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1286,7 +1286,8 @@
{
Mutexed<Config>::Locked config(mConfig);
inputFormat = config->mInputFormat;
- outputFormat = config->mOutputFormat;
+ // start triggers format dup
+ outputFormat = config->mOutputFormat = config->mOutputFormat->dup();
if (config->mInputSurface) {
err2 = config->mInputSurface->start();
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 5f969db..a4c30fa 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1073,7 +1073,7 @@
} else {
output->buffers.reset(new LinearOutputBuffers(mName));
}
- output->buffers->setFormat(outputFormat->dup());
+ output->buffers->setFormat(outputFormat);
// Try to set output surface to created block pool if given.
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index 752bc46..0470a31 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -600,7 +600,7 @@
}
std::shared_ptr<C2AllocationIon> alloc
- = std::make_shared<C2AllocationIon>(dup(mIonFd), capacity, align, heapMask, flags, mTraits->id);
+ = std::make_shared<C2AllocationIon>(dup(mIonFd), capacity, align, heapMask, flags, getId());
ret = alloc->status();
if (ret == C2_OK) {
*allocation = alloc;
@@ -622,7 +622,7 @@
// TODO: get capacity and validate it
const C2HandleIon *h = static_cast<const C2HandleIon*>(handle);
std::shared_ptr<C2AllocationIon> alloc
- = std::make_shared<C2AllocationIon>(dup(mIonFd), h->size(), h->bufferFd(), mTraits->id);
+ = std::make_shared<C2AllocationIon>(dup(mIonFd), h->size(), h->bufferFd(), getId());
c2_status_t ret = alloc->status();
if (ret == C2_OK) {
*allocation = alloc;
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index 5b2bd7b..e0408b7 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -35,6 +35,10 @@
#include <memory>
#include <mutex>
+#ifdef __ANDROID_APEX__
+#include <android-base/properties.h>
+#endif
+
namespace android {
/**
@@ -599,9 +603,33 @@
struct Setter {
static C2R setIonUsage(bool /* mayBlock */, C2P<C2StoreIonUsageInfo> &me) {
+#ifdef __ANDROID_APEX__
+ static int32_t defaultHeapMask = [] {
+ int32_t heapmask = base::GetIntProperty(
+ "ro.com.android.media.swcodec.ion.heapmask", int32_t(0xFFFFFFFF));
+ ALOGD("Default ION heapmask = %d", heapmask);
+ return heapmask;
+ }();
+ static int32_t defaultFlags = [] {
+ int32_t flags = base::GetIntProperty(
+ "ro.com.android.media.swcodec.ion.flags", 0);
+ ALOGD("Default ION flags = %d", flags);
+ return flags;
+ }();
+ static uint32_t defaultAlign = [] {
+ uint32_t align = base::GetUintProperty(
+ "ro.com.android.media.swcodec.ion.align", 0u);
+ ALOGD("Default ION align = %d", align);
+ return align;
+ }();
+ me.set().heapMask = defaultHeapMask;
+ me.set().allocFlags = defaultFlags;
+ me.set().minAlignment = defaultAlign;
+#else
me.set().heapMask = ~0;
me.set().allocFlags = 0;
me.set().minAlignment = 0;
+#endif
return C2R::Ok();
}
};
diff --git a/media/codec2/vndk/util/C2InterfaceUtils.cpp b/media/codec2/vndk/util/C2InterfaceUtils.cpp
index 61ec911..0c1729b 100644
--- a/media/codec2/vndk/util/C2InterfaceUtils.cpp
+++ b/media/codec2/vndk/util/C2InterfaceUtils.cpp
@@ -216,9 +216,14 @@
if (limit.contains(minMask) && contains(minMask)) {
values[0] = minMask;
// keep only flags that are covered by limit
- std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
- T value = v.ref<ValueType>() | minMask;
- return value == minMask || !limit.contains(value); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit, minMask](
+ const C2Value::Primitive &v) -> bool {
+ T value = v.ref<ValueType>() | minMask;
+ return value == minMask ||
+ !limit.contains(value);
+ }),
+ values.end());
// we also need to do it vice versa
for (const C2Value::Primitive &v : _mValues) {
T value = v.ref<ValueType>() | minMask;
@@ -264,24 +269,33 @@
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index ffeff42..26431a4 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -144,6 +144,7 @@
AMRExtractor::AMRExtractor(DataSourceHelper *source)
: mDataSource(source),
+ mMeta(NULL),
mInitCheck(NO_INIT),
mOffsetTableLength(0) {
float confidence;
@@ -191,7 +192,9 @@
AMRExtractor::~AMRExtractor() {
delete mDataSource;
- AMediaFormat_delete(mMeta);
+ if (mMeta) {
+ AMediaFormat_delete(mMeta);
+ }
}
media_status_t AMRExtractor::getMetaData(AMediaFormat *meta) {
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 8c8e6d1..0773387 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -700,8 +700,8 @@
}
private:
- uint32_t mWidth;
- uint32_t mHeight;
+ int32_t mWidth;
+ int32_t mHeight;
};
status_t IspeBox::parse(off64_t offset, size_t size) {
@@ -715,12 +715,19 @@
if (size < 8) {
return ERROR_MALFORMED;
}
- if (!source()->getUInt32(offset, &mWidth)
- || !source()->getUInt32(offset + 4, &mHeight)) {
+ if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
+ || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
return ERROR_IO;
}
- ALOGV("property ispe: %dx%d", mWidth, mHeight);
+ // Validate that the dimension doesn't cause overflow on calculated max input size.
+ // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
+ // we don't need to cast to int64_t when doing mults.
+ if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
+ return ERROR_MALFORMED;
+ }
+
+ ALOGV("property ispe: %dx%d", mWidth, mHeight);
return OK;
}
@@ -1524,8 +1531,9 @@
default: break; // don't set if invalid
}
}
+ // we validated no overflow in IspeBox::parse()
AMediaFormat_setInt32(meta,
- AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
if (!image->thumbnails.empty()) {
ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
@@ -1561,8 +1569,9 @@
AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
AMediaFormat_setInt32(meta,
AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
+ // we validated no overflow in IspeBox::parse()
AMediaFormat_setInt32(meta,
- AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
+ AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
}
if (image->hvcc == NULL) {
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 9d5890c..86ed610 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1561,8 +1561,12 @@
{
*offset += chunk_size;
- if (mLastTrack == NULL)
+ // the absolute minimum size of a compliant mett box is 11 bytes:
+ // 6 byte reserved, 2 byte index, null byte, one char mime_format, null byte
+ // The resulting mime_format would be invalid at that size though.
+ if (mLastTrack == NULL || chunk_data_size < 11) {
return ERROR_MALFORMED;
+ }
auto buffer = heapbuffer<uint8_t>(chunk_data_size);
if (buffer.get() == NULL) {
@@ -1574,10 +1578,24 @@
return ERROR_IO;
}
+ // ISO-14496-12:
+ // int8 reserved[6]; // should be all zeroes
+ // int16_t data_reference_index;
+ // char content_encoding[]; // null terminated, optional (= just the null byte)
+ // char mime_format[]; // null terminated, mandatory
+ // optional other boxes
+ //
+ // API < 29:
+ // char mime_format[]; // null terminated
+ //
+ // API >= 29
+ // char mime_format[]; // null terminated
+ // char mime_format[]; // null terminated
+
// Prior to API 29, the metadata track was not compliant with ISO/IEC
// 14496-12-2015. This led to some ISO-compliant parsers failing to read the
// metatrack. As of API 29 and onwards, a change was made to metadata track to
- // make it compliant with the standard. The workaround is to write the
+ // make it somewhat compatible with the standard. The workaround is to write the
// null-terminated mime_format string twice. This allows compliant parsers to
// read the missing reserved, data_reference_index, and content_encoding fields
// from the first mime_type string. The actual mime_format field would then be
@@ -1586,27 +1604,27 @@
// as it would only read the first null-terminated mime_format string. To enable
// reading metadata tracks generated from both the non-compliant and compliant
// formats, a check needs to be done to see which format is used.
- int null_pos = 0;
- const unsigned char *str = buffer.get();
- while (null_pos < chunk_data_size) {
- if (*(str + null_pos) == '\0') {
- break;
- }
- ++null_pos;
- }
+ const char *str = (const char*) buffer.get();
+ size_t string_length = strnlen(str, chunk_data_size);
- if (null_pos == chunk_data_size - 1) {
- // This is not a standard ompliant metadata track.
- String8 mimeFormat((const char *)(buffer.get()), chunk_data_size);
- AMediaFormat_setString(mLastTrack->meta,
- AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+ if (string_length == chunk_data_size - 1) {
+ // This is likely a pre API 29 file, since it's a single null terminated
+ // string filling the entire box.
+ AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, str);
} else {
- // This is a standard compliant metadata track.
- String8 contentEncoding((const char *)(buffer.get() + 8));
- String8 mimeFormat((const char *)(buffer.get() + 8 + contentEncoding.size() + 1),
- chunk_data_size - 8 - contentEncoding.size() - 1);
- AMediaFormat_setString(mLastTrack->meta,
- AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
+ // This might be a fully compliant metadata track, a "double mime" compatibility
+ // track, or anything else, including a single non-terminated string, so we need
+ // to determine the length of each string we want to parse out of the box.
+ size_t encoding_length = strnlen(str + 8, chunk_data_size - 8);
+ if (encoding_length + 8 >= chunk_data_size - 2) {
+ // the encoding extends to the end of the box, so there's no mime_format
+ return ERROR_MALFORMED;
+ }
+ String8 contentEncoding(str + 8, encoding_length);
+ String8 mimeFormat(str + 8 + encoding_length + 1,
+ chunk_data_size - 8 - encoding_length - 1);
+ AMediaFormat_setString(mLastTrack->meta,
+ AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
}
break;
}
@@ -5775,11 +5793,11 @@
meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
- int32_t byteOrder;
- AMediaFormat_getInt32(mFormat,
+ int32_t byteOrder = 0;
+ bool isGetBigEndian = AMediaFormat_getInt32(mFormat,
AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &byteOrder);
- if (byteOrder == 1) {
+ if (isGetBigEndian && byteOrder == 1) {
// Big-endian -> little-endian
uint16_t *dstData = (uint16_t *)buf;
uint16_t *srcData = (uint16_t *)buf;
diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp
index bf29bf1..9dddf2c 100644
--- a/media/extractors/mp4/SampleTable.cpp
+++ b/media/extractors/mp4/SampleTable.cpp
@@ -655,6 +655,7 @@
}
mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+ memset(mSampleTimeEntries, 0, sizeof(SampleTimeEntry) * mNumSampleSizes);
if (!mSampleTimeEntries) {
ALOGE("Cannot allocate sample entry table with %llu entries.",
(unsigned long long)mNumSampleSizes);
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
index 64eda75..fca66a4 100644
--- a/media/extractors/tests/ExtractorUnitTest.cpp
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -22,6 +22,7 @@
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataUtils.h>
+#include <media/stagefright/foundation/OpusHeader.h>
#include "aac/AACExtractor.h"
#include "amr/AMRExtractor.h"
@@ -43,7 +44,9 @@
#define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput"
constexpr int32_t kMaxCount = 10;
-constexpr int32_t kOpusSeekPreRollUs = 80000; // 80 ms;
+constexpr int32_t kAudioDefaultSampleDuration = 20000; // 20ms
+constexpr int32_t kRandomSeekToleranceUs = 2 * kAudioDefaultSampleDuration; // 40 ms;
+constexpr int32_t kRandomSeed = 700;
static ExtractorUnitTestEnvironment *gEnv = nullptr;
@@ -168,6 +171,47 @@
return 0;
}
+void randomSeekTest(MediaTrackHelper *track, int64_t clipDuration) {
+ int32_t status = 0;
+ int32_t seekCount = 0;
+ bool hasTimestamp = false;
+ vector<int64_t> seekToTimeStamp;
+ string seekPtsString;
+
+ srand(kRandomSeed);
+ while (seekCount < kMaxCount) {
+ int64_t timeStamp = ((double)rand() / RAND_MAX) * clipDuration;
+ seekToTimeStamp.push_back(timeStamp);
+ seekPtsString.append(to_string(timeStamp));
+ seekPtsString.append(", ");
+ seekCount++;
+ }
+
+ for (int64_t seekPts : seekToTimeStamp) {
+ MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
+ CMediaTrackReadOptions::SEEK_CLOSEST | CMediaTrackReadOptions::SEEK, seekPts);
+ ASSERT_NE(options, nullptr) << "Cannot create read option";
+
+ MediaBufferHelper *buffer = nullptr;
+ status = track->read(&buffer, options);
+ if (buffer) {
+ AMediaFormat *metaData = buffer->meta_data();
+ int64_t timeStamp = 0;
+ hasTimestamp = AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
+ ASSERT_TRUE(hasTimestamp) << "Extractor didn't set timestamp for the given sample";
+
+ buffer->release();
+ EXPECT_LE(abs(timeStamp - seekPts), kRandomSeekToleranceUs)
+ << "Seek unsuccessful. Expected timestamp range ["
+ << seekPts - kRandomSeekToleranceUs << ", " << seekPts + kRandomSeekToleranceUs
+ << "] "
+ << "received " << timeStamp << ", list of input seek timestamps ["
+ << seekPtsString << "]";
+ }
+ delete options;
+ }
+}
+
void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) {
int32_t status = 0;
if (!seekablePoints.empty()) {
@@ -380,11 +424,7 @@
}
TEST_P(ExtractorUnitTest, SeekTest) {
- // Flac, Midi and Wav extractor can give samples from any pts and mark the given sample as
- // sync frame. So, this seek test is not applicable to these extractors
- if (mDisableTest || mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) {
- return;
- }
+ if (mDisableTest) return;
ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str());
string inputFileName = gEnv->getRes() + GetParam().second;
@@ -417,6 +457,32 @@
MediaBufferGroup *bufferGroup = new MediaBufferGroup();
status = cTrack->start(track, bufferGroup->wrap());
ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
+
+ // For Flac, Wav and Midi extractor, all samples are seek points.
+ // We cannot create list of all seekable points for these.
+ // This means that if we pass a seekToTimeStamp between two seek points, we may
+ // end up getting the timestamp of next sample as a seekable timestamp.
+ // This timestamp may/may not be a part of the seekable point vector thereby failing the
+ // test. So we test these extractors using random seek test.
+ if (mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) {
+ AMediaFormat *trackMeta = AMediaFormat_new();
+ ASSERT_NE(trackMeta, nullptr) << "AMediaFormat_new returned null AMediaformat";
+
+ status = mExtractor->getTrackMetaData(trackMeta, idx, 1);
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData";
+
+ int64_t clipDuration = 0;
+ AMediaFormat_getInt64(trackMeta, AMEDIAFORMAT_KEY_DURATION, &clipDuration);
+ ASSERT_GT(clipDuration, 0) << "Invalid clip duration ";
+ randomSeekTest(track, clipDuration);
+ AMediaFormat_delete(trackMeta);
+ continue;
+ }
+ // Request seekable points for remaining extractors which will be used to validate the seek
+ // accuracy for the extractors. Depending on SEEK Mode, we expect the extractors to return
+ // the expected sync frame. We don't prefer random seek test for these extractors because
+ // they aren't expected to seek to random samples. MP4 for instance can seek to
+ // next/previous sync frames but not to samples between two sync frames.
getSeekablePoints(seekablePoints, track);
ASSERT_GT(seekablePoints.size(), 0)
<< "Failed to get seekable points for " << GetParam().first << " extractor";
@@ -427,9 +493,31 @@
ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
bool isOpus = false;
+ int64_t opusSeekPreRollUs = 0;
const char *mime;
AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
- if (!strcmp(mime, "audio/opus")) isOpus = true;
+ if (!strcmp(mime, "audio/opus")) {
+ isOpus = true;
+ void *seekPreRollBuf = nullptr;
+ size_t size = 0;
+ if (!AMediaFormat_getBuffer(trackFormat, "csd-2", &seekPreRollBuf, &size)) {
+ size_t opusHeadSize = 0;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *csdBuffer = nullptr;
+ void *opusHeadBuf = nullptr;
+ void *codecDelayBuf = nullptr;
+ AMediaFormat_getBuffer(trackFormat, "csd-0", &csdBuffer, &size);
+ ASSERT_NE(csdBuffer, nullptr);
+
+ GetOpusHeaderBuffers((uint8_t *)csdBuffer, size, &opusHeadBuf, &opusHeadSize,
+ &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ }
+ ASSERT_NE(seekPreRollBuf, nullptr)
+ << "Invalid track format. SeekPreRoll info missing for Opus file";
+ opusSeekPreRollUs = *((int64_t *)seekPreRollBuf);
+ }
AMediaFormat_delete(trackFormat);
int32_t seekIdx = 0;
@@ -450,7 +538,7 @@
// extractor is calculated based on (seekPts - seekPreRollUs).
// So we add the preRoll value to the timeStamp we want to seek to.
if (isOpus) {
- seekToTimeStamp += kOpusSeekPreRollUs;
+ seekToTimeStamp += opusSeekPreRollUs;
}
MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
diff --git a/media/libaudioclient/AudioAttributes.cpp b/media/libaudioclient/AudioAttributes.cpp
index 1ee6930..ff4ba06 100644
--- a/media/libaudioclient/AudioAttributes.cpp
+++ b/media/libaudioclient/AudioAttributes.cpp
@@ -57,7 +57,7 @@
parcel->writeInt32(0);
} else {
parcel->writeInt32(1);
- parcel->writeUtf8AsUtf16(mAttributes.tags);
+ parcel->writeUtf8AsUtf16(std::string(mAttributes.tags));
}
parcel->writeInt32(static_cast<int32_t>(mStreamType));
parcel->writeUint32(static_cast<uint32_t>(mGroupId));
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 480930b..481becc 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -783,6 +783,13 @@
// ---------------------------------------------------------------------------
+void AudioSystem::onNewAudioModulesAvailable()
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return;
+ aps->onNewAudioModulesAvailable();
+}
+
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 3f8b52b..53d46f1 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -1361,10 +1361,14 @@
}
case GET_EFFECT_DESCRIPTOR: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- effect_uuid_t uuid;
- data.read(&uuid, sizeof(effect_uuid_t));
- effect_uuid_t type;
- data.read(&type, sizeof(effect_uuid_t));
+ effect_uuid_t uuid = {};
+ if (data.read(&uuid, sizeof(effect_uuid_t)) != NO_ERROR) {
+ android_errorWriteLog(0x534e4554, "139417189");
+ }
+ effect_uuid_t type = {};
+ if (data.read(&type, sizeof(effect_uuid_t)) != NO_ERROR) {
+ android_errorWriteLog(0x534e4554, "139417189");
+ }
uint32_t preferredTypeFlag = data.readUint32();
effect_descriptor_t desc = {};
status_t status = getEffectDescriptor(&uuid, &type, preferredTypeFlag, &desc);
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0facaf8..24d7c92 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -108,6 +108,7 @@
SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
+ AUDIO_MODULES_UPDATED, // oneway
};
#define MAX_ITEMS_PER_LIST 1024
@@ -1336,6 +1337,13 @@
}
return static_cast<status_t>(reply.readInt32());
}
+
+ virtual void onNewAudioModulesAvailable()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ remote()->transact(AUDIO_MODULES_UPDATED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -1401,7 +1409,8 @@
case SET_RTT_ENABLED:
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
case REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
- case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
+ case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
+ case AUDIO_MODULES_UPDATED: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@@ -2458,6 +2467,12 @@
return NO_ERROR;
}
+ case AUDIO_MODULES_UPDATED: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ onNewAudioModulesAvailable();
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index a86297d..925bc89 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -211,6 +211,7 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
+ static void onNewAudioModulesAvailable();
static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state,
const char *device_address, const char *device_name,
audio_format_t encodedFormat);
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 9b91d6d..3edac33 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -42,6 +42,7 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
+ virtual void onNewAudioModulesAvailable() = 0;
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp
index 8dcc421..93bc4d9 100644
--- a/media/libaudiofoundation/Android.bp
+++ b/media/libaudiofoundation/Android.bp
@@ -12,7 +12,7 @@
],
}
-cc_library_shared {
+cc_library {
name: "libaudiofoundation",
vendor_available: true,
double_loadable: true,
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
index e6e9688..1c0eacb 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
@@ -15,12 +15,13 @@
*/
#include <string.h>
-#include <vector>
+#include <set>
#define LOG_TAG "DevicesFactoryHalHidl"
//#define LOG_NDEBUG 0
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hidl/manager/1.0/IServiceNotification.h>
#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h)
#include <media/audiohal/hidl/HalDeathHandler.h>
#include <utils/Log.h>
@@ -29,33 +30,57 @@
#include "DeviceHalHidl.h"
#include "DevicesFactoryHalHidl.h"
-#include <set>
-
using ::android::hardware::audio::CPP_VERSION::IDevice;
using ::android::hardware::audio::CPP_VERSION::Result;
using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::hidl::manager::V1_0::IServiceNotification;
namespace android {
namespace CPP_VERSION {
-DevicesFactoryHalHidl::DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory) {
- ALOG_ASSERT(devicesFactory != nullptr, "Provided IDevicesFactory service is NULL");
+class ServiceNotificationListener : public IServiceNotification {
+ public:
+ explicit ServiceNotificationListener(sp<DevicesFactoryHalHidl> factory)
+ : mFactory(factory) {}
- mDeviceFactories.push_back(devicesFactory);
- if (MAJOR_VERSION >= 4) {
- // The MSD factory is optional and only available starting at HAL 4.0
- sp<IDevicesFactory> msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)};
- if (msdFactory) {
- mDeviceFactories.push_back(msdFactory);
+ Return<void> onRegistration(const hidl_string& /*fully_qualified_name*/,
+ const hidl_string& instance_name,
+ bool /*pre_existing*/) override {
+ if (static_cast<std::string>(instance_name) == "default") return Void();
+ sp<DevicesFactoryHalHidl> factory = mFactory.promote();
+ if (!factory) return Void();
+ sp<IDevicesFactory> halFactory = IDevicesFactory::getService(instance_name);
+ if (halFactory) {
+ factory->addDeviceFactory(halFactory, true /*needToNotify*/);
}
+ return Void();
}
- for (const auto& factory : mDeviceFactories) {
- // It is assumed that the DevicesFactoryHalInterface instance is owned
- // by AudioFlinger and thus have the same lifespan.
- factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
- }
+
+ private:
+ wp<DevicesFactoryHalHidl> mFactory;
+};
+
+DevicesFactoryHalHidl::DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory) {
+ ALOG_ASSERT(devicesFactory != nullptr, "Provided default IDevicesFactory service is NULL");
+ addDeviceFactory(devicesFactory, false /*needToNotify*/);
}
+void DevicesFactoryHalHidl::onFirstRef() {
+ sp<IServiceManager> sm = IServiceManager::getService();
+ ALOG_ASSERT(sm != nullptr, "Hardware service manager is not running");
+ sp<ServiceNotificationListener> listener = new ServiceNotificationListener(this);
+ Return<bool> result = sm->registerForNotifications(
+ IDevicesFactory::descriptor, "", listener);
+ if (result.isOk()) {
+ ALOGE_IF(!static_cast<bool>(result),
+ "Hardware service manager refused to register listener");
+ } else {
+ ALOGE("Failed to register for hardware service manager notifications: %s",
+ result.description().c_str());
+ }
+}
#if MAJOR_VERSION == 2
static IDevicesFactory::Device idFromHal(const char *name, status_t* status) {
@@ -83,12 +108,13 @@
#endif
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
- if (mDeviceFactories.empty()) return NO_INIT;
+ auto factories = copyDeviceFactories();
+ if (factories.empty()) return NO_INIT;
status_t status;
auto hidlId = idFromHal(name, &status);
if (status != OK) return status;
Result retval = Result::NOT_INITIALIZED;
- for (const auto& factory : mDeviceFactories) {
+ for (const auto& factory : factories) {
Return<void> ret = factory->openDevice(
hidlId,
[&](Result r, const sp<IDevice>& result) {
@@ -113,10 +139,9 @@
status_t DevicesFactoryHalHidl::getHalPids(std::vector<pid_t> *pids) {
std::set<pid_t> pidsSet;
-
- for (const auto& factory : mDeviceFactories) {
+ auto factories = copyDeviceFactories();
+ for (const auto& factory : factories) {
using ::android::hidl::base::V1_0::DebugInfo;
- using android::hidl::manager::V1_0::IServiceManager;
DebugInfo debugInfo;
auto ret = factory->getDebugInfo([&] (const auto &info) {
@@ -135,5 +160,48 @@
return NO_ERROR;
}
+status_t DevicesFactoryHalHidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
+ ALOG_ASSERT(callback != nullptr);
+ bool needToCallCallback = false;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mCallback.unsafe_get()) return INVALID_OPERATION;
+ mCallback = callback;
+ if (mHaveUndeliveredNotifications) {
+ needToCallCallback = true;
+ mHaveUndeliveredNotifications = false;
+ }
+ }
+ if (needToCallCallback) {
+ callback->onNewDevicesAvailable();
+ }
+ return NO_ERROR;
+}
+
+void DevicesFactoryHalHidl::addDeviceFactory(sp<IDevicesFactory> factory, bool needToNotify) {
+ // It is assumed that the DevicesFactoryHalInterface instance is owned
+ // by AudioFlinger and thus have the same lifespan.
+ factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
+ sp<DevicesFactoryHalCallback> callback;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mDeviceFactories.push_back(factory);
+ if (needToNotify) {
+ callback = mCallback.promote();
+ if (!callback) {
+ mHaveUndeliveredNotifications = true;
+ }
+ }
+ }
+ if (callback) {
+ callback->onNewDevicesAvailable();
+ }
+}
+
+std::vector<sp<IDevicesFactory>> DevicesFactoryHalHidl::copyDeviceFactories() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mDeviceFactories;
+}
+
} // namespace CPP_VERSION
} // namespace android
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.h b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
index 52185c8..6f84efe 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
+#include <mutex>
+#include <vector>
+
#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <utils/Errors.h>
@@ -32,16 +35,26 @@
class DevicesFactoryHalHidl : public DevicesFactoryHalInterface
{
public:
- DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory);
+ explicit DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory);
+ void onFirstRef() override;
// Opens a device with the specified name. To close the device, it is
// necessary to release references to the returned object.
- virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+ status_t openDevice(const char *name, sp<DeviceHalInterface> *device) override;
- status_t getHalPids(std::vector<pid_t> *pids) override;
+ status_t getHalPids(std::vector<pid_t> *pids) override;
+
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
private:
- std::vector<sp<IDevicesFactory>> mDeviceFactories;
+ friend class ServiceNotificationListener;
+ void addDeviceFactory(sp<IDevicesFactory> factory, bool needToNotify);
+ std::vector<sp<IDevicesFactory>> copyDeviceFactories();
+
+ std::mutex mLock;
+ std::vector<sp<IDevicesFactory>> mDeviceFactories; // GUARDED_BY(mLock)
+ wp<DevicesFactoryHalCallback> mCallback; // GUARDED_BY(mLock)
+ bool mHaveUndeliveredNotifications = false; // GUARDED_BY(mLock)
virtual ~DevicesFactoryHalHidl() = default;
};
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
index 52f150a..cde8d85 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
@@ -44,6 +44,13 @@
return INVALID_OPERATION;
}
+status_t DevicesFactoryHalHybrid::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
+ if (mHidlFactory) {
+ return mHidlFactory->setCallbackOnce(callback);
+ }
+ return INVALID_OPERATION;
+}
+
} // namespace CPP_VERSION
extern "C" __attribute__((visibility("default"))) void* createIDevicesFactory() {
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
index 2189b36..568a1fb 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
@@ -38,6 +38,8 @@
status_t getHalPids(std::vector<pid_t> *pids) override;
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
+
private:
sp<DevicesFactoryHalInterface> mLocalFactory;
sp<DevicesFactoryHalInterface> mHidlFactory;
diff --git a/media/libaudiohal/impl/DevicesFactoryHalLocal.h b/media/libaudiohal/impl/DevicesFactoryHalLocal.h
index 2b011f4..32bf362 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalLocal.h
@@ -37,6 +37,10 @@
return INVALID_OPERATION;
}
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback __unused) override {
+ return INVALID_OPERATION;
+ }
+
private:
friend class DevicesFactoryHalHybrid;
diff --git a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
index e9ac1ce..5091558 100644
--- a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
@@ -24,6 +24,12 @@
namespace android {
+class DevicesFactoryHalCallback : public RefBase
+{
+ public:
+ virtual void onNewDevicesAvailable() = 0;
+};
+
class DevicesFactoryHalInterface : public RefBase
{
public:
@@ -33,6 +39,10 @@
virtual status_t getHalPids(std::vector<pid_t> *pids) = 0;
+ // Sets a DevicesFactoryHalCallback to notify the client.
+ // The callback can be only set once.
+ virtual status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) = 0;
+
static sp<DevicesFactoryHalInterface> create();
protected:
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index afdcd37..f21d2b3 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -130,29 +130,32 @@
} else if (n < 0) {
break;
} else {
- if (buffer[0] == 0x00) {
+ if (buffer[0] == 0x00) { // OK to access buffer[0] since n must be > 0 here
// XXX legacy
if (extra == NULL) {
extra = new AMessage;
}
- uint8_t type = buffer[1];
+ uint8_t type = 0;
+ if (n > 1) {
+ type = buffer[1];
- if (type & 2) {
- int64_t mediaTimeUs;
- memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
+ if ((type & 2) && (n >= 2 + sizeof(int64_t))) {
+ int64_t mediaTimeUs;
+ memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
- extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+ extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+ }
}
mTSParser->signalDiscontinuity(
((type & 1) == 0)
- ? ATSParser::DISCONTINUITY_TIME
- : ATSParser::DISCONTINUITY_FORMATCHANGE,
+ ? ATSParser::DISCONTINUITY_TIME
+ : ATSParser::DISCONTINUITY_FORMATCHANGE,
extra);
} else {
- status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+ status_t err = mTSParser->feedTSPacket(buffer, n);
if (err != OK) {
ALOGE("TS Parser returned error %d", err);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index cfefe11..eacaea8 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -6902,7 +6902,7 @@
}
}
- if (mCodec->mMaxPtsGapUs != 0LL) {
+ if (mCodec->mIsVideo && mCodec->mMaxPtsGapUs != 0LL) {
OMX_PARAM_U32TYPE maxPtsGapParams;
InitOMXParams(&maxPtsGapParams);
maxPtsGapParams.nPortIndex = kPortIndexInput;
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index ea8b073..bcf63d5 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libstagefright_amrnb_common",
vendor_available: true,
+ host_supported: true,
srcs: [
"src/add.cpp",
@@ -73,6 +74,12 @@
"-Werror",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
//addressing b/25409744
//sanitize: {
// misc_undefined: [
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index e18117e..3381d2e 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_amrnbdec",
vendor_available: true,
+ host_supported: true,
srcs: [
"src/a_refl.cpp",
@@ -61,6 +62,12 @@
"libstagefright_amrnb_common",
"liblog",
],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
@@ -100,6 +107,7 @@
cc_test {
name: "libstagefright_amrnbdec_test",
gtest: false,
+ host_supported: true,
srcs: ["test/amrnbdec_test.cpp"],
@@ -118,6 +126,12 @@
"liblog",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
//sanitize: {
// misc_undefined: [
// "signed-integer-overflow",
diff --git a/media/libstagefright/codecs/amrnb/fuzzer/Android.bp b/media/libstagefright/codecs/amrnb/fuzzer/Android.bp
new file mode 100644
index 0000000..54de1cc
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/fuzzer/Android.bp
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+ name: "amrnb_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "amrnb_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_amrnbdec",
+ "libstagefright_amrnb_common",
+ "liblog",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libstagefright/codecs/amrnb/fuzzer/README.md b/media/libstagefright/codecs/amrnb/fuzzer/README.md
new file mode 100644
index 0000000..7791b83
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/fuzzer/README.md
@@ -0,0 +1,62 @@
+# Fuzzer for libstagefright_amrnbdec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for AMR-NB is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AMR-NB supports the following parameters:
+1. Stream format (parameter name: `input_format`)
+2. 3GPP frame type (parameter name: `frame_type`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `input_format` | 0. `MIME_IETF` 1. `IF2` | Bit 0 (LSB) of 1st byte of data. |
+| `frame_type` | 0. `AMR_475` 1. `AMR_515` 2. `AMR_59` 3. `AMR_67` 4. `AMR_74` 5. `AMR_795` 6. `AMR_102` 7. `AMR_122` | Bits 3, 4 and 5 of 1st byte of data. |
+
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+If the decode operation was successful, the input is advanced by the frame size
+which is based on `input_format` and `frame_type` selected.
+If the decode operation was un-successful, the input is still advanced by frame size so
+that the fuzzer can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build amrnb_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) amrnb_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some amrnb files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/amrnb_dec_fuzzer/amrnb_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/amrnb_dec_fuzzer/amrnb_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/amrnb/fuzzer/amrnb_dec_fuzzer.cpp b/media/libstagefright/codecs/amrnb/fuzzer/amrnb_dec_fuzzer.cpp
new file mode 100644
index 0000000..d4e7e5c
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/fuzzer/amrnb_dec_fuzzer.cpp
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <string.h>
+#include <algorithm>
+#include "gsmamr_dec.h"
+
+// Constants for AMR-NB
+constexpr int32_t kSamplesPerFrame = L_FRAME;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const bitstream_format kBitStreamFormats[2] = {MIME_IETF, IF2};
+const int32_t kLocalWmfDecBytesPerFrame[8] = {12, 13, 15, 17, 19, 20, 26, 31};
+const int32_t kLocalIf2DecBytesPerFrame[8] = {13, 14, 16, 18, 19, 21, 26, 31};
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+ int16_t initDecoder();
+ void deInitDecoder();
+ void decodeFrames(const uint8_t *data, size_t size);
+
+ private:
+ void *mAmrHandle = nullptr;
+};
+
+int16_t Codec::initDecoder() { return GSMInitDecode(&mAmrHandle, (Word8 *)"AMRNBDecoder"); }
+
+void Codec::deInitDecoder() { GSMDecodeFrameExit(&mAmrHandle); }
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ while (size > 0) {
+ uint8_t mode = *data;
+ bool bit = mode & 0x01;
+ bitstream_format bitsreamFormat = kBitStreamFormats[bit];
+ int32_t frameSize = 0;
+ /* Find frame type */
+ Frame_Type_3GPP frameType = static_cast<Frame_Type_3GPP>((mode >> 3) & 0x07);
+ ++data;
+ --size;
+ if (bit) {
+ frameSize = kLocalIf2DecBytesPerFrame[frameType];
+ } else {
+ frameSize = kLocalWmfDecBytesPerFrame[frameType];
+ }
+ int16_t outputBuf[kOutputBufferSize];
+ uint8_t *inputBuf = new uint8_t[frameSize];
+ if (!inputBuf) {
+ return;
+ }
+ int32_t minSize = std::min((int32_t)size, frameSize);
+ memcpy(inputBuf, data, minSize);
+ AMRDecode(mAmrHandle, frameType, inputBuf, outputBuf, bitsreamFormat);
+ /* AMRDecode() decodes minSize number of bytes if decode is successful.
+ * AMRDecode() returns -1 if decode fails.
+ * Even if no bytes are decoded, increment by minSize to ensure fuzzer proceeds
+ * to feed next data */
+ data += minSize;
+ size -= minSize;
+ delete[] inputBuf;
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder() == 0) {
+ codec->decodeFrames(data, size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index 88cf7f2..d8cb568 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_amrwbdec",
vendor_available: true,
+ host_supported: true,
srcs: [
"src/agc2_amr_wb.cpp",
@@ -44,8 +45,6 @@
"src/weight_amrwb_lpc.cpp",
],
- header_libs: ["libstagefright_headers"],
-
export_include_dirs: [
"src",
"include",
@@ -63,12 +62,19 @@
"signed-integer-overflow",
],
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
cc_test {
name: "libstagefright_amrwbdec_test",
gtest: false,
+ host_supported: true,
srcs: ["test/amrwbdec_test.cpp"],
@@ -88,4 +94,10 @@
"signed-integer-overflow",
],
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/Android.bp b/media/libstagefright/codecs/amrwb/fuzzer/Android.bp
new file mode 100644
index 0000000..46f77e3
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/Android.bp
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+ name: "amrwb_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "amrwb_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_amrwbdec",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/README.md b/media/libstagefright/codecs/amrwb/fuzzer/README.md
new file mode 100644
index 0000000..de45784
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/README.md
@@ -0,0 +1,61 @@
+# Fuzzer for libstagefright_amrwbdec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for AMR-WB is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AMR-WB supports the following parameters:
+1. Quality (parameter name: `quality`)
+2. Mode (parameter name: `mode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `quality` | 0. `Bad Quality` 1. `Good quality` | Bit 0 (LSB) of 1st byte of data. |
+| `mode` | 0. `MODE_7k` 1. `MODE_9k` 2. `MODE_12k` 3. `MODE_14k` 4. `MODE_16k ` 5. `MODE_18k` 6. `MODE_20k` 7. `MODE_23k` 8. `MODE_24k` 9. `MRDTX` | Bits 3, 4, 5 and 6 of 1st byte of data. |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+If the decode operation was successful, the input is advanced by the frame size
+which is based on `mode` and `quality` selected.
+If the decode operation was un-successful, the input is still advanced by frame size so
+that the fuzzer can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build amrwb_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) amrwb_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some amrwb files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/amrwb_dec_fuzzer/amrwb_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/amrwb_dec_fuzzer/amrwb_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
new file mode 100644
index 0000000..6dc0270
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
@@ -0,0 +1,110 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <malloc.h>
+#include <string.h>
+#include <algorithm>
+#include "pvamrwbdecoder.h"
+#include "pvamrwbdecoder_api.h"
+
+// Constants for AMR-WB.
+constexpr int32_t kSamplesPerFrame = 320;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kMaxSourceDataUnitSize = KAMRWB_NB_BITS_MAX * sizeof(int16_t);
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+constexpr int32_t kFrameSizes[16] = {17, 23, 32, 36, 40, 46, 50, 58,
+ 60, 17, 23, 32, 36, 40, 46, 50};
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+ bool initDecoder();
+ void decodeFrames(const uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ void *mAmrHandle = nullptr;
+ int16_t *mDecoderCookie = nullptr;
+ void *mDecoderBuffer = nullptr;
+};
+
+bool Codec::initDecoder() {
+ mDecoderBuffer = malloc(pvDecoder_AmrWbMemRequirements());
+ if (mDecoderBuffer) {
+ pvDecoder_AmrWb_Init(&mAmrHandle, mDecoderBuffer, &mDecoderCookie);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void Codec::deInitDecoder() {
+ if (mDecoderBuffer) {
+ free(mDecoderBuffer);
+ mDecoderBuffer = nullptr;
+ }
+ mAmrHandle = nullptr;
+ mDecoderCookie = nullptr;
+}
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ while (size > 0) {
+ uint8_t modeByte = *data;
+ bool quality = modeByte & 0x01;
+ int16 mode = ((modeByte >> 3) & 0x0f);
+ ++data;
+ --size;
+ int32_t frameSize = kFrameSizes[mode];
+ int16_t inputSampleBuf[kMaxSourceDataUnitSize];
+ uint8_t *inputBuf = new uint8_t[frameSize];
+ if (!inputBuf) {
+ return;
+ }
+ int32_t minSize = std::min((int32_t)size, frameSize);
+ memcpy(inputBuf, data, minSize);
+ int16 frameMode = mode;
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, quality, &rx_state);
+
+ int16_t numSamplesOutput;
+ int16_t outputBuf[kOutputBufferSize];
+ pvDecoder_AmrWb(frameMode, inputSampleBuf, outputBuf, &numSamplesOutput, mDecoderBuffer,
+ frameType, mDecoderCookie);
+ data += minSize;
+ size -= minSize;
+ delete[] inputBuf;
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder()) {
+ codec->decodeFrames(data, size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp
index 513e41f..42ac026 100644
--- a/media/libstagefright/foundation/OpusHeader.cpp
+++ b/media/libstagefright/foundation/OpusHeader.cpp
@@ -126,12 +126,20 @@
}
header->num_streams = data[kOpusHeaderNumStreamsOffset];
header->num_coupled = data[kOpusHeaderNumCoupledStreamsOffset];
- if (header->num_streams + header->num_coupled != header->channels) {
- ALOGV("Inconsistent channel mapping.");
+ if (header->num_coupled > header->num_streams ||
+ header->num_streams + header->num_coupled != header->channels) {
+ ALOGV("Inconsistent channel mapping, streams: %d coupled: %d channels: %d",
+ header->num_streams, header->num_coupled, header->channels);
return false;
}
- for (int i = 0; i < header->channels; ++i)
- header->stream_map[i] = data[kOpusHeaderStreamMapOffset + i];
+ for (int i = 0; i < header->channels; ++i) {
+ uint8_t value = data[kOpusHeaderStreamMapOffset + i];
+ if (value != 255 && value >= header->channels) {
+ ALOGV("Invalid channel mapping for index %i : %d", i, value);
+ return false;
+ }
+ header->stream_map[i] = value;
+ }
return true;
}
diff --git a/media/libstagefright/foundation/TEST_MAPPING b/media/libstagefright/foundation/TEST_MAPPING
new file mode 100644
index 0000000..3301c4b
--- /dev/null
+++ b/media/libstagefright/foundation/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+ "presubmit": [
+ { "name": "sf_foundation_test" }
+ ]
+}
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
new file mode 100644
index 0000000..f2157c9
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -0,0 +1,27 @@
+cc_test {
+ name: "sf_foundation_test",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ include_dirs: [
+ "frameworks/av/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ srcs: [
+ "AData_test.cpp",
+ "Base64_test.cpp",
+ "Flagged_test.cpp",
+ "TypeTraits_test.cpp",
+ "Utils_test.cpp",
+ ],
+}
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
deleted file mode 100644
index a9e3c76..0000000
--- a/media/libstagefright/foundation/tests/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE := sf_foundation_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
- AData_test.cpp \
- Base64_test.cpp \
- Flagged_test.cpp \
- TypeTraits_test.cpp \
- Utils_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libstagefright_foundation \
- libutils \
-
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/media/libstagefright/foundation/tests/OpusHeader/Android.bp b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
new file mode 100644
index 0000000..c1251a8
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "OpusHeaderTest",
+ gtest: true,
+
+ srcs: [
+ "OpusHeaderTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_foundation",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml b/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml
new file mode 100644
index 0000000..afee16a
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for opus header unit tests">
+ <option name="test-suite-tag" value="OpusHeaderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="OpusHeaderTest->/data/local/tmp/OpusHeaderTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/OpusHeader/OpusHeader.zip?unzip=true"
+ value="/data/local/tmp/OpusHeaderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="OpusHeaderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/OpusHeaderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
new file mode 100644
index 0000000..d142781
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpusHeaderTest"
+#include <utils/Log.h>
+
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/OpusHeader.h>
+
+#include "OpusHeaderTestEnvironment.h"
+
+using namespace android;
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/OpusOutput"
+
+// Opus in WebM is a well-known, yet under-documented, format. The codec private data
+// of the track is an Opus Ogg header (https://tools.ietf.org/html/rfc7845#section-5.1)
+// channel mapping offset in opus header
+constexpr size_t kOpusHeaderStreamMapOffset = 21;
+constexpr size_t kMaxOpusHeaderSize = 100;
+// AOPUSHDR + AOPUSHDRLength +
+// (8 + 8 ) +
+// Header(csd) + num_streams + num_coupled + 1
+// (19 + 1 + 1 + 1) +
+// AOPUSDLY + AOPUSDLYLength + DELAY + AOPUSPRL + AOPUSPRLLength + PRL
+// (8 + 8 + 8 + 8 + 8 + 8)
+// = 86
+constexpr size_t kOpusHeaderChannelMapOffset = 86;
+constexpr uint32_t kOpusSampleRate = 48000;
+constexpr uint64_t kOpusSeekPrerollNs = 80000000;
+constexpr int64_t kNsecPerSec = 1000000000ll;
+
+// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+constexpr int kMaxChannels = 8;
+constexpr uint8_t kOpusChannelMap[kMaxChannels][kMaxChannels] = {
+ {0},
+ {0, 1},
+ {0, 2, 1},
+ {0, 1, 2, 3},
+ {0, 4, 1, 2, 3},
+ {0, 4, 1, 2, 3, 5},
+ {0, 4, 1, 2, 3, 5, 6},
+ {0, 6, 1, 2, 3, 4, 5, 7},
+};
+
+static OpusHeaderTestEnvironment *gEnv = nullptr;
+
+class OpusHeaderTest {
+ public:
+ OpusHeaderTest() : mInputBuffer(nullptr) {}
+
+ ~OpusHeaderTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mInputBuffer) {
+ free(mInputBuffer);
+ mInputBuffer = nullptr;
+ }
+ }
+ ifstream mEleStream;
+ uint8_t *mInputBuffer;
+};
+
+class OpusHeaderParseTest : public OpusHeaderTest,
+ public ::testing::TestWithParam<
+ tuple<string /* InputFileName */, int32_t /* ChannelCount */,
+ bool /* isHeaderValid */, bool /* isCodecDelayValid */,
+ bool /* isSeekPreRollValid */, bool /* isInputValid */>> {
+};
+
+class OpusHeaderWriteTest
+ : public OpusHeaderTest,
+ public ::testing::TestWithParam<pair<int32_t /* ChannelCount */, int32_t /* skipSamples */>> {
+};
+
+TEST_P(OpusHeaderWriteTest, WriteTest) {
+ OpusHeader writtenHeader;
+ memset(&writtenHeader, 0, sizeof(writtenHeader));
+ int32_t channels = GetParam().first;
+ writtenHeader.channels = channels;
+ writtenHeader.num_streams = channels;
+ writtenHeader.channel_mapping = ((channels > 8) ? 255 : (channels > 2));
+ int32_t skipSamples = GetParam().second;
+ writtenHeader.skip_samples = skipSamples;
+ uint64_t codecDelayNs = skipSamples * kNsecPerSec / kOpusSampleRate;
+ uint8_t headerData[kMaxOpusHeaderSize];
+ int32_t headerSize = WriteOpusHeaders(writtenHeader, kOpusSampleRate, headerData,
+ sizeof(headerData), codecDelayNs, kOpusSeekPrerollNs);
+ ASSERT_GT(headerSize, 0) << "failed to generate Opus header";
+ ASSERT_LE(headerSize, kMaxOpusHeaderSize)
+ << "Invalid header written. Header size can't exceed kMaxOpusHeaderSize";
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, ofstream::binary);
+ ASSERT_TRUE(ostrm.is_open()) << "Failed to open output file " << OUTPUT_FILE_NAME;
+
+ // TODO : Validate bitstream (b/150116402)
+ ostrm.write(reinterpret_cast<char *>(headerData), sizeof(headerData));
+ ostrm.close();
+
+ size_t opusHeadSize = 0;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = nullptr;
+ void *codecDelayBuf = nullptr;
+ void *seekPreRollBuf = nullptr;
+ bool status = GetOpusHeaderBuffers(headerData, headerSize, &opusHeadBuf, &opusHeadSize,
+ &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ ASSERT_TRUE(status) << "Encountered error in GetOpusHeaderBuffers";
+
+ uint64_t value = *((uint64_t *)codecDelayBuf);
+ ASSERT_EQ(value, codecDelayNs);
+
+ value = *((uint64_t *)seekPreRollBuf);
+ ASSERT_EQ(value, kOpusSeekPrerollNs);
+
+ OpusHeader parsedHeader;
+ status = ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &parsedHeader);
+ ASSERT_TRUE(status) << "Encountered error while Parsing Opus Header.";
+
+ ASSERT_EQ(writtenHeader.channels, parsedHeader.channels)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(writtenHeader.skip_samples, parsedHeader.skip_samples)
+ << "Mismatch between no of skipSamples written "
+ "and no of skipSamples got after parsing";
+
+ ASSERT_EQ(writtenHeader.channel_mapping, parsedHeader.channel_mapping)
+ << "Mismatch between channelMapping written "
+ "and channelMapping got after parsing";
+
+ if (parsedHeader.channel_mapping) {
+ ASSERT_GT(parsedHeader.channels, 2);
+ ASSERT_EQ(writtenHeader.num_streams, parsedHeader.num_streams)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(writtenHeader.num_coupled, parsedHeader.num_coupled)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(parsedHeader.num_coupled + parsedHeader.num_streams, parsedHeader.channels);
+
+ ASSERT_LE(parsedHeader.num_coupled, parsedHeader.num_streams)
+ << "Invalid header generated. Number of coupled streams cannot be greater than "
+ "number "
+ "of streams.";
+
+ ASSERT_EQ(headerSize, kOpusHeaderChannelMapOffset + writtenHeader.channels)
+ << "Invalid header written. Header size should be equal to 86 + "
+ "writtenHeader.channels";
+
+ uint8_t mappedChannelNumber;
+ for (int32_t channelNumber = 0; channelNumber < channels; channelNumber++) {
+ mappedChannelNumber = *(reinterpret_cast<uint8_t *>(opusHeadBuf) +
+ kOpusHeaderStreamMapOffset + channelNumber);
+ ASSERT_LT(mappedChannelNumber, channels) << "Invalid header generated. Channel mapping "
+ "cannot be greater than channel count.";
+
+ ASSERT_EQ(mappedChannelNumber, kOpusChannelMap[channels - 1][channelNumber])
+ << "Invalid header generated. Channel mapping is not as per specification.";
+ }
+ } else {
+ ASSERT_LE(parsedHeader.channels, 2);
+ }
+}
+
+TEST_P(OpusHeaderParseTest, ParseTest) {
+ tuple<string, int32_t, bool, bool, bool, bool> params = GetParam();
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << get<0>(params);
+ bool isHeaderValid = get<2>(params);
+ bool isCodecDelayValid = get<3>(params);
+ bool isSeekPreRollValid = get<4>(params);
+ bool isInputValid = get<5>(params);
+
+ struct stat buf;
+ stat(inputFileName.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ mInputBuffer = (uint8_t *)malloc(fileSize);
+ ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory. Malloc failed for size " << fileSize;
+
+ mEleStream.read(reinterpret_cast<char *>(mInputBuffer), fileSize);
+ ASSERT_EQ(mEleStream.gcount(), fileSize) << "mEleStream.gcount() != bytesCount";
+
+ OpusHeader header;
+ size_t opusHeadSize = 0;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = nullptr;
+ void *codecDelayBuf = nullptr;
+ void *seekPreRollBuf = nullptr;
+ bool status = GetOpusHeaderBuffers(mInputBuffer, fileSize, &opusHeadBuf, &opusHeadSize,
+ &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ if (!isHeaderValid) {
+ ASSERT_EQ(opusHeadBuf, nullptr);
+ } else {
+ ASSERT_NE(opusHeadBuf, nullptr);
+ }
+ if (!isCodecDelayValid) {
+ ASSERT_EQ(codecDelayBuf, nullptr);
+ } else {
+ ASSERT_NE(codecDelayBuf, nullptr);
+ }
+ if (!isSeekPreRollValid) {
+ ASSERT_EQ(seekPreRollBuf, nullptr);
+ } else {
+ ASSERT_NE(seekPreRollBuf, nullptr);
+ }
+ if (!status) {
+ ASSERT_FALSE(isInputValid) << "GetOpusHeaderBuffers failed";
+ return;
+ }
+
+ status = ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &header);
+
+ if (status) {
+ ASSERT_TRUE(isInputValid) << "Parse opus header didn't fail for invalid input";
+ } else {
+ ASSERT_FALSE(isInputValid);
+ return;
+ }
+
+ int32_t channels = get<1>(params);
+ ASSERT_EQ(header.channels, channels) << "Parser returned invalid channel count";
+ ASSERT_LE(header.channels, kMaxChannels);
+
+ ASSERT_LE(header.num_coupled, header.num_streams)
+ << "Invalid header generated. Number of coupled streams cannot be greater than number "
+ "of streams.";
+
+ ASSERT_EQ(header.num_coupled + header.num_streams, header.channels);
+
+ if (header.channel_mapping) {
+ uint8_t mappedChannelNumber;
+ for (int32_t channelNumber = 0; channelNumber < channels; channelNumber++) {
+ mappedChannelNumber = *(reinterpret_cast<uint8_t *>(opusHeadBuf) +
+ kOpusHeaderStreamMapOffset + channelNumber);
+ ASSERT_LT(mappedChannelNumber, channels)
+ << "Invalid header. Channel mapping cannot be greater than channel count.";
+
+ ASSERT_EQ(mappedChannelNumber, kOpusChannelMap[channels - 1][channelNumber])
+ << "Invalid header generated. Channel mapping "
+ "is not as per specification.";
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(OpusHeaderTestAll, OpusHeaderWriteTest,
+ ::testing::Values(make_pair(1, 312),
+ make_pair(2, 312),
+ make_pair(5, 312),
+ make_pair(6, 312),
+ make_pair(1, 0),
+ make_pair(2, 0),
+ make_pair(5, 0),
+ make_pair(6, 0),
+ make_pair(1, 624),
+ make_pair(2, 624),
+ make_pair(5, 624),
+ make_pair(6, 624)));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpusHeaderTestAll, OpusHeaderParseTest,
+ ::testing::Values(
+ make_tuple("2ch_valid_size83B.opus", 2, true, true, true, true),
+ make_tuple("3ch_valid_size88B.opus", 3, true, true, true, true),
+ make_tuple("5ch_valid.opus", 5, true, false, false, true),
+ make_tuple("6ch_valid.opus", 6, true, false, false, true),
+ make_tuple("1ch_valid.opus", 1, true, false, false, true),
+ make_tuple("2ch_valid.opus", 2, true, false, false, true),
+ make_tuple("3ch_invalid_size.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_streams.opus", 3, true, true, true, false),
+ make_tuple("5ch_invalid_channelmapping.opus", 5, true, false, false, false),
+ make_tuple("5ch_invalid_coupledstreams.opus", 5, true, false, false, false),
+ make_tuple("6ch_invalid_channelmapping.opus", 6, true, false, false, false),
+ make_tuple("9ch_invalid_channels.opus", 9, true, true, true, false),
+ make_tuple("2ch_invalid_header.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_headerlength_16.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_headerlength_256.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_size.opus", 2, false, false, false, false),
+ make_tuple("3ch_invalid_channelmapping_0.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_coupledstreams.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_headerlength.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_headerSize1.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_headerSize2.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_headerSize3.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_nodelay.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_nopreroll.opus", 3, false, false, false, false)));
+
+int main(int argc, char **argv) {
+ gEnv = new OpusHeaderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD("Opus Header Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h
new file mode 100644
index 0000000..d0163c3
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OPUS_HEADER_TEST_ENVIRONMENT_H__
+#define __OPUS_HEADER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class OpusHeaderTestEnvironment : public ::testing::Environment {
+ public:
+ OpusHeaderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int OpusHeaderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __OPUS_HEADER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/foundation/tests/OpusHeader/README.md b/media/libstagefright/foundation/tests/OpusHeader/README.md
new file mode 100644
index 0000000..860c827
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### Opus Header
+The OpusHeader Test Suite validates the OPUS header available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m OpusHeaderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/OpusHeaderTest/OpusHeaderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/OpusHeaderTest/OpusHeaderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/OpusHeader/OpusHeader.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push OpusHeader /data/local/tmp/
+```
+
+usage: OpusHeaderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/OpusHeaderTest -P /data/local/tmp/OpusHeader/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest OpusHeaderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index 409f141..ff063e3 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -243,6 +243,10 @@
sp<MetaData> trackMeta = new MetaData;
convertMessageToMetaData(format, trackMeta);
mCurrentTrack = new MediaAdapter(trackMeta);
+ if (mCurrentTrack == nullptr) {
+ ALOGE("MediaAdapter returned nullptr");
+ return -1;
+ }
status_t result = mWriter->addSource(mCurrentTrack);
return result;
}
@@ -301,7 +305,7 @@
getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
- getInputBufferInfo(inputFile, inputInfo);
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
status = addWriterSource(isAudio, param);
ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
@@ -338,7 +342,7 @@
getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
- getInputBufferInfo(inputFile, inputInfo);
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
status = addWriterSource(isAudio, param);
ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
@@ -397,7 +401,7 @@
getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
- getInputBufferInfo(inputFile, inputInfo);
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
status = addWriterSource(isAudio, param);
ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 10a9c63..9e36f77 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -29,6 +29,7 @@
#include <string>
#include <sys/time.h>
#include <sys/resource.h>
+#include <thread>
#include <android/os/IExternalVibratorService.h>
#include <binder/IPCThreadState.h>
@@ -143,6 +144,19 @@
return sExternalVibratorService;
}
+class DevicesFactoryHalCallbackImpl : public DevicesFactoryHalCallback {
+ public:
+ void onNewDevicesAvailable() override {
+ // Start a detached thread to execute notification in parallel.
+ // This is done to prevent mutual blocking of audio_flinger and
+ // audio_policy services during system initialization.
+ std::thread notifier([]() {
+ AudioSystem::onNewAudioModulesAvailable();
+ });
+ notifier.detach();
+ }
+};
+
// ----------------------------------------------------------------------------
std::string formatToString(audio_format_t format) {
@@ -221,6 +235,9 @@
mMode = AUDIO_MODE_NORMAL;
gAudioFlinger = this;
+
+ mDevicesFactoryHalCallback = new DevicesFactoryHalCallbackImpl;
+ mDevicesFactoryHal->setCallbackOnce(mDevicesFactoryHalCallback);
}
status_t AudioFlinger::setAudioHalPids(const std::vector<pid_t>& pids) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a43a6dc..115bbb8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -103,6 +103,7 @@
class AudioBuffer;
class AudioResampler;
class DeviceHalInterface;
+class DevicesFactoryHalCallback;
class DevicesFactoryHalInterface;
class EffectsFactoryHalInterface;
class FastMixer;
@@ -827,6 +828,7 @@
DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*> mAudioHwDevs;
sp<DevicesFactoryHalInterface> mDevicesFactoryHal;
+ sp<DevicesFactoryHalCallback> mDevicesFactoryHalCallback;
// for dump, indicates which hardware operation is currently in progress (but not stream ops)
enum hardware_call_state {
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 1fe60d4..9676d09 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -75,6 +75,10 @@
// configuration functions
//
+ // Informs APM that new HAL modules are available. This typically happens
+ // due to registration of an audio HAL service.
+ virtual void onNewAudioModulesAvailable() = 0;
+
// indicate a change in device connection status
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index 56596f5..47c6e68 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -37,13 +37,13 @@
{
public:
AudioPolicyConfig(HwModuleCollection &hwModules,
- DeviceVector &availableOutputDevices,
- DeviceVector &availableInputDevices,
+ DeviceVector &outputDevices,
+ DeviceVector &inputDevices,
sp<DeviceDescriptor> &defaultOutputDevice)
: mEngineLibraryNameSuffix(kDefaultEngineLibraryNameSuffix),
mHwModules(hwModules),
- mAvailableOutputDevices(availableOutputDevices),
- mAvailableInputDevices(availableInputDevices),
+ mOutputDevices(outputDevices),
+ mInputDevices(inputDevices),
mDefaultOutputDevice(defaultOutputDevice),
mIsSpeakerDrcEnabled(false)
{}
@@ -69,23 +69,23 @@
mHwModules = hwModules;
}
- void addAvailableDevice(const sp<DeviceDescriptor> &availableDevice)
+ void addDevice(const sp<DeviceDescriptor> &device)
{
- if (audio_is_output_device(availableDevice->type())) {
- mAvailableOutputDevices.add(availableDevice);
- } else if (audio_is_input_device(availableDevice->type())) {
- mAvailableInputDevices.add(availableDevice);
+ if (audio_is_output_device(device->type())) {
+ mOutputDevices.add(device);
+ } else if (audio_is_input_device(device->type())) {
+ mInputDevices.add(device);
}
}
- void addAvailableInputDevices(const DeviceVector &availableInputDevices)
+ void addInputDevices(const DeviceVector &inputDevices)
{
- mAvailableInputDevices.add(availableInputDevices);
+ mInputDevices.add(inputDevices);
}
- void addAvailableOutputDevices(const DeviceVector &availableOutputDevices)
+ void addOutputDevices(const DeviceVector &outputDevices)
{
- mAvailableOutputDevices.add(availableOutputDevices);
+ mOutputDevices.add(outputDevices);
}
bool isSpeakerDrcEnabled() const { return mIsSpeakerDrcEnabled; }
@@ -97,14 +97,14 @@
const HwModuleCollection getHwModules() const { return mHwModules; }
- const DeviceVector &getAvailableInputDevices() const
+ const DeviceVector &getInputDevices() const
{
- return mAvailableInputDevices;
+ return mInputDevices;
}
- const DeviceVector &getAvailableOutputDevices() const
+ const DeviceVector &getOutputDevices() const
{
- return mAvailableOutputDevices;
+ return mOutputDevices;
}
void setDefaultOutputDevice(const sp<DeviceDescriptor> &defaultDevice)
@@ -125,13 +125,11 @@
sp<AudioProfile> micProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000);
defaultInputDevice->addAudioProfile(micProfile);
- mAvailableOutputDevices.add(mDefaultOutputDevice);
- mAvailableInputDevices.add(defaultInputDevice);
+ mOutputDevices.add(mDefaultOutputDevice);
+ mInputDevices.add(defaultInputDevice);
sp<HwModule> module = new HwModule(AUDIO_HARDWARE_MODULE_ID_PRIMARY, 2 /*halVersionMajor*/);
mHwModules.add(module);
- mDefaultOutputDevice->attach(module);
- defaultInputDevice->attach(module);
sp<OutputProfile> outProfile = new OutputProfile("primary");
outProfile->addAudioProfile(
@@ -182,8 +180,8 @@
std::string mSource;
std::string mEngineLibraryNameSuffix;
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
- DeviceVector &mAvailableOutputDevices;
- DeviceVector &mAvailableInputDevices;
+ DeviceVector &mOutputDevices;
+ DeviceVector &mInputDevices;
sp<DeviceDescriptor> &mDefaultOutputDevice;
// TODO: remove when legacy conf file is removed. true on devices that use DRC on the
// DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly.
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index f0bb28e..b8e1cbd 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -652,7 +652,7 @@
sp<DeviceDescriptor> device = module->getDeclaredDevices().
getDeviceFromTagName(std::string(reinterpret_cast<const char*>(
attachedDevice.get())));
- ctx->addAvailableDevice(device);
+ ctx->addDevice(device);
}
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index b747dd6..8de8ecd 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4340,7 +4340,7 @@
mpClientInterface(clientInterface),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
- mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice),
+ mConfig(mHwModulesAll, mOutputDevicesAll, mInputDevicesAll, mDefaultOutputDevice),
mAudioPortGeneration(1),
mBeaconMuteRefCount(0),
mBeaconPlayingRefCount(0),
@@ -4385,141 +4385,9 @@
return status;
}
- // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
+ // after parsing the config, mOutputDevicesAll and mInputDevicesAll contain all known devices;
// open all output streams needed to access attached devices
- for (const auto& hwModule : mHwModulesAll) {
- hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
- if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
- ALOGW("could not open HW module %s", hwModule->getName());
- continue;
- }
- mHwModules.push_back(hwModule);
- // open all output streams needed to access attached devices
- // except for direct output streams that are only opened when they are actually
- // required by an app.
- // This also validates mAvailableOutputDevices list
- for (const auto& outProfile : hwModule->getOutputProfiles()) {
- if (!outProfile->canOpenNewIo()) {
- ALOGE("Invalid Output profile max open count %u for profile %s",
- outProfile->maxOpenCount, outProfile->getTagName().c_str());
- continue;
- }
- if (!outProfile->hasSupportedDevices()) {
- ALOGW("Output profile contains no device on module %s", hwModule->getName());
- continue;
- }
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_TTS) != 0) {
- mTtsOutputAvailable = true;
- }
-
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
- continue;
- }
- const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
- DeviceVector availProfileDevices = supportedDevices.filter(mAvailableOutputDevices);
- sp<DeviceDescriptor> supportedDevice = 0;
- if (supportedDevices.contains(mDefaultOutputDevice)) {
- supportedDevice = mDefaultOutputDevice;
- } else {
- // choose first device present in profile's SupportedDevices also part of
- // mAvailableOutputDevices.
- if (availProfileDevices.isEmpty()) {
- continue;
- }
- supportedDevice = availProfileDevices.itemAt(0);
- }
- if (!mAvailableOutputDevices.contains(supportedDevice)) {
- continue;
- }
- sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
- mpClientInterface);
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),
- AUDIO_STREAM_DEFAULT,
- AUDIO_OUTPUT_FLAG_NONE, &output);
- if (status != NO_ERROR) {
- ALOGW("Cannot open output stream for devices %s on hw module %s",
- supportedDevice->toString().c_str(), hwModule->getName());
- continue;
- }
- for (const auto &device : availProfileDevices) {
- // give a valid ID to an attached device once confirmed it is reachable
- if (!device->isAttached()) {
- device->attach(hwModule);
- }
- }
- if (mPrimaryOutput == 0 &&
- outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
- mPrimaryOutput = outputDesc;
- }
- addOutput(output, outputDesc);
- setOutputDevices(outputDesc,
- DeviceVector(supportedDevice),
- true,
- 0,
- NULL);
- }
- // open input streams needed to access attached devices to validate
- // mAvailableInputDevices list
- for (const auto& inProfile : hwModule->getInputProfiles()) {
- if (!inProfile->canOpenNewIo()) {
- ALOGE("Invalid Input profile max open count %u for profile %s",
- inProfile->maxOpenCount, inProfile->getTagName().c_str());
- continue;
- }
- if (!inProfile->hasSupportedDevices()) {
- ALOGW("Input profile contains no device on module %s", hwModule->getName());
- continue;
- }
- // chose first device present in profile's SupportedDevices also part of
- // available input devices
- const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
- DeviceVector availProfileDevices = supportedDevices.filter(mAvailableInputDevices);
- if (availProfileDevices.isEmpty()) {
- ALOGE("%s: Input device list is empty!", __FUNCTION__);
- continue;
- }
- sp<AudioInputDescriptor> inputDesc =
- new AudioInputDescriptor(inProfile, mpClientInterface);
-
- audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = inputDesc->open(nullptr,
- availProfileDevices.itemAt(0),
- AUDIO_SOURCE_MIC,
- AUDIO_INPUT_FLAG_NONE,
- &input);
- if (status != NO_ERROR) {
- ALOGW("Cannot open input stream for device %s on hw module %s",
- availProfileDevices.toString().c_str(),
- hwModule->getName());
- continue;
- }
- for (const auto &device : availProfileDevices) {
- // give a valid ID to an attached device once confirmed it is reachable
- if (!device->isAttached()) {
- device->attach(hwModule);
- device->importAudioPortAndPickAudioProfile(inProfile, true);
- }
- }
- inputDesc->close();
- }
- }
- // make sure all attached devices have been allocated a unique ID
- auto checkAndSetAvailable = [this](auto& devices) {
- for (size_t i = 0; i < devices.size();) {
- const auto &device = devices[i];
- if (!device->isAttached()) {
- ALOGW("device %s is unreachable", device->toString().c_str());
- devices.remove(device);
- continue;
- }
- // Device is now validated and can be appended to the available devices of the engine
- setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
- i++;
- }
- };
- checkAndSetAvailable(mAvailableOutputDevices);
- checkAndSetAvailable(mAvailableInputDevices);
+ onNewAudioModulesAvailable();
// make sure default device is reachable
if (mDefaultOutputDevice == 0 || !mAvailableOutputDevices.contains(mDefaultOutputDevice)) {
@@ -4574,6 +4442,134 @@
// ---
+void AudioPolicyManager::onNewAudioModulesAvailable()
+{
+ for (const auto& hwModule : mHwModulesAll) {
+ if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {
+ continue;
+ }
+ hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
+ if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
+ ALOGW("could not open HW module %s", hwModule->getName());
+ continue;
+ }
+ mHwModules.push_back(hwModule);
+ // open all output streams needed to access attached devices
+ // except for direct output streams that are only opened when they are actually
+ // required by an app.
+ // This also validates mAvailableOutputDevices list
+ for (const auto& outProfile : hwModule->getOutputProfiles()) {
+ if (!outProfile->canOpenNewIo()) {
+ ALOGE("Invalid Output profile max open count %u for profile %s",
+ outProfile->maxOpenCount, outProfile->getTagName().c_str());
+ continue;
+ }
+ if (!outProfile->hasSupportedDevices()) {
+ ALOGW("Output profile contains no device on module %s", hwModule->getName());
+ continue;
+ }
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_TTS) != 0) {
+ mTtsOutputAvailable = true;
+ }
+
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ continue;
+ }
+ const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
+ DeviceVector availProfileDevices = supportedDevices.filter(mOutputDevicesAll);
+ sp<DeviceDescriptor> supportedDevice = 0;
+ if (supportedDevices.contains(mDefaultOutputDevice)) {
+ supportedDevice = mDefaultOutputDevice;
+ } else {
+ // choose first device present in profile's SupportedDevices also part of
+ // mAvailableOutputDevices.
+ if (availProfileDevices.isEmpty()) {
+ continue;
+ }
+ supportedDevice = availProfileDevices.itemAt(0);
+ }
+ if (!mOutputDevicesAll.contains(supportedDevice)) {
+ continue;
+ }
+ sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+ mpClientInterface);
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),
+ AUDIO_STREAM_DEFAULT,
+ AUDIO_OUTPUT_FLAG_NONE, &output);
+ if (status != NO_ERROR) {
+ ALOGW("Cannot open output stream for devices %s on hw module %s",
+ supportedDevice->toString().c_str(), hwModule->getName());
+ continue;
+ }
+ for (const auto &device : availProfileDevices) {
+ // give a valid ID to an attached device once confirmed it is reachable
+ if (!device->isAttached()) {
+ device->attach(hwModule);
+ mAvailableOutputDevices.add(device);
+ setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
+ }
+ }
+ if (mPrimaryOutput == 0 &&
+ outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
+ mPrimaryOutput = outputDesc;
+ }
+ addOutput(output, outputDesc);
+ setOutputDevices(outputDesc,
+ DeviceVector(supportedDevice),
+ true,
+ 0,
+ NULL);
+ }
+ // open input streams needed to access attached devices to validate
+ // mAvailableInputDevices list
+ for (const auto& inProfile : hwModule->getInputProfiles()) {
+ if (!inProfile->canOpenNewIo()) {
+ ALOGE("Invalid Input profile max open count %u for profile %s",
+ inProfile->maxOpenCount, inProfile->getTagName().c_str());
+ continue;
+ }
+ if (!inProfile->hasSupportedDevices()) {
+ ALOGW("Input profile contains no device on module %s", hwModule->getName());
+ continue;
+ }
+ // chose first device present in profile's SupportedDevices also part of
+ // available input devices
+ const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
+ DeviceVector availProfileDevices = supportedDevices.filter(mInputDevicesAll);
+ if (availProfileDevices.isEmpty()) {
+ ALOGE("%s: Input device list is empty!", __FUNCTION__);
+ continue;
+ }
+ sp<AudioInputDescriptor> inputDesc =
+ new AudioInputDescriptor(inProfile, mpClientInterface);
+
+ audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+ status_t status = inputDesc->open(nullptr,
+ availProfileDevices.itemAt(0),
+ AUDIO_SOURCE_MIC,
+ AUDIO_INPUT_FLAG_NONE,
+ &input);
+ if (status != NO_ERROR) {
+ ALOGW("Cannot open input stream for device %s on hw module %s",
+ availProfileDevices.toString().c_str(),
+ hwModule->getName());
+ continue;
+ }
+ for (const auto &device : availProfileDevices) {
+ // give a valid ID to an attached device once confirmed it is reachable
+ if (!device->isAttached()) {
+ device->attach(hwModule);
+ device->importAudioPortAndPickAudioProfile(inProfile, true);
+ mAvailableInputDevices.add(device);
+ setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
+ }
+ }
+ inputDesc->close();
+ }
+ }
+}
+
void AudioPolicyManager::addOutput(audio_io_handle_t output,
const sp<SwAudioOutputDescriptor>& outputDesc)
{
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 634eb31..4604676 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -315,6 +315,8 @@
return volumeGroup != VOLUME_GROUP_NONE ? NO_ERROR : BAD_VALUE;
}
+ void onNewAudioModulesAvailable() override;
+
status_t initialize();
protected:
@@ -717,6 +719,8 @@
SwAudioOutputCollection mPreviousOutputs;
AudioInputCollection mInputs; // list of input descriptors
+ DeviceVector mOutputDevicesAll; // all output devices from the config
+ DeviceVector mInputDevicesAll; // all input devices from the config
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
@@ -727,9 +731,8 @@
EffectDescriptorCollection mEffects; // list of registered audio effects
sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time
- HwModuleCollection mHwModules; // contains only modules that have been loaded successfully
- HwModuleCollection mHwModulesAll; // normally not needed, used during construction and for
- // dumps
+ HwModuleCollection mHwModules; // contains modules that have been loaded successfully
+ HwModuleCollection mHwModulesAll; // contains all modules declared in the config
AudioPolicyConfig mConfig;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index c1190be..227adc7 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -28,6 +28,14 @@
// ----------------------------------------------------------------------------
+void AudioPolicyService::doOnNewAudioModulesAvailable()
+{
+ if (mAudioPolicyManager == NULL) return;
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ mAudioPolicyManager->onNewAudioModulesAvailable();
+}
+
status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 90939ce..3d9278b 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -1278,6 +1278,16 @@
mLock.lock();
}
} break;
+ case AUDIO_MODULES_UPDATE: {
+ ALOGV("AudioCommandThread() processing audio modules update");
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnNewAudioModulesAvailable();
+ mLock.lock();
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
@@ -1567,6 +1577,13 @@
sendCommand(command);
}
+void AudioPolicyService::AudioCommandThread::audioModulesUpdateCommand()
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = AUDIO_MODULES_UPDATE;
+ sendCommand(command);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
@@ -1814,6 +1831,11 @@
mAudioCommandThread->setEffectSuspendedCommand(effectId, sessionId, suspended);
}
+void AudioPolicyService::onNewAudioModulesAvailable()
+{
+ mAudioCommandThread->audioModulesUpdateCommand();
+}
+
extern "C" {
audio_module_handle_t aps_load_hw_module(void *service __unused,
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 7b72dc1..0370438 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -60,6 +60,7 @@
// BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
//
+ void onNewAudioModulesAvailable() override;
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
@@ -268,6 +269,7 @@
virtual status_t setRttEnabled(bool enabled);
+ void doOnNewAudioModulesAvailable();
status_t doStopOutput(audio_port_handle_t portId);
void doReleaseOutput(audio_port_handle_t portId);
@@ -450,6 +452,7 @@
DYN_POLICY_MIX_STATE_UPDATE,
RECORDING_CONFIGURATION_UPDATE,
SET_EFFECT_SUSPENDED,
+ AUDIO_MODULES_UPDATE,
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -495,6 +498,7 @@
void setEffectSuspendedCommand(int effectId,
audio_session_t sessionId,
bool suspended);
+ void audioModulesUpdateCommand();
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
class AudioCommandData;
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index c2a92d7..af69466 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -15,6 +15,7 @@
*/
#include <map>
+#include <set>
#include <system/audio.h>
#include <utils/Log.h>
@@ -27,7 +28,10 @@
class AudioPolicyManagerTestClient : public AudioPolicyTestClient {
public:
// AudioPolicyClientInterface implementation
- audio_module_handle_t loadHwModule(const char * /*name*/) override {
+ audio_module_handle_t loadHwModule(const char* name) override {
+ if (!mAllowedModuleNames.empty() && !mAllowedModuleNames.count(name)) {
+ return AUDIO_MODULE_HANDLE_NONE;
+ }
return mNextModuleHandle++;
}
@@ -101,11 +105,18 @@
return &it->second;
};
+ audio_module_handle_t peekNextModuleHandle() const { return mNextModuleHandle; }
+
+ void swapAllowedModuleNames(std::set<std::string>&& names = {}) {
+ mAllowedModuleNames.swap(names);
+ }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
audio_patch_handle_t mNextPatchHandle = AUDIO_PATCH_HANDLE_NONE + 1;
std::map<audio_patch_handle_t, struct audio_patch> mActivePatches;
+ std::set<std::string> mAllowedModuleNames;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index c77dcdc..e8c2d33 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -26,6 +26,8 @@
using AudioPolicyManager::getConfig;
using AudioPolicyManager::loadConfig;
using AudioPolicyManager::initialize;
+ using AudioPolicyManager::getAvailableOutputDevices;
+ using AudioPolicyManager::getAvailableInputDevices;
};
} // namespace android
diff --git a/services/audiopolicy/tests/audio_health_tests.cpp b/services/audiopolicy/tests/audio_health_tests.cpp
index 8736cf1..b5c67a1 100644
--- a/services/audiopolicy/tests/audio_health_tests.cpp
+++ b/services/audiopolicy/tests/audio_health_tests.cpp
@@ -67,10 +67,10 @@
manager.loadConfig();
ASSERT_NE("AudioPolicyConfig::setDefault", manager.getConfig().getSource());
- for (auto desc : manager.getConfig().getAvailableInputDevices()) {
+ for (auto desc : manager.getConfig().getInputDevices()) {
ASSERT_NE(attachedDevices.end(), attachedDevices.find(desc->type()));
}
- for (auto desc : manager.getConfig().getAvailableOutputDevices()) {
+ for (auto desc : manager.getConfig().getOutputDevices()) {
ASSERT_NE(attachedDevices.end(), attachedDevices.find(desc->type()));
}
}
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 0263597..acd61ed 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -292,9 +292,9 @@
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
uid_t uid = 42;
const PatchCountCheck patchCount = snapshotPatchCount();
- ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty());
+ ASSERT_FALSE(mManager->getAvailableInputDevices().isEmpty());
PatchBuilder patchBuilder;
- patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]).
+ patchBuilder.addSource(mManager->getAvailableInputDevices()[0]).
addSink(mManager->getConfig().getDefaultOutputDevice());
ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid));
ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle);
@@ -328,15 +328,13 @@
sp<AudioProfile> pcmInputProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, 44100);
mMsdInputDevice->addAudioProfile(pcmInputProfile);
- config.addAvailableDevice(mMsdOutputDevice);
- config.addAvailableDevice(mMsdInputDevice);
+ config.addDevice(mMsdOutputDevice);
+ config.addDevice(mMsdInputDevice);
sp<HwModule> msdModule = new HwModule(AUDIO_HARDWARE_MODULE_ID_MSD, 2 /*halVersionMajor*/);
HwModuleCollection modules = config.getHwModules();
modules.add(msdModule);
config.setHwModules(modules);
- mMsdOutputDevice->attach(msdModule);
- mMsdInputDevice->attach(msdModule);
sp<OutputProfile> msdOutputProfile = new OutputProfile("msd input");
msdOutputProfile->addAudioProfile(pcmOutputProfile);
@@ -1058,3 +1056,40 @@
"hfp_client_out"})
)
);
+
+class AudioPolicyManagerDynamicHwModulesTest : public AudioPolicyManagerTestWithConfigurationFile {
+protected:
+ void SetUpManagerConfig() override;
+};
+
+void AudioPolicyManagerDynamicHwModulesTest::SetUpManagerConfig() {
+ AudioPolicyManagerTestWithConfigurationFile::SetUpManagerConfig();
+ // Only allow successful opening of "primary" hw module during APM initialization.
+ mClient->swapAllowedModuleNames({"primary"});
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, DynamicAddition) {
+ const auto handleBefore = mClient->peekNextModuleHandle();
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(handleBefore, mClient->peekNextModuleHandle());
+ // Reset module loading restrictions.
+ mClient->swapAllowedModuleNames();
+ mManager->onNewAudioModulesAvailable();
+ const auto handleAfter = mClient->peekNextModuleHandle();
+ ASSERT_GT(handleAfter, handleBefore);
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(handleAfter, mClient->peekNextModuleHandle());
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, AddedDeviceAvailable) {
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, mManager->getDeviceConnectionState(
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0"));
+ mClient->swapAllowedModuleNames({"primary", "r_submix"});
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_AVAILABLE, mManager->getDeviceConnectionState(
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0"));
+}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1283148..4e04f0e 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -50,6 +50,7 @@
#include <string>
#include <map>
#include <memory>
+#include <optional>
#include <utility>
#include <unordered_map>
#include <unordered_set>
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index c7a4f2b..3587db3 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -2004,6 +2004,15 @@
}
}
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ auto ret = mCompositeStreamMap.valueAt(i)->deleteInternalStreams();
+ if (ret != OK) {
+ ALOGE("%s: Failed removing composite stream %s (%d)", __FUNCTION__,
+ strerror(-ret), ret);
+ }
+ }
+ mCompositeStreamMap.clear();
+
Camera2ClientBase::detachDevice();
}
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 8ebaa2b..0b91016 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -247,7 +247,7 @@
return ret;
}
-status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) {
+status_t DepthCompositeStream::processInputFrame(nsecs_t ts, const InputFrame &inputFrame) {
status_t res;
sp<ANativeWindow> outputANW = mOutputSurface;
ANativeWindowBuffer *anb;
@@ -370,6 +370,13 @@
return NO_MEMORY;
}
+ res = native_window_set_buffers_timestamp(mOutputSurface.get(), ts);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", __FUNCTION__,
+ getStreamId(), strerror(-res), res);
+ return res;
+ }
+
ALOGV("%s: Final jpeg size: %zu", __func__, finalJpegSize);
uint8_t* header = static_cast<uint8_t *> (dstBuffer) +
(gb->getWidth() - sizeof(struct camera3_jpeg_blob));
@@ -459,7 +466,7 @@
}
}
- auto res = processInputFrame(mPendingInputFrames[currentTs]);
+ auto res = processInputFrame(currentTs, mPendingInputFrames[currentTs]);
Mutex::Autolock l(mMutex);
if (res != OK) {
ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)", __FUNCTION__,
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 975c59b..28a7826 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -97,7 +97,7 @@
size_t maxJpegSize, uint8_t jpegQuality,
std::vector<std::unique_ptr<Item>>* items /*out*/);
std::unique_ptr<ImagingModel> getImagingModel();
- status_t processInputFrame(const InputFrame &inputFrame);
+ status_t processInputFrame(nsecs_t ts, const InputFrame &inputFrame);
// Buffer/Results handling
void compilePendingInputLocked();
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index d21641c..9790e32 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -31,7 +31,6 @@
#include <mediadrm/ICrypto.h>
#include <media/MediaCodecBuffer.h>
#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/MediaCodecConstants.h>
@@ -61,12 +60,13 @@
mUseGrid(false),
mAppSegmentStreamId(-1),
mAppSegmentSurfaceId(-1),
- mAppSegmentBufferAcquired(false),
mMainImageStreamId(-1),
mMainImageSurfaceId(-1),
mYuvBufferAcquired(false),
mProducerListener(new ProducerListener()),
- mOutputBufferCounter(0),
+ mDequeuedOutputBufferCnt(0),
+ mLockedAppSegmentBufferCnt(0),
+ mCodecOutputCounter(0),
mGridTimestampUs(0) {
}
@@ -132,7 +132,7 @@
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
- mAppSegmentConsumer = new CpuConsumer(consumer, 1);
+ mAppSegmentConsumer = new CpuConsumer(consumer, kMaxAcquiredAppSegment);
mAppSegmentConsumer->setFrameAvailableListener(this);
mAppSegmentConsumer->setName(String8("Camera3-HeicComposite-AppSegmentStream"));
mAppSegmentSurface = new Surface(producer);
@@ -231,6 +231,8 @@
if (bufferInfo.mError) return;
mCodecOutputBufferTimestamps.push(bufferInfo.mTimestamp);
+ ALOGV("%s: [%" PRId64 "]: Adding codecOutputBufferTimestamp (%zu timestamps in total)",
+ __FUNCTION__, bufferInfo.mTimestamp, mCodecOutputBufferTimestamps.size());
}
// We need to get the settings early to handle the case where the codec output
@@ -361,6 +363,8 @@
mCodecOutputBuffers.push_back(outputBufferInfo);
mInputReadyCondition.signal();
} else {
+ ALOGV("%s: Releasing output buffer: size %d flags: 0x%x ", __FUNCTION__,
+ outputBufferInfo.size, outputBufferInfo.flags);
mCodec->releaseOutputBuffer(outputBufferInfo.index);
}
} else {
@@ -414,8 +418,10 @@
mNumOutputTiles = 1;
}
- ALOGV("%s: mNumOutputTiles is %zu", __FUNCTION__, mNumOutputTiles);
mFormat = newFormat;
+
+ ALOGV("%s: mNumOutputTiles is %zu", __FUNCTION__, mNumOutputTiles);
+ mInputReadyCondition.signal();
}
void HeicCompositeStream::onHeicCodecError() {
@@ -459,9 +465,8 @@
// Cannot use SourceSurface buffer count since it could be codec's 512*512 tile
// buffer count.
- int maxProducerBuffers = 1;
if ((res = native_window_set_buffer_count(
- anwConsumer, maxProducerBuffers + maxConsumerBuffers)) != OK) {
+ anwConsumer, kMaxOutputSurfaceProducerCount + maxConsumerBuffers)) != OK) {
ALOGE("%s: Unable to set buffer count for stream %d", __FUNCTION__, mMainImageStreamId);
return res;
}
@@ -505,6 +510,8 @@
}
if (mSettingsByFrameNumber.find(resultExtras.frameNumber) != mSettingsByFrameNumber.end()) {
+ ALOGV("%s: [%" PRId64 "]: frameNumber %" PRId64, __FUNCTION__,
+ timestamp, resultExtras.frameNumber);
mFrameNumberMap.emplace(resultExtras.frameNumber, timestamp);
mSettingsByTimestamp[timestamp] = mSettingsByFrameNumber[resultExtras.frameNumber];
mSettingsByFrameNumber.erase(resultExtras.frameNumber);
@@ -520,12 +527,12 @@
mSettingsByTimestamp.erase(it);
}
- while (!mInputAppSegmentBuffers.empty() && !mAppSegmentBufferAcquired) {
+ while (!mInputAppSegmentBuffers.empty()) {
CpuConsumer::LockedBuffer imgBuffer;
auto it = mInputAppSegmentBuffers.begin();
auto res = mAppSegmentConsumer->lockNextBuffer(&imgBuffer);
if (res == NOT_ENOUGH_DATA) {
- // Canot not lock any more buffers.
+ // Can not lock any more buffers.
break;
} else if ((res != OK) || (*it != imgBuffer.timestamp)) {
if (res != OK) {
@@ -535,6 +542,7 @@
ALOGE("%s: Expecting JPEG_APP_SEGMENTS buffer with time stamp: %" PRId64
" received buffer with time stamp: %" PRId64, __FUNCTION__,
*it, imgBuffer.timestamp);
+ mAppSegmentConsumer->unlockBuffer(imgBuffer);
}
mPendingInputFrames[*it].error = true;
mInputAppSegmentBuffers.erase(it);
@@ -546,7 +554,7 @@
mAppSegmentConsumer->unlockBuffer(imgBuffer);
} else {
mPendingInputFrames[imgBuffer.timestamp].appSegmentBuffer = imgBuffer;
- mAppSegmentBufferAcquired = true;
+ mLockedAppSegmentBufferCnt++;
}
mInputAppSegmentBuffers.erase(it);
}
@@ -556,7 +564,7 @@
auto it = mInputYuvBuffers.begin();
auto res = mMainImageConsumer->lockNextBuffer(&imgBuffer);
if (res == NOT_ENOUGH_DATA) {
- // Canot not lock any more buffers.
+ // Can not lock any more buffers.
break;
} else if (res != OK) {
ALOGE("%s: Error locking YUV_888 image buffer: %s (%d)", __FUNCTION__,
@@ -589,17 +597,20 @@
// to look up timestamp.
int64_t bufferTime = -1;
if (mCodecOutputBufferTimestamps.empty()) {
- ALOGE("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__);
+ ALOGV("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__);
+ break;
} else {
// Direct mapping between camera timestamp (in ns) and codec timestamp (in us).
bufferTime = mCodecOutputBufferTimestamps.front();
- mOutputBufferCounter++;
- if (mOutputBufferCounter == mNumOutputTiles) {
+ mCodecOutputCounter++;
+ if (mCodecOutputCounter == mNumOutputTiles) {
mCodecOutputBufferTimestamps.pop();
- mOutputBufferCounter = 0;
+ mCodecOutputCounter = 0;
}
mPendingInputFrames[bufferTime].codecOutputBuffers.push_back(*it);
+ ALOGV("%s: [%" PRId64 "]: Pushing codecOutputBuffers (time %" PRId64 " us)",
+ __FUNCTION__, bufferTime, it->timeUs);
}
mCodecOutputBuffers.erase(it);
}
@@ -607,6 +618,7 @@
while (!mFrameNumberMap.empty()) {
auto it = mFrameNumberMap.begin();
mPendingInputFrames[it->second].frameNumber = it->first;
+ ALOGV("%s: [%" PRId64 "]: frameNumber is %" PRId64, __FUNCTION__, it->second, it->first);
mFrameNumberMap.erase(it);
}
@@ -675,16 +687,29 @@
}
bool newInputAvailable = false;
- for (const auto& it : mPendingInputFrames) {
+ for (auto& it : mPendingInputFrames) {
+ // New input is considered to be available only if:
+ // 1. input buffers are ready, or
+ // 2. App segment and muxer is created, or
+ // 3. A codec output tile is ready, and an output buffer is available.
+ // This makes sure that muxer gets created only when an output tile is
+ // generated, because right now we only handle 1 HEIC output buffer at a
+ // time (max dequeued buffer count is 1).
bool appSegmentReady = (it.second.appSegmentBuffer.data != nullptr) &&
- !it.second.appSegmentWritten && it.second.result != nullptr;
+ !it.second.appSegmentWritten && it.second.result != nullptr &&
+ it.second.muxer != nullptr;
bool codecOutputReady = !it.second.codecOutputBuffers.empty();
bool codecInputReady = (it.second.yuvBuffer.data != nullptr) &&
(!it.second.codecInputBuffers.empty());
+ bool hasOutputBuffer = it.second.muxer != nullptr ||
+ (mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
if ((!it.second.error) &&
(it.first < *currentTs) &&
- (appSegmentReady || codecOutputReady || codecInputReady)) {
+ (appSegmentReady || (codecOutputReady && hasOutputBuffer) || codecInputReady)) {
*currentTs = it.first;
+ if (it.second.format == nullptr && mFormat != nullptr) {
+ it.second.format = mFormat->dup();
+ }
newInputAvailable = true;
break;
}
@@ -716,15 +741,17 @@
status_t res = OK;
bool appSegmentReady = inputFrame.appSegmentBuffer.data != nullptr &&
- !inputFrame.appSegmentWritten && inputFrame.result != nullptr;
+ !inputFrame.appSegmentWritten && inputFrame.result != nullptr &&
+ inputFrame.muxer != nullptr;
bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0;
bool codecInputReady = inputFrame.yuvBuffer.data != nullptr &&
- !inputFrame.codecInputBuffers.empty();
+ !inputFrame.codecInputBuffers.empty();
+ bool hasOutputBuffer = inputFrame.muxer != nullptr ||
+ (mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
- if (!appSegmentReady && !codecOutputReady && !codecInputReady) {
- ALOGW("%s: No valid appSegmentBuffer/codec input/outputBuffer available!", __FUNCTION__);
- return OK;
- }
+ ALOGV("%s: [%" PRId64 "]: appSegmentReady %d, codecOutputReady %d, codecInputReady %d,"
+ " dequeuedOutputBuffer %d", __FUNCTION__, timestamp, appSegmentReady,
+ codecOutputReady, codecInputReady, mDequeuedOutputBufferCnt);
// Handle inputs for Hevc tiling
if (codecInputReady) {
@@ -736,7 +763,13 @@
}
}
- // Initialize and start muxer if not yet done so
+ if (!(codecOutputReady && hasOutputBuffer) && !appSegmentReady) {
+ return OK;
+ }
+
+ // Initialize and start muxer if not yet done so. In this case,
+ // codecOutputReady must be true. Otherwise, appSegmentReady is guaranteed
+ // to be false, and the function must have returned early.
if (inputFrame.muxer == nullptr) {
res = startMuxerForInputFrame(timestamp, inputFrame);
if (res != OK) {
@@ -747,7 +780,7 @@
}
// Write JPEG APP segments data to the muxer.
- if (appSegmentReady && inputFrame.muxer != nullptr) {
+ if (appSegmentReady) {
res = processAppSegment(timestamp, inputFrame);
if (res != OK) {
ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__,
@@ -766,12 +799,18 @@
}
}
- if (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0) {
- res = processCompletedInputFrame(timestamp, inputFrame);
- if (res != OK) {
- ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__,
- strerror(-res), res);
- return res;
+ if (inputFrame.pendingOutputTiles == 0) {
+ if (inputFrame.appSegmentWritten) {
+ res = processCompletedInputFrame(timestamp, inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ } else if (mLockedAppSegmentBufferCnt == kMaxAcquiredAppSegment) {
+ ALOGE("%s: Out-of-order app segment buffers reaches limit %u", __FUNCTION__,
+ kMaxAcquiredAppSegment);
+ return INVALID_OPERATION;
}
}
@@ -780,11 +819,6 @@
status_t HeicCompositeStream::startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame) {
sp<ANativeWindow> outputANW = mOutputSurface;
- if (inputFrame.codecOutputBuffers.size() == 0) {
- // No single codec output buffer has been generated. Continue to
- // wait.
- return OK;
- }
auto res = outputANW->dequeueBuffer(mOutputSurface.get(), &inputFrame.anb, &inputFrame.fenceFd);
if (res != OK) {
@@ -792,6 +826,7 @@
res);
return res;
}
+ mDequeuedOutputBufferCnt++;
// Combine current thread id, stream id and timestamp to uniquely identify image.
std::ostringstream tempOutputFile;
@@ -828,7 +863,7 @@
}
}
- ssize_t trackId = inputFrame.muxer->addTrack(mFormat);
+ ssize_t trackId = inputFrame.muxer->addTrack(inputFrame.format);
if (trackId < 0) {
ALOGE("%s: Failed to addTrack to the muxer: %zd", __FUNCTION__, trackId);
return NO_INIT;
@@ -844,6 +879,8 @@
return res;
}
+ ALOGV("%s: [%" PRId64 "]: Muxer started for inputFrame", __FUNCTION__,
+ timestamp);
return OK;
}
@@ -852,9 +889,6 @@
auto appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
&app1Size);
- ALOGV("%s: appSegmentSize is %zu, width %d, height %d, app1Size %zu", __FUNCTION__,
- appSegmentSize, inputFrame.appSegmentBuffer.width,
- inputFrame.appSegmentBuffer.height, app1Size);
if (appSegmentSize == 0) {
ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
return NO_INIT;
@@ -910,7 +944,16 @@
__FUNCTION__, strerror(-res), res);
return res;
}
+
+ ALOGV("%s: [%" PRId64 "]: appSegmentSize is %zu, width %d, height %d, app1Size %zu",
+ __FUNCTION__, timestamp, appSegmentSize, inputFrame.appSegmentBuffer.width,
+ inputFrame.appSegmentBuffer.height, app1Size);
+
inputFrame.appSegmentWritten = true;
+ // Release the buffer now so any pending input app segments can be processed
+ mAppSegmentConsumer->unlockBuffer(inputFrame.appSegmentBuffer);
+ inputFrame.appSegmentBuffer.data = nullptr;
+ mLockedAppSegmentBufferCnt--;
return OK;
}
@@ -934,8 +977,9 @@
mOutputWidth - tileX * mGridWidth : mGridWidth;
size_t height = (tileY == static_cast<size_t>(mGridRows) - 1) ?
mOutputHeight - tileY * mGridHeight : mGridHeight;
- ALOGV("%s: inputBuffer tileIndex [%zu, %zu], top %zu, left %zu, width %zu, height %zu",
- __FUNCTION__, tileX, tileY, top, left, width, height);
+ ALOGV("%s: inputBuffer tileIndex [%zu, %zu], top %zu, left %zu, width %zu, height %zu,"
+ " timeUs %" PRId64, __FUNCTION__, tileX, tileY, top, left, width, height,
+ inputBuffer.timeUs);
res = copyOneYuvTile(buffer, inputFrame.yuvBuffer, top, left, width, height);
if (res != OK) {
@@ -990,6 +1034,9 @@
}
inputFrame.codecOutputBuffers.erase(inputFrame.codecOutputBuffers.begin());
+
+ ALOGV("%s: [%" PRId64 "]: Output buffer index %d",
+ __FUNCTION__, timestamp, it->index);
return OK;
}
@@ -1046,7 +1093,9 @@
return res;
}
inputFrame.anb = nullptr;
+ mDequeuedOutputBufferCnt--;
+ ALOGV("%s: [%" PRId64 "]", __FUNCTION__, timestamp);
ATRACE_ASYNC_END("HEIC capture", inputFrame.frameNumber);
return OK;
}
@@ -1060,7 +1109,6 @@
if (inputFrame->appSegmentBuffer.data != nullptr) {
mAppSegmentConsumer->unlockBuffer(inputFrame->appSegmentBuffer);
inputFrame->appSegmentBuffer.data = nullptr;
- mAppSegmentBufferAcquired = false;
}
while (!inputFrame->codecOutputBuffers.empty()) {
@@ -1098,11 +1146,13 @@
}
}
-void HeicCompositeStream::releaseInputFramesLocked(int64_t currentTs) {
+void HeicCompositeStream::releaseInputFramesLocked() {
auto it = mPendingInputFrames.begin();
while (it != mPendingInputFrames.end()) {
- if (it->first <= currentTs) {
- releaseInputFrameLocked(&it->second);
+ auto& inputFrame = it->second;
+ if (inputFrame.error ||
+ (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
+ releaseInputFrameLocked(&inputFrame);
it = mPendingInputFrames.erase(it);
} else {
it++;
@@ -1506,7 +1556,7 @@
// In case we landed in error state, return any pending buffers and
// halt all further processing.
compilePendingInputLocked();
- releaseInputFramesLocked(currentTs);
+ releaseInputFramesLocked();
return false;
}
@@ -1548,11 +1598,7 @@
mPendingInputFrames[currentTs].error = true;
}
- if (mPendingInputFrames[currentTs].error ||
- (mPendingInputFrames[currentTs].appSegmentWritten &&
- mPendingInputFrames[currentTs].pendingOutputTiles == 0)) {
- releaseInputFramesLocked(currentTs);
- }
+ releaseInputFramesLocked();
return true;
}
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index 260c68e..04e7b83 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -25,6 +25,7 @@
#include <media/hardware/VideoAPI.h>
#include <media/MediaCodecBuffer.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaMuxer.h>
@@ -157,6 +158,7 @@
bool errorNotified;
int64_t frameNumber;
+ sp<AMessage> format;
sp<MediaMuxer> muxer;
int fenceFd;
int fileFd;
@@ -187,7 +189,7 @@
status_t processCompletedInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
void releaseInputFrameLocked(InputFrame *inputFrame /*out*/);
- void releaseInputFramesLocked(int64_t currentTs);
+ void releaseInputFramesLocked();
size_t findAppSegmentsSize(const uint8_t* appSegmentBuffer, size_t maxSize,
size_t* app1SegmentSize);
@@ -205,11 +207,13 @@
static_cast<android_dataspace>(HAL_DATASPACE_JPEG_APP_SEGMENTS);
static const android_dataspace kHeifDataSpace =
static_cast<android_dataspace>(HAL_DATASPACE_HEIF);
+ // Use the limit of pipeline depth in the API sepc as maximum number of acquired
+ // app segment buffers.
+ static const uint32_t kMaxAcquiredAppSegment = 8;
int mAppSegmentStreamId, mAppSegmentSurfaceId;
sp<CpuConsumer> mAppSegmentConsumer;
sp<Surface> mAppSegmentSurface;
- bool mAppSegmentBufferAcquired;
size_t mAppSegmentMaxSize;
CameraMetadata mStaticInfo;
@@ -218,9 +222,10 @@
sp<CpuConsumer> mMainImageConsumer; // Only applicable for HEVC codec.
bool mYuvBufferAcquired; // Only applicable to HEVC codec
+ static const int32_t kMaxOutputSurfaceProducerCount = 1;
sp<Surface> mOutputSurface;
sp<ProducerListener> mProducerListener;
-
+ int32_t mDequeuedOutputBufferCnt;
// Map from frame number to JPEG setting of orientation+quality
std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByFrameNumber;
@@ -229,11 +234,12 @@
// Keep all incoming APP segment Blob buffer pending further processing.
std::vector<int64_t> mInputAppSegmentBuffers;
+ int32_t mLockedAppSegmentBufferCnt;
// Keep all incoming HEIC blob buffer pending further processing.
std::vector<CodecOutputBufferInfo> mCodecOutputBuffers;
std::queue<int64_t> mCodecOutputBufferTimestamps;
- size_t mOutputBufferCounter;
+ size_t mCodecOutputCounter;
// Keep all incoming Yuv buffer pending tiling and encoding (for HEVC YUV tiling only)
std::vector<int64_t> mInputYuvBuffers;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index acb8b3c..e1d35e8 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -54,9 +54,8 @@
mState = STATE_ERROR;
}
- if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
- mBufferReleasedListener = new BufferReleasedListener(this);
- }
+ bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+ mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
}
Camera3OutputStream::Camera3OutputStream(int id,
@@ -87,9 +86,8 @@
mState = STATE_ERROR;
}
- if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
- mBufferReleasedListener = new BufferReleasedListener(this);
- }
+ bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+ mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
}
Camera3OutputStream::Camera3OutputStream(int id,
@@ -124,10 +122,8 @@
}
mConsumerName = String8("Deferred");
- if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
- mBufferReleasedListener = new BufferReleasedListener(this);
- }
-
+ bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+ mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
}
Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
@@ -151,9 +147,8 @@
mDropBuffers(false),
mDequeueBufferLatency(kDequeueLatencyBinSize) {
- if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
- mBufferReleasedListener = new BufferReleasedListener(this);
- }
+ bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;
+ mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
// Subclasses expected to initialize mConsumer themselves
}
@@ -261,7 +256,7 @@
notifyBufferReleased(anwBuffer);
if (mUseBufferManager) {
// Return this buffer back to buffer manager.
- mBufferReleasedListener->onBufferReleased();
+ mBufferProducerListener->onBufferReleased();
}
} else {
if (mTraceFirstBuffer && (stream_type == CAMERA3_STREAM_OUTPUT)) {
@@ -387,8 +382,8 @@
// Configure consumer-side ANativeWindow interface. The listener may be used
// to notify buffer manager (if it is used) of the returned buffers.
res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA,
- /*listener*/mBufferReleasedListener,
- /*reportBufferRemoval*/true);
+ /*reportBufferRemoval*/true,
+ /*listener*/mBufferProducerListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
@@ -790,7 +785,7 @@
return INVALID_OPERATION;
}
-void Camera3OutputStream::BufferReleasedListener::onBufferReleased() {
+void Camera3OutputStream::BufferProducerListener::onBufferReleased() {
sp<Camera3OutputStream> stream = mParent.promote();
if (stream == nullptr) {
ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
@@ -823,6 +818,25 @@
}
}
+void Camera3OutputStream::BufferProducerListener::onBuffersDiscarded(
+ const std::vector<sp<GraphicBuffer>>& buffers) {
+ sp<Camera3OutputStream> stream = mParent.promote();
+ if (stream == nullptr) {
+ ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
+ return;
+ }
+
+ if (buffers.size() > 0) {
+ Mutex::Autolock l(stream->mLock);
+ stream->onBuffersRemovedLocked(buffers);
+ if (stream->mUseBufferManager) {
+ stream->mBufferManager->onBuffersRemoved(stream->getId(),
+ stream->getStreamSetId(), buffers.size());
+ }
+ ALOGV("Stream %d: %zu Buffers discarded.", stream->getId(), buffers.size());
+ }
+}
+
void Camera3OutputStream::onBuffersRemovedLocked(
const std::vector<sp<GraphicBuffer>>& removedBuffers) {
sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 729c655..b4e49f9 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -146,18 +146,22 @@
*/
virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
- class BufferReleasedListener : public BnProducerListener {
+ class BufferProducerListener : public SurfaceListener {
public:
- BufferReleasedListener(wp<Camera3OutputStream> parent) : mParent(parent) {}
+ BufferProducerListener(wp<Camera3OutputStream> parent, bool needsReleaseNotify)
+ : mParent(parent), mNeedsReleaseNotify(needsReleaseNotify) {}
- /**
- * Implementation of IProducerListener, used to notify this stream that the consumer
- * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
- */
- virtual void onBufferReleased();
+ /**
+ * Implementation of IProducerListener, used to notify this stream that the consumer
+ * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
+ */
+ virtual void onBufferReleased();
+ virtual bool needsReleaseNotify() { return mNeedsReleaseNotify; }
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers);
private:
- wp<Camera3OutputStream> mParent;
+ wp<Camera3OutputStream> mParent;
+ bool mNeedsReleaseNotify;
};
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
@@ -262,10 +266,10 @@
sp<Camera3BufferManager> mBufferManager;
/**
- * Buffer released listener, used to notify the buffer manager that a buffer is released
- * from consumer side.
+ * Buffer producer listener, used to handle notification when a buffer is released
+ * from consumer side, or a set of buffers are discarded by the consumer.
*/
- sp<BufferReleasedListener> mBufferReleasedListener;
+ sp<BufferProducerListener> mBufferProducerListener;
/**
* Flag indicating if the buffer manager is used to allocate the stream buffers
diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp
index 765ac99..fa5bc4a 100644
--- a/services/mediacodec/registrant/Android.bp
+++ b/services/mediacodec/registrant/Android.bp
@@ -14,7 +14,6 @@
"libbase",
"libcodec2_hidl@1.0",
"libcodec2_vndk",
- "libhidlbase",
"libutils",
],
diff --git a/services/mediacodec/registrant/CodecServiceRegistrant.cpp b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
index 7f7ceca..706ebee 100644
--- a/services/mediacodec/registrant/CodecServiceRegistrant.cpp
+++ b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
@@ -18,411 +18,21 @@
#define LOG_TAG "CodecServiceRegistrant"
#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <C2Component.h>
#include <C2PlatformSupport.h>
#include <codec2/hidl/1.0/ComponentStore.h>
-#include <codec2/hidl/1.0/Configurable.h>
-#include <codec2/hidl/1.0/types.h>
-#include <hidl/HidlSupport.h>
#include <media/CodecServiceRegistrant.h>
-namespace /* unnamed */ {
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::sp;
-using namespace ::android::hardware::media::c2::V1_0;
-using namespace ::android::hardware::media::c2::V1_0::utils;
-
-constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
-
-// Converter from IComponentStore to C2ComponentStore.
-class H2C2ComponentStore : public C2ComponentStore {
-protected:
- sp<IComponentStore> mStore;
- sp<IConfigurable> mConfigurable;
-public:
- explicit H2C2ComponentStore(sp<IComponentStore> const& store)
- : mStore{store},
- mConfigurable{[store]() -> sp<IConfigurable>{
- if (!store) {
- return nullptr;
- }
- Return<sp<IConfigurable>> transResult =
- store->getConfigurable();
- return transResult.isOk() ?
- static_cast<sp<IConfigurable>>(transResult) :
- nullptr;
- }()} {
- if (!mConfigurable) {
- LOG(ERROR) << "Preferred store is corrupted.";
- }
- }
-
- virtual ~H2C2ComponentStore() override = default;
-
- virtual c2_status_t config_sm(
- std::vector<C2Param*> const ¶ms,
- std::vector<std::unique_ptr<C2SettingResult>>* const failures
- ) override {
- Params hidlParams;
- if (!createParamsBlob(&hidlParams, params)) {
- LOG(ERROR) << "config -- bad input.";
- return C2_TRANSACTION_FAILED;
- }
- c2_status_t status{};
- Return<void> transResult = mConfigurable->config(
- hidlParams,
- true,
- [&status, ¶ms, failures](
- Status s,
- const hidl_vec<SettingResult> f,
- const Params& o) {
- status = static_cast<c2_status_t>(s);
- if (status != C2_OK && status != C2_BAD_INDEX) {
- LOG(DEBUG) << "config -- call failed: "
- << status << ".";
- }
- size_t i = failures->size();
- failures->resize(i + f.size());
- for (const SettingResult& sf : f) {
- if (!objcpy(&(*failures)[i++], sf)) {
- LOG(ERROR) << "config -- "
- << "invalid SettingResult returned.";
- return;
- }
- }
- if (!updateParamsFromBlob(params, o)) {
- LOG(ERROR) << "config -- "
- << "failed to parse returned params.";
- status = C2_CORRUPTED;
- }
- });
- if (!transResult.isOk()) {
- LOG(ERROR) << "config -- transaction failed.";
- return C2_TRANSACTION_FAILED;
- }
- return status;
- };
-
- virtual c2_status_t copyBuffer(
- std::shared_ptr<C2GraphicBuffer>,
- std::shared_ptr<C2GraphicBuffer>) override {
- LOG(ERROR) << "copyBuffer -- not supported.";
- return C2_OMITTED;
- }
-
- virtual c2_status_t createComponent(
- C2String, std::shared_ptr<C2Component> *const component) override {
- component->reset();
- LOG(ERROR) << "createComponent -- not supported.";
- return C2_OMITTED;
- }
-
- virtual c2_status_t createInterface(
- C2String, std::shared_ptr<C2ComponentInterface> *const interface) {
- interface->reset();
- LOG(ERROR) << "createInterface -- not supported.";
- return C2_OMITTED;
- }
-
- virtual c2_status_t query_sm(
- const std::vector<C2Param *> &stackParams,
- const std::vector<C2Param::Index> &heapParamIndices,
- std::vector<std::unique_ptr<C2Param>> *const heapParams) const
- override {
- hidl_vec<ParamIndex> indices(
- stackParams.size() + heapParamIndices.size());
- size_t numIndices = 0;
- for (C2Param* const& stackParam : stackParams) {
- if (!stackParam) {
- LOG(WARNING) << "query -- null stack param encountered.";
- continue;
- }
- indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
- }
- size_t numStackIndices = numIndices;
- for (const C2Param::Index& index : heapParamIndices) {
- indices[numIndices++] =
- static_cast<ParamIndex>(static_cast<uint32_t>(index));
- }
- indices.resize(numIndices);
- if (heapParams) {
- heapParams->reserve(heapParams->size() + numIndices);
- }
- c2_status_t status;
- Return<void> transResult = mConfigurable->query(
- indices,
- true,
- [&status, &numStackIndices, &stackParams, heapParams](
- Status s, const Params& p) {
- status = static_cast<c2_status_t>(s);
- if (status != C2_OK && status != C2_BAD_INDEX) {
- LOG(DEBUG) << "query -- call failed: "
- << status << ".";
- return;
- }
- std::vector<C2Param*> paramPointers;
- if (!parseParamsBlob(¶mPointers, p)) {
- LOG(ERROR) << "query -- error while parsing params.";
- status = C2_CORRUPTED;
- return;
- }
- size_t i = 0;
- for (auto it = paramPointers.begin();
- it != paramPointers.end(); ) {
- C2Param* paramPointer = *it;
- if (numStackIndices > 0) {
- --numStackIndices;
- if (!paramPointer) {
- LOG(WARNING) << "query -- null stack param.";
- ++it;
- continue;
- }
- for (; i < stackParams.size() && !stackParams[i]; ) {
- ++i;
- }
- if (i >= stackParams.size()) {
- LOG(ERROR) << "query -- unexpected error.";
- status = C2_CORRUPTED;
- return;
- }
- if (stackParams[i]->index() != paramPointer->index()) {
- LOG(WARNING) << "query -- param skipped: "
- "index = "
- << stackParams[i]->index() << ".";
- stackParams[i++]->invalidate();
- continue;
- }
- if (!stackParams[i++]->updateFrom(*paramPointer)) {
- LOG(WARNING) << "query -- param update failed: "
- "index = "
- << paramPointer->index() << ".";
- }
- } else {
- if (!paramPointer) {
- LOG(WARNING) << "query -- null heap param.";
- ++it;
- continue;
- }
- if (!heapParams) {
- LOG(WARNING) << "query -- "
- "unexpected extra stack param.";
- } else {
- heapParams->emplace_back(
- C2Param::Copy(*paramPointer));
- }
- }
- ++it;
- }
- });
- if (!transResult.isOk()) {
- LOG(ERROR) << "query -- transaction failed.";
- return C2_TRANSACTION_FAILED;
- }
- return status;
- }
-
- virtual c2_status_t querySupportedParams_nb(
- std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
- c2_status_t status;
- Return<void> transResult = mConfigurable->querySupportedParams(
- std::numeric_limits<uint32_t>::min(),
- std::numeric_limits<uint32_t>::max(),
- [&status, params](
- Status s,
- const hidl_vec<ParamDescriptor>& p) {
- status = static_cast<c2_status_t>(s);
- if (status != C2_OK) {
- LOG(DEBUG) << "querySupportedParams -- call failed: "
- << status << ".";
- return;
- }
- size_t i = params->size();
- params->resize(i + p.size());
- for (const ParamDescriptor& sp : p) {
- if (!objcpy(&(*params)[i++], sp)) {
- LOG(ERROR) << "querySupportedParams -- "
- << "invalid returned ParamDescriptor.";
- return;
- }
- }
- });
- if (!transResult.isOk()) {
- LOG(ERROR) << "querySupportedParams -- transaction failed.";
- return C2_TRANSACTION_FAILED;
- }
- return status;
- }
-
- virtual c2_status_t querySupportedValues_sm(
- std::vector<C2FieldSupportedValuesQuery> &fields) const {
- hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
- for (size_t i = 0; i < fields.size(); ++i) {
- if (!objcpy(&inFields[i], fields[i])) {
- LOG(ERROR) << "querySupportedValues -- bad input";
- return C2_TRANSACTION_FAILED;
- }
- }
-
- c2_status_t status;
- Return<void> transResult = mConfigurable->querySupportedValues(
- inFields,
- true,
- [&status, &inFields, &fields](
- Status s,
- const hidl_vec<FieldSupportedValuesQueryResult>& r) {
- status = static_cast<c2_status_t>(s);
- if (status != C2_OK) {
- LOG(DEBUG) << "querySupportedValues -- call failed: "
- << status << ".";
- return;
- }
- if (r.size() != fields.size()) {
- LOG(ERROR) << "querySupportedValues -- "
- "input and output lists "
- "have different sizes.";
- status = C2_CORRUPTED;
- return;
- }
- for (size_t i = 0; i < fields.size(); ++i) {
- if (!objcpy(&fields[i], inFields[i], r[i])) {
- LOG(ERROR) << "querySupportedValues -- "
- "invalid returned value.";
- status = C2_CORRUPTED;
- return;
- }
- }
- });
- if (!transResult.isOk()) {
- LOG(ERROR) << "querySupportedValues -- transaction failed.";
- return C2_TRANSACTION_FAILED;
- }
- return status;
- }
-
- virtual C2String getName() const {
- C2String outName;
- Return<void> transResult = mConfigurable->getName(
- [&outName](const hidl_string& name) {
- outName = name.c_str();
- });
- if (!transResult.isOk()) {
- LOG(ERROR) << "getName -- transaction failed.";
- }
- return outName;
- }
-
- virtual std::shared_ptr<C2ParamReflector> getParamReflector() const
- override {
- struct SimpleParamReflector : public C2ParamReflector {
- virtual std::unique_ptr<C2StructDescriptor> describe(
- C2Param::CoreIndex coreIndex) const {
- hidl_vec<ParamIndex> indices(1);
- indices[0] = static_cast<ParamIndex>(coreIndex.coreIndex());
- std::unique_ptr<C2StructDescriptor> descriptor;
- Return<void> transResult = mBase->getStructDescriptors(
- indices,
- [&descriptor](
- Status s,
- const hidl_vec<StructDescriptor>& sd) {
- c2_status_t status = static_cast<c2_status_t>(s);
- if (status != C2_OK) {
- LOG(DEBUG) << "SimpleParamReflector -- "
- "getStructDescriptors() failed: "
- << status << ".";
- descriptor.reset();
- return;
- }
- if (sd.size() != 1) {
- LOG(DEBUG) << "SimpleParamReflector -- "
- "getStructDescriptors() "
- "returned vector of size "
- << sd.size() << ". "
- "It should be 1.";
- descriptor.reset();
- return;
- }
- if (!objcpy(&descriptor, sd[0])) {
- LOG(DEBUG) << "SimpleParamReflector -- "
- "getStructDescriptors() returned "
- "corrupted data.";
- descriptor.reset();
- return;
- }
- });
- return descriptor;
- }
-
- explicit SimpleParamReflector(sp<IComponentStore> base)
- : mBase(base) { }
-
- sp<IComponentStore> mBase;
- };
-
- return std::make_shared<SimpleParamReflector>(mStore);
- }
-
- virtual std::vector<std::shared_ptr<const C2Component::Traits>>
- listComponents() override {
- LOG(ERROR) << "listComponents -- not supported.";
- return {};
- }
-};
-
-bool ionPropertiesDefined() {
- using namespace ::android::base;
- std::string heapMask =
- GetProperty("ro.com.android.media.swcodec.ion.heapmask", "undefined");
- std::string flags =
- GetProperty("ro.com.android.media.swcodec.ion.flags", "undefined");
- std::string align =
- GetProperty("ro.com.android.media.swcodec.ion.align", "undefined");
- if (heapMask != "undefined" ||
- flags != "undefined" ||
- align != "undefined") {
- LOG(INFO)
- << "Some system properties for mediaswcodec ION usage are set: "
- << "heapmask = " << heapMask << ", "
- << "flags = " << flags << ", "
- << "align = " << align << ". "
- << "Preferred Codec2 store is defaulted to \"software\".";
- return true;
- }
- return false;
-}
-
-} // unnamed namespace
-
extern "C" void RegisterCodecServices() {
- using ComponentStore = ::android::hardware::media::c2::V1_0::utils::
- ComponentStore;
+ using namespace ::android::hardware::media::c2::V1_0;
LOG(INFO) << "Creating software Codec2 service...";
- sp<ComponentStore> store =
- new ComponentStore(::android::GetCodec2PlatformComponentStore());
+ android::sp<IComponentStore> store =
+ new utils::ComponentStore(
+ android::GetCodec2PlatformComponentStore());
if (store == nullptr) {
LOG(ERROR) <<
"Cannot create software Codec2 service.";
} else {
- if (!ionPropertiesDefined()) {
- std::string preferredStoreName = "default";
- sp<IComponentStore> preferredStore =
- IComponentStore::getService(preferredStoreName.c_str());
- if (preferredStore) {
- ::android::SetPreferredCodec2ComponentStore(
- std::make_shared<H2C2ComponentStore>(preferredStore));
- LOG(INFO) <<
- "Preferred Codec2 store is set to \"" <<
- preferredStoreName << "\".";
- } else {
- LOG(INFO) <<
- "Preferred Codec2 store is defaulted to \"software\".";
- }
- }
if (store->registerAsService("software") != android::OK) {
LOG(ERROR) <<
"Cannot register software Codec2 service.";
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
index 16b036f..e906500 100644
--- a/services/mediaextractor/Android.bp
+++ b/services/mediaextractor/Android.bp
@@ -29,9 +29,6 @@
"liblog",
"libavservices_minijail",
],
- header_libs: [
- "bionic_libc_platform_headers",
- ],
target: {
android: {
product_variables: {
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index afb7692..3c4125b 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -28,8 +28,6 @@
#include <android-base/properties.h>
#include <utils/misc.h>
-#include <bionic/reserved_signals.h>
-
// from LOCAL_C_INCLUDES
#include "MediaExtractorService.h"
#include "MediaUtils.h"
@@ -51,10 +49,6 @@
signal(SIGPIPE, SIG_IGN);
- // Do not assist platform profilers (relevant only on debug builds).
- // Otherwise, the signal handler can violate the seccomp policy.
- signal(BIONIC_SIGNAL_PROFILER, SIG_IGN);
-
//b/62255959: this forces libutis.so to dlopen vendor version of libutils.so
//before minijail is on. This is dirty but required since some syscalls such
//as pread64 are used by linker but aren't allowed in the minijail. By