diff options
46 files changed, 1443 insertions, 357 deletions
diff --git a/aidl/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl/android/media/audio/IHalAdapterVendorExtension.aidl index b7a7678ee8..48fb291dd0 100644 --- a/aidl/android/media/audio/IHalAdapterVendorExtension.aidl +++ b/aidl/android/media/audio/IHalAdapterVendorExtension.aidl @@ -23,6 +23,8 @@ import android.hardware.audio.core.VendorParameter; * is optional. Vendors may provide an implementation on the system_ext * partition. The default instance of this interface, if provided, must be * registered prior to the moment when the audio server connects to HAL modules. + * Vendors need to set the system property `ro.audio.ihaladaptervendorextension_enabled` + * to `true` for the framework to bind to this service. * * {@hide} */ diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index 5d3b65b56f..8c3424fb23 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -255,6 +255,7 @@ void CameraManagerGlobal::drainPendingCallbacksLocked() { template<class T> void CameraManagerGlobal::registerAvailCallback(const T *callback) { Mutex::Autolock _l(mLock); + getCameraServiceLocked(); Callback cb(callback); auto pair = mCallbacks.insert(cb); // Send initial callbacks if callback is newly registered diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp index 3aa7817f1b..099786b678 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -396,6 +396,7 @@ void CameraManagerGlobal::drainPendingCallbacksLocked() { template <class T> void CameraManagerGlobal::registerAvailCallback(const T *callback) { + getCameraService(); Mutex::Autolock _l(mLock); Callback cb(callback); auto res = mCallbacks.insert(cb); diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig index 1832f9475d..3092091dc8 100644 --- a/media/aconfig/codec_fwk.aconfig +++ b/media/aconfig/codec_fwk.aconfig @@ -29,20 +29,48 @@ flag { flag { name: "in_process_sw_audio_codec" namespace: "codec_fwk" - description: "Feature flag for in-process software audio codec support" + description: "Feature flag for in-process software audio codec API" bug: "297922713" } flag { + name: "in_process_sw_audio_codec_support" + namespace: "codec_fwk" + description: "Feature flag for in-process software audio codec support" + bug: "325520135" +} + +flag { + name: "large_audio_frame_finish" + namespace: "codec_fwk" + description: "Implementation flag for large audio frame finishing tasks" + bug: "325512893" +} + +flag { name: "null_output_surface" namespace: "codec_fwk" - description: "Feature flag for null output Surface support" + description: "Feature flag for null output Surface API" bug: "297920102" } flag { + name: "null_output_surface_support" + namespace: "codec_fwk" + description: "Feature flag for null output Surface support" + bug: "325550522" +} + +flag { name: "region_of_interest" namespace: "codec_fwk" - description: "Feature flag for region of interest support" + description: "Feature flag for region of interest API" bug: "299191092" } + +flag { + name: "region_of_interest_support" + namespace: "codec_fwk" + description: "Feature flag for region of interest support" + bug: "325549730" +} diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp index cd9fd9fc0a..9221a24cbd 100644 --- a/media/codec2/hal/common/MultiAccessUnitHelper.cpp +++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp @@ -177,33 +177,29 @@ c2_status_t MultiAccessUnitHelper::flush( std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) { c2_status_t c2res = C2_OK; std::lock_guard<std::mutex> l(mLock); - for (std::unique_ptr<C2Work>& w : *c2flushedWorks) { + for (auto iterWork = c2flushedWorks->begin() ; iterWork != c2flushedWorks->end(); ) { bool foundFlushedFrame = false; std::list<MultiAccessUnitInfo>::iterator frame = mFrameHolder.begin(); while (frame != mFrameHolder.end() && !foundFlushedFrame) { auto it = frame->mComponentFrameIds.find( - w->input.ordinal.frameIndex.peekull()); + (*iterWork)->input.ordinal.frameIndex.peekull()); if (it != frame->mComponentFrameIds.end()) { - LOG(DEBUG) << "Multi access-unit flush" - << w->input.ordinal.frameIndex.peekull() + LOG(DEBUG) << "Multi access-unit flush " + << (*iterWork)->input.ordinal.frameIndex.peekull() << " with " << frame->inOrdinal.frameIndex.peekull(); - w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex; - bool removeEntry = w->worklets.empty() - || !w->worklets.front() - || (w->worklets.front()->output.flags - & C2FrameData::FLAG_INCOMPLETE) == 0; - if (removeEntry) { - frame->mComponentFrameIds.erase(it); - } - foundFlushedFrame = true; - } - if (frame->mComponentFrameIds.empty()) { + (*iterWork)->input.ordinal.frameIndex = frame->inOrdinal.frameIndex; frame = mFrameHolder.erase(frame); + foundFlushedFrame = true; } else { ++frame; } } + if (!foundFlushedFrame) { + iterWork = c2flushedWorks->erase(iterWork); + } else { + ++iterWork; + } } return c2res; } @@ -297,13 +293,15 @@ c2_status_t MultiAccessUnitHelper::scatter( std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au))); LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal " << inputOrdinal.frameIndex.peekull() - << " total offset " << offset << " info.size " << info.size - << " : TS " << newWork->input.ordinal.timestamp.peekull(); + << " info.size " << info.size + << " : TS " << newWork->input.ordinal.timestamp.peekull() + << " with index " << newFrameIdx - 1; // add to worklist sliceWork.push_back(std::move(newWork)); processedWork->push_back(std::move(sliceWork)); offset += info.size; } + mFrameIndex--; if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) { if (!processedWork->empty()) { std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back(); @@ -498,7 +496,7 @@ c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame, } frame.mLargeWork = std::move(work); frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex; - finalizeWork(frame); + finalizeWork(frame, (*worklet)->output.flags, true); addWork(frame.mLargeWork); frame.reset(); return C2_OK; diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 7b1721e7f7..40656ff62a 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -483,6 +483,130 @@ int32_t CCodecBufferChannel::getHeapSeqNum(const sp<HidlMemory> &memory) { return heapSeqNum; } +typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper; +typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper; +status_t CCodecBufferChannel::attachEncryptedBuffers( + const sp<hardware::HidlMemory> &memory, + size_t offset, + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString* errorDetailMsg) { + static const C2MemoryUsage kDefaultReadWriteUsage{ + C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + if (!hasCryptoOrDescrambler()) { + ALOGE("attachEncryptedBuffers requires Crypto/descrambler object"); + return -ENOSYS; + } + size_t size = 0; + CHECK(buffer->meta()->findSize("ssize", &size)); + if (size == 0) { + buffer->setRange(0, 0); + return OK; + } + sp<RefBase> obj; + CHECK(buffer->meta()->findObject("cryptoInfos", &obj)); + sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()}; + CHECK(buffer->meta()->findObject("accessUnitInfo", &obj)); + sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()}; + if (secure || (mCrypto == nullptr)) { + if (cryptoInfos->value.size() != 1) { + ALOGE("Cannot decrypt multiple access units"); + return -ENOSYS; + } + // we are dealing with just one cryptoInfo or descrambler. + std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]); + if (info == nullptr) { + ALOGE("Cannot decrypt, CryptoInfos are null."); + return -ENOSYS; + } + return attachEncryptedBuffer( + memory, + secure, + info->mKey, + info->mIv, + info->mMode, + info->mPattern, + offset, + info->mSubSamples, + info->mNumSubSamples, + buffer, + errorDetailMsg); + } + std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool; + std::shared_ptr<C2LinearBlock> block; + c2_status_t err = pool->fetchLinearBlock( + size, + kDefaultReadWriteUsage, + &block); + if (err != C2_OK) { + ALOGI("[%s] attachEncryptedBuffers: fetchLinearBlock failed: size = %zu (%s) err = %d", + mName, size, secure ? "secure" : "non-secure", err); + return NO_MEMORY; + } + ensureDecryptDestination(size); + C2WriteView wView = block->map().get(); + if (wView.error() != C2_OK) { + ALOGI("[%s] attachEncryptedBuffers: block map error: %d (non-secure)", + mName, wView.error()); + return UNKNOWN_ERROR; + } + + ssize_t result = -1; + ssize_t codecDataOffset = 0; + size_t inBufferOffset = 0; + size_t outBufferSize = 0; + uint32_t cryptoInfoIdx = 0; + int32_t heapSeqNum = getHeapSeqNum(memory); + hardware::drm::V1_0::SharedBuffer src{(uint32_t)heapSeqNum, offset, size}; + hardware::drm::V1_0::DestinationBuffer dst; + dst.type = DrmBufferType::SHARED_MEMORY; + IMemoryToSharedBuffer( + mDecryptDestination, mHeapSeqNum, &dst.nonsecureMemory); + for (int i = 0; i < bufferInfos->value.size(); i++) { + if (bufferInfos->value[i].mSize > 0) { + std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[cryptoInfoIdx++]); + result = mCrypto->decrypt( + (uint8_t*)info->mKey, + (uint8_t*)info->mIv, + info->mMode, + info->mPattern, + src, + inBufferOffset, + info->mSubSamples, + info->mNumSubSamples, + dst, + errorDetailMsg); + inBufferOffset += bufferInfos->value[i].mSize; + if (result < 0) { + ALOGI("[%s] attachEncryptedBuffers: decrypt failed: result = %zd", + mName, result); + return result; + } + if (wView.error() == C2_OK) { + if (wView.size() < result) { + ALOGI("[%s] attachEncryptedBuffers: block size too small:" + "size=%u result=%zd (non-secure)", mName, wView.size(), result); + return UNKNOWN_ERROR; + } + memcpy(wView.data(), mDecryptDestination->unsecurePointer(), result); + bufferInfos->value[i].mSize = result; + wView.setOffset(wView.offset() + result); + } + outBufferSize += result; + } + } + if (wView.error() == C2_OK) { + wView.setOffset(0); + } + std::shared_ptr<C2Buffer> c2Buffer{C2Buffer::CreateLinearBuffer( + block->share(codecDataOffset, outBufferSize - codecDataOffset, C2Fence{}))}; + if (!buffer->copy(c2Buffer)) { + ALOGI("[%s] attachEncryptedBuffers: buffer copy failed", mName); + return -ENOSYS; + } + return OK; +} + status_t CCodecBufferChannel::attachEncryptedBuffer( const sp<hardware::HidlMemory> &memory, bool secure, @@ -777,6 +901,138 @@ status_t CCodecBufferChannel::queueSecureInputBuffer( return queueInputBufferInternal(buffer, block, bufferSize); } +status_t CCodecBufferChannel::queueSecureInputBuffers( + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString *errorDetailMsg) { + QueueGuard guard(mSync); + if (!guard.isRunning()) { + ALOGD("[%s] No more buffers should be queued at current state.", mName); + return -ENOSYS; + } + + if (!hasCryptoOrDescrambler()) { + ALOGE("queueSecureInputBuffers requires a Crypto/descrambler Object"); + return -ENOSYS; + } + sp<RefBase> obj; + CHECK(buffer->meta()->findObject("cryptoInfos", &obj)); + sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()}; + CHECK(buffer->meta()->findObject("accessUnitInfo", &obj)); + sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()}; + if (secure || mCrypto == nullptr) { + if (cryptoInfos->value.size() != 1) { + ALOGE("Cannot decrypt multiple access units on native handles"); + return -ENOSYS; + } + std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]); + if (info == nullptr) { + ALOGE("Cannot decrypt, CryptoInfos are null"); + return -ENOSYS; + } + return queueSecureInputBuffer( + buffer, + secure, + info->mKey, + info->mIv, + info->mMode, + info->mPattern, + info->mSubSamples, + info->mNumSubSamples, + errorDetailMsg); + } + sp<EncryptedLinearBlockBuffer> encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get()); + + std::shared_ptr<C2LinearBlock> block; + size_t allocSize = buffer->size(); + size_t bufferSize = 0; + c2_status_t blockRes = C2_OK; + bool copied = false; + ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf( + "CCodecBufferChannel::decrypt(%s)", mName).c_str()); + if (mSendEncryptedInfoBuffer) { + static const C2MemoryUsage kDefaultReadWriteUsage{ + C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + constexpr int kAllocGranule0 = 1024 * 64; + constexpr int kAllocGranule1 = 1024 * 1024; + std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool; + // round up encrypted sizes to limit fragmentation and encourage buffer reuse + if (allocSize <= kAllocGranule1) { + bufferSize = align(allocSize, kAllocGranule0); + } else { + bufferSize = align(allocSize, kAllocGranule1); + } + blockRes = pool->fetchLinearBlock( + bufferSize, kDefaultReadWriteUsage, &block); + + if (blockRes == C2_OK) { + C2WriteView view = block->map().get(); + if (view.error() == C2_OK && view.size() == bufferSize) { + copied = true; + // TODO: only copy clear sections + memcpy(view.data(), buffer->data(), allocSize); + } + } + } + + if (!copied) { + block.reset(); + } + // size of cryptoInfo and accessUnitInfo should be the same? + ssize_t result = -1; + ssize_t codecDataOffset = 0; + size_t inBufferOffset = 0; + size_t outBufferSize = 0; + uint32_t cryptoInfoIdx = 0; + { + // scoped this block to enable destruction of mappedBlock + std::unique_ptr<EncryptedLinearBlockBuffer::MappedBlock> mappedBlock = nullptr; + hardware::drm::V1_0::DestinationBuffer destination; + destination.type = DrmBufferType::SHARED_MEMORY; + IMemoryToSharedBuffer( + mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory); + encryptedBuffer->getMappedBlock(&mappedBlock); + hardware::drm::V1_0::SharedBuffer source; + encryptedBuffer->fillSourceBuffer(&source); + for (int i = 0 ; i < bufferInfos->value.size(); i++) { + if (bufferInfos->value[i].mSize > 0) { + std::unique_ptr<CodecCryptoInfo> info = + std::move(cryptoInfos->value[cryptoInfoIdx++]); + if (info->mNumSubSamples == 1 + && info->mSubSamples[0].mNumBytesOfClearData == 0 + && info->mSubSamples[0].mNumBytesOfEncryptedData == 0) { + // no data so we only populate the bufferInfo + result = 0; + } else { + result = mCrypto->decrypt( + (uint8_t*)info->mKey, + (uint8_t*)info->mIv, + info->mMode, + info->mPattern, + source, + inBufferOffset, + info->mSubSamples, + info->mNumSubSamples, + destination, + errorDetailMsg); + inBufferOffset += bufferInfos->value[i].mSize; + if (result < 0) { + ALOGI("[%s] decrypt failed: result=%zd", mName, result); + return result; + } + if (destination.type == DrmBufferType::SHARED_MEMORY && mappedBlock) { + mappedBlock->copyDecryptedContent(mDecryptDestination, result); + } + bufferInfos->value[i].mSize = result; + outBufferSize += result; + } + } + } + buffer->setRange(codecDataOffset, outBufferSize - codecDataOffset); + } + return queueInputBufferInternal(buffer, block, bufferSize); +} + void CCodecBufferChannel::feedInputBufferIfAvailable() { QueueGuard guard(mSync); if (!guard.isRunning()) { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 8dc9fb6984..b47065529b 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -73,6 +73,10 @@ public: const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, AString *errorDetailMsg) override; + status_t queueSecureInputBuffers( + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString *errorDetailMsg) override; status_t attachBuffer( const std::shared_ptr<C2Buffer> &c2Buffer, const sp<MediaCodecBuffer> &buffer) override; @@ -88,6 +92,12 @@ public: size_t numSubSamples, const sp<MediaCodecBuffer> &buffer, AString* errorDetailMsg) override; + status_t attachEncryptedBuffers( + const sp<hardware::HidlMemory> &memory, + size_t offset, + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString* errorDetailMsg) override; status_t renderOutputBuffer( const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override; void pollForRenderedBuffers() override; diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp index 8a4877725d..d313f33a45 100644 --- a/media/codec2/sfplugin/CCodecBuffers.cpp +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -160,6 +160,12 @@ public: SkipCutBuffer(skip, cut, num16BitChannels), mFrontPaddingDelay(0), mSize(0) { } + void clearAll() { + mInfos.clear(); + mFrontPaddingDelay = 0; + mSize = 0; + SkipCutBuffer::clear(); + } virtual ~MultiAccessUnitSkipCutBuffer() { @@ -1378,7 +1384,7 @@ void OutputBuffersArray::flush(const std::list<std::unique_ptr<C2Work>> &flushed (void)flushedWork; mImpl.flush(); if (mSkipCutBuffer != nullptr) { - mSkipCutBuffer->clear(); + mSkipCutBuffer->clearAll(); } } @@ -1536,7 +1542,7 @@ uint32_t FlexOutputBuffers::getPixelFormatIfApplicable() { return mPixelFormat; void LinearOutputBuffers::flush( const std::list<std::unique_ptr<C2Work>> &flushedWork) { if (mSkipCutBuffer != nullptr) { - mSkipCutBuffer->clear(); + mSkipCutBuffer->clearAll(); } FlexOutputBuffers::flush(flushedWork); } diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 4f466c5330..9c514f2eb3 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -1036,6 +1036,37 @@ native_handle_t *EncryptedLinearBlockBuffer::handle() const { return const_cast<native_handle_t *>(mBlock->handle()); } +void EncryptedLinearBlockBuffer::getMappedBlock( + std::unique_ptr<MappedBlock> * const mappedBlock) const { + if (mappedBlock) { + mappedBlock->reset(new EncryptedLinearBlockBuffer::MappedBlock(mBlock)); + } + return; +} + +EncryptedLinearBlockBuffer::MappedBlock::MappedBlock( + const std::shared_ptr<C2LinearBlock> &block) : mView(block->map().get()) { +} + +bool EncryptedLinearBlockBuffer::MappedBlock::copyDecryptedContent( + const sp<IMemory> &decrypted, size_t length) { + if (mView.error() != C2_OK) { + return false; + } + if (mView.size() < length) { + ALOGE("View size(%d) less than decrypted length(%zu)", + mView.size(), length); + return false; + } + memcpy(mView.data(), decrypted->unsecurePointer(), length); + mView.setOffset(mView.offset() + length); + return true; +} + +EncryptedLinearBlockBuffer::MappedBlock::~MappedBlock() { + mView.setOffset(0); +} + using ::aidl::android::hardware::graphics::common::Cta861_3; using ::aidl::android::hardware::graphics::common::Smpte2086; diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h index b73acab69c..5e969219c2 100644 --- a/media/codec2/sfplugin/Codec2Buffer.h +++ b/media/codec2/sfplugin/Codec2Buffer.h @@ -384,6 +384,17 @@ public: */ native_handle_t *handle() const; + class MappedBlock { + public: + explicit MappedBlock(const std::shared_ptr<C2LinearBlock> &block); + virtual ~MappedBlock(); + bool copyDecryptedContent(const sp<IMemory> &decrypted, size_t length); + private: + C2WriteView mView; + }; + + void getMappedBlock(std::unique_ptr<MappedBlock> * const mappedBlock) const; + private: std::shared_ptr<C2LinearBlock> mBlock; diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index 71ffefb748..971b5a512b 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -939,7 +939,7 @@ uint32_t ExtractFormatFromCodec2GrallocHandle(const C2Handle *const handle) { return 0; } -bool EXtractMetadataFromCodec2GrallocHandle( +bool ExtractMetadataFromCodec2GrallocHandle( const C2Handle *const handle, uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage, uint32_t *stride) { if (handle == nullptr) { @@ -959,7 +959,7 @@ bool EXtractMetadataFromCodec2GrallocHandle( (void)C2HandleAhwb::Import(handle, width, height, format, usage, stride, &origId); return true; } - ALOGE("EXtractMetadata from non compatible handle"); + ALOGE("ExtractMetadata from non compatible handle"); return false; } diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index ae37152eaa..660f16113c 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -1699,29 +1699,42 @@ audio_io_handle_t AudioTrack::getOutput() const } status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) { + status_t result = NO_ERROR; AutoMutex lock(mLock); - ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d mRoutedDeviceId %d", - __func__, mPortId, deviceId, mSelectedDeviceId, mRoutedDeviceId); + ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d", + __func__, mPortId, deviceId, mSelectedDeviceId); if (mSelectedDeviceId != deviceId) { mSelectedDeviceId = deviceId; if (mStatus == NO_ERROR) { - // allow track invalidation when track is not playing to propagate - // the updated mSelectedDeviceId - if (isPlaying_l()) { - if (mSelectedDeviceId != mRoutedDeviceId) { - android_atomic_or(CBLK_INVALID, &mCblk->mFlags); - mProxy->interrupt(); + if (isOffloadedOrDirect_l()) { + if (mState == STATE_STOPPED || mState == STATE_FLUSHED) { + ALOGD("%s(%d): creating a new AudioTrack", __func__, mPortId); + result = restoreTrack_l("setOutputDevice", true /* forceRestore */); + } else { + ALOGW("%s(%d). Offloaded or Direct track is not STOPPED or FLUSHED. " + "State: %s.", + __func__, mPortId, stateToString(mState)); + result = INVALID_OPERATION; } } else { - // if the track is idle, try to restore now and - // defer to next start if not possible - if (restoreTrack_l("setOutputDevice") != OK) { - android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + // allow track invalidation when track is not playing to propagate + // the updated mSelectedDeviceId + if (isPlaying_l()) { + if (mSelectedDeviceId != mRoutedDeviceId) { + android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + mProxy->interrupt(); + } + } else { + // if the track is idle, try to restore now and + // defer to next start if not possible + if (restoreTrack_l("setOutputDevice") != OK) { + android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + } } } } } - return NO_ERROR; + return result; } audio_port_handle_t AudioTrack::getOutputDevice() { @@ -2835,7 +2848,7 @@ nsecs_t AudioTrack::processAudioBuffer() return 0; } -status_t AudioTrack::restoreTrack_l(const char *from) +status_t AudioTrack::restoreTrack_l(const char *from, bool forceRestore) { status_t result = NO_ERROR; // logged: make sure to set this before returning. const int64_t beginNs = systemTime(); @@ -2856,7 +2869,8 @@ status_t AudioTrack::restoreTrack_l(const char *from) // output parameters and new IAudioFlinger in createTrack_l() AudioSystem::clearAudioConfigCache(); - if (isOffloadedOrDirect_l() || mDoNotReconnect) { + if (!forceRestore && + (isOffloadedOrDirect_l() || mDoNotReconnect)) { // FIXME re-creation of offloaded and direct tracks is not yet implemented; // reconsider enabling for linear PCM encodings when position can be preserved. result = DEAD_OBJECT; diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 8f712dbdd2..4ae7377779 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -1220,7 +1220,7 @@ public: void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount); // FIXME enum is faster than strcmp() for parameter 'from' - status_t restoreTrack_l(const char *from); + status_t restoreTrack_l(const char *from, bool forceRestore = false); uint32_t getUnderrunCount_l() const; diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp index 5b90158bfb..a8c8010a17 100644 --- a/media/libaudioclient/tests/Android.bp +++ b/media/libaudioclient/tests/Android.bp @@ -133,6 +133,10 @@ cc_defaults { "libaudioutils", ], data: ["bbb*.raw"], + srcs: [ + "audio_test_utils.cpp", + "test_execution_tracer.cpp", + ], test_config_template: "audio_test_template.xml", } @@ -141,7 +145,6 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audiorecord_tests.cpp", - "audio_test_utils.cpp", ], } @@ -150,7 +153,6 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audiotrack_tests.cpp", - "audio_test_utils.cpp", ], } @@ -159,7 +161,6 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audioeffect_tests.cpp", - "audio_test_utils.cpp", ], } @@ -172,7 +173,6 @@ cc_test { ], srcs: [ "audioeffect_analyser.cpp", - "audio_test_utils.cpp", ], static_libs: [ "libpffft", @@ -184,7 +184,6 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audiorouting_tests.cpp", - "audio_test_utils.cpp", ], } @@ -193,14 +192,15 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audioclient_serialization_tests.cpp", - "audio_test_utils.cpp", ], } cc_test { name: "trackplayerbase_tests", defaults: ["libaudioclient_gtests_defaults"], - srcs: ["trackplayerbase_tests.cpp"], + srcs: [ + "trackplayerbase_tests.cpp", + ], } cc_test { @@ -208,6 +208,5 @@ cc_test { defaults: ["libaudioclient_gtests_defaults"], srcs: [ "audiosystem_tests.cpp", - "audio_test_utils.cpp", ], } diff --git a/media/libaudioclient/tests/audioclient_serialization_tests.cpp b/media/libaudioclient/tests/audioclient_serialization_tests.cpp index 707b9b356a..5debabc284 100644 --- a/media/libaudioclient/tests/audioclient_serialization_tests.cpp +++ b/media/libaudioclient/tests/audioclient_serialization_tests.cpp @@ -15,18 +15,23 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "AudioClientSerializationUnitTests" +#define LOG_TAG "AudioClientSerializationTests" #include <cstdint> #include <cstdlib> #include <ctime> - -#include <gtest/gtest.h> +#include <vector> #include <android_audio_policy_configuration_V7_0-enums.h> +#include <gtest/gtest.h> +#include <media/AudioPolicy.h> +#include <media/AudioProductStrategy.h> +#include <media/AudioVolumeGroup.h> +#include <media/VolumeGroupAttributes.h> +#include <system/audio.h> #include <xsdc/XsdcSupport.h> -#include "audio_test_utils.h" +#include "test_execution_tracer.h" using namespace android; namespace xsd { @@ -310,3 +315,9 @@ TEST_P(AudioAttributesParameterizedTest, AudioAttributesBinderization) { // audioStream INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest, ::testing::Combine(testing::ValuesIn(kStreamtypes))); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); + return RUN_ALL_TESTS(); +} diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp index 94accaebe0..f4d37bc133 100644 --- a/media/libaudioclient/tests/audioeffect_analyser.cpp +++ b/media/libaudioclient/tests/audioeffect_analyser.cpp @@ -14,23 +14,26 @@ * limitations under the License. */ +#include <fstream> +#include <iostream> +#include <string> +#include <tuple> +#include <vector> + // #define LOG_NDEBUG 0 #define LOG_TAG "AudioEffectAnalyser" #include <android-base/file.h> #include <android-base/stringprintf.h> +#include <binder/ProcessState.h> #include <gtest/gtest.h> #include <media/AudioEffect.h> #include <system/audio_effects/effect_bassboost.h> #include <system/audio_effects/effect_equalizer.h> -#include <fstream> -#include <iostream> -#include <string> -#include <tuple> -#include <vector> #include "audio_test_utils.h" #include "pffft.hpp" +#include "test_execution_tracer.h" #define CHECK_OK(expr, msg) \ mStatus = (expr); \ @@ -417,3 +420,10 @@ TEST(AudioEffectTest, CheckBassBoostEffect) { prevGain = diffB; } } + +int main(int argc, char** argv) { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); + return RUN_ALL_TESTS(); +} diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp index e12ae23ffe..59d0c6af90 100644 --- a/media/libaudioclient/tests/audioeffect_tests.cpp +++ b/media/libaudioclient/tests/audioeffect_tests.cpp @@ -15,8 +15,9 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "AudioEffectUnitTests" +#define LOG_TAG "AudioEffectTests" +#include <binder/ProcessState.h> #include <gtest/gtest.h> #include <media/AudioEffect.h> #include <system/audio_effects/effect_hapticgenerator.h> @@ -24,6 +25,7 @@ #include <system/audio_effects/effect_visualizer.h> #include "audio_test_utils.h" +#include "test_execution_tracer.h" using namespace android; @@ -563,3 +565,10 @@ TEST(AudioEffectTest, TestHapticEffect) { EXPECT_TRUE(cb->receivedFramesProcessed) << "AudioEffect frames processed callback not received"; } + +int main(int argc, char** argv) { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); + return RUN_ALL_TESTS(); +} diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp index 61edd4ddb8..0bf2e82d44 100644 --- a/media/libaudioclient/tests/audiorecord_tests.cpp +++ b/media/libaudioclient/tests/audiorecord_tests.cpp @@ -24,6 +24,7 @@ #include <gtest/gtest.h> #include "audio_test_utils.h" +#include "test_execution_tracer.h" using namespace android; @@ -261,26 +262,6 @@ INSTANTIATE_TEST_SUITE_P(AudioRecordMiscInput, AudioRecordCreateTest, AUDIO_SOURCE_UNPROCESSED)), GetRecordTestName); -namespace { - -class TestExecutionTracer : public ::testing::EmptyTestEventListener { - public: - void OnTestStart(const ::testing::TestInfo& test_info) override { - TraceTestState("Started", test_info); - } - void OnTestEnd(const ::testing::TestInfo& test_info) override { - TraceTestState("Finished", test_info); - } - void OnTestPartResult(const ::testing::TestPartResult& result) override { LOG(INFO) << result; } - - private: - static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) { - LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name(); - } -}; - -} // namespace - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp index fa990b5d44..764011ba32 100644 --- a/media/libaudioclient/tests/audiorouting_tests.cpp +++ b/media/libaudioclient/tests/audiorouting_tests.cpp @@ -17,13 +17,12 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioRoutingTest" -#include <string.h> - #include <binder/ProcessState.h> #include <cutils/properties.h> #include <gtest/gtest.h> #include "audio_test_utils.h" +#include "test_execution_tracer.h" using namespace android; @@ -267,21 +266,6 @@ TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) { playback->stop(); } -class TestExecutionTracer : public ::testing::EmptyTestEventListener { - public: - void OnTestStart(const ::testing::TestInfo& test_info) override { - TraceTestState("Started", test_info); - } - void OnTestEnd(const ::testing::TestInfo& test_info) override { - TraceTestState("Completed", test_info); - } - - private: - static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) { - ALOGI("%s %s::%s", state.c_str(), test_info.test_suite_name(), test_info.name()); - } -}; - int main(int argc, char** argv) { android::ProcessState::self()->startThreadPool(); ::testing::InitGoogleTest(&argc, argv); diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp index d9789f14d5..03c15f4efd 100644 --- a/media/libaudioclient/tests/audiosystem_tests.cpp +++ b/media/libaudioclient/tests/audiosystem_tests.cpp @@ -14,18 +14,19 @@ * limitations under the License. */ -#define LOG_TAG "AudioSystemTest" - #include <string.h> #include <set> +#define LOG_TAG "AudioSystemTest" + #include <gtest/gtest.h> #include <log/log.h> #include <media/AidlConversionCppNdk.h> #include <media/IAudioFlinger.h> #include "audio_test_utils.h" +#include "test_execution_tracer.h" using android::media::audio::common::AudioDeviceAddress; using android::media::audio::common::AudioDeviceDescription; @@ -706,21 +707,6 @@ TEST_F(AudioSystemTest, SetDeviceConnectedState) { } } -class TestExecutionTracer : public ::testing::EmptyTestEventListener { - public: - void OnTestStart(const ::testing::TestInfo& test_info) override { - TraceTestState("Started", test_info); - } - void OnTestEnd(const ::testing::TestInfo& test_info) override { - TraceTestState("Completed", test_info); - } - - private: - static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) { - ALOGI("%s %s::%s", state.c_str(), test_info.test_suite_name(), test_info.name()); - } -}; - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp index 2b6822575c..0282bd7a5b 100644 --- a/media/libaudioclient/tests/audiotrack_tests.cpp +++ b/media/libaudioclient/tests/audiotrack_tests.cpp @@ -15,10 +15,13 @@ */ //#define LOG_NDEBUG 0 +#define LOG_TAG "AudioTrackTests" +#include <binder/ProcessState.h> #include <gtest/gtest.h> #include "audio_test_utils.h" +#include "test_execution_tracer.h" using namespace android; @@ -209,3 +212,10 @@ INSTANTIATE_TEST_SUITE_P( AUDIO_OUTPUT_FLAG_RAW | AUDIO_OUTPUT_FLAG_FAST, AUDIO_OUTPUT_FLAG_DEEP_BUFFER), ::testing::Values(AUDIO_SESSION_NONE))); + +int main(int argc, char** argv) { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); + return RUN_ALL_TESTS(); +} diff --git a/media/libaudioclient/tests/test_execution_tracer.cpp b/media/libaudioclient/tests/test_execution_tracer.cpp new file mode 100644 index 0000000000..797bb4b1c6 --- /dev/null +++ b/media/libaudioclient/tests/test_execution_tracer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TestExecutionTracer" + +#include "test_execution_tracer.h" + +#include <android-base/logging.h> + +void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) { + TraceTestState("Started", test_info); +} + +void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) { + TraceTestState("Finished", test_info); +} + +void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) { + if (result.failed()) { + LOG(ERROR) << result; + } else { + LOG(INFO) << result; + } +} + +// static +void TestExecutionTracer::TraceTestState(const std::string& state, + const ::testing::TestInfo& test_info) { + LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name(); +} diff --git a/media/libaudioclient/tests/test_execution_tracer.h b/media/libaudioclient/tests/test_execution_tracer.h new file mode 100644 index 0000000000..9031aafa3c --- /dev/null +++ b/media/libaudioclient/tests/test_execution_tracer.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gtest/gtest.h> + +class TestExecutionTracer : public ::testing::EmptyTestEventListener { + public: + void OnTestStart(const ::testing::TestInfo& test_info) override; + void OnTestEnd(const ::testing::TestInfo& test_info) override; + void OnTestPartResult(const ::testing::TestPartResult& result) override; + + private: + static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info); +}; diff --git a/media/libaudioclient/tests/trackplayerbase_tests.cpp b/media/libaudioclient/tests/trackplayerbase_tests.cpp index c9b704dbc3..7317bf0a9f 100644 --- a/media/libaudioclient/tests/trackplayerbase_tests.cpp +++ b/media/libaudioclient/tests/trackplayerbase_tests.cpp @@ -16,10 +16,12 @@ #define LOG_TAG "TrackPlayerBaseTest" +#include <binder/ProcessState.h> #include <gtest/gtest.h> - #include <media/TrackPlayerBase.h> +#include "test_execution_tracer.h" + using namespace android; using namespace android::media; @@ -159,3 +161,10 @@ TEST_P(PauseTestParam, PauseTest) { INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PauseTestParam, ::testing::Values(std::make_tuple(1.0, 75.0, 2, 24000))); + +int main(int argc, char** argv) { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer()); + return RUN_ALL_TESTS(); +} diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp index 1d8fec07b4..af06581028 100644 --- a/media/libaudiohal/impl/DeviceHalAidl.cpp +++ b/media/libaudiohal/impl/DeviceHalAidl.cpp @@ -271,15 +271,16 @@ status_t DeviceHalAidl::getParameters(const String8& keys, String8 *values) { return parseAndGetVendorParameters(mVendorExt, mModule, parameterKeys, values); } -status_t DeviceHalAidl::getInputBufferSize(const struct audio_config* config, size_t* size) { +status_t DeviceHalAidl::getInputBufferSize(struct audio_config* config, size_t* size) { ALOGD("%p %s::%s", this, getClassName().c_str(), __func__); TIME_CHECK(); if (mModule == nullptr) return NO_INIT; if (config == nullptr || size == nullptr) { return BAD_VALUE; } + constexpr bool isInput = true; AudioConfig aidlConfig = VALUE_OR_RETURN_STATUS( - ::aidl::android::legacy2aidl_audio_config_t_AudioConfig(*config, true /*isInput*/)); + ::aidl::android::legacy2aidl_audio_config_t_AudioConfig(*config, isInput)); AudioDevice aidlDevice; aidlDevice.type.type = AudioDeviceType::IN_DEFAULT; AudioSource aidlSource = AudioSource::DEFAULT; @@ -293,6 +294,9 @@ status_t DeviceHalAidl::getInputBufferSize(const struct audio_config* config, si 0 /*handle*/, aidlDevice, aidlFlags, aidlSource, &cleanups, &aidlConfig, &mixPortConfig, &aidlPatch)); } + *config = VALUE_OR_RETURN_STATUS( + ::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput)); + if (mixPortConfig.id == 0) return BAD_VALUE; // HAL suggests a different config. *size = aidlConfig.frameCount * getFrameSizeInBytes(aidlConfig.base.format, aidlConfig.base.channelMask); // Do not disarm cleanups to release temporary port configs. diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h index 9493e47c43..6f8afe50f7 100644 --- a/media/libaudiohal/impl/DeviceHalAidl.h +++ b/media/libaudiohal/impl/DeviceHalAidl.h @@ -113,7 +113,7 @@ class DeviceHalAidl : public DeviceHalInterface, public ConversionHelperAidl, status_t getParameters(const String8& keys, String8 *values) override; // Returns audio input buffer size according to parameters passed. - status_t getInputBufferSize(const struct audio_config* config, size_t* size) override; + status_t getInputBufferSize(struct audio_config* config, size_t* size) override; // Creates and opens the audio hardware output stream. The stream is closed // by releasing all references to the returned object. diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index e8e1f46688..478e0f0026 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -236,7 +236,7 @@ status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { } status_t DeviceHalHidl::getInputBufferSize( - const struct audio_config *config, size_t *size) { + struct audio_config *config, size_t *size) { TIME_CHECK(); if (mDevice == 0) return NO_INIT; AudioConfig hidlConfig; diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h index 7a712df5fc..1362dab4e2 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -67,7 +67,7 @@ class DeviceHalHidl : public DeviceHalInterface, public CoreConversionHelperHidl status_t getParameters(const String8& keys, String8 *values) override; // Returns audio input buffer size according to parameters passed. - status_t getInputBufferSize(const struct audio_config* config, size_t* size) override; + status_t getInputBufferSize(struct audio_config* config, size_t* size) override; // Creates and opens the audio hardware output stream. The stream is closed // by releasing all references to the returned object. diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp index 01fc7fbb6b..347afa60be 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp @@ -26,6 +26,7 @@ #include <aidl/android/hardware/audio/core/IModule.h> #include <aidl/android/media/audio/BnHalAdapterVendorExtension.h> #include <android/binder_manager.h> +#include <cutils/properties.h> #include <media/AidlConversionNdkCpp.h> #include <media/AidlConversionUtil.h> #include <utils/Log.h> @@ -121,8 +122,8 @@ class HalAdapterVendorExtensionWrapper : std::shared_ptr<IHalAdapterVendorExtension> getService(bool reset = false) { std::lock_guard l(mLock); if (reset || !mVendorExt.has_value()) { - auto serviceName = std::string(IHalAdapterVendorExtension::descriptor) + "/default"; - if (AServiceManager_isDeclared(serviceName.c_str())) { + if (property_get_bool("ro.audio.ihaladaptervendorextension_enabled", false)) { + auto serviceName = std::string(IHalAdapterVendorExtension::descriptor) + "/default"; mVendorExt = std::shared_ptr<IHalAdapterVendorExtension>( IHalAdapterVendorExtension::fromBinder(ndk::SpAIBinder( AServiceManager_waitForService(serviceName.c_str())))); diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h index c4841c50c7..0c0184e577 100644 --- a/media/libaudiohal/impl/EffectConversionHelperAidl.h +++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h @@ -69,9 +69,6 @@ class EffectConversionHelperAidl { void* pReplyData); private: - const aidl::android::media::audio::common::AudioFormatDescription kDefaultFormatDescription = { - .type = aidl::android::media::audio::common::AudioFormatType::PCM, - .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT}; const bool mIsProxyEffect; static constexpr int kDefaultframeCount = 0x100; @@ -81,13 +78,16 @@ class EffectConversionHelperAidl { return pt ? std::to_string(*pt) : "nullptr"; } - using AudioChannelLayout = aidl::android::media::audio::common::AudioChannelLayout; const aidl::android::media::audio::common::AudioConfig kDefaultAudioConfig = { .base = {.sampleRate = 44100, - .channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>( - AudioChannelLayout::LAYOUT_STEREO), - .format = kDefaultFormatDescription}, + .channelMask = aidl::android::media::audio::common::AudioChannelLayout::make< + aidl::android::media::audio::common::AudioChannelLayout::layoutMask>( + aidl::android::media::audio::common::AudioChannelLayout:: + LAYOUT_STEREO), + .format = {.type = aidl::android::media::audio::common::AudioFormatType::PCM, + .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT}}, .frameCount = kDefaultframeCount}; + // command handler map typedef status_t (EffectConversionHelperAidl::*CommandHandler)(uint32_t /* cmdSize */, const void* /* pCmdData */, diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp index 49e6827d75..d1794f091b 100644 --- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp +++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp @@ -16,17 +16,17 @@ #include <cstdint> #include <cstring> -#include <optional> #define LOG_TAG "AidlConversionSpatializer" //#define LOG_NDEBUG 0 #include <aidl/android/hardware/audio/effect/DefaultExtension.h> #include <aidl/android/hardware/audio/effect/VendorExtension.h> #include <error/expected_utils.h> -#include <media/AidlConversionNdk.h> +#include <media/AidlConversionCppNdk.h> #include <media/AidlConversionEffect.h> +#include <media/AidlConversionNdk.h> +#include <system/audio_effects/aidl_effects_utils.h> #include <system/audio_effects/effect_spatializer.h> - #include <utils/Log.h> #include "AidlConversionSpatializer.h" @@ -34,38 +34,321 @@ namespace android { namespace effect { -using ::aidl::android::aidl_utils::statusTFromBinderStatus; -using ::aidl::android::hardware::audio::effect::DefaultExtension; -using ::aidl::android::hardware::audio::effect::Parameter; -using ::aidl::android::hardware::audio::effect::VendorExtension; -using ::android::status_t; +using aidl::android::getParameterSpecificField; +using aidl::android::aidl_utils::statusTFromBinderStatus; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::hardware::audio::effect::DefaultExtension; +using aidl::android::hardware::audio::effect::Parameter; +using aidl::android::hardware::audio::effect::Range; +using aidl::android::hardware::audio::effect::Spatializer; +using aidl::android::hardware::audio::effect::VendorExtension; +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::HeadTracking; +using aidl::android::media::audio::common::Spatialization; +using aidl::android::media::audio::common::toString; +using android::status_t; using utils::EffectParamReader; using utils::EffectParamWriter; +bool AidlConversionSpatializer::isSpatializerParameterSupported() { + return mIsSpatializerAidlParamSupported.value_or( + (mIsSpatializerAidlParamSupported = + [&]() { + ::aidl::android::hardware::audio::effect::Parameter aidlParam; + auto id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag, + Spatializer::vendor); + // No range defined in descriptor capability means no Spatializer AIDL + // implementation BAD_VALUE return from getParameter indicates the + // parameter is not supported by HAL + return mDesc.capability.range.getTag() == Range::spatializer && + mEffect->getParameter(id, &aidlParam).getStatus() != + android::BAD_VALUE; + }()) + .value()); +} + status_t AidlConversionSpatializer::setParameter(EffectParamReader& param) { - Parameter aidlParam = VALUE_OR_RETURN_STATUS( - ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param)); + Parameter aidlParam; + if (isSpatializerParameterSupported()) { + uint32_t command = 0; + if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) || + OK != param.readFromParameter(&command)) { + ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str()); + return BAD_VALUE; + } + + switch (command) { + case SPATIALIZER_PARAM_LEVEL: { + Spatialization::Level level = Spatialization::Level::NONE; + if (OK != param.readFromValue(&level)) { + ALOGE("%s invalid level value %s", __func__, param.toString().c_str()); + return BAD_VALUE; + } + aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, + spatializationLevel, level); + break; + } + case SPATIALIZER_PARAM_HEADTRACKING_MODE: { + HeadTracking::Mode mode = HeadTracking::Mode::DISABLED; + if (OK != param.readFromValue(&mode)) { + ALOGE("%s invalid mode value %s", __func__, param.toString().c_str()); + return BAD_VALUE; + } + aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingMode, + mode); + break; + } + case SPATIALIZER_PARAM_HEAD_TO_STAGE: { + const size_t valueSize = param.getValueSize(); + if (valueSize / sizeof(float) > 6 || valueSize % sizeof(float) != 0) { + ALOGE("%s invalid parameter value size %zu", __func__, valueSize); + return BAD_VALUE; + } + std::array<float, 6> headToStage = {}; + for (size_t i = 0; i < valueSize / sizeof(float); i++) { + if (OK != param.readFromValue(&headToStage[i])) { + ALOGE("%s failed to read headToStage from %s", __func__, + param.toString().c_str()); + return BAD_VALUE; + } + } + HeadTracking::SensorData sensorData = + HeadTracking::SensorData::make<HeadTracking::SensorData::headToStage>( + headToStage); + aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, + headTrackingSensorData, sensorData); + break; + } + case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: { + int32_t modeInt32 = 0; + int32_t sensorId = -1; + if (OK != param.readFromValue(&modeInt32) || OK != param.readFromValue(&sensorId)) { + ALOGE("%s %d invalid parameter value %s", __func__, __LINE__, + param.toString().c_str()); + return BAD_VALUE; + } + + const auto mode = static_cast<HeadTracking::ConnectionMode>(modeInt32); + if (mode < *ndk::enum_range<HeadTracking::ConnectionMode>().begin() || + mode > *ndk::enum_range<HeadTracking::ConnectionMode>().end()) { + ALOGE("%s %d invalid mode %d", __func__, __LINE__, modeInt32); + return BAD_VALUE; + } + aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, + headTrackingConnectionMode, mode); + if (status_t status = statusTFromBinderStatus(mEffect->setParameter(aidlParam)); + status != OK) { + ALOGE("%s failed to set headTrackingConnectionMode %s", __func__, + toString(mode).c_str()); + return status; + } + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingSensorId, + sensorId); + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + return statusTFromBinderStatus(mEffect->setParameter(aidlParam)); + } + default: { + ALOGE("%s %d invalid command %u", __func__, __LINE__, command); + return BAD_VALUE; + } + } + } else { + aidlParam = VALUE_OR_RETURN_STATUS( + ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param)); + } + + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); return statusTFromBinderStatus(mEffect->setParameter(aidlParam)); } status_t AidlConversionSpatializer::getParameter(EffectParamWriter& param) { - DefaultExtension defaultExt; - // read parameters into DefaultExtension vector<uint8_t> - defaultExt.bytes.resize(param.getParameterSize()); - if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) { - ALOGE("%s invalid param %s", __func__, param.toString().c_str()); - param.setStatus(BAD_VALUE); - return BAD_VALUE; - } + if (isSpatializerParameterSupported()) { + uint32_t command = 0; + if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) || + OK != param.readFromParameter(&command)) { + ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str()); + return BAD_VALUE; + } - VendorExtension idTag; - idTag.extension.setParcelable(defaultExt); - Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag); - Parameter aidlParam; - RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); - // copy the AIDL extension data back to effect_param_t - return VALUE_OR_RETURN_STATUS( - ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param)); + switch (command) { + case SPATIALIZER_PARAM_SUPPORTED_LEVELS: { + const auto& range = getRange<Range::spatializer, Range::SpatializerRange>( + mDesc.capability, Spatializer::spatializationLevel); + if (!range) { + return BAD_VALUE; + } + for (const auto level : ::ndk::enum_range<Spatialization::Level>()) { + const auto spatializer = + Spatializer::make<Spatializer::spatializationLevel>(level); + if (spatializer >= range->min && spatializer <= range->max) { + if (status_t status = param.writeToValue(&level); status != OK) { + ALOGI("%s %d: write level %s to value failed %d", __func__, __LINE__, + toString(level).c_str(), status); + return status; + } + } + } + return OK; + } + case SPATIALIZER_PARAM_LEVEL: { + Parameter aidlParam; + Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag, + Spatializer::spatializationLevel); + RETURN_STATUS_IF_ERROR( + statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + const auto level = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD( + aidlParam, Spatializer, spatializer, Spatializer::spatializationLevel, + Spatialization::Level)); + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + return param.writeToValue(&level); + } + case SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED: { + const auto& range = getRange<Range::spatializer, Range::SpatializerRange>( + mDesc.capability, Spatializer::spatializationLevel); + if (!range) { + ALOGE("%s %d: range not defined for spatializationLevel", __func__, __LINE__); + return BAD_VALUE; + } + const auto& nonSupport = Spatializer::make<Spatializer::spatializationLevel>( + Spatialization::Level::NONE); + const bool support = (range->min > range->max || + (range->min == nonSupport && range->max == nonSupport)) + ? false + : true; + return param.writeToValue(&support); + } + case SPATIALIZER_PARAM_HEADTRACKING_MODE: { + Parameter aidlParam; + Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag, + Spatializer::headTrackingMode); + RETURN_STATUS_IF_ERROR( + statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD( + aidlParam, Spatializer, spatializer, Spatializer::headTrackingMode, + HeadTracking::Mode)); + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + return param.writeToValue(&mode); + } + case SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS: { + Parameter aidlParam; + Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag, + Spatializer::supportedChannelLayout); + RETURN_STATUS_IF_ERROR( + statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + const auto& supportedLayouts = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD( + aidlParam, Spatializer, spatializer, Spatializer::supportedChannelLayout, + std::vector<AudioChannelLayout>)); + for (const auto& layout : supportedLayouts) { + audio_channel_mask_t mask = VALUE_OR_RETURN_STATUS( + ::aidl::android::aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + layout, false /* isInput */)); + if (status_t status = param.writeToValue(&mask); status != OK) { + ALOGI("%s %d: write mask %s to value failed %d", __func__, __LINE__, + layout.toString().c_str(), status); + return status; + } + } + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + return OK; + } + case SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES: { + const auto& range = getRange<Range::spatializer, Range::SpatializerRange>( + mDesc.capability, Spatializer::spatializationMode); + if (!range) { + return BAD_VALUE; + } + for (const auto mode : ::ndk::enum_range<Spatialization::Mode>()) { + if (const auto spatializer = + Spatializer::make<Spatializer::spatializationMode>(mode); + spatializer >= range->min && spatializer <= range->max) { + if (status_t status = param.writeToValue(&mode); status != OK) { + ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__, + toString(mode).c_str(), status); + return status; + } + } + } + return OK; + } + case SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION: { + const auto& range = getRange<Range::spatializer, Range::SpatializerRange>( + mDesc.capability, Spatializer::headTrackingConnectionMode); + if (!range) { + return BAD_VALUE; + } + for (const auto mode : ::ndk::enum_range<HeadTracking::ConnectionMode>()) { + if (const auto spatializer = + Spatializer::make<Spatializer::headTrackingConnectionMode>(mode); + spatializer < range->min || spatializer > range->max) { + continue; + } + if (status_t status = param.writeToValue(&mode); status != OK) { + ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__, + toString(mode).c_str(), status); + return status; + } + } + return OK; + } + case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: { + status_t status = OK; + Parameter aidlParam; + Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID( + Spatializer, spatializerTag, Spatializer::headTrackingConnectionMode); + RETURN_STATUS_IF_ERROR( + statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD( + aidlParam, Spatializer, spatializer, + Spatializer::headTrackingConnectionMode, HeadTracking::ConnectionMode)); + + id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag, + Spatializer::headTrackingSensorId); + RETURN_STATUS_IF_ERROR( + statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + const auto sensorId = VALUE_OR_RETURN_STATUS( + GET_PARAMETER_SPECIFIC_FIELD(aidlParam, Spatializer, spatializer, + Spatializer::headTrackingSensorId, int32_t)); + uint32_t modeInt32 = static_cast<int32_t>(mode); + if (status = param.writeToValue(&modeInt32); status != OK) { + ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__, + toString(mode).c_str(), status); + return status; + } + if (status = param.writeToValue(&sensorId); status != OK) { + ALOGI("%s %d: write sensorId %d to value failed %d", __func__, __LINE__, + sensorId, status); + return status; + } + ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str()); + return OK; + } + default: { + ALOGE("%s %d invalid command %u", __func__, __LINE__, command); + return BAD_VALUE; + } + } + } else { + Parameter aidlParam; + DefaultExtension defaultExt; + // read parameters into DefaultExtension vector<uint8_t> + defaultExt.bytes.resize(param.getParameterSize()); + if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) { + ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str()); + param.setStatus(BAD_VALUE); + return BAD_VALUE; + } + + VendorExtension idTag; + idTag.extension.setParcelable(defaultExt); + Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag); + RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam))); + ALOGI("%s %d: %s", __func__, __LINE__, + aidlParam.get<Parameter::specific>().toString().c_str()); + // copy the AIDL extension data back to effect_param_t + return VALUE_OR_RETURN_STATUS( + ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param)); + } } } // namespace effect diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h index 7c60b14694..444e5a765a 100644 --- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h +++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h @@ -32,6 +32,8 @@ class AidlConversionSpatializer : public EffectConversionHelperAidl { ~AidlConversionSpatializer() {} private: + std::optional<bool> mIsSpatializerAidlParamSupported; + bool isSpatializerParameterSupported(); status_t setParameter(utils::EffectParamReader& param) override; status_t getParameter(utils::EffectParamWriter& param) override; }; diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h index bb5f851c0b..7f6c1fb144 100644 --- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h @@ -78,8 +78,9 @@ class DeviceHalInterface : public virtual RefBase virtual status_t getParameters(const String8& keys, String8 *values) = 0; // Returns audio input buffer size according to parameters passed. - virtual status_t getInputBufferSize(const struct audio_config *config, - size_t *size) = 0; + // If there is no possibility for the HAL to open an input with the provided + // parameters, the method will return BAD_VALUE and modify the provided `config`. + virtual status_t getInputBufferSize(struct audio_config *config, size_t *size) = 0; // Creates and opens the audio hardware output stream. The stream is closed // by releasing all references to the returned object. diff --git a/media/libaudiohal/tests/Android.bp b/media/libaudiohal/tests/Android.bp index 97510d6da9..1a54500d12 100644 --- a/media/libaudiohal/tests/Android.bp +++ b/media/libaudiohal/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { shared_libs: [ "libvibrator", ], + test_config_template: "AudioHalTestTemplate.xml", } cc_test { diff --git a/media/libaudiohal/tests/AudioHalTestTemplate.xml b/media/libaudiohal/tests/AudioHalTestTemplate.xml new file mode 100644 index 0000000000..b1cb2f017d --- /dev/null +++ b/media/libaudiohal/tests/AudioHalTestTemplate.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 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="Runs {MODULE}."> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="{MODULE}" /> + <option name="native-test-timeout" value="10m" /> + </test> +</configuration> diff --git a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp index 0cb654c13b..d783c647fe 100644 --- a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp +++ b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp @@ -21,10 +21,13 @@ #include <cstdint> #include <cstring> #include <memory> +#include <string> #include <utility> #define LOG_TAG "EffectsFactoryHalInterfaceTest" #include <aidl/android/media/audio/common/AudioUuid.h> +#include <android/media/audio/common/HeadTracking.h> +#include <android/media/audio/common/Spatialization.h> #include <gtest/gtest.h> #include <media/AidlConversionCppNdk.h> #include <media/audiohal/EffectsFactoryHalInterface.h> @@ -40,15 +43,18 @@ #include <system/audio_effects/effect_hapticgenerator.h> #include <system/audio_effects/effect_loudnessenhancer.h> #include <system/audio_effects/effect_ns.h> +#include <system/audio_effects/effect_spatializer.h> #include <utils/RefBase.h> #include <vibrator/ExternalVibrationUtils.h> namespace android { -using ::aidl::android::media::audio::common::AudioUuid; -using ::android::audio::utils::toString; +using aidl::android::media::audio::common::AudioUuid; +using android::audio::utils::toString; using effect::utils::EffectParamReader; using effect::utils::EffectParamWriter; +using media::audio::common::HeadTracking; +using media::audio::common::Spatialization; // EffectsFactoryHalInterface TEST(libAudioHalTest, createEffectsFactoryHalInterface) { @@ -144,34 +150,68 @@ TEST(libAudioHalTest, getHalVersion) { EXPECT_NE(0, version.getMajorVersion()); } +enum ParamSetGetType { SET_N_GET, SET_ONLY, GET_ONLY }; class EffectParamCombination { public: template <typename P, typename V> - void init(const P& p, const V& v, size_t len) { - setBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4); - getBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); - expectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); - parameterSet = - std::make_shared<EffectParamReader>(createEffectParam(setBuffer.data(), p, v)); - parameterGet = - std::make_shared<EffectParamReader>(createEffectParam(getBuffer.data(), p, v)); - parameterExpect = - std::make_shared<EffectParamReader>(createEffectParam(expectBuffer.data(), p, v)); - valueSize = len; + void init(const P& p, const V& v, size_t len, ParamSetGetType type) { + if (type != GET_ONLY) { + mSetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4); + mParameterSet = + std::make_shared<EffectParamReader>(createEffectParam(mSetBuffer.data(), p, v)); + } + + if (type != SET_ONLY) { + mGetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); + mExpectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); + mParameterGet = + std::make_shared<EffectParamReader>(createEffectParam(mGetBuffer.data(), p, v)); + mParameterExpect = std::make_shared<EffectParamReader>( + createEffectParam(mExpectBuffer.data(), p, v)); + mValueSize = len; + } + mType = type; } - std::shared_ptr<EffectParamReader> parameterSet; /* setParameter */ - std::shared_ptr<EffectParamReader> parameterGet; /* getParameter */ - std::shared_ptr<EffectParamReader> parameterExpect; /* expected from getParameter */ - size_t valueSize; /* ValueSize expect to write in reply data buffer */ + std::shared_ptr<EffectParamReader> mParameterSet; /* setParameter */ + std::shared_ptr<EffectParamReader> mParameterGet; /* getParameter */ + std::shared_ptr<EffectParamReader> mParameterExpect; /* expected from getParameter */ + size_t mValueSize = 0ul; /* ValueSize expect to write in reply data buffer */ + ParamSetGetType mType = SET_N_GET; + + std::string toString() { + uint32_t command = 0; + std::string str = "Command: "; + if (mType != GET_ONLY) { + str += (OK == mParameterSet->readFromParameter(&command) ? std::to_string(command) + : mParameterSet->toString()); + } else { + str += (OK == mParameterGet->readFromParameter(&command) ? std::to_string(command) + : mParameterSet->toString()); + } + str += "_"; + str += toString(mType); + return str; + } + + static std::string toString(ParamSetGetType type) { + switch (type) { + case SET_N_GET: + return "Type:SetAndGet"; + case SET_ONLY: + return "Type:SetOnly"; + case GET_ONLY: + return "Type:GetOnly"; + } + } private: - std::vector<uint8_t> setBuffer; - std::vector<uint8_t> getBuffer; - std::vector<uint8_t> expectBuffer; + std::vector<uint8_t> mSetBuffer; + std::vector<uint8_t> mGetBuffer; + std::vector<uint8_t> mExpectBuffer; template <typename P, typename V> - EffectParamReader createEffectParam(void* buf, const P& p, const V& v) { + static EffectParamReader createEffectParam(void* buf, const P& p, const V& v) { effect_param_t* paramRet = (effect_param_t*)buf; paramRet->psize = sizeof(P); paramRet->vsize = sizeof(V); @@ -184,48 +224,106 @@ class EffectParamCombination { }; template <typename P, typename V> -std::shared_ptr<EffectParamCombination> createEffectParamCombination(const P& p, const V& v, - size_t len) { +std::shared_ptr<EffectParamCombination> createEffectParamCombination( + const P& p, const V& v, size_t len, ParamSetGetType type = SET_N_GET) { auto comb = std::make_shared<EffectParamCombination>(); - comb->init(p, v, len); + comb->init(p, v, len, type); return comb; } -enum ParamName { TUPLE_UUID, TUPLE_PARAM_COMBINATION }; -using EffectParamTestTuple = - std::tuple<const effect_uuid_t* /* type UUID */, std::shared_ptr<EffectParamCombination>>; - +enum ParamName { TUPLE_UUID, TUPLE_IS_INPUT, TUPLE_PARAM_COMBINATION }; +using EffectParamTestTuple = std::tuple<const effect_uuid_t* /* type UUID */, bool /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>>; static const effect_uuid_t EXTEND_EFFECT_TYPE_UUID = { 0xfa81dbde, 0x588b, 0x11ed, 0x9b6a, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}}; constexpr std::array<uint8_t, 10> kVendorExtensionData({0xff, 0x5, 0x50, 0xab, 0xcd, 0x00, 0xbd, 0xdb, 0xee, 0xff}); -std::vector<EffectParamTestTuple> testPairs = { - std::make_tuple(FX_IID_AEC, +static std::vector<EffectParamTestTuple> testPairs = { + std::make_tuple( + FX_IID_AEC, true /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{ createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */, - sizeof(int32_t) /* returnValueSize */)), - std::make_tuple(FX_IID_AGC, + sizeof(int32_t) /* returnValueSize */)}), + std::make_tuple( + FX_IID_AGC, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{ createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */, - sizeof(int16_t) /* returnValueSize */)), - std::make_tuple(SL_IID_BASSBOOST, + sizeof(int16_t) /* returnValueSize */)}), + std::make_tuple( + SL_IID_BASSBOOST, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{ createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */, - sizeof(int16_t) /* returnValueSize */)), - std::make_tuple(EFFECT_UIID_DOWNMIX, + sizeof(int16_t) /* returnValueSize */)}), + std::make_tuple( + EFFECT_UIID_DOWNMIX, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{ createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD, - sizeof(int16_t) /* returnValueSize */)), - std::make_tuple(SL_IID_DYNAMICSPROCESSING, + sizeof(int16_t) /* returnValueSize */)}), + std::make_tuple( + SL_IID_DYNAMICSPROCESSING, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination( + std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}), + 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)}), + std::make_tuple( + FX_IID_LOUDNESS_ENHANCER, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination( + LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */, + sizeof(int32_t) /* returnValueSize */)}), + std::make_tuple( + FX_IID_NS, true /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination( + NS_PARAM_LEVEL, 1 /* level */, sizeof(int32_t) /* returnValueSize */)}), + std::make_tuple( + FX_IID_SPATIALIZER, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{ + createEffectParamCombination(SPATIALIZER_PARAM_LEVEL, + SPATIALIZATION_LEVEL_MULTICHANNEL, + sizeof(uint8_t), SET_N_GET), + createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_MODE, + HeadTracking::Mode::RELATIVE_WORLD, + sizeof(uint8_t), SET_N_GET), + createEffectParamCombination( + SPATIALIZER_PARAM_HEAD_TO_STAGE, + std::array<float, 6>{.55f, 0.2f, 1.f, .999f, .43f, 19.f}, + sizeof(std::array<float, 6>), SET_ONLY), + createEffectParamCombination( + SPATIALIZER_PARAM_HEADTRACKING_CONNECTION, + std::array<uint32_t, 2>{ + static_cast<uint32_t>(HeadTracking::ConnectionMode:: + DIRECT_TO_SENSOR_TUNNEL), + 0x5e /* sensorId */}, + sizeof(std::array<uint32_t, 2>), SET_N_GET), + createEffectParamCombination( + SPATIALIZER_PARAM_SUPPORTED_LEVELS, + std::array<Spatialization::Level, 3>{ + Spatialization::Level::NONE, + Spatialization::Level::MULTICHANNEL, + Spatialization::Level::BED_PLUS_OBJECTS}, + sizeof(std::array<uint8_t, 3>), GET_ONLY), + createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED, true, + sizeof(bool), GET_ONLY), + createEffectParamCombination(SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS, + AUDIO_CHANNEL_OUT_5POINT1, sizeof(uint8_t), + GET_ONLY), + createEffectParamCombination( + SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES, + std::array<Spatialization::Mode, 2>{ + Spatialization::Mode::BINAURAL, + Spatialization::Mode::TRANSAURAL}, + sizeof(std::array<uint8_t, 2>), GET_ONLY), createEffectParamCombination( - std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}), - 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)), + SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION, + std::array<HeadTracking::ConnectionMode, 3>{ + HeadTracking::ConnectionMode::FRAMEWORK_PROCESSED, + HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_SW, + HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_TUNNEL}, + sizeof(std::array<uint8_t, 3>), GET_ONLY), + }), std::make_tuple( - FX_IID_LOUDNESS_ENHANCER, - createEffectParamCombination(LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */, - sizeof(int32_t) /* returnValueSize */)), - std::make_tuple(FX_IID_NS, - createEffectParamCombination(NS_PARAM_LEVEL, 1 /* level */, - sizeof(int32_t) /* returnValueSize */)), - std::make_tuple(&EXTEND_EFFECT_TYPE_UUID, - createEffectParamCombination(8, kVendorExtensionData, - sizeof(kVendorExtensionData)))}; + &EXTEND_EFFECT_TYPE_UUID, false /* isInput */, + std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination( + uint32_t{8}, kVendorExtensionData, sizeof(kVendorExtensionData))}), +}; class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTestTuple> { public: @@ -233,13 +331,8 @@ class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTe : mParamTuple(GetParam()), mFactory(EffectsFactoryHalInterface::create()), mTypeUuid(std::get<TUPLE_UUID>(mParamTuple)), - mCombination(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)), - mExpectedValue([&]() { - std::vector<uint8_t> expectData(mCombination->valueSize); - mCombination->parameterExpect->readFromValue(expectData.data(), - mCombination->valueSize); - return expectData; - }()), + mCombinations(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)), + mIsInput(std::get<TUPLE_IS_INPUT>(mParamTuple)), mDescs([&]() { std::vector<effect_descriptor_t> descs; if (mFactory && mTypeUuid && OK == mFactory->getDescriptors(mTypeUuid, &descs)) { @@ -263,7 +356,8 @@ class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTe uint32_t reply = 0; uint32_t replySize = sizeof(reply); ASSERT_EQ(OK, interface->command(EFFECT_CMD_INIT, 0, nullptr, &replySize, &reply)); - ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(mEffectConfig), + + ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), &mEffectConfig, &replySize, &reply)); } @@ -284,60 +378,85 @@ class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTe } void setAndGetParameter(const sp<EffectHalInterface>& interface) { - uint32_t replySize = sizeof(uint32_t); - uint8_t reply[replySize]; - auto parameterSet = mCombination->parameterSet; - ASSERT_EQ(OK, - interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)parameterSet->getTotalSize(), - const_cast<effect_param_t*>(¶meterSet->getEffectParam()), - &replySize, &reply)) - << parameterSet->toString(); - ASSERT_EQ(replySize, sizeof(uint32_t)); - - effect_param_t* getParam = - const_cast<effect_param_t*>(&mCombination->parameterGet->getEffectParam()); - size_t maxReplySize = mCombination->valueSize + sizeof(effect_param_t) + - sizeof(parameterSet->getPaddedParameterSize()); - replySize = maxReplySize; - EXPECT_EQ(OK, - interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)parameterSet->getTotalSize(), - const_cast<effect_param_t*>(¶meterSet->getEffectParam()), - &replySize, getParam)); - EffectParamReader parameterGet(*getParam); - EXPECT_EQ(replySize, parameterGet.getTotalSize()) << parameterGet.toString(); - if (mCombination->valueSize) { - std::vector<uint8_t> response(mCombination->valueSize); - EXPECT_EQ(OK, parameterGet.readFromValue(response.data(), mCombination->valueSize)) - << " try get valueSize " << mCombination->valueSize << " from " - << parameterGet.toString() << " set " << parameterSet->toString(); - EXPECT_EQ(response, mExpectedValue); + for (const auto combination : mCombinations) { + uint32_t replySize = kSetParamReplySize; + uint8_t reply[replySize]; + const auto type = combination->mType; + if (type != GET_ONLY) { + const auto& set = combination->mParameterSet; + ASSERT_EQ(OK, + interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)set->getTotalSize(), + const_cast<effect_param_t*>(&set->getEffectParam()), + &replySize, &reply)) + << set->toString(); + ASSERT_EQ(replySize, kSetParamReplySize); + } + + if (type != SET_ONLY) { + auto get = combination->mParameterGet; + auto expect = combination->mParameterExpect; + effect_param_t* getParam = const_cast<effect_param_t*>(&get->getEffectParam()); + size_t maxReplySize = combination->mValueSize + sizeof(effect_param_t) + + sizeof(expect->getPaddedParameterSize()); + replySize = maxReplySize; + EXPECT_EQ(OK, + interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)expect->getTotalSize(), + const_cast<effect_param_t*>(&expect->getEffectParam()), + &replySize, getParam)); + + EffectParamReader getReader(*getParam); + EXPECT_EQ(replySize, getReader.getTotalSize()) << getReader.toString(); + if (combination->mValueSize) { + std::vector<uint8_t> expectedData(combination->mValueSize); + EXPECT_EQ(OK, expect->readFromValue(expectedData.data(), expectedData.size())) + << combination->toString(); + std::vector<uint8_t> response(combination->mValueSize); + EXPECT_EQ(OK, getReader.readFromValue(response.data(), combination->mValueSize)) + << " try get valueSize " << combination->mValueSize << " from:\n" + << getReader.toString() << "\nexpect:\n" + << expect->toString(); + EXPECT_EQ(expectedData, response) << combination->toString(); + } + } } } + static constexpr size_t kSetParamReplySize = sizeof(uint32_t); const EffectParamTestTuple mParamTuple; const sp<EffectsFactoryHalInterface> mFactory; const effect_uuid_t* mTypeUuid; - std::shared_ptr<EffectParamCombination> mCombination; - const std::vector<uint8_t> mExpectedValue; + std::vector<std::shared_ptr<EffectParamCombination>> mCombinations{}; + const bool mIsInput; const std::vector<effect_descriptor_t> mDescs; - std::vector<sp<EffectHalInterface>> mHalInterfaces; - effect_config_t mEffectConfig = {.inputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_READ, - .format = AUDIO_FORMAT_PCM_FLOAT, - .bufferProvider.getBuffer = nullptr, - .bufferProvider.releaseBuffer = nullptr, - .bufferProvider.cookie = nullptr, - .mask = EFFECT_CONFIG_ALL, - .samplingRate = 48000, - .channels = AUDIO_CHANNEL_IN_STEREO}, - - .outputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_WRITE, - .format = AUDIO_FORMAT_PCM_FLOAT, - .bufferProvider.getBuffer = nullptr, - .bufferProvider.releaseBuffer = nullptr, - .bufferProvider.cookie = nullptr, - .mask = EFFECT_CONFIG_ALL, - .samplingRate = 48000, - .channels = AUDIO_CHANNEL_OUT_STEREO}}; + std::vector<sp<EffectHalInterface>> mHalInterfaces{}; + effect_config_t mEffectConfig = { + .inputCfg = + { + .buffer = {.frameCount = 0x100}, + .samplingRate = 48000, + .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO + : AUDIO_CHANNEL_IN_STEREO, + .bufferProvider = {.getBuffer = nullptr, + .releaseBuffer = nullptr, + .cookie = nullptr}, + .format = AUDIO_FORMAT_PCM_FLOAT, + .accessMode = EFFECT_BUFFER_ACCESS_READ, + .mask = EFFECT_CONFIG_ALL, + }, + .outputCfg = + { + .buffer = {.frameCount = 0x100}, + .samplingRate = 48000, + .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO + : AUDIO_CHANNEL_OUT_STEREO, + .bufferProvider = {.getBuffer = nullptr, + .releaseBuffer = nullptr, + .cookie = nullptr}, + .format = AUDIO_FORMAT_PCM_FLOAT, + .accessMode = EFFECT_BUFFER_ACCESS_WRITE, + .mask = EFFECT_CONFIG_ALL, + }, + }; }; TEST_P(libAudioHalEffectParamTest, setAndGetParam) { @@ -392,7 +511,8 @@ INSTANTIATE_TEST_SUITE_P( AudioUuid uuid = ::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid( *std::get<TUPLE_UUID>(info.param)) .value(); - std::string name = "UUID_" + toString(uuid); + std::string name = "UUID_" + toString(uuid) + "_"; + name += std::get<TUPLE_IS_INPUT>(info.param) ? "_input" : "_output"; std::replace_if( name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); return name; @@ -404,6 +524,4 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -// TODO: b/263986405 Add multi-thread testing - } // namespace android diff --git a/media/libstagefright/CryptoAsync.cpp b/media/libstagefright/CryptoAsync.cpp index 8b5c8ed36b..0fc78ec195 100644 --- a/media/libstagefright/CryptoAsync.cpp +++ b/media/libstagefright/CryptoAsync.cpp @@ -30,6 +30,36 @@ namespace android { +CryptoAsync::CryptoAsyncInfo::CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info) { + if (info == nullptr) { + return; + } + size_t key_len = (info->mKey != nullptr)? 16 : 0; + size_t iv_len = (info->mIv != nullptr)? 16 : 0; + mNumSubSamples = info->mNumSubSamples; + mMode = info->mMode; + mPattern = info->mPattern; + if (key_len > 0) { + mKeyBuffer = ABuffer::CreateAsCopy((void*)info->mKey, key_len); + mKey = (uint8_t*)(mKeyBuffer.get() != nullptr ? mKeyBuffer.get()->data() : nullptr); + } + if (iv_len > 0) { + mIvBuffer = ABuffer::CreateAsCopy((void*)info->mIv, iv_len); + mIv = (uint8_t*)(mIvBuffer.get() != nullptr ? mIvBuffer.get()->data() : nullptr); + } + mSubSamplesBuffer = + new ABuffer(sizeof(CryptoPlugin::SubSample) * mNumSubSamples); + if (mSubSamplesBuffer.get()) { + CryptoPlugin::SubSample * samples = + (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data()); + for (int s = 0 ; s < mNumSubSamples ; s++) { + samples[s].mNumBytesOfClearData = info->mSubSamples[s].mNumBytesOfClearData; + samples[s].mNumBytesOfEncryptedData = info->mSubSamples[s].mNumBytesOfEncryptedData; + } + mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data(); + } +} + CryptoAsync::~CryptoAsync() { } @@ -79,23 +109,27 @@ status_t CryptoAsync::decryptAndQueue(sp<AMessage> & msg) { sp<ABuffer> keyBuffer; sp<ABuffer> ivBuffer; sp<ABuffer> subSamplesBuffer; - msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks); - msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks); - msg->findBuffer("key", &keyBuffer); - msg->findBuffer("iv", &ivBuffer); - msg->findBuffer("subSamples", &subSamplesBuffer); - msg->findInt32("secure", &secure); - msg->findSize("numSubSamples", &numSubSamples); - msg->findObject("buffer", &obj); - msg->findInt32("mode", (int32_t*)&mode); AString errorDetailMsg; - const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr; - const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr; - const CryptoPlugin::SubSample * subSamples = - (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data()); + msg->findObject("buffer", &obj); + msg->findInt32("secure", &secure); sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get()); - err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode, - pattern, subSamples, numSubSamples, &errorDetailMsg); + if (buffer->meta()->findObject("cryptoInfos", &obj)) { + err = channel->queueSecureInputBuffers(buffer, secure, &errorDetailMsg); + } else { + msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks); + msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks); + msg->findBuffer("key", &keyBuffer); + msg->findBuffer("iv", &ivBuffer); + msg->findBuffer("subSamples", &subSamplesBuffer); + msg->findSize("numSubSamples", &numSubSamples); + msg->findInt32("mode", (int32_t*)&mode); + const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr; + const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr; + const CryptoPlugin::SubSample * subSamples = + (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data()); + err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode, + pattern, subSamples, numSubSamples, &errorDetailMsg); + } if (err != OK) { std::list<sp<AMessage>> errorList; msg->removeEntryByName("buffer"); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index d50c06b8ff..a18dbfeaf1 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -1640,6 +1640,11 @@ off64_t MPEG4Writer::addSample_l( ALOGV("buffer->range_length:%lld", (long long)buffer->range_length()); if (buffer->meta_data().findInt64(kKeySampleFileOffset, &offset)) { ALOGV("offset:%lld, old_offset:%lld", (long long)offset, (long long)old_offset); + if (mMaxOffsetAppend > offset) { + // This has already been appended, skip updating mOffset value. + *bytesWritten = buffer->range_length(); + return offset; + } if (old_offset == offset) { mOffset += buffer->range_length(); } else { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 770da65c7b..305d42fda7 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -300,7 +300,6 @@ static status_t generateFlagsFromAccessUnitInfo( return -EINVAL; } msg->setInt32("flags", bufferFlags); - msg->setObject("accessUnitInfo", bufferInfos); } return OK; } @@ -3299,6 +3298,58 @@ status_t MediaCodec::queueSecureInputBuffer( return err; } +status_t MediaCodec::queueSecureInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp<BufferInfosWrapper> &auInfo, + const sp<CryptoInfosWrapper> &cryptoInfos, + AString *errorDetailMsg) { + if (errorDetailMsg != NULL) { + errorDetailMsg->clear(); + } + sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); + uint32_t bufferFlags = 0; + uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG; + uint32_t andFlags = flagsinAllAU; + if (auInfo == nullptr + || auInfo->value.empty() + || cryptoInfos == nullptr + || cryptoInfos->value.empty()) { + ALOGE("ERROR: Large Audio frame with no BufferInfo/CryptoInfo"); + return BAD_VALUE; + } + int infoIdx = 0; + std::vector<AccessUnitInfo> &accessUnitInfo = auInfo->value; + int64_t minTimeUs = accessUnitInfo.front().mTimestamp; + bool foundEndOfStream = false; + for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) { + bufferFlags |= accessUnitInfo[infoIdx].mFlags; + andFlags &= accessUnitInfo[infoIdx].mFlags; + if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) { + foundEndOfStream = true; + } + } + bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU)); + if (infoIdx != accessUnitInfo.size()) { + ALOGE("queueInputBuffers has incorrect access-units"); + return -EINVAL; + } + msg->setSize("index", index); + msg->setSize("offset", offset); + msg->setSize("ssize", size); + msg->setInt64("timeUs", minTimeUs); + msg->setInt32("flags", bufferFlags); + msg->setObject("accessUnitInfo", auInfo); + msg->setObject("cryptoInfos", cryptoInfos); + msg->setPointer("errorDetailMsg", errorDetailMsg); + + sp<AMessage> response; + status_t err = PostAndAwaitResponse(msg, &response); + + return err; +} + status_t MediaCodec::queueBuffer( size_t index, const std::shared_ptr<C2Buffer> &buffer, @@ -3320,6 +3371,7 @@ status_t MediaCodec::queueBuffer( if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) { return err; } + msg->setObject("accessUnitInfo", bufferInfos); if (tunings && tunings->countEntries() > 0) { msg->setMessage("tunings", tunings); } @@ -3334,13 +3386,9 @@ status_t MediaCodec::queueEncryptedBuffer( size_t index, const sp<hardware::HidlMemory> &buffer, size_t offset, - const CryptoPlugin::SubSample *subSamples, - size_t numSubSamples, - const uint8_t key[16], - const uint8_t iv[16], - CryptoPlugin::Mode mode, - const CryptoPlugin::Pattern &pattern, + size_t size, const sp<BufferInfosWrapper> &bufferInfos, + const sp<CryptoInfosWrapper> &cryptoInfos, const sp<AMessage> &tunings, AString *errorDetailMsg) { if (errorDetailMsg != NULL) { @@ -3349,6 +3397,9 @@ status_t MediaCodec::queueEncryptedBuffer( if (bufferInfos == nullptr || bufferInfos->value.empty()) { return BAD_VALUE; } + if (cryptoInfos == nullptr || cryptoInfos->value.empty()) { + return BAD_VALUE; + } status_t err = OK; sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); msg->setSize("index", index); @@ -3356,13 +3407,9 @@ status_t MediaCodec::queueEncryptedBuffer( new WrapperObject<sp<hardware::HidlMemory>>{buffer}}; msg->setObject("memory", memory); msg->setSize("offset", offset); - msg->setPointer("subSamples", (void *)subSamples); - msg->setSize("numSubSamples", numSubSamples); - msg->setPointer("key", (void *)key); - msg->setPointer("iv", (void *)iv); - msg->setInt32("mode", mode); - msg->setInt32("encryptBlocks", pattern.mEncryptBlocks); - msg->setInt32("skipBlocks", pattern.mSkipBlocks); + msg->setSize("ssize", size); + msg->setObject("cryptoInfos", cryptoInfos); + msg->setObject("accessUnitInfo", bufferInfos); if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) { return err; } @@ -6072,22 +6119,26 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { mErrorLog.log(LOG_TAG, "queuing secure buffer without mCrypto or mDescrambler!"); return -EINVAL; } - CHECK(msg->findPointer("subSamples", (void **)&subSamples)); - CHECK(msg->findSize("numSubSamples", &numSubSamples)); - CHECK(msg->findPointer("key", (void **)&key)); - CHECK(msg->findPointer("iv", (void **)&iv)); - CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks)); - CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks)); - - int32_t tmp; - CHECK(msg->findInt32("mode", &tmp)); - - mode = (CryptoPlugin::Mode)tmp; - - size = 0; - for (size_t i = 0; i < numSubSamples; ++i) { - size += subSamples[i].mNumBytesOfClearData; - size += subSamples[i].mNumBytesOfEncryptedData; + sp<RefBase> obj; + if (msg->findObject("cryptoInfos", &obj)) { + CHECK(msg->findSize("ssize", &size)); + } else { + CHECK(msg->findPointer("subSamples", (void **)&subSamples)); + CHECK(msg->findSize("numSubSamples", &numSubSamples)); + CHECK(msg->findPointer("key", (void **)&key)); + CHECK(msg->findPointer("iv", (void **)&iv)); + CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks)); + CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks)); + + int32_t tmp; + CHECK(msg->findInt32("mode", &tmp)); + + mode = (CryptoPlugin::Mode)tmp; + size = 0; + for (size_t i = 0; i < numSubSamples; ++i) { + size += subSamples[i].mNumBytesOfClearData; + size += subSamples[i].mNumBytesOfEncryptedData; + } } } @@ -6114,7 +6165,7 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { status_t err = OK; sp<RefBase> obj; if (msg->findObject("accessUnitInfo", &obj)) { - buffer->meta()->setObject("accessUnitInfo", obj); + buffer->meta()->setObject("accessUnitInfo", obj); } buffer->meta()->setInt64("timeUs", timeUs); if (flags & BUFFER_FLAG_EOS) { @@ -6152,35 +6203,48 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { return err; }; auto buildCryptoInfoAMessage = [&](const sp<AMessage> & cryptoInfo, int32_t action) { - size_t key_len = (key != nullptr)? 16 : 0; - size_t iv_len = (iv != nullptr)? 16 : 0; - sp<ABuffer> shared_key; - sp<ABuffer> shared_iv; - if (key_len > 0) { - shared_key = ABuffer::CreateAsCopy((void*)key, key_len); - } - if (iv_len > 0) { - shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len); - } - sp<ABuffer> subSamples_buffer = - new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples); - CryptoPlugin::SubSample * samples = - (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data()); - for (int s = 0 ; s < numSubSamples ; s++) { - samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData; - samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData; - } // set decrypt Action cryptoInfo->setInt32("action", action); cryptoInfo->setObject("buffer", buffer); cryptoInfo->setInt32("secure", mFlags & kFlagIsSecure); - cryptoInfo->setBuffer("key", shared_key); - cryptoInfo->setBuffer("iv", shared_iv); - cryptoInfo->setInt32("mode", (int)mode); - cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks); - cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks); - cryptoInfo->setBuffer("subSamples", subSamples_buffer); - cryptoInfo->setSize("numSubSamples", numSubSamples); + sp<RefBase> obj; + if (msg->findObject("cryptoInfos", &obj)) { + sp<CryptoInfosWrapper> infos{(CryptoInfosWrapper*)obj.get()}; + sp<CryptoInfosWrapper> asyncInfos{ + new CryptoInfosWrapper(std::vector<std::unique_ptr<CodecCryptoInfo>>())}; + for (std::unique_ptr<CodecCryptoInfo> &info : infos->value) { + if (info) { + asyncInfos->value.emplace_back(new CryptoAsync::CryptoAsyncInfo(info)); + } + } + buffer->meta()->setObject("cryptoInfos", asyncInfos); + } else { + size_t key_len = (key != nullptr)? 16 : 0; + size_t iv_len = (iv != nullptr)? 16 : 0; + sp<ABuffer> shared_key; + sp<ABuffer> shared_iv; + if (key_len > 0) { + shared_key = ABuffer::CreateAsCopy((void*)key, key_len); + } + if (iv_len > 0) { + shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len); + } + sp<ABuffer> subSamples_buffer = + new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples); + CryptoPlugin::SubSample * samples = + (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data()); + for (int s = 0 ; s < numSubSamples ; s++) { + samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData; + samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData; + } + cryptoInfo->setBuffer("key", shared_key); + cryptoInfo->setBuffer("iv", shared_iv); + cryptoInfo->setInt32("mode", (int)mode); + cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks); + cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks); + cryptoInfo->setBuffer("subSamples", subSamples_buffer); + cryptoInfo->setSize("numSubSamples", numSubSamples); + } }; if (c2Buffer || memory) { sp<AMessage> tunings = NULL; @@ -6190,15 +6254,37 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { status_t err = OK; if (c2Buffer) { err = mBufferChannel->attachBuffer(c2Buffer, buffer); + // to prevent unnecessary copy for single info case. + if (msg->findObject("accessUnitInfo", &obj)) { + sp<BufferInfosWrapper> infos{(BufferInfosWrapper*)(obj.get())}; + if (infos->value.size() == 1) { + msg->removeEntryByName("accessUnitInfo"); + } + } } else if (memory) { AString errorDetailMsg; - err = mBufferChannel->attachEncryptedBuffer( - memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern, - offset, subSamples, numSubSamples, buffer, &errorDetailMsg); + if (msg->findObject("cryptoInfos", &obj)) { + buffer->meta()->setSize("ssize", size); + buffer->meta()->setObject("cryptoInfos", obj); + if (msg->findObject("accessUnitInfo", &obj)) { + // the reference will be same here and + // setBufferParams + buffer->meta()->setObject("accessUnitInfo", obj); + } + err = mBufferChannel->attachEncryptedBuffers( + memory, + offset, + buffer, + (mFlags & kFlagIsSecure), + &errorDetailMsg); + } else { + err = mBufferChannel->attachEncryptedBuffer( + memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern, + offset, subSamples, numSubSamples, buffer, &errorDetailMsg); + } if (err != OK && hasCryptoOrDescrambler() && (mFlags & kFlagUseCryptoAsync)) { // create error detail - AString errorDetailMsg; sp<AMessage> cryptoErrorInfo = new AMessage(); buildCryptoInfoAMessage(cryptoErrorInfo, CryptoAsync::kActionDecrypt); cryptoErrorInfo->setInt32("err", err); @@ -6270,10 +6356,17 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { } } if (mCryptoAsync) { + // TODO b/316565675 - enable async path for audio // prepare a message and enqueue sp<AMessage> cryptoInfo = new AMessage(); buildCryptoInfoAMessage(cryptoInfo, CryptoAsync::kActionDecrypt); mCryptoAsync->decrypt(cryptoInfo); + } else if (msg->findObject("cryptoInfos", &obj)) { + buffer->meta()->setObject("cryptoInfos", obj); + err = mBufferChannel->queueSecureInputBuffers( + buffer, + (mFlags & kFlagIsSecure), + errorDetailMsg); } else { err = mBufferChannel->queueSecureInputBuffer( buffer, @@ -6647,7 +6740,7 @@ void MediaCodec::onOutputBufferAvailable() { if (accessUnitInfoObj) { outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE; msg->setObject("accessUnitInfo", accessUnitInfoObj); - sp<BufferInfosWrapper> auInfo( + sp<BufferInfosWrapper> auInfo( (decltype(auInfo.get()))accessUnitInfoObj.get()); auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM; } diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h index 8741daa0b2..bffb29488d 100644 --- a/media/libstagefright/include/media/stagefright/CodecBase.h +++ b/media/libstagefright/include/media/stagefright/CodecBase.h @@ -71,6 +71,26 @@ struct AccessUnitInfo { ~AccessUnitInfo() {} }; +struct CodecCryptoInfo { + size_t mNumSubSamples{0}; + CryptoPlugin::SubSample *mSubSamples{nullptr}; + uint8_t *mIv{nullptr}; + uint8_t *mKey{nullptr}; + enum CryptoPlugin::Mode mMode; + CryptoPlugin::Pattern mPattern; + + virtual ~CodecCryptoInfo() {} +protected: + CodecCryptoInfo(): + mNumSubSamples(0), + mSubSamples(nullptr), + mIv(nullptr), + mKey(nullptr), + mMode{CryptoPlugin::kMode_Unencrypted}, + mPattern{0, 0} { + } +}; + struct CodecParameterDescriptor { std::string name; AMessage::Type type; @@ -372,6 +392,30 @@ public: const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, AString *errorDetailMsg) = 0; + + /** + * Queue a secure input buffer with multiple access units into the buffer channel. + * + * @param buffer The buffer to queue. The access unit delimiters and crypto + * subsample information is included in the buffer metadata. + * @param secure Whether the buffer is secure. + * @param errorDetailMsg The error message to be set in case of error. + * @return OK if successful; + * -ENOENT of the buffer is not known + * -ENOSYS if mCrypto is not set so that decryption is not + * possible; + * other errors if decryption failed. + */ + virtual status_t queueSecureInputBuffers( + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString *errorDetailMsg) { + (void)buffer; + (void)secure; + (void)errorDetailMsg; + return -ENOSYS; + } + /** * Attach a Codec 2.0 buffer to MediaCodecBuffer. * @@ -418,6 +462,34 @@ public: (void)errorDetailMsg; return -ENOSYS; } + + /** + * Attach an encrypted HidlMemory buffer containing multiple access units to an index + * + * @param memory The memory to attach. + * @param offset index??? + * @param buffer The MediaCodecBuffer to attach the memory to. The access + * unit delimiters and crypto subsample information is included + * in the buffer metadata. + * @param secure Whether the buffer is secure. + * @param errorDetailMsg The error message to be set if an error occurs. + * @return OK if successful; + * -ENOENT if index is not recognized + * -ENOSYS if attaching buffer is not possible or not supported + */ + virtual status_t attachEncryptedBuffers( + const sp<hardware::HidlMemory> &memory, + size_t offset, + const sp<MediaCodecBuffer> &buffer, + bool secure, + AString* errorDetailMsg) { + (void)memory; + (void)offset; + (void)buffer; + (void)secure; + (void)errorDetailMsg; + return -ENOSYS; + } /** * Request buffer rendering at specified time. * diff --git a/media/libstagefright/include/media/stagefright/CryptoAsync.h b/media/libstagefright/include/media/stagefright/CryptoAsync.h index b675518d01..acb3daef51 100644 --- a/media/libstagefright/include/media/stagefright/CryptoAsync.h +++ b/media/libstagefright/include/media/stagefright/CryptoAsync.h @@ -85,6 +85,18 @@ public: kActionDecrypt = (1 << 0), kActionAttachEncryptedBuffer = (1 << 1) }; + + // This struct is meant to copy the mapped contents from the original info. + struct CryptoAsyncInfo : public CodecCryptoInfo { + public: + explicit CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info); + virtual ~CryptoAsyncInfo() = default; + protected: + // all backup buffers for the base object. + sp<ABuffer> mKeyBuffer; + sp<ABuffer> mIvBuffer; + sp<ABuffer> mSubSamplesBuffer; + }; protected: // Message types for the looper diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index b0b1427e73..9ecb12e946 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -59,6 +59,7 @@ struct BatteryChecker; class BufferChannelBase; struct AccessUnitInfo; struct CodecBase; +struct CodecCryptoInfo; struct CodecParameterDescriptor; class IBatteryStats; struct ICrypto; @@ -81,6 +82,7 @@ using aidl::android::media::MediaResourceParcel; using aidl::android::media::ClientConfigParcel; typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper; +typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper; struct MediaCodec : public AHandler { enum Domain { @@ -210,6 +212,14 @@ struct MediaCodec : public AHandler { uint32_t flags, AString *errorDetailMsg = NULL); + status_t queueSecureInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp<BufferInfosWrapper> &accessUnitInfo, + const sp<CryptoInfosWrapper> &cryptoInfos, + AString *errorDetailMsg = NULL); + status_t queueBuffer( size_t index, const std::shared_ptr<C2Buffer> &buffer, @@ -221,13 +231,9 @@ struct MediaCodec : public AHandler { size_t index, const sp<hardware::HidlMemory> &memory, size_t offset, - const CryptoPlugin::SubSample *subSamples, - size_t numSubSamples, - const uint8_t key[16], - const uint8_t iv[16], - CryptoPlugin::Mode mode, - const CryptoPlugin::Pattern &pattern, + size_t size, const sp<BufferInfosWrapper> &bufferInfos, + const sp<CryptoInfosWrapper> &cryptoInfos, const sp<AMessage> &tunings, AString *errorDetailMsg = NULL); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 7d63afbf67..c78e98e87f 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1933,7 +1933,7 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form mHardwareStatus = AUDIO_HW_IDLE; // Change parameters of the configuration each iteration until we find a - // configuration that the device will support. + // configuration that the device will support, or HAL suggests what it supports. audio_config_t config = AUDIO_CONFIG_INITIALIZER; for (auto testChannelMask : channelMasks) { config.channel_mask = testChannelMask; @@ -1943,11 +1943,16 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form config.sample_rate = testSampleRate; size_t bytes = 0; + audio_config_t loopConfig = config; status_t result = dev->getInputBufferSize(&config, &bytes); + if (result == BAD_VALUE) { + // Retry with the config suggested by the HAL. + result = dev->getInputBufferSize(&config, &bytes); + } if (result != OK || bytes == 0) { + config = loopConfig; continue; } - if (config.sample_rate != sampleRate || config.channel_mask != channelMask || config.format != format) { uint32_t dstChannelCount = audio_channel_count_from_in_mask(channelMask); diff --git a/services/audioparameterparser/Android.bp b/services/audioparameterparser/Android.bp index 18205bd9d1..b3da333efa 100644 --- a/services/audioparameterparser/Android.bp +++ b/services/audioparameterparser/Android.bp @@ -57,7 +57,6 @@ cc_binary { relative_install_path: "hw", init_rc: ["android.hardware.audio.parameter_parser.example_service.rc"], - vintf_fragments: ["android.hardware.audio.parameter_parser.example_service.xml"], defaults: [ "android.hardware.audio.parameter_parser.example_defaults", diff --git a/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml b/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml deleted file mode 100644 index 91addaa280..0000000000 --- a/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml +++ /dev/null @@ -1,7 +0,0 @@ -<manifest version="1.0" type="framework"> - <hal format="aidl"> - <name>android.media.audio</name> - <version>1</version> - <fqname>IHalAdapterVendorExtension/default</fqname> - </hal> -</manifest> diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 57d2b20669..0bc2e8a756 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -5172,8 +5172,7 @@ void CameraService::updateStatus(StatusInternal status, const std::string& camer for (auto& listener : mListenerList) { bool isVendorListener = listener->isVendorListener(); if (shouldSkipStatusUpdates(deviceKind, isVendorListener, - listener->getListenerPid(), listener->getListenerUid()) || - isVendorListener) { + listener->getListenerPid(), listener->getListenerUid())) { ALOGV("Skipping discovery callback for system-only camera device %s", cameraId.c_str()); continue; |