Merge "fix -Wunreachable-code-loop-increment"
diff --git a/apex/Android.bp b/apex/Android.bp
index 73dc264..c5dd420 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -42,7 +42,14 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media-androidManifest",
- legacy_android10_support: true,
+ // IMPORTANT: For the APEX to be installed on Android 10 (API 29),
+ // min_sdk_version should be 29. This enables the build system to make
+ // sure the package compatible to Android 10 in two ways:
+ // - build the APEX package compatible to Android 10
+ // so that the package can be installed.
+ // - build artifacts (lib/javalib/bin) against Android 10 SDK
+ // so that the artifacts can run.
+ min_sdk_version: "29",
}
apex {
@@ -79,7 +86,14 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media.swcodec-androidManifest",
- legacy_android10_support: true,
+ // IMPORTANT: For the APEX to be installed on Android 10 (API 29),
+ // min_sdk_version should be 29. This enables the build system to make
+ // sure the package compatible to Android 10 in two ways:
+ // - build the APEX package compatible to Android 10
+ // so that the package can be installed.
+ // - build artifacts (lib/javalib/bin) against Android 10 SDK
+ // so that the artifacts can run.
+ min_sdk_version: "29",
}
prebuilt_etc {
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
index 70c887a..9aab29a 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -574,9 +574,8 @@
if (!serviceRet.isOk() || status != Status::NO_ERROR) {
ALOGE("%s: connect camera device failed", __FUNCTION__);
- // TODO: Convert serviceRet to camera_status_t
delete device;
- return ACAMERA_ERROR_UNKNOWN;
+ return utils::convertFromHidl(status);
}
if (deviceRemote == nullptr) {
ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__);
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
index 19ccbf9..c2d2540 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -628,6 +628,7 @@
mComplexity = mIntf->getComplexity_l();
mQuality = mIntf->getQuality_l();
mGop = mIntf->getGop_l();
+ mRequestSync = mIntf->getRequestSync_l();
}
c2_status_t status = initEncParams();
@@ -956,7 +957,7 @@
}
}
- // handle dynamic config parameters
+ // handle dynamic bitrate change
{
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
@@ -983,6 +984,26 @@
work->workletsProcessed = 1u;
return;
}
+ // handle request key frame
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync;
+ requestSync = mIntf->getRequestSync_l();
+ lock.unlock();
+ if (requestSync != mRequestSync) {
+ // we can handle IDR immediately
+ if (requestSync->value) {
+ // unset request
+ C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
+ ALOGV("Got sync request");
+ //Force this as an IDR frame
+ s_encode_ip.i4_force_idr_flag = 1;
+ }
+ mRequestSync = requestSync;
+ }
+ }
uint64_t timeDelay = 0;
uint64_t timeTaken = 0;
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h
index 140b4a9..5ea4602 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.h
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.h
@@ -88,6 +88,7 @@
std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
std::shared_ptr<C2StreamQualityTuning::output> mQuality;
std::shared_ptr<C2StreamGopTuning::output> mGop;
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
#ifdef FILE_DUMP_ENABLE
char mInFile[200];
char mOutFile[200];
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index a759e8f..fbc9c8a 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -783,7 +783,13 @@
}
}
- CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016);
+ if(img->fmt != VPX_IMG_FMT_I420 && img->fmt != VPX_IMG_FMT_I42016) {
+ ALOGE("img->fmt %d not supported", img->fmt);
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return false;
+ }
std::shared_ptr<C2GraphicBlock> block;
uint32_t format = HAL_PIXEL_FORMAT_YV12;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 31b870e..1cbcb8d 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1783,7 +1783,7 @@
// copy standard infos to graphic buffers if not already present (otherwise, we
// may overwrite the actual intermediate value with a final value)
stream = 0;
- const static std::vector<C2Param::Index> stdGfxInfos = {
+ const static C2Param::Index stdGfxInfos[] = {
C2StreamRotationInfo::output::PARAM_TYPE,
C2StreamColorAspectsInfo::output::PARAM_TYPE,
C2StreamDataSpaceInfo::output::PARAM_TYPE,
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 6b75eba..d7f38c5 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -319,10 +319,11 @@
// Obtain Codec2Client
std::vector<Traits> traits = Codec2Client::ListComponents();
- // parse APEX XML first, followed by vendor XML
+ // parse APEX XML first, followed by vendor XML.
+ // Note: APEX XML names do not depend on ro.media.xml_variant.* properties.
MediaCodecsXmlParser parser;
parser.parseXmlFilesInSearchDirs(
- parser.getDefaultXmlNames(),
+ { "media_codecs.xml", "media_codecs_performance.xml" },
{ "/apex/com.android.media.swcodec/etc" });
// TODO: remove these c2-specific files once product moved to default file names
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 28190ea..2b2506c 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -144,11 +144,13 @@
&mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
- char typeBuffer[64], uuidBuffer[64];
+ char typeBuffer[64] = {}, uuidBuffer[64] = {};
guidToString(type, typeBuffer, sizeof(typeBuffer));
guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
- typeBuffer, uuidBuffer, mStatus);
+ type != nullptr ? typeBuffer : "NULL",
+ uuid != nullptr ? uuidBuffer : "NULL",
+ mStatus);
if (iEffect == 0) {
mStatus = NO_INIT;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index a1b04ca..271e186 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -884,7 +884,6 @@
{
// previous and new IAudioRecord sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -902,7 +901,7 @@
// start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -939,6 +938,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -959,6 +959,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioRecord instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioRecord sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mInOverrun = false;
mProxy->releaseBuffer(&buffer);
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 480930b..481becc 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -783,6 +783,13 @@
// ---------------------------------------------------------------------------
+void AudioSystem::onNewAudioModulesAvailable()
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return;
+ aps->onNewAudioModulesAvailable();
+}
+
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 4a80cd3..9a66d48 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1665,7 +1665,6 @@
{
// previous and new IAudioTrack sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -1682,7 +1681,7 @@
{ // start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -1729,6 +1728,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -1752,6 +1752,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioTrack instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioTrack sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mReleased += stepCount;
mInUnderrun = false;
mProxy->releaseBuffer(&buffer);
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0facaf8..24d7c92 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -108,6 +108,7 @@
SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
+ AUDIO_MODULES_UPDATED, // oneway
};
#define MAX_ITEMS_PER_LIST 1024
@@ -1336,6 +1337,13 @@
}
return static_cast<status_t>(reply.readInt32());
}
+
+ virtual void onNewAudioModulesAvailable()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ remote()->transact(AUDIO_MODULES_UPDATED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -1401,7 +1409,8 @@
case SET_RTT_ENABLED:
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
case REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
- case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
+ case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
+ case AUDIO_MODULES_UPDATED: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@@ -2458,6 +2467,12 @@
return NO_ERROR;
}
+ case AUDIO_MODULES_UPDATED: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ onNewAudioModulesAvailable();
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index a3c0fe4..574302b 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -92,6 +92,11 @@
int8_t* i8; // unsigned 8-bit, offset by 0x80
// input to obtainBuffer(): unused, output: pointer to buffer
};
+
+ uint32_t sequence; // IAudioRecord instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -420,14 +425,17 @@
* frameCount number of frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioRecord instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index a86297d..925bc89 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -211,6 +211,7 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
+ static void onNewAudioModulesAvailable();
static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state,
const char *device_address, const char *device_name,
audio_format_t encodedFormat);
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index df5eabc..c607918 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -107,6 +107,11 @@
int16_t* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
}; // input to obtainBuffer(): unused, output: pointer to buffer
+
+ uint32_t sequence; // IAudioTrack instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -692,14 +697,17 @@
* frameCount number of [empty slots for] frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of [empty slots for] frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioTrack instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
size_t *nonContig = NULL);
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 9b91d6d..3edac33 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -42,6 +42,7 @@
//
// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
//
+ virtual void onNewAudioModulesAvailable() = 0;
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
index e6e9688..1c0eacb 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
@@ -15,12 +15,13 @@
*/
#include <string.h>
-#include <vector>
+#include <set>
#define LOG_TAG "DevicesFactoryHalHidl"
//#define LOG_NDEBUG 0
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hidl/manager/1.0/IServiceNotification.h>
#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h)
#include <media/audiohal/hidl/HalDeathHandler.h>
#include <utils/Log.h>
@@ -29,33 +30,57 @@
#include "DeviceHalHidl.h"
#include "DevicesFactoryHalHidl.h"
-#include <set>
-
using ::android::hardware::audio::CPP_VERSION::IDevice;
using ::android::hardware::audio::CPP_VERSION::Result;
using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::hidl::manager::V1_0::IServiceNotification;
namespace android {
namespace CPP_VERSION {
-DevicesFactoryHalHidl::DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory) {
- ALOG_ASSERT(devicesFactory != nullptr, "Provided IDevicesFactory service is NULL");
+class ServiceNotificationListener : public IServiceNotification {
+ public:
+ explicit ServiceNotificationListener(sp<DevicesFactoryHalHidl> factory)
+ : mFactory(factory) {}
- mDeviceFactories.push_back(devicesFactory);
- if (MAJOR_VERSION >= 4) {
- // The MSD factory is optional and only available starting at HAL 4.0
- sp<IDevicesFactory> msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)};
- if (msdFactory) {
- mDeviceFactories.push_back(msdFactory);
+ Return<void> onRegistration(const hidl_string& /*fully_qualified_name*/,
+ const hidl_string& instance_name,
+ bool /*pre_existing*/) override {
+ if (static_cast<std::string>(instance_name) == "default") return Void();
+ sp<DevicesFactoryHalHidl> factory = mFactory.promote();
+ if (!factory) return Void();
+ sp<IDevicesFactory> halFactory = IDevicesFactory::getService(instance_name);
+ if (halFactory) {
+ factory->addDeviceFactory(halFactory, true /*needToNotify*/);
}
+ return Void();
}
- for (const auto& factory : mDeviceFactories) {
- // It is assumed that the DevicesFactoryHalInterface instance is owned
- // by AudioFlinger and thus have the same lifespan.
- factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
- }
+
+ private:
+ wp<DevicesFactoryHalHidl> mFactory;
+};
+
+DevicesFactoryHalHidl::DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory) {
+ ALOG_ASSERT(devicesFactory != nullptr, "Provided default IDevicesFactory service is NULL");
+ addDeviceFactory(devicesFactory, false /*needToNotify*/);
}
+void DevicesFactoryHalHidl::onFirstRef() {
+ sp<IServiceManager> sm = IServiceManager::getService();
+ ALOG_ASSERT(sm != nullptr, "Hardware service manager is not running");
+ sp<ServiceNotificationListener> listener = new ServiceNotificationListener(this);
+ Return<bool> result = sm->registerForNotifications(
+ IDevicesFactory::descriptor, "", listener);
+ if (result.isOk()) {
+ ALOGE_IF(!static_cast<bool>(result),
+ "Hardware service manager refused to register listener");
+ } else {
+ ALOGE("Failed to register for hardware service manager notifications: %s",
+ result.description().c_str());
+ }
+}
#if MAJOR_VERSION == 2
static IDevicesFactory::Device idFromHal(const char *name, status_t* status) {
@@ -83,12 +108,13 @@
#endif
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
- if (mDeviceFactories.empty()) return NO_INIT;
+ auto factories = copyDeviceFactories();
+ if (factories.empty()) return NO_INIT;
status_t status;
auto hidlId = idFromHal(name, &status);
if (status != OK) return status;
Result retval = Result::NOT_INITIALIZED;
- for (const auto& factory : mDeviceFactories) {
+ for (const auto& factory : factories) {
Return<void> ret = factory->openDevice(
hidlId,
[&](Result r, const sp<IDevice>& result) {
@@ -113,10 +139,9 @@
status_t DevicesFactoryHalHidl::getHalPids(std::vector<pid_t> *pids) {
std::set<pid_t> pidsSet;
-
- for (const auto& factory : mDeviceFactories) {
+ auto factories = copyDeviceFactories();
+ for (const auto& factory : factories) {
using ::android::hidl::base::V1_0::DebugInfo;
- using android::hidl::manager::V1_0::IServiceManager;
DebugInfo debugInfo;
auto ret = factory->getDebugInfo([&] (const auto &info) {
@@ -135,5 +160,48 @@
return NO_ERROR;
}
+status_t DevicesFactoryHalHidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
+ ALOG_ASSERT(callback != nullptr);
+ bool needToCallCallback = false;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mCallback.unsafe_get()) return INVALID_OPERATION;
+ mCallback = callback;
+ if (mHaveUndeliveredNotifications) {
+ needToCallCallback = true;
+ mHaveUndeliveredNotifications = false;
+ }
+ }
+ if (needToCallCallback) {
+ callback->onNewDevicesAvailable();
+ }
+ return NO_ERROR;
+}
+
+void DevicesFactoryHalHidl::addDeviceFactory(sp<IDevicesFactory> factory, bool needToNotify) {
+ // It is assumed that the DevicesFactoryHalInterface instance is owned
+ // by AudioFlinger and thus have the same lifespan.
+ factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
+ sp<DevicesFactoryHalCallback> callback;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mDeviceFactories.push_back(factory);
+ if (needToNotify) {
+ callback = mCallback.promote();
+ if (!callback) {
+ mHaveUndeliveredNotifications = true;
+ }
+ }
+ }
+ if (callback) {
+ callback->onNewDevicesAvailable();
+ }
+}
+
+std::vector<sp<IDevicesFactory>> DevicesFactoryHalHidl::copyDeviceFactories() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mDeviceFactories;
+}
+
} // namespace CPP_VERSION
} // namespace android
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.h b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
index 52185c8..6f84efe 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
+#include <mutex>
+#include <vector>
+
#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <utils/Errors.h>
@@ -32,16 +35,26 @@
class DevicesFactoryHalHidl : public DevicesFactoryHalInterface
{
public:
- DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory);
+ explicit DevicesFactoryHalHidl(sp<IDevicesFactory> devicesFactory);
+ void onFirstRef() override;
// Opens a device with the specified name. To close the device, it is
// necessary to release references to the returned object.
- virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+ status_t openDevice(const char *name, sp<DeviceHalInterface> *device) override;
- status_t getHalPids(std::vector<pid_t> *pids) override;
+ status_t getHalPids(std::vector<pid_t> *pids) override;
+
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
private:
- std::vector<sp<IDevicesFactory>> mDeviceFactories;
+ friend class ServiceNotificationListener;
+ void addDeviceFactory(sp<IDevicesFactory> factory, bool needToNotify);
+ std::vector<sp<IDevicesFactory>> copyDeviceFactories();
+
+ std::mutex mLock;
+ std::vector<sp<IDevicesFactory>> mDeviceFactories; // GUARDED_BY(mLock)
+ wp<DevicesFactoryHalCallback> mCallback; // GUARDED_BY(mLock)
+ bool mHaveUndeliveredNotifications = false; // GUARDED_BY(mLock)
virtual ~DevicesFactoryHalHidl() = default;
};
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
index 52f150a..cde8d85 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
@@ -44,6 +44,13 @@
return INVALID_OPERATION;
}
+status_t DevicesFactoryHalHybrid::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
+ if (mHidlFactory) {
+ return mHidlFactory->setCallbackOnce(callback);
+ }
+ return INVALID_OPERATION;
+}
+
} // namespace CPP_VERSION
extern "C" __attribute__((visibility("default"))) void* createIDevicesFactory() {
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
index 2189b36..568a1fb 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h
@@ -38,6 +38,8 @@
status_t getHalPids(std::vector<pid_t> *pids) override;
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) override;
+
private:
sp<DevicesFactoryHalInterface> mLocalFactory;
sp<DevicesFactoryHalInterface> mHidlFactory;
diff --git a/media/libaudiohal/impl/DevicesFactoryHalLocal.h b/media/libaudiohal/impl/DevicesFactoryHalLocal.h
index 2b011f4..32bf362 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalLocal.h
@@ -37,6 +37,10 @@
return INVALID_OPERATION;
}
+ status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback __unused) override {
+ return INVALID_OPERATION;
+ }
+
private:
friend class DevicesFactoryHalHybrid;
diff --git a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
index e9ac1ce..5091558 100644
--- a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h
@@ -24,6 +24,12 @@
namespace android {
+class DevicesFactoryHalCallback : public RefBase
+{
+ public:
+ virtual void onNewDevicesAvailable() = 0;
+};
+
class DevicesFactoryHalInterface : public RefBase
{
public:
@@ -33,6 +39,10 @@
virtual status_t getHalPids(std::vector<pid_t> *pids) = 0;
+ // Sets a DevicesFactoryHalCallback to notify the client.
+ // The callback can be only set once.
+ virtual status_t setCallbackOnce(sp<DevicesFactoryHalCallback> callback) = 0;
+
static sp<DevicesFactoryHalInterface> create();
protected:
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1cb81a6..39f5bb6 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -1906,11 +1906,15 @@
//ALOGV("\tReverb_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
effect_param_t *p = (effect_param_t *)pCmdData;
+ if (pCmdData == nullptr) {
+ ALOGW("\tLVM_ERROR : pCmdData is NULL");
+ return -EINVAL;
+ }
if (SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
android_errorWriteLog(0x534e4554, "26347509");
return -EINVAL;
}
- if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
+ if (cmdSize < sizeof(effect_param_t) ||
cmdSize < (sizeof(effect_param_t) + p->psize) ||
pReplyData == NULL || replySize == NULL ||
*replySize < (sizeof(effect_param_t) + p->psize)) {
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 98c5497..637322f 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -29,9 +29,55 @@
#include <OMX_Video.h>
#include <sys/stat.h>
+#include <array>
+#include <string>
+#include <vector>
+
namespace android {
-constexpr char const * const MediaProfiles::xmlFiles[];
+namespace /* unnamed */ {
+
+// Returns a list of possible paths for the media_profiles XML file.
+std::array<char const*, 5> const& getXmlPaths() {
+ static std::array<std::string const, 5> const paths =
+ []() -> decltype(paths) {
+ // Directories for XML file that will be searched (in this order).
+ constexpr std::array<char const*, 4> searchDirs = {
+ "product/etc/",
+ "odm/etc/",
+ "vendor/etc/",
+ "system/etc/",
+ };
+
+ // The file name may contain a variant if the vendor property
+ // ro.vendor.media_profiles_xml_variant is set.
+ char variant[PROPERTY_VALUE_MAX];
+ property_get("ro.media.xml_variant.profiles",
+ variant,
+ "_V1_0");
+
+ std::string fileName =
+ std::string("media_profiles") + variant + ".xml";
+
+ return { searchDirs[0] + fileName,
+ searchDirs[1] + fileName,
+ searchDirs[2] + fileName,
+ searchDirs[3] + fileName,
+ "system/etc/media_profiles_V1_0.xml" // System fallback
+ };
+ }();
+ static std::array<char const*, 5> const cPaths = {
+ paths[0].data(),
+ paths[1].data(),
+ paths[2].data(),
+ paths[3].data(),
+ paths[4].data()
+ };
+ return cPaths;
+}
+
+} // unnamed namespace
+
Mutex MediaProfiles::sLock;
bool MediaProfiles::sIsInitialized = false;
MediaProfiles *MediaProfiles::sInstance = NULL;
@@ -48,7 +94,7 @@
{"amrwb", AUDIO_ENCODER_AMR_WB},
{"aac", AUDIO_ENCODER_AAC},
{"heaac", AUDIO_ENCODER_HE_AAC},
- {"aaceld", AUDIO_ENCODER_AAC_ELD},
+ {"aaceld", AUDIO_ENCODER_AAC_ELD},
{"opus", AUDIO_ENCODER_OPUS}
};
@@ -610,7 +656,7 @@
char value[PROPERTY_VALUE_MAX];
if (property_get("media.settings.xml", value, NULL) <= 0) {
const char* xmlFile = nullptr;
- for (auto const& f : xmlFiles) {
+ for (auto const& f : getXmlPaths()) {
if (checkXmlFile(f)) {
xmlFile = f;
break;
diff --git a/media/libmedia/include/media/MediaProfiles.h b/media/libmedia/include/media/MediaProfiles.h
index 3e8e7c8..4cc5b95 100644
--- a/media/libmedia/include/media/MediaProfiles.h
+++ b/media/libmedia/include/media/MediaProfiles.h
@@ -1,18 +1,18 @@
/*
- **
- ** Copyright 2010, 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.
+ *
+ * Copyright 2010, The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#ifndef ANDROID_MEDIAPROFILES_H
@@ -82,29 +82,12 @@
{
public:
- /*
- * If property media.settings.xml is not set:
- *
- * getInstance() will search through paths listed in xmlFiles.
- * The search goes through members of xmlFiles in the order that they are
- * defined, so files at lower indices have higher priority than those at
- * higher indices.
- *
- * TODO: Add runtime validation of xml files. A search should be considered
- * successful only when validation is successful.
- */
- static constexpr char const * const xmlFiles[] = {
- "odm/etc/media_profiles_V1_0.xml",
- "vendor/etc/media_profiles_V1_0.xml",
- "system/etc/media_profiles.xml"
- };
-
/**
* Returns the singleton instance for subsequence queries or NULL if error.
*
* If property media.settings.xml is set, getInstance() will attempt to read
* from file path in media.settings.xml. Otherwise, getInstance() will
- * search through the list xmlFiles as described above.
+ * search through the list of preset XML file paths.
*
* If the search is unsuccessful, the default instance will be created
* instead.
diff --git a/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp b/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
index 7729d52..4f3951a 100644
--- a/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
+++ b/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
@@ -14,23 +14,68 @@
* limitations under the License.
*/
+#include <fstream>
#include <string>
#include <android-base/file.h>
#include <android-base/properties.h>
#include "utility/ValidateXml.h"
+bool isFileReadable(std::string const& path) {
+ std::ifstream f(path);
+ return f.good();
+}
+
TEST(CheckConfig, mediaProfilesValidation) {
RecordProperty("description",
"Verify that the media profiles file "
"is valid according to the schema");
+ // Schema path.
+ constexpr char const* xsdPath = "/data/local/tmp/media_profiles.xsd";
+
+ // If "media.settings.xml" is set, it will be used as an absolute path.
std::string mediaSettingsPath = android::base::GetProperty("media.settings.xml", "");
if (mediaSettingsPath.empty()) {
- mediaSettingsPath.assign("/vendor/etc/media_profiles_V1_0.xml");
- }
+ // If "media.settings.xml" is not set, we will search through a list of
+ // file paths.
- EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(android::base::Basename(mediaSettingsPath).c_str(),
- {android::base::Dirname(mediaSettingsPath).c_str()},
- "/data/local/tmp/media_profiles.xsd");
+ constexpr char const* xmlSearchDirs[] = {
+ "/product/etc/",
+ "/odm/etc/",
+ "/vendor/etc/",
+ };
+
+ // The vendor may provide a vendor variant for the file name.
+ std::string variant = android::base::GetProperty(
+ "ro.media.xml_variant.profiles", "_V1_0");
+ std::string fileName = "media_profiles" + variant + ".xml";
+
+ // Fallback path does not depend on the property defined from the vendor
+ // partition.
+ constexpr char const* fallbackXmlPath =
+ "/system/etc/media_profiles_V1_0.xml";
+
+ std::vector<std::string> xmlPaths = {
+ xmlSearchDirs[0] + fileName,
+ xmlSearchDirs[1] + fileName,
+ xmlSearchDirs[2] + fileName,
+ fallbackXmlPath
+ };
+
+ auto findXmlPath =
+ std::find_if(xmlPaths.begin(), xmlPaths.end(), isFileReadable);
+ ASSERT_TRUE(findXmlPath != xmlPaths.end())
+ << "Cannot read from " << fileName
+ << " in any search directories ("
+ << xmlSearchDirs[0] << ", "
+ << xmlSearchDirs[1] << ", "
+ << xmlSearchDirs[2] << ") and from "
+ << fallbackXmlPath << ".";
+
+ char const* xmlPath = findXmlPath->c_str();
+ EXPECT_VALID_XML(xmlPath, xsdPath);
+ } else {
+ EXPECT_VALID_XML(mediaSettingsPath.c_str(), xsdPath);
+ }
}
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index 88cf7f2..d8cb568 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_amrwbdec",
vendor_available: true,
+ host_supported: true,
srcs: [
"src/agc2_amr_wb.cpp",
@@ -44,8 +45,6 @@
"src/weight_amrwb_lpc.cpp",
],
- header_libs: ["libstagefright_headers"],
-
export_include_dirs: [
"src",
"include",
@@ -63,12 +62,19 @@
"signed-integer-overflow",
],
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
cc_test {
name: "libstagefright_amrwbdec_test",
gtest: false,
+ host_supported: true,
srcs: ["test/amrwbdec_test.cpp"],
@@ -88,4 +94,10 @@
"signed-integer-overflow",
],
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/Android.bp b/media/libstagefright/codecs/amrwb/fuzzer/Android.bp
new file mode 100644
index 0000000..46f77e3
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/Android.bp
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+ name: "amrwb_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "amrwb_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_amrwbdec",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/README.md b/media/libstagefright/codecs/amrwb/fuzzer/README.md
new file mode 100644
index 0000000..de45784
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/README.md
@@ -0,0 +1,61 @@
+# Fuzzer for libstagefright_amrwbdec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for AMR-WB is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AMR-WB supports the following parameters:
+1. Quality (parameter name: `quality`)
+2. Mode (parameter name: `mode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `quality` | 0. `Bad Quality` 1. `Good quality` | Bit 0 (LSB) of 1st byte of data. |
+| `mode` | 0. `MODE_7k` 1. `MODE_9k` 2. `MODE_12k` 3. `MODE_14k` 4. `MODE_16k ` 5. `MODE_18k` 6. `MODE_20k` 7. `MODE_23k` 8. `MODE_24k` 9. `MRDTX` | Bits 3, 4, 5 and 6 of 1st byte of data. |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+If the decode operation was successful, the input is advanced by the frame size
+which is based on `mode` and `quality` selected.
+If the decode operation was un-successful, the input is still advanced by frame size so
+that the fuzzer can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build amrwb_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) amrwb_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some amrwb files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/amrwb_dec_fuzzer/amrwb_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/amrwb_dec_fuzzer/amrwb_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
new file mode 100644
index 0000000..6dc0270
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
@@ -0,0 +1,110 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <malloc.h>
+#include <string.h>
+#include <algorithm>
+#include "pvamrwbdecoder.h"
+#include "pvamrwbdecoder_api.h"
+
+// Constants for AMR-WB.
+constexpr int32_t kSamplesPerFrame = 320;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kMaxSourceDataUnitSize = KAMRWB_NB_BITS_MAX * sizeof(int16_t);
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+constexpr int32_t kFrameSizes[16] = {17, 23, 32, 36, 40, 46, 50, 58,
+ 60, 17, 23, 32, 36, 40, 46, 50};
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+ bool initDecoder();
+ void decodeFrames(const uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ void *mAmrHandle = nullptr;
+ int16_t *mDecoderCookie = nullptr;
+ void *mDecoderBuffer = nullptr;
+};
+
+bool Codec::initDecoder() {
+ mDecoderBuffer = malloc(pvDecoder_AmrWbMemRequirements());
+ if (mDecoderBuffer) {
+ pvDecoder_AmrWb_Init(&mAmrHandle, mDecoderBuffer, &mDecoderCookie);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void Codec::deInitDecoder() {
+ if (mDecoderBuffer) {
+ free(mDecoderBuffer);
+ mDecoderBuffer = nullptr;
+ }
+ mAmrHandle = nullptr;
+ mDecoderCookie = nullptr;
+}
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ while (size > 0) {
+ uint8_t modeByte = *data;
+ bool quality = modeByte & 0x01;
+ int16 mode = ((modeByte >> 3) & 0x0f);
+ ++data;
+ --size;
+ int32_t frameSize = kFrameSizes[mode];
+ int16_t inputSampleBuf[kMaxSourceDataUnitSize];
+ uint8_t *inputBuf = new uint8_t[frameSize];
+ if (!inputBuf) {
+ return;
+ }
+ int32_t minSize = std::min((int32_t)size, frameSize);
+ memcpy(inputBuf, data, minSize);
+ int16 frameMode = mode;
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, quality, &rx_state);
+
+ int16_t numSamplesOutput;
+ int16_t outputBuf[kOutputBufferSize];
+ pvDecoder_AmrWb(frameMode, inputSampleBuf, outputBuf, &numSamplesOutput, mDecoderBuffer,
+ frameType, mDecoderCookie);
+ data += minSize;
+ size -= minSize;
+ delete[] inputBuf;
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder()) {
+ codec->decodeFrames(data, size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 6b45ea2..b8b83d5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_m4vh263dec",
vendor_available: true,
+ host_supported: true,
shared_libs: ["liblog"],
srcs: [
@@ -38,11 +39,6 @@
"src/zigzag_tab.cpp",
],
- header_libs: [
- "media_plugin_headers",
- "libstagefright_headers"
- ],
-
local_include_dirs: ["src"],
export_include_dirs: ["include"],
@@ -61,6 +57,12 @@
],
cfi: true,
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp b/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp
new file mode 100644
index 0000000..aa79d37
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+ name: "mpeg4_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "mpeg4_h263_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_m4vh263dec",
+ "liblog",
+ ],
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-DMPEG4",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_fuzz {
+ name: "h263_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "mpeg4_h263_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_m4vh263dec",
+ "liblog",
+ ],
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/README.md b/media/libstagefright/codecs/m4v_h263/fuzzer/README.md
new file mode 100644
index 0000000..c2a4f69
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/README.md
@@ -0,0 +1,57 @@
+# Fuzzer for libstagefright_m4vh263dec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for MPEG4/H263 is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+Dict files (dictionary files) are created for MPEG4 and H263 to ensure that the required start
+bytes are present in every input file that goes to the fuzzer.
+This ensures that decoder does not reject any input file in the first check
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+ * If the decode operation was successful, the input is advanced by the number of bytes consumed
+ in the decode call.
+ * If the decode operation was un-successful, the input is advanced by 1 byte so that the fuzzer
+ can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge, malformed, etc)
+and doesnt `exit()` on any input and thereby increasing the chance of identifying vulnerabilities.
+
+##### Other considerations
+ * Two fuzzer binaries - mpeg4_dec_fuzzer and h263_dec_fuzzer are generated based on the presence
+ of a flag - 'MPEG4'
+ * The number of decode calls are kept to a maximum of 100 so that the fuzzer does not timeout.
+
+## Build
+
+This describes steps to build mpeg4_dec_fuzzer and h263_dec_fuzzer binary.
+
+### Android
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) mpeg4_dec_fuzzer
+ $ mm -j$(nproc) h263_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some MPEG4 or H263 files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mpeg4_dec_fuzzer/mpeg4_dec_fuzzer CORPUS_DIR
+ $ adb shell /data/fuzz/arm64/h263_dec_fuzzer/h263_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/mpeg4_dec_fuzzer/mpeg4_dec_fuzzer CORPUS_DIR
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/h263_dec_fuzzer/h263_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict b/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict
new file mode 100644
index 0000000..591d37e
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict
@@ -0,0 +1,2 @@
+# Start code (bytes 0-3)
+kw1="\x00\x00\x80\x02"
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict
new file mode 100644
index 0000000..76241a6
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict
@@ -0,0 +1,2 @@
+# Start code (bytes 0-3)
+kw1="\x00\x00\x01\xB0"
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp
new file mode 100644
index 0000000..912c821
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include "mp4dec_api.h"
+#define MPEG4_MAX_WIDTH 1920
+#define MPEG4_MAX_HEIGHT 1080
+#define H263_MAX_WIDTH 352
+#define H263_MAX_HEIGHT 288
+#define DEFAULT_WIDTH 352
+#define DEFAULT_HEIGHT 288
+
+constexpr size_t kMaxNumDecodeCalls = 100;
+constexpr uint8_t kNumOutputBuffers = 2;
+constexpr int kLayer = 1;
+
+struct tagvideoDecControls;
+
+/* == ceil(num / den) * den. T must be integer type, alignment must be positive power of 2 */
+template <class T, class U>
+inline static const T align(const T &num, const U &den) {
+ return (num + (T)(den - 1)) & (T) ~(den - 1);
+}
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+ bool initDecoder();
+ bool allocOutputBuffer(size_t outputBufferSize);
+ void freeOutputBuffer();
+ void handleResolutionChange();
+ void decodeFrames(const uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ tagvideoDecControls *mDecHandle = nullptr;
+ uint8_t *mOutputBuffer[kNumOutputBuffers];
+ bool mInitialized = false;
+ bool mFramesConfigured = false;
+#ifdef MPEG4
+ MP4DecodingMode mInputMode = MPEG4_MODE;
+ size_t mMaxWidth = MPEG4_MAX_WIDTH;
+ size_t mMaxHeight = MPEG4_MAX_HEIGHT;
+#else
+ MP4DecodingMode mInputMode = H263_MODE;
+ size_t mMaxWidth = H263_MAX_WIDTH;
+ size_t mMaxHeight = H263_MAX_HEIGHT;
+#endif
+ uint32_t mNumSamplesOutput = 0;
+ uint32_t mWidth = DEFAULT_WIDTH;
+ uint32_t mHeight = DEFAULT_HEIGHT;
+};
+
+bool Codec::initDecoder() {
+ mDecHandle = new tagvideoDecControls;
+ if (!mDecHandle) {
+ return false;
+ }
+ memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+ return true;
+}
+
+bool Codec::allocOutputBuffer(size_t outputBufferSize) {
+ for (uint8_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (!mOutputBuffer[i]) {
+ mOutputBuffer[i] = static_cast<uint8_t *>(malloc(outputBufferSize));
+ if (!mOutputBuffer[i]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void Codec::freeOutputBuffer() {
+ for (uint8_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+}
+
+void Codec::handleResolutionChange() {
+ int32_t dispWidth, dispHeight;
+ PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight);
+
+ int32_t bufWidth, bufHeight;
+ PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight);
+
+ if (dispWidth != mWidth || dispHeight != mHeight) {
+ mWidth = dispWidth;
+ mHeight = dispHeight;
+ }
+}
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ size_t outputBufferSize = align(mMaxWidth, 16) * align(mMaxHeight, 16) * 3 / 2;
+ uint8_t *start_code = const_cast<uint8_t *>(data);
+ static const uint8_t volInfo[] = {0x00, 0x00, 0x01, 0xB0};
+ bool volHeader = memcmp(start_code, volInfo, 4) == 0;
+ if (volHeader) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ mInitialized = false;
+ }
+
+ if (!mInitialized) {
+ uint8_t *volData[1]{};
+ int32_t volSize = 0;
+
+ if (volHeader) { /* removed some codec config part */
+ volData[0] = const_cast<uint8_t *>(data);
+ volSize = size;
+ }
+
+ if (!PVInitVideoDecoder(mDecHandle, volData, &volSize, kLayer, mMaxWidth, mMaxHeight,
+ mInputMode)) {
+ return;
+ }
+ mInitialized = true;
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+ if (mInputMode != actualMode) {
+ return;
+ }
+
+ PVSetPostProcType(mDecHandle, 0);
+ }
+ size_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+ if (outputBufferSize < yFrameSize * 3 / 2) {
+ return;
+ }
+ if (!allocOutputBuffer(outputBufferSize)) {
+ return;
+ }
+ size_t numDecodeCalls = 0;
+ while ((size > 0) && (numDecodeCalls < kMaxNumDecodeCalls)) {
+ if (!mFramesConfigured) {
+ PVSetReferenceYUV(mDecHandle, mOutputBuffer[1]);
+ mFramesConfigured = true;
+ }
+
+ // Need to check if header contains new info, e.g., width/height, etc.
+ VopHeaderInfo header_info;
+ uint32_t useExtTimestamp = (numDecodeCalls == 0);
+ int32_t tempSize = (int32_t)size;
+ uint8_t *bitstreamTmp = const_cast<uint8_t *>(data);
+ uint32_t timestamp = 0;
+ if (PVDecodeVopHeader(mDecHandle, &bitstreamTmp, ×tamp, &tempSize, &header_info,
+ &useExtTimestamp, mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
+ return;
+ }
+
+ handleResolutionChange();
+
+ PVDecodeVopBody(mDecHandle, &tempSize);
+ uint32_t bytesConsumed = 1;
+ if (size > tempSize) {
+ bytesConsumed = size - tempSize;
+ }
+ data += bytesConsumed;
+ size -= bytesConsumed;
+ ++mNumSamplesOutput;
+ ++numDecodeCalls;
+ }
+ freeOutputBuffer();
+}
+
+void Codec::deInitDecoder() {
+ PVCleanUpVideoDecoder(mDecHandle);
+ delete mDecHandle;
+ mDecHandle = nullptr;
+ mInitialized = false;
+ freeOutputBuffer();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 4) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder()) {
+ codec->decodeFrames(data, size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/foundation/TEST_MAPPING b/media/libstagefright/foundation/TEST_MAPPING
new file mode 100644
index 0000000..3301c4b
--- /dev/null
+++ b/media/libstagefright/foundation/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+ "presubmit": [
+ { "name": "sf_foundation_test" }
+ ]
+}
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
new file mode 100644
index 0000000..f2157c9
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -0,0 +1,27 @@
+cc_test {
+ name: "sf_foundation_test",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ include_dirs: [
+ "frameworks/av/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ srcs: [
+ "AData_test.cpp",
+ "Base64_test.cpp",
+ "Flagged_test.cpp",
+ "TypeTraits_test.cpp",
+ "Utils_test.cpp",
+ ],
+}
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
deleted file mode 100644
index a9e3c76..0000000
--- a/media/libstagefright/foundation/tests/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE := sf_foundation_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
- AData_test.cpp \
- Base64_test.cpp \
- Flagged_test.cpp \
- TypeTraits_test.cpp \
- Utils_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libstagefright_foundation \
- libutils \
-
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/media/libstagefright/foundation/tests/OpusHeader/Android.bp b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
new file mode 100644
index 0000000..c1251a8
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "OpusHeaderTest",
+ gtest: true,
+
+ srcs: [
+ "OpusHeaderTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_foundation",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml b/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml
new file mode 100644
index 0000000..afee16a
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for opus header unit tests">
+ <option name="test-suite-tag" value="OpusHeaderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="OpusHeaderTest->/data/local/tmp/OpusHeaderTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/OpusHeader/OpusHeader.zip?unzip=true"
+ value="/data/local/tmp/OpusHeaderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="OpusHeaderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/OpusHeaderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
new file mode 100644
index 0000000..d142781
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpusHeaderTest"
+#include <utils/Log.h>
+
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/OpusHeader.h>
+
+#include "OpusHeaderTestEnvironment.h"
+
+using namespace android;
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/OpusOutput"
+
+// Opus in WebM is a well-known, yet under-documented, format. The codec private data
+// of the track is an Opus Ogg header (https://tools.ietf.org/html/rfc7845#section-5.1)
+// channel mapping offset in opus header
+constexpr size_t kOpusHeaderStreamMapOffset = 21;
+constexpr size_t kMaxOpusHeaderSize = 100;
+// AOPUSHDR + AOPUSHDRLength +
+// (8 + 8 ) +
+// Header(csd) + num_streams + num_coupled + 1
+// (19 + 1 + 1 + 1) +
+// AOPUSDLY + AOPUSDLYLength + DELAY + AOPUSPRL + AOPUSPRLLength + PRL
+// (8 + 8 + 8 + 8 + 8 + 8)
+// = 86
+constexpr size_t kOpusHeaderChannelMapOffset = 86;
+constexpr uint32_t kOpusSampleRate = 48000;
+constexpr uint64_t kOpusSeekPrerollNs = 80000000;
+constexpr int64_t kNsecPerSec = 1000000000ll;
+
+// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+constexpr int kMaxChannels = 8;
+constexpr uint8_t kOpusChannelMap[kMaxChannels][kMaxChannels] = {
+ {0},
+ {0, 1},
+ {0, 2, 1},
+ {0, 1, 2, 3},
+ {0, 4, 1, 2, 3},
+ {0, 4, 1, 2, 3, 5},
+ {0, 4, 1, 2, 3, 5, 6},
+ {0, 6, 1, 2, 3, 4, 5, 7},
+};
+
+static OpusHeaderTestEnvironment *gEnv = nullptr;
+
+class OpusHeaderTest {
+ public:
+ OpusHeaderTest() : mInputBuffer(nullptr) {}
+
+ ~OpusHeaderTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mInputBuffer) {
+ free(mInputBuffer);
+ mInputBuffer = nullptr;
+ }
+ }
+ ifstream mEleStream;
+ uint8_t *mInputBuffer;
+};
+
+class OpusHeaderParseTest : public OpusHeaderTest,
+ public ::testing::TestWithParam<
+ tuple<string /* InputFileName */, int32_t /* ChannelCount */,
+ bool /* isHeaderValid */, bool /* isCodecDelayValid */,
+ bool /* isSeekPreRollValid */, bool /* isInputValid */>> {
+};
+
+class OpusHeaderWriteTest
+ : public OpusHeaderTest,
+ public ::testing::TestWithParam<pair<int32_t /* ChannelCount */, int32_t /* skipSamples */>> {
+};
+
+TEST_P(OpusHeaderWriteTest, WriteTest) {
+ OpusHeader writtenHeader;
+ memset(&writtenHeader, 0, sizeof(writtenHeader));
+ int32_t channels = GetParam().first;
+ writtenHeader.channels = channels;
+ writtenHeader.num_streams = channels;
+ writtenHeader.channel_mapping = ((channels > 8) ? 255 : (channels > 2));
+ int32_t skipSamples = GetParam().second;
+ writtenHeader.skip_samples = skipSamples;
+ uint64_t codecDelayNs = skipSamples * kNsecPerSec / kOpusSampleRate;
+ uint8_t headerData[kMaxOpusHeaderSize];
+ int32_t headerSize = WriteOpusHeaders(writtenHeader, kOpusSampleRate, headerData,
+ sizeof(headerData), codecDelayNs, kOpusSeekPrerollNs);
+ ASSERT_GT(headerSize, 0) << "failed to generate Opus header";
+ ASSERT_LE(headerSize, kMaxOpusHeaderSize)
+ << "Invalid header written. Header size can't exceed kMaxOpusHeaderSize";
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, ofstream::binary);
+ ASSERT_TRUE(ostrm.is_open()) << "Failed to open output file " << OUTPUT_FILE_NAME;
+
+ // TODO : Validate bitstream (b/150116402)
+ ostrm.write(reinterpret_cast<char *>(headerData), sizeof(headerData));
+ ostrm.close();
+
+ size_t opusHeadSize = 0;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = nullptr;
+ void *codecDelayBuf = nullptr;
+ void *seekPreRollBuf = nullptr;
+ bool status = GetOpusHeaderBuffers(headerData, headerSize, &opusHeadBuf, &opusHeadSize,
+ &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ ASSERT_TRUE(status) << "Encountered error in GetOpusHeaderBuffers";
+
+ uint64_t value = *((uint64_t *)codecDelayBuf);
+ ASSERT_EQ(value, codecDelayNs);
+
+ value = *((uint64_t *)seekPreRollBuf);
+ ASSERT_EQ(value, kOpusSeekPrerollNs);
+
+ OpusHeader parsedHeader;
+ status = ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &parsedHeader);
+ ASSERT_TRUE(status) << "Encountered error while Parsing Opus Header.";
+
+ ASSERT_EQ(writtenHeader.channels, parsedHeader.channels)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(writtenHeader.skip_samples, parsedHeader.skip_samples)
+ << "Mismatch between no of skipSamples written "
+ "and no of skipSamples got after parsing";
+
+ ASSERT_EQ(writtenHeader.channel_mapping, parsedHeader.channel_mapping)
+ << "Mismatch between channelMapping written "
+ "and channelMapping got after parsing";
+
+ if (parsedHeader.channel_mapping) {
+ ASSERT_GT(parsedHeader.channels, 2);
+ ASSERT_EQ(writtenHeader.num_streams, parsedHeader.num_streams)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(writtenHeader.num_coupled, parsedHeader.num_coupled)
+ << "Invalid header generated. Mismatch between channel counts";
+
+ ASSERT_EQ(parsedHeader.num_coupled + parsedHeader.num_streams, parsedHeader.channels);
+
+ ASSERT_LE(parsedHeader.num_coupled, parsedHeader.num_streams)
+ << "Invalid header generated. Number of coupled streams cannot be greater than "
+ "number "
+ "of streams.";
+
+ ASSERT_EQ(headerSize, kOpusHeaderChannelMapOffset + writtenHeader.channels)
+ << "Invalid header written. Header size should be equal to 86 + "
+ "writtenHeader.channels";
+
+ uint8_t mappedChannelNumber;
+ for (int32_t channelNumber = 0; channelNumber < channels; channelNumber++) {
+ mappedChannelNumber = *(reinterpret_cast<uint8_t *>(opusHeadBuf) +
+ kOpusHeaderStreamMapOffset + channelNumber);
+ ASSERT_LT(mappedChannelNumber, channels) << "Invalid header generated. Channel mapping "
+ "cannot be greater than channel count.";
+
+ ASSERT_EQ(mappedChannelNumber, kOpusChannelMap[channels - 1][channelNumber])
+ << "Invalid header generated. Channel mapping is not as per specification.";
+ }
+ } else {
+ ASSERT_LE(parsedHeader.channels, 2);
+ }
+}
+
+TEST_P(OpusHeaderParseTest, ParseTest) {
+ tuple<string, int32_t, bool, bool, bool, bool> params = GetParam();
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << get<0>(params);
+ bool isHeaderValid = get<2>(params);
+ bool isCodecDelayValid = get<3>(params);
+ bool isSeekPreRollValid = get<4>(params);
+ bool isInputValid = get<5>(params);
+
+ struct stat buf;
+ stat(inputFileName.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ mInputBuffer = (uint8_t *)malloc(fileSize);
+ ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory. Malloc failed for size " << fileSize;
+
+ mEleStream.read(reinterpret_cast<char *>(mInputBuffer), fileSize);
+ ASSERT_EQ(mEleStream.gcount(), fileSize) << "mEleStream.gcount() != bytesCount";
+
+ OpusHeader header;
+ size_t opusHeadSize = 0;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = nullptr;
+ void *codecDelayBuf = nullptr;
+ void *seekPreRollBuf = nullptr;
+ bool status = GetOpusHeaderBuffers(mInputBuffer, fileSize, &opusHeadBuf, &opusHeadSize,
+ &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ if (!isHeaderValid) {
+ ASSERT_EQ(opusHeadBuf, nullptr);
+ } else {
+ ASSERT_NE(opusHeadBuf, nullptr);
+ }
+ if (!isCodecDelayValid) {
+ ASSERT_EQ(codecDelayBuf, nullptr);
+ } else {
+ ASSERT_NE(codecDelayBuf, nullptr);
+ }
+ if (!isSeekPreRollValid) {
+ ASSERT_EQ(seekPreRollBuf, nullptr);
+ } else {
+ ASSERT_NE(seekPreRollBuf, nullptr);
+ }
+ if (!status) {
+ ASSERT_FALSE(isInputValid) << "GetOpusHeaderBuffers failed";
+ return;
+ }
+
+ status = ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &header);
+
+ if (status) {
+ ASSERT_TRUE(isInputValid) << "Parse opus header didn't fail for invalid input";
+ } else {
+ ASSERT_FALSE(isInputValid);
+ return;
+ }
+
+ int32_t channels = get<1>(params);
+ ASSERT_EQ(header.channels, channels) << "Parser returned invalid channel count";
+ ASSERT_LE(header.channels, kMaxChannels);
+
+ ASSERT_LE(header.num_coupled, header.num_streams)
+ << "Invalid header generated. Number of coupled streams cannot be greater than number "
+ "of streams.";
+
+ ASSERT_EQ(header.num_coupled + header.num_streams, header.channels);
+
+ if (header.channel_mapping) {
+ uint8_t mappedChannelNumber;
+ for (int32_t channelNumber = 0; channelNumber < channels; channelNumber++) {
+ mappedChannelNumber = *(reinterpret_cast<uint8_t *>(opusHeadBuf) +
+ kOpusHeaderStreamMapOffset + channelNumber);
+ ASSERT_LT(mappedChannelNumber, channels)
+ << "Invalid header. Channel mapping cannot be greater than channel count.";
+
+ ASSERT_EQ(mappedChannelNumber, kOpusChannelMap[channels - 1][channelNumber])
+ << "Invalid header generated. Channel mapping "
+ "is not as per specification.";
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(OpusHeaderTestAll, OpusHeaderWriteTest,
+ ::testing::Values(make_pair(1, 312),
+ make_pair(2, 312),
+ make_pair(5, 312),
+ make_pair(6, 312),
+ make_pair(1, 0),
+ make_pair(2, 0),
+ make_pair(5, 0),
+ make_pair(6, 0),
+ make_pair(1, 624),
+ make_pair(2, 624),
+ make_pair(5, 624),
+ make_pair(6, 624)));
+
+INSTANTIATE_TEST_SUITE_P(
+ OpusHeaderTestAll, OpusHeaderParseTest,
+ ::testing::Values(
+ make_tuple("2ch_valid_size83B.opus", 2, true, true, true, true),
+ make_tuple("3ch_valid_size88B.opus", 3, true, true, true, true),
+ make_tuple("5ch_valid.opus", 5, true, false, false, true),
+ make_tuple("6ch_valid.opus", 6, true, false, false, true),
+ make_tuple("1ch_valid.opus", 1, true, false, false, true),
+ make_tuple("2ch_valid.opus", 2, true, false, false, true),
+ make_tuple("3ch_invalid_size.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_streams.opus", 3, true, true, true, false),
+ make_tuple("5ch_invalid_channelmapping.opus", 5, true, false, false, false),
+ make_tuple("5ch_invalid_coupledstreams.opus", 5, true, false, false, false),
+ make_tuple("6ch_invalid_channelmapping.opus", 6, true, false, false, false),
+ make_tuple("9ch_invalid_channels.opus", 9, true, true, true, false),
+ make_tuple("2ch_invalid_header.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_headerlength_16.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_headerlength_256.opus", 2, false, false, false, false),
+ make_tuple("2ch_invalid_size.opus", 2, false, false, false, false),
+ make_tuple("3ch_invalid_channelmapping_0.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_coupledstreams.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_headerlength.opus", 3, true, true, true, false),
+ make_tuple("3ch_invalid_headerSize1.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_headerSize2.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_headerSize3.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_nodelay.opus", 3, false, false, false, false),
+ make_tuple("3ch_invalid_nopreroll.opus", 3, false, false, false, false)));
+
+int main(int argc, char **argv) {
+ gEnv = new OpusHeaderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD("Opus Header Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h
new file mode 100644
index 0000000..d0163c3
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OPUS_HEADER_TEST_ENVIRONMENT_H__
+#define __OPUS_HEADER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class OpusHeaderTestEnvironment : public ::testing::Environment {
+ public:
+ OpusHeaderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int OpusHeaderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __OPUS_HEADER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/foundation/tests/OpusHeader/README.md b/media/libstagefright/foundation/tests/OpusHeader/README.md
new file mode 100644
index 0000000..860c827
--- /dev/null
+++ b/media/libstagefright/foundation/tests/OpusHeader/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### Opus Header
+The OpusHeader Test Suite validates the OPUS header available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m OpusHeaderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/OpusHeaderTest/OpusHeaderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/OpusHeaderTest/OpusHeaderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/OpusHeader/OpusHeader.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push OpusHeader /data/local/tmp/
+```
+
+usage: OpusHeaderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/OpusHeaderTest -P /data/local/tmp/OpusHeader/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest OpusHeaderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/mpeg2ts/test/Android.bp b/media/libstagefright/mpeg2ts/test/Android.bp
new file mode 100644
index 0000000..4e4832a
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Android.bp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test{
+ name: "Mpeg2tsUnitTest",
+ gtest: true,
+
+ srcs: [
+ "Mpeg2tsUnitTest.cpp"
+ ],
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.token@1.0-utils",
+ "android.hidl.allocator@1.0",
+ "libcrypto",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libmedia",
+ "libbinder",
+ "libbinder_ndk",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libdatasource",
+ "libstagefright",
+ "libstagefright_foundation",
+ "libstagefright_metadatautils",
+ "libstagefright_mpeg2support",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/extractors/",
+ "frameworks/av/media/libstagefright/",
+ ],
+
+ header_libs: [
+ "libmedia_headers",
+ "libaudioclient_headers",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/mpeg2ts/test/AndroidTest.xml b/media/libstagefright/mpeg2ts/test/AndroidTest.xml
new file mode 100644
index 0000000..ac1294d
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for Mpeg2ts unit tests">
+ <option name="test-suite-tag" value="Mpeg2tsUnitTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="Mpeg2tsUnitTest->/data/local/tmp/Mpeg2tsUnitTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.zip?unzip=true"
+ value="/data/local/tmp/Mpeg2tsUnitTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="Mpeg2tsUnitTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/Mpeg2tsUnitTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp
new file mode 100644
index 0000000..79c233b
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Mpeg2tsUnitTest"
+
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataBase.h>
+#include <media/stagefright/foundation/AUtils.h>
+
+#include "mpeg2ts/ATSParser.h"
+#include "mpeg2ts/AnotherPacketSource.h"
+
+#include "Mpeg2tsUnitTestEnvironment.h"
+
+constexpr size_t kTSPacketSize = 188;
+constexpr uint16_t kPIDMask = 0x1FFF;
+// Max value of PID which is also used for Null packets
+constexpr uint16_t kPIDMaxValue = 8191;
+constexpr uint8_t kTSSyncByte = 0x47;
+constexpr uint8_t kVideoPresent = 0x01;
+constexpr uint8_t kAudioPresent = 0x02;
+constexpr uint8_t kMetaDataPresent = 0x04;
+
+static Mpeg2tsUnitTestEnvironment *gEnv = nullptr;
+
+using namespace android;
+
+class Mpeg2tsUnitTest
+ : public ::testing ::TestWithParam<
+ tuple</*fileName*/ string, /*sourceType*/ char, /*numSource*/ uint16_t>> {
+ public:
+ Mpeg2tsUnitTest()
+ : mInputBuffer(nullptr), mSource(nullptr), mFpInput(nullptr), mParser(nullptr) {}
+
+ ~Mpeg2tsUnitTest() {
+ if (mInputBuffer) free(mInputBuffer);
+ if (mFpInput) fclose(mFpInput);
+ mSource.clear();
+ }
+
+ void SetUp() override {
+ mOffset = 0;
+ mNumDataSource = 0;
+ tuple<string, char, uint16_t> params = GetParam();
+ char sourceType = get<1>(params);
+ /* mSourceType = 0b x x x x x M A V
+ / | \
+ metaData audio video */
+ mMediaType = (sourceType & 0x07);
+ mNumDataSource = get<2>(params);
+ string inputFile = gEnv->getRes() + get<0>(params);
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Failed to open file: " << inputFile;
+
+ struct stat buf;
+ int8_t err = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;
+
+ long fileSize = buf.st_size;
+ mTotalPackets = fileSize / kTSPacketSize;
+ int32_t fd = fileno(mFpInput);
+ ASSERT_GE(fd, 0) << "Failed to get the integer file descriptor";
+
+ mSource = new FileSource(dup(fd), 0, buf.st_size);
+ ASSERT_NE(mSource, nullptr) << "Failed to get the data source!";
+
+ mParser = new ATSParser();
+ ASSERT_NE(mParser, nullptr) << "Unable to create ATS parser!";
+ mInputBuffer = (uint8_t *)malloc(kTSPacketSize);
+ ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate memory for TS packet!";
+ }
+
+ uint64_t mOffset;
+ uint64_t mTotalPackets;
+ uint16_t mNumDataSource;
+
+ int8_t mMediaType;
+
+ uint8_t *mInputBuffer;
+ string mInputFile;
+ sp<DataSource> mSource;
+ FILE *mFpInput;
+ ATSParser *mParser;
+};
+
+TEST_P(Mpeg2tsUnitTest, MediaInfoTest) {
+ bool videoFound = false;
+ bool audioFound = false;
+ bool metaDataFound = false;
+ bool syncPointPresent = false;
+
+ int16_t totalDataSource = 0;
+ int32_t val32 = 0;
+ uint8_t numDataSource = 0;
+ uint8_t packet[kTSPacketSize];
+ ssize_t numBytesRead = -1;
+
+ ATSParser::SyncEvent event(mOffset);
+ static const ATSParser::SourceType mediaType[] = {ATSParser::VIDEO, ATSParser::AUDIO,
+ ATSParser::META, ATSParser::NUM_SOURCE_TYPES};
+ const uint32_t nMediaTypes = sizeof(mediaType) / sizeof(mediaType[0]);
+
+ while ((numBytesRead = mSource->readAt(mOffset, packet, kTSPacketSize)) == kTSPacketSize) {
+ ASSERT_TRUE(packet[0] == kTSSyncByte) << "Sync byte error!";
+
+ // pid is 13 bits
+ uint16_t pid = (packet[1] + (packet[2] << 8)) & kPIDMask;
+ ASSERT_TRUE(pid <= kPIDMaxValue) << "Invalid PID: " << pid;
+
+ status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event);
+ ASSERT_EQ(err, (status_t)OK) << "Unable to feed TS packet!";
+
+ mOffset += numBytesRead;
+ for (int i = 0; i < nMediaTypes; i++) {
+ if (mParser->hasSource(mediaType[i])) {
+ switch (mediaType[i]) {
+ case ATSParser::VIDEO:
+ videoFound = true;
+ break;
+ case ATSParser::AUDIO:
+ audioFound = true;
+ break;
+ case ATSParser::META:
+ metaDataFound = true;
+ break;
+ case ATSParser::NUM_SOURCE_TYPES:
+ numDataSource = 3;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (videoFound && audioFound && metaDataFound && (numDataSource == 3)) break;
+ }
+
+ for (int i = 0; i < nMediaTypes; i++) {
+ ATSParser::SourceType currentMediaType = mediaType[i];
+ if (mParser->hasSource(currentMediaType)) {
+ if (event.hasReturnedData()) {
+ syncPointPresent = true;
+ sp<AnotherPacketSource> syncPacketSource = event.getMediaSource();
+ ASSERT_NE(syncPacketSource, nullptr)
+ << "Cannot get sync source for media type: " << currentMediaType;
+
+ status_t err = syncPacketSource->start();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";
+
+ sp<MetaData> format = syncPacketSource->getFormat();
+ ASSERT_NE(format, nullptr) << "Unable to get the format of the source packet!";
+
+ MediaBufferBase *buf;
+ syncPacketSource->read(&buf, nullptr);
+ ASSERT_NE(buf, nullptr) << "Failed to read sync packet source data";
+
+ MetaDataBase &inMeta = buf->meta_data();
+ bool status = inMeta.findInt32(kKeyIsSyncFrame, &val32);
+ ASSERT_EQ(status, true) << "Sync frame key is not set";
+
+ status = inMeta.findInt32(kKeyCryptoMode, &val32);
+ ASSERT_EQ(status, false) << "Invalid packet, found scrambled packets!";
+
+ err = syncPacketSource->stop();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
+ }
+ sp<AnotherPacketSource> packetSource = mParser->getSource(currentMediaType);
+ ASSERT_NE(packetSource, nullptr)
+ << "Cannot get source for media type: " << currentMediaType;
+
+ status_t err = packetSource->start();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";
+ sp<MetaData> format = packetSource->getFormat();
+ ASSERT_NE(format, nullptr) << "Unable to get the format of the packet!";
+
+ err = packetSource->stop();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
+ }
+ }
+
+ ASSERT_EQ(videoFound, bool(mMediaType & kVideoPresent)) << "No Video packets found!";
+ ASSERT_EQ(audioFound, bool(mMediaType & kAudioPresent)) << "No Audio packets found!";
+ ASSERT_EQ(metaDataFound, bool(mMediaType & kMetaDataPresent)) << "No meta data found!";
+
+ if (videoFound || audioFound) {
+ ASSERT_TRUE(syncPointPresent) << "No sync points found for audio/video";
+ }
+
+ if (videoFound) totalDataSource += 1;
+ if (audioFound) totalDataSource += 1;
+ if (metaDataFound) totalDataSource += 1;
+
+ ASSERT_TRUE(totalDataSource == mNumDataSource)
+ << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
+ if (numDataSource == 3) {
+ ASSERT_EQ(numDataSource, mNumDataSource)
+ << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ infoTest, Mpeg2tsUnitTest,
+ ::testing::Values(make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", 0x01, 1),
+ make_tuple("segment000001.ts", 0x03, 2),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_5mins.ts", 0x02, 1)));
+
+int32_t main(int argc, char **argv) {
+ gEnv = new Mpeg2tsUnitTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ uint8_t status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Mpeg2tsUnit Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.h b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.h
new file mode 100644
index 0000000..9e41db7
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
+#define __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg2tsUnitTestEnvironment : public::testing::Environment {
+ public:
+ Mpeg2tsUnitTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int Mpeg2tsUnitTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/mpeg2ts/test/README.md b/media/libstagefright/mpeg2ts/test/README.md
new file mode 100644
index 0000000..237ce72
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/README.md
@@ -0,0 +1,38 @@
+## Media Testing ##
+---
+#### Mpeg2TS Unit Test :
+The Mpeg2TS Unit Test Suite validates the functionality of the libraries present in Mpeg2TS.
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/libstagefright/mpeg2ts/test/
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+
+adb push ${OUT}/data/nativetest64/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+
+adb push ${OUT}/data/nativetest/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.zip ).
+Download, unzip and push these files into device for testing.
+
+```
+adb push Mpeg2tsUnitTestRes/. /data/local/tmp/
+```
+
+usage: Mpeg2tsUnitTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg2tsUnitTest -P /data/local/tmp/Mpeg2tsUnitTestRes/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mpeg2tsUnitTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index d905b8d..a232150 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <utils/Log.h>
#include <media/stagefright/MediaErrors.h>
@@ -38,8 +39,6 @@
namespace android {
-using MCXP = MediaCodecsXmlParser;
-
namespace {
bool fileExists(const std::string &path) {
@@ -118,8 +117,8 @@
}
}
-MCXP::StringSet parseCommaSeparatedStringSet(const char *s) {
- MCXP::StringSet result;
+MediaCodecsXmlParser::StringSet parseCommaSeparatedStringSet(const char *s) {
+ MediaCodecsXmlParser::StringSet result;
for (const char *ptr = s ? : ""; *ptr; ) {
const char *end = strchrnul(ptr, ',');
if (ptr != end) { // skip empty values
@@ -136,6 +135,23 @@
} // unnamed namespace
+std::vector<std::string> MediaCodecsXmlParser::getDefaultXmlNames() {
+ static constexpr char const* prefixes[] = {
+ "media_codecs",
+ "media_codecs_performance"
+ };
+ static std::vector<std::string> variants = {
+ android::base::GetProperty("ro.media.xml_variant.codecs", ""),
+ android::base::GetProperty("ro.media.xml_variant.codecs_performance", "")
+ };
+ static std::vector<std::string> names = {
+ prefixes[0] + variants[0] + ".xml",
+ prefixes[1] + variants[1] + ".xml"
+ };
+ return names;
+}
+
+
struct MediaCodecsXmlParser::Impl {
// status + error message
struct Result {
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index b666de4..e224452 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -33,13 +33,17 @@
class MediaCodecsXmlParser {
public:
- // Treblized media codec list will be located in /odm/etc or /vendor/etc.
+ // Treblized media codec list will be located in /product/etc, /odm/etc or
+ // /vendor/etc.
static std::vector<std::string> getDefaultSearchDirs() {
- return { "/odm/etc", "/vendor/etc", "/etc" };
+ return { "/product/etc",
+ "/odm/etc",
+ "/vendor/etc",
+ "/system/etc" };
}
- static std::vector<std::string> getDefaultXmlNames() {
- return { "media_codecs.xml", "media_codecs_performance.xml" };
- }
+
+ static std::vector<std::string> getDefaultXmlNames();
+
static constexpr char const* defaultProfilingResultsXmlPath =
"/data/misc/media/media_codecs_profiling_results.xml";
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
index e88d011..5254f2f 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
@@ -154,9 +154,11 @@
mStats->setDeInitTime(timeTaken);
}
-void C2Decoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void C2Decoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile) {
string operation = "c2decode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ string mode = "async";
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void C2Decoder::resetDecoder() {
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.h b/media/tests/benchmark/src/native/decoder/C2Decoder.h
index 0e79d51..32e1f61 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.h
@@ -35,7 +35,8 @@
void deInitCodec();
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile);
void resetDecoder();
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
index 33429ef..6a50d40 100644
--- a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -251,9 +251,11 @@
mStats->setDeInitTime(timeTaken);
}
-void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile) {
string operation = "c2encode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ string mode = "async";
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void C2Encoder::resetEncoder() {
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.h b/media/tests/benchmark/src/native/encoder/C2Encoder.h
index a4ca097..7a021f4 100644
--- a/media/tests/benchmark/src/native/encoder/C2Encoder.h
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.h
@@ -44,7 +44,8 @@
void deInitCodec();
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile);
void resetEncoder();
diff --git a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
index ae2eee1..4edb048 100644
--- a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
+++ b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
@@ -25,7 +25,9 @@
class BenchmarkTestEnvironment : public ::testing::Environment {
public:
- BenchmarkTestEnvironment() : res("/sdcard/media/") {}
+ BenchmarkTestEnvironment()
+ : res("/data/local/tmp/MediaBenchmark/res/"),
+ statsFile("/data/local/tmp/MediaBenchmark/res/stats.csv") {}
// Parses the command line argument
int initFromOptions(int argc, char **argv);
@@ -34,8 +36,15 @@
const string getRes() const { return res; }
+ void setStatsFile(const string module) { statsFile = getRes() + module; }
+
+ const string getStatsFile() const { return statsFile; }
+
+ bool writeStatsHeader();
+
private:
string res;
+ string statsFile;
};
int BenchmarkTestEnvironment::initFromOptions(int argc, char **argv) {
@@ -70,4 +79,26 @@
return 0;
}
+/**
+ * Writes the stats header to a file
+ * <p>
+ * \param statsFile file where the stats data is to be written
+ **/
+bool BenchmarkTestEnvironment::writeStatsHeader() {
+ char statsHeader[] =
+ "currentTime, fileName, operation, componentName, NDK/SDK, sync/async, setupTime, "
+ "destroyTime, minimumTime, maximumTime, averageTime, timeToProcess1SecContent, "
+ "totalBytesProcessedPerSec, timeToFirstFrame, totalSizeInBytes, totalTime\n";
+ FILE *fpStats = fopen(statsFile.c_str(), "w");
+ if(!fpStats) {
+ return false;
+ }
+ int32_t numBytes = fwrite(statsHeader, sizeof(char), sizeof(statsHeader), fpStats);
+ fclose(fpStats);
+ if(numBytes != sizeof(statsHeader)) {
+ return false;
+ }
+ return true;
+}
+
#endif // __BENCHMARK_TEST_ENVIRONMENT_H__
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
index dedc743..85dcbc1 100644
--- a/media/tests/benchmark/tests/C2DecoderTest.cpp
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -136,7 +136,8 @@
mDecoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
ALOGV("codec : %s", codecName.c_str());
- mDecoder->dumpStatistics(GetParam().first, durationUs);
+ mDecoder->dumpStatistics(GetParam().first, durationUs, codecName,
+ gEnv->getStatsFile());
mDecoder->resetDecoder();
}
}
@@ -178,6 +179,9 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("C2Decoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
ALOGV("C2 Decoder Test result = %d\n", status);
}
diff --git a/media/tests/benchmark/tests/C2EncoderTest.cpp b/media/tests/benchmark/tests/C2EncoderTest.cpp
index 98eb17a6..b18d856 100644
--- a/media/tests/benchmark/tests/C2EncoderTest.cpp
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -108,7 +108,7 @@
}
string decName = "";
- string outputFileName = "decode.out";
+ string outputFileName = "/data/local/tmp/decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
<< " for dumping decoder's output";
@@ -140,7 +140,8 @@
mEncoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
ALOGV("codec : %s", codecName.c_str());
- mEncoder->dumpStatistics(GetParam().first, durationUs);
+ mEncoder->dumpStatistics(GetParam().first, durationUs, codecName,
+ gEnv->getStatsFile());
mEncoder->resetEncoder();
}
}
@@ -180,6 +181,9 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("C2Encoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
ALOGV("C2 Encoder Test result = %d\n", status);
}
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
index 9f96d3b..81ef02a 100644
--- a/media/tests/benchmark/tests/DecoderTest.cpp
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -84,7 +84,8 @@
decoder->deInitCodec();
ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
- decoder->dumpStatistics(inputReference);
+ decoder->dumpStatistics(inputReference, codecName, (asyncMode ? "async" : "sync"),
+ gEnv->getStatsFile());
free(inputBuffer);
decoder->resetDecoder();
}
@@ -179,8 +180,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Decoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD("Decoder Test result = %d\n", status);
+ ALOGV("Decoder Test result = %d\n", status);
}
return status;
}
\ No newline at end of file
diff --git a/media/tests/benchmark/tests/EncoderTest.cpp b/media/tests/benchmark/tests/EncoderTest.cpp
index dc2a2dd..faac847 100644
--- a/media/tests/benchmark/tests/EncoderTest.cpp
+++ b/media/tests/benchmark/tests/EncoderTest.cpp
@@ -78,7 +78,7 @@
}
string decName = "";
- string outputFileName = "decode.out";
+ string outputFileName = "/data/local/tmp/decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
<< " for dumping decoder's output";
@@ -133,7 +133,8 @@
encoder->deInitCodec();
ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
- encoder->dumpStatistics(inputReference, extractor->getClipDuration());
+ encoder->dumpStatistics(inputReference, extractor->getClipDuration(), codecName,
+ (asyncMode ? "async" : "sync"), gEnv->getStatsFile());
eleStream.close();
if (outFp) fclose(outFp);
@@ -214,8 +215,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Encoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD("Encoder Test result = %d\n", status);
+ ALOGV("Encoder Test result = %d\n", status);
}
return status;
}
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
index ad8f1e6..d14d15b 100644
--- a/media/tests/benchmark/tests/ExtractorTest.cpp
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -48,8 +48,7 @@
ASSERT_EQ(status, AMEDIA_OK) << "Extraction failed \n";
extractObj->deInitExtractor();
-
- extractObj->dumpStatistics(GetParam().first);
+ extractObj->dumpStatistics(GetParam().first, "", gEnv->getStatsFile());
fclose(inputFp);
delete extractObj;
@@ -79,8 +78,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Extractor.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD(" Extractor Test result = %d\n", status);
+ ALOGV("Extractor Test result = %d\n", status);
}
return status;
}
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
index fa2635d..991644b 100644
--- a/media/tests/benchmark/tests/MuxerTest.cpp
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -113,7 +113,7 @@
ASSERT_EQ(status, 0) << "Mux failed";
muxerObj->deInitMuxer();
- muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
+ muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str(), fmt, gEnv->getStatsFile());
free(inputBuffer);
fclose(outputFp);
muxerObj->resetMuxer();
@@ -151,8 +151,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Muxer.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGV("Test result = %d\n", status);
+ ALOGV("Muxer Test result = %d\n", status);
}
return status;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 10a9c63..9e36f77 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -29,6 +29,7 @@
#include <string>
#include <sys/time.h>
#include <sys/resource.h>
+#include <thread>
#include <android/os/IExternalVibratorService.h>
#include <binder/IPCThreadState.h>
@@ -143,6 +144,19 @@
return sExternalVibratorService;
}
+class DevicesFactoryHalCallbackImpl : public DevicesFactoryHalCallback {
+ public:
+ void onNewDevicesAvailable() override {
+ // Start a detached thread to execute notification in parallel.
+ // This is done to prevent mutual blocking of audio_flinger and
+ // audio_policy services during system initialization.
+ std::thread notifier([]() {
+ AudioSystem::onNewAudioModulesAvailable();
+ });
+ notifier.detach();
+ }
+};
+
// ----------------------------------------------------------------------------
std::string formatToString(audio_format_t format) {
@@ -221,6 +235,9 @@
mMode = AUDIO_MODE_NORMAL;
gAudioFlinger = this;
+
+ mDevicesFactoryHalCallback = new DevicesFactoryHalCallbackImpl;
+ mDevicesFactoryHal->setCallbackOnce(mDevicesFactoryHalCallback);
}
status_t AudioFlinger::setAudioHalPids(const std::vector<pid_t>& pids) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a43a6dc..115bbb8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -103,6 +103,7 @@
class AudioBuffer;
class AudioResampler;
class DeviceHalInterface;
+class DevicesFactoryHalCallback;
class DevicesFactoryHalInterface;
class EffectsFactoryHalInterface;
class FastMixer;
@@ -827,6 +828,7 @@
DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*> mAudioHwDevs;
sp<DevicesFactoryHalInterface> mDevicesFactoryHal;
+ sp<DevicesFactoryHalCallback> mDevicesFactoryHalCallback;
// for dump, indicates which hardware operation is currently in progress (but not stream ops)
enum hardware_call_state {
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 1fe60d4..9676d09 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -75,6 +75,10 @@
// configuration functions
//
+ // Informs APM that new HAL modules are available. This typically happens
+ // due to registration of an audio HAL service.
+ virtual void onNewAudioModulesAvailable() = 0;
+
// indicate a change in device connection status
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index 56596f5..47c6e68 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -37,13 +37,13 @@
{
public:
AudioPolicyConfig(HwModuleCollection &hwModules,
- DeviceVector &availableOutputDevices,
- DeviceVector &availableInputDevices,
+ DeviceVector &outputDevices,
+ DeviceVector &inputDevices,
sp<DeviceDescriptor> &defaultOutputDevice)
: mEngineLibraryNameSuffix(kDefaultEngineLibraryNameSuffix),
mHwModules(hwModules),
- mAvailableOutputDevices(availableOutputDevices),
- mAvailableInputDevices(availableInputDevices),
+ mOutputDevices(outputDevices),
+ mInputDevices(inputDevices),
mDefaultOutputDevice(defaultOutputDevice),
mIsSpeakerDrcEnabled(false)
{}
@@ -69,23 +69,23 @@
mHwModules = hwModules;
}
- void addAvailableDevice(const sp<DeviceDescriptor> &availableDevice)
+ void addDevice(const sp<DeviceDescriptor> &device)
{
- if (audio_is_output_device(availableDevice->type())) {
- mAvailableOutputDevices.add(availableDevice);
- } else if (audio_is_input_device(availableDevice->type())) {
- mAvailableInputDevices.add(availableDevice);
+ if (audio_is_output_device(device->type())) {
+ mOutputDevices.add(device);
+ } else if (audio_is_input_device(device->type())) {
+ mInputDevices.add(device);
}
}
- void addAvailableInputDevices(const DeviceVector &availableInputDevices)
+ void addInputDevices(const DeviceVector &inputDevices)
{
- mAvailableInputDevices.add(availableInputDevices);
+ mInputDevices.add(inputDevices);
}
- void addAvailableOutputDevices(const DeviceVector &availableOutputDevices)
+ void addOutputDevices(const DeviceVector &outputDevices)
{
- mAvailableOutputDevices.add(availableOutputDevices);
+ mOutputDevices.add(outputDevices);
}
bool isSpeakerDrcEnabled() const { return mIsSpeakerDrcEnabled; }
@@ -97,14 +97,14 @@
const HwModuleCollection getHwModules() const { return mHwModules; }
- const DeviceVector &getAvailableInputDevices() const
+ const DeviceVector &getInputDevices() const
{
- return mAvailableInputDevices;
+ return mInputDevices;
}
- const DeviceVector &getAvailableOutputDevices() const
+ const DeviceVector &getOutputDevices() const
{
- return mAvailableOutputDevices;
+ return mOutputDevices;
}
void setDefaultOutputDevice(const sp<DeviceDescriptor> &defaultDevice)
@@ -125,13 +125,11 @@
sp<AudioProfile> micProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000);
defaultInputDevice->addAudioProfile(micProfile);
- mAvailableOutputDevices.add(mDefaultOutputDevice);
- mAvailableInputDevices.add(defaultInputDevice);
+ mOutputDevices.add(mDefaultOutputDevice);
+ mInputDevices.add(defaultInputDevice);
sp<HwModule> module = new HwModule(AUDIO_HARDWARE_MODULE_ID_PRIMARY, 2 /*halVersionMajor*/);
mHwModules.add(module);
- mDefaultOutputDevice->attach(module);
- defaultInputDevice->attach(module);
sp<OutputProfile> outProfile = new OutputProfile("primary");
outProfile->addAudioProfile(
@@ -182,8 +180,8 @@
std::string mSource;
std::string mEngineLibraryNameSuffix;
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
- DeviceVector &mAvailableOutputDevices;
- DeviceVector &mAvailableInputDevices;
+ DeviceVector &mOutputDevices;
+ DeviceVector &mInputDevices;
sp<DeviceDescriptor> &mDefaultOutputDevice;
// TODO: remove when legacy conf file is removed. true on devices that use DRC on the
// DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly.
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index dd51658..aaa28bc 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -33,7 +33,10 @@
namespace android {
-DeviceTypeSet APM_AUDIO_OUT_DEVICE_REMOTE_ALL = {AUDIO_DEVICE_OUT_REMOTE_SUBMIX};
+static const DeviceTypeSet& getAllOutRemoteDevices() {
+ static const DeviceTypeSet allOutRemoteDevices = {AUDIO_DEVICE_OUT_REMOTE_SUBMIX};
+ return allOutRemoteDevices;
+}
AudioOutputDescriptor::AudioOutputDescriptor(const sp<PolicyAudioPort>& policyAudioPort,
AudioPolicyClientInterface *clientInterface)
@@ -681,7 +684,7 @@
const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
if (outputDesc->isActive(volumeSource, inPastMs, sysTime)
&& (!(outputDesc->devices()
- .containsDeviceAmongTypes(APM_AUDIO_OUT_DEVICE_REMOTE_ALL)))) {
+ .containsDeviceAmongTypes(getAllOutRemoteDevices())))) {
return true;
}
}
@@ -693,7 +696,7 @@
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < size(); i++) {
const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
- if (outputDesc->devices().containsDeviceAmongTypes(APM_AUDIO_OUT_DEVICE_REMOTE_ALL) &&
+ if (outputDesc->devices().containsDeviceAmongTypes(getAllOutRemoteDevices()) &&
outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
// do not consider re routing (when the output is going to a dynamic policy)
// as "remote playback"
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index f0bb28e..b8e1cbd 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -652,7 +652,7 @@
sp<DeviceDescriptor> device = module->getDeclaredDevices().
getDeviceFromTagName(std::string(reinterpret_cast<const char*>(
attachedDevice.get())));
- ctx->addAvailableDevice(device);
+ ctx->addDevice(device);
}
}
}
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
index fe15ff6..151c7bb 100644
--- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -73,10 +73,18 @@
audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(
const audio_attributes_t &attr) const
{
- const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
+ const auto &iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
[&attr](const auto &supportedAttr) {
return AudioProductStrategy::attributesMatches(supportedAttr.mAttributes, attr); });
- return iter != end(mAttributesVector) ? iter->mStream : AUDIO_STREAM_DEFAULT;
+ if (iter == end(mAttributesVector)) {
+ return AUDIO_STREAM_DEFAULT;
+ }
+ audio_stream_type_t streamType = iter->mStream;
+ ALOGW_IF(streamType == AUDIO_STREAM_DEFAULT,
+ "%s: Strategy %s supporting attributes %s has not stream type associated"
+ "fallback on MUSIC. Do not use stream volume API", __func__, mName.c_str(),
+ toString(attr).c_str());
+ return streamType != AUDIO_STREAM_DEFAULT ? streamType : AUDIO_STREAM_MUSIC;
}
audio_attributes_t ProductStrategy::getAttributesForStreamType(audio_stream_type_t streamType) const
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 02b99d0..41bb4e4 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -41,18 +41,21 @@
{
struct legacy_strategy_map { const char *name; legacy_strategy id; };
-static const std::vector<legacy_strategy_map> gLegacyStrategy = {
- { "STRATEGY_NONE", STRATEGY_NONE },
- { "STRATEGY_MEDIA", STRATEGY_MEDIA },
- { "STRATEGY_PHONE", STRATEGY_PHONE },
- { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION },
- { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL },
- { "STRATEGY_DTMF", STRATEGY_DTMF },
- { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE },
- { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER },
- { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
- { "STRATEGY_REROUTING", STRATEGY_REROUTING },
- { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume
+static const std::vector<legacy_strategy_map>& getLegacyStrategy() {
+ static const std::vector<legacy_strategy_map> legacyStrategy = {
+ { "STRATEGY_NONE", STRATEGY_NONE },
+ { "STRATEGY_MEDIA", STRATEGY_MEDIA },
+ { "STRATEGY_PHONE", STRATEGY_PHONE },
+ { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION },
+ { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL },
+ { "STRATEGY_DTMF", STRATEGY_DTMF },
+ { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE },
+ { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER },
+ { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
+ { "STRATEGY_REROUTING", STRATEGY_REROUTING },
+ { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume
+ };
+ return legacyStrategy;
};
Engine::Engine()
@@ -62,7 +65,8 @@
"Policy Engine configuration is partially invalid, skipped %zu elements",
result.nbSkippedElement);
- for (const auto &strategy : gLegacyStrategy) {
+ auto legacyStrategy = getLegacyStrategy();
+ for (const auto &strategy : legacyStrategy) {
mLegacyStrategyMap[getProductStrategyByName(strategy.name)] = strategy.id;
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index b747dd6..b6f9d7b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2441,6 +2441,10 @@
audio_devices_t device)
{
auto attributes = mEngine->getAttributesForStreamType(stream);
+ if (attributes == AUDIO_ATTRIBUTES_INITIALIZER) {
+ ALOGW("%s: no group for stream %s, bailing out", __func__, toString(stream).c_str());
+ return NO_ERROR;
+ }
ALOGV("%s: stream %s attributes=%s", __func__,
toString(stream).c_str(), toString(attributes).c_str());
return setVolumeIndexForAttributes(attributes, index, device);
@@ -4340,7 +4344,7 @@
mpClientInterface(clientInterface),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
- mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice),
+ mConfig(mHwModulesAll, mOutputDevicesAll, mInputDevicesAll, mDefaultOutputDevice),
mAudioPortGeneration(1),
mBeaconMuteRefCount(0),
mBeaconPlayingRefCount(0),
@@ -4385,141 +4389,9 @@
return status;
}
- // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
+ // after parsing the config, mOutputDevicesAll and mInputDevicesAll contain all known devices;
// open all output streams needed to access attached devices
- for (const auto& hwModule : mHwModulesAll) {
- hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
- if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
- ALOGW("could not open HW module %s", hwModule->getName());
- continue;
- }
- mHwModules.push_back(hwModule);
- // open all output streams needed to access attached devices
- // except for direct output streams that are only opened when they are actually
- // required by an app.
- // This also validates mAvailableOutputDevices list
- for (const auto& outProfile : hwModule->getOutputProfiles()) {
- if (!outProfile->canOpenNewIo()) {
- ALOGE("Invalid Output profile max open count %u for profile %s",
- outProfile->maxOpenCount, outProfile->getTagName().c_str());
- continue;
- }
- if (!outProfile->hasSupportedDevices()) {
- ALOGW("Output profile contains no device on module %s", hwModule->getName());
- continue;
- }
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_TTS) != 0) {
- mTtsOutputAvailable = true;
- }
-
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
- continue;
- }
- const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
- DeviceVector availProfileDevices = supportedDevices.filter(mAvailableOutputDevices);
- sp<DeviceDescriptor> supportedDevice = 0;
- if (supportedDevices.contains(mDefaultOutputDevice)) {
- supportedDevice = mDefaultOutputDevice;
- } else {
- // choose first device present in profile's SupportedDevices also part of
- // mAvailableOutputDevices.
- if (availProfileDevices.isEmpty()) {
- continue;
- }
- supportedDevice = availProfileDevices.itemAt(0);
- }
- if (!mAvailableOutputDevices.contains(supportedDevice)) {
- continue;
- }
- sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
- mpClientInterface);
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),
- AUDIO_STREAM_DEFAULT,
- AUDIO_OUTPUT_FLAG_NONE, &output);
- if (status != NO_ERROR) {
- ALOGW("Cannot open output stream for devices %s on hw module %s",
- supportedDevice->toString().c_str(), hwModule->getName());
- continue;
- }
- for (const auto &device : availProfileDevices) {
- // give a valid ID to an attached device once confirmed it is reachable
- if (!device->isAttached()) {
- device->attach(hwModule);
- }
- }
- if (mPrimaryOutput == 0 &&
- outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
- mPrimaryOutput = outputDesc;
- }
- addOutput(output, outputDesc);
- setOutputDevices(outputDesc,
- DeviceVector(supportedDevice),
- true,
- 0,
- NULL);
- }
- // open input streams needed to access attached devices to validate
- // mAvailableInputDevices list
- for (const auto& inProfile : hwModule->getInputProfiles()) {
- if (!inProfile->canOpenNewIo()) {
- ALOGE("Invalid Input profile max open count %u for profile %s",
- inProfile->maxOpenCount, inProfile->getTagName().c_str());
- continue;
- }
- if (!inProfile->hasSupportedDevices()) {
- ALOGW("Input profile contains no device on module %s", hwModule->getName());
- continue;
- }
- // chose first device present in profile's SupportedDevices also part of
- // available input devices
- const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
- DeviceVector availProfileDevices = supportedDevices.filter(mAvailableInputDevices);
- if (availProfileDevices.isEmpty()) {
- ALOGE("%s: Input device list is empty!", __FUNCTION__);
- continue;
- }
- sp<AudioInputDescriptor> inputDesc =
- new AudioInputDescriptor(inProfile, mpClientInterface);
-
- audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = inputDesc->open(nullptr,
- availProfileDevices.itemAt(0),
- AUDIO_SOURCE_MIC,
- AUDIO_INPUT_FLAG_NONE,
- &input);
- if (status != NO_ERROR) {
- ALOGW("Cannot open input stream for device %s on hw module %s",
- availProfileDevices.toString().c_str(),
- hwModule->getName());
- continue;
- }
- for (const auto &device : availProfileDevices) {
- // give a valid ID to an attached device once confirmed it is reachable
- if (!device->isAttached()) {
- device->attach(hwModule);
- device->importAudioPortAndPickAudioProfile(inProfile, true);
- }
- }
- inputDesc->close();
- }
- }
- // make sure all attached devices have been allocated a unique ID
- auto checkAndSetAvailable = [this](auto& devices) {
- for (size_t i = 0; i < devices.size();) {
- const auto &device = devices[i];
- if (!device->isAttached()) {
- ALOGW("device %s is unreachable", device->toString().c_str());
- devices.remove(device);
- continue;
- }
- // Device is now validated and can be appended to the available devices of the engine
- setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
- i++;
- }
- };
- checkAndSetAvailable(mAvailableOutputDevices);
- checkAndSetAvailable(mAvailableInputDevices);
+ onNewAudioModulesAvailable();
// make sure default device is reachable
if (mDefaultOutputDevice == 0 || !mAvailableOutputDevices.contains(mDefaultOutputDevice)) {
@@ -4574,6 +4446,134 @@
// ---
+void AudioPolicyManager::onNewAudioModulesAvailable()
+{
+ for (const auto& hwModule : mHwModulesAll) {
+ if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {
+ continue;
+ }
+ hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
+ if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
+ ALOGW("could not open HW module %s", hwModule->getName());
+ continue;
+ }
+ mHwModules.push_back(hwModule);
+ // open all output streams needed to access attached devices
+ // except for direct output streams that are only opened when they are actually
+ // required by an app.
+ // This also validates mAvailableOutputDevices list
+ for (const auto& outProfile : hwModule->getOutputProfiles()) {
+ if (!outProfile->canOpenNewIo()) {
+ ALOGE("Invalid Output profile max open count %u for profile %s",
+ outProfile->maxOpenCount, outProfile->getTagName().c_str());
+ continue;
+ }
+ if (!outProfile->hasSupportedDevices()) {
+ ALOGW("Output profile contains no device on module %s", hwModule->getName());
+ continue;
+ }
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_TTS) != 0) {
+ mTtsOutputAvailable = true;
+ }
+
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ continue;
+ }
+ const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
+ DeviceVector availProfileDevices = supportedDevices.filter(mOutputDevicesAll);
+ sp<DeviceDescriptor> supportedDevice = 0;
+ if (supportedDevices.contains(mDefaultOutputDevice)) {
+ supportedDevice = mDefaultOutputDevice;
+ } else {
+ // choose first device present in profile's SupportedDevices also part of
+ // mAvailableOutputDevices.
+ if (availProfileDevices.isEmpty()) {
+ continue;
+ }
+ supportedDevice = availProfileDevices.itemAt(0);
+ }
+ if (!mOutputDevicesAll.contains(supportedDevice)) {
+ continue;
+ }
+ sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+ mpClientInterface);
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),
+ AUDIO_STREAM_DEFAULT,
+ AUDIO_OUTPUT_FLAG_NONE, &output);
+ if (status != NO_ERROR) {
+ ALOGW("Cannot open output stream for devices %s on hw module %s",
+ supportedDevice->toString().c_str(), hwModule->getName());
+ continue;
+ }
+ for (const auto &device : availProfileDevices) {
+ // give a valid ID to an attached device once confirmed it is reachable
+ if (!device->isAttached()) {
+ device->attach(hwModule);
+ mAvailableOutputDevices.add(device);
+ setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
+ }
+ }
+ if (mPrimaryOutput == 0 &&
+ outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
+ mPrimaryOutput = outputDesc;
+ }
+ addOutput(output, outputDesc);
+ setOutputDevices(outputDesc,
+ DeviceVector(supportedDevice),
+ true,
+ 0,
+ NULL);
+ }
+ // open input streams needed to access attached devices to validate
+ // mAvailableInputDevices list
+ for (const auto& inProfile : hwModule->getInputProfiles()) {
+ if (!inProfile->canOpenNewIo()) {
+ ALOGE("Invalid Input profile max open count %u for profile %s",
+ inProfile->maxOpenCount, inProfile->getTagName().c_str());
+ continue;
+ }
+ if (!inProfile->hasSupportedDevices()) {
+ ALOGW("Input profile contains no device on module %s", hwModule->getName());
+ continue;
+ }
+ // chose first device present in profile's SupportedDevices also part of
+ // available input devices
+ const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
+ DeviceVector availProfileDevices = supportedDevices.filter(mInputDevicesAll);
+ if (availProfileDevices.isEmpty()) {
+ ALOGE("%s: Input device list is empty!", __FUNCTION__);
+ continue;
+ }
+ sp<AudioInputDescriptor> inputDesc =
+ new AudioInputDescriptor(inProfile, mpClientInterface);
+
+ audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+ status_t status = inputDesc->open(nullptr,
+ availProfileDevices.itemAt(0),
+ AUDIO_SOURCE_MIC,
+ AUDIO_INPUT_FLAG_NONE,
+ &input);
+ if (status != NO_ERROR) {
+ ALOGW("Cannot open input stream for device %s on hw module %s",
+ availProfileDevices.toString().c_str(),
+ hwModule->getName());
+ continue;
+ }
+ for (const auto &device : availProfileDevices) {
+ // give a valid ID to an attached device once confirmed it is reachable
+ if (!device->isAttached()) {
+ device->attach(hwModule);
+ device->importAudioPortAndPickAudioProfile(inProfile, true);
+ mAvailableInputDevices.add(device);
+ setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
+ }
+ }
+ inputDesc->close();
+ }
+ }
+}
+
void AudioPolicyManager::addOutput(audio_io_handle_t output,
const sp<SwAudioOutputDescriptor>& outputDesc)
{
@@ -5941,7 +5941,7 @@
int delayMs,
bool force)
{
- ALOGVV("applyStreamVolumes() for device %08x", device);
+ ALOGVV("applyStreamVolumes() for device %s", dumpDeviceTypes(deviceTypes).c_str());
for (const auto &volumeGroup : mEngine->getVolumeGroups()) {
auto &curves = getVolumeCurves(toVolumeSource(volumeGroup));
checkAndSetVolume(curves, toVolumeSource(volumeGroup),
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 634eb31..4604676 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -315,6 +315,8 @@
return volumeGroup != VOLUME_GROUP_NONE ? NO_ERROR : BAD_VALUE;
}
+ void onNewAudioModulesAvailable() override;
+
status_t initialize();
protected:
@@ -717,6 +719,8 @@
SwAudioOutputCollection mPreviousOutputs;
AudioInputCollection mInputs; // list of input descriptors
+ DeviceVector mOutputDevicesAll; // all output devices from the config
+ DeviceVector mInputDevicesAll; // all input devices from the config
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
@@ -727,9 +731,8 @@
EffectDescriptorCollection mEffects; // list of registered audio effects
sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time
- HwModuleCollection mHwModules; // contains only modules that have been loaded successfully
- HwModuleCollection mHwModulesAll; // normally not needed, used during construction and for
- // dumps
+ HwModuleCollection mHwModules; // contains modules that have been loaded successfully
+ HwModuleCollection mHwModulesAll; // contains all modules declared in the config
AudioPolicyConfig mConfig;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index c1190be..227adc7 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -28,6 +28,14 @@
// ----------------------------------------------------------------------------
+void AudioPolicyService::doOnNewAudioModulesAvailable()
+{
+ if (mAudioPolicyManager == NULL) return;
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ mAudioPolicyManager->onNewAudioModulesAvailable();
+}
+
status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 90939ce..3d9278b 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -1278,6 +1278,16 @@
mLock.lock();
}
} break;
+ case AUDIO_MODULES_UPDATE: {
+ ALOGV("AudioCommandThread() processing audio modules update");
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnNewAudioModulesAvailable();
+ mLock.lock();
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
@@ -1567,6 +1577,13 @@
sendCommand(command);
}
+void AudioPolicyService::AudioCommandThread::audioModulesUpdateCommand()
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = AUDIO_MODULES_UPDATE;
+ sendCommand(command);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
@@ -1814,6 +1831,11 @@
mAudioCommandThread->setEffectSuspendedCommand(effectId, sessionId, suspended);
}
+void AudioPolicyService::onNewAudioModulesAvailable()
+{
+ mAudioCommandThread->audioModulesUpdateCommand();
+}
+
extern "C" {
audio_module_handle_t aps_load_hw_module(void *service __unused,
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 7b72dc1..0370438 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -60,6 +60,7 @@
// BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
//
+ void onNewAudioModulesAvailable() override;
virtual status_t setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
@@ -268,6 +269,7 @@
virtual status_t setRttEnabled(bool enabled);
+ void doOnNewAudioModulesAvailable();
status_t doStopOutput(audio_port_handle_t portId);
void doReleaseOutput(audio_port_handle_t portId);
@@ -450,6 +452,7 @@
DYN_POLICY_MIX_STATE_UPDATE,
RECORDING_CONFIGURATION_UPDATE,
SET_EFFECT_SUSPENDED,
+ AUDIO_MODULES_UPDATE,
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -495,6 +498,7 @@
void setEffectSuspendedCommand(int effectId,
audio_session_t sessionId,
bool suspended);
+ void audioModulesUpdateCommand();
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
class AudioCommandData;
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index c2a92d7..af69466 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -15,6 +15,7 @@
*/
#include <map>
+#include <set>
#include <system/audio.h>
#include <utils/Log.h>
@@ -27,7 +28,10 @@
class AudioPolicyManagerTestClient : public AudioPolicyTestClient {
public:
// AudioPolicyClientInterface implementation
- audio_module_handle_t loadHwModule(const char * /*name*/) override {
+ audio_module_handle_t loadHwModule(const char* name) override {
+ if (!mAllowedModuleNames.empty() && !mAllowedModuleNames.count(name)) {
+ return AUDIO_MODULE_HANDLE_NONE;
+ }
return mNextModuleHandle++;
}
@@ -101,11 +105,18 @@
return &it->second;
};
+ audio_module_handle_t peekNextModuleHandle() const { return mNextModuleHandle; }
+
+ void swapAllowedModuleNames(std::set<std::string>&& names = {}) {
+ mAllowedModuleNames.swap(names);
+ }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
audio_patch_handle_t mNextPatchHandle = AUDIO_PATCH_HANDLE_NONE + 1;
std::map<audio_patch_handle_t, struct audio_patch> mActivePatches;
+ std::set<std::string> mAllowedModuleNames;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index c77dcdc..e8c2d33 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -26,6 +26,8 @@
using AudioPolicyManager::getConfig;
using AudioPolicyManager::loadConfig;
using AudioPolicyManager::initialize;
+ using AudioPolicyManager::getAvailableOutputDevices;
+ using AudioPolicyManager::getAvailableInputDevices;
};
} // namespace android
diff --git a/services/audiopolicy/tests/audio_health_tests.cpp b/services/audiopolicy/tests/audio_health_tests.cpp
index 8736cf1..b5c67a1 100644
--- a/services/audiopolicy/tests/audio_health_tests.cpp
+++ b/services/audiopolicy/tests/audio_health_tests.cpp
@@ -67,10 +67,10 @@
manager.loadConfig();
ASSERT_NE("AudioPolicyConfig::setDefault", manager.getConfig().getSource());
- for (auto desc : manager.getConfig().getAvailableInputDevices()) {
+ for (auto desc : manager.getConfig().getInputDevices()) {
ASSERT_NE(attachedDevices.end(), attachedDevices.find(desc->type()));
}
- for (auto desc : manager.getConfig().getAvailableOutputDevices()) {
+ for (auto desc : manager.getConfig().getOutputDevices()) {
ASSERT_NE(attachedDevices.end(), attachedDevices.find(desc->type()));
}
}
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 0263597..acd61ed 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -292,9 +292,9 @@
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
uid_t uid = 42;
const PatchCountCheck patchCount = snapshotPatchCount();
- ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty());
+ ASSERT_FALSE(mManager->getAvailableInputDevices().isEmpty());
PatchBuilder patchBuilder;
- patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]).
+ patchBuilder.addSource(mManager->getAvailableInputDevices()[0]).
addSink(mManager->getConfig().getDefaultOutputDevice());
ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid));
ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle);
@@ -328,15 +328,13 @@
sp<AudioProfile> pcmInputProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, 44100);
mMsdInputDevice->addAudioProfile(pcmInputProfile);
- config.addAvailableDevice(mMsdOutputDevice);
- config.addAvailableDevice(mMsdInputDevice);
+ config.addDevice(mMsdOutputDevice);
+ config.addDevice(mMsdInputDevice);
sp<HwModule> msdModule = new HwModule(AUDIO_HARDWARE_MODULE_ID_MSD, 2 /*halVersionMajor*/);
HwModuleCollection modules = config.getHwModules();
modules.add(msdModule);
config.setHwModules(modules);
- mMsdOutputDevice->attach(msdModule);
- mMsdInputDevice->attach(msdModule);
sp<OutputProfile> msdOutputProfile = new OutputProfile("msd input");
msdOutputProfile->addAudioProfile(pcmOutputProfile);
@@ -1058,3 +1056,40 @@
"hfp_client_out"})
)
);
+
+class AudioPolicyManagerDynamicHwModulesTest : public AudioPolicyManagerTestWithConfigurationFile {
+protected:
+ void SetUpManagerConfig() override;
+};
+
+void AudioPolicyManagerDynamicHwModulesTest::SetUpManagerConfig() {
+ AudioPolicyManagerTestWithConfigurationFile::SetUpManagerConfig();
+ // Only allow successful opening of "primary" hw module during APM initialization.
+ mClient->swapAllowedModuleNames({"primary"});
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, DynamicAddition) {
+ const auto handleBefore = mClient->peekNextModuleHandle();
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(handleBefore, mClient->peekNextModuleHandle());
+ // Reset module loading restrictions.
+ mClient->swapAllowedModuleNames();
+ mManager->onNewAudioModulesAvailable();
+ const auto handleAfter = mClient->peekNextModuleHandle();
+ ASSERT_GT(handleAfter, handleBefore);
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(handleAfter, mClient->peekNextModuleHandle());
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, AddedDeviceAvailable) {
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, mManager->getDeviceConnectionState(
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0"));
+ mClient->swapAllowedModuleNames({"primary", "r_submix"});
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_AVAILABLE, mManager->getDeviceConnectionState(
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0"));
+}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c566485..595c3e8 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -3309,8 +3309,15 @@
Mutex::Autolock lock(mStatusListenerLock);
for (auto& listener : mListenerList) {
- if (!listener.first && (isHidden || !supportsHAL3)) {
- ALOGV("Skipping camera discovery callback for system-only / HAL1 camera %s",
+ bool isVendorListener = listener.first;
+ if (isVendorListener && !supportsHAL3) {
+ ALOGV("Skipping vendor listener camera discovery callback for HAL1 camera %s",
+ cameraId.c_str());
+ continue;
+ }
+
+ if (!isVendorListener && isHidden) {
+ ALOGV("Skipping camera discovery callback for system-only camera %s",
cameraId.c_str());
continue;
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 162b50f..ac86563 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -732,6 +732,10 @@
ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+ if (params.state == Parameters::DISCONNECTED) {
+ ALOGE("%s: Camera %d has been disconnected.", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
if ( (params.state == Parameters::PREVIEW ||
params.state == Parameters::RECORD ||
params.state == Parameters::VIDEO_SNAPSHOT)
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bda35f3..dfe5eb0 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -339,100 +339,103 @@
status_t Camera3Device::disconnectImpl() {
ATRACE_CALL();
- Mutex::Autolock il(mInterfaceLock);
-
ALOGI("%s: E", __FUNCTION__);
status_t res = OK;
std::vector<wp<Camera3StreamInterface>> streams;
- nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
{
- Mutex::Autolock l(mLock);
- if (mStatus == STATUS_UNINITIALIZED) return res;
+ Mutex::Autolock il(mInterfaceLock);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
+ {
+ Mutex::Autolock l(mLock);
+ if (mStatus == STATUS_UNINITIALIZED) return res;
- if (mStatus == STATUS_ACTIVE ||
- (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
- res = mRequestThread->clearRepeatingRequests();
- if (res != OK) {
- SET_ERR_L("Can't stop streaming");
- // Continue to close device even in case of error
- } else {
- res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
+ if (mStatus == STATUS_ACTIVE ||
+ (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
+ res = mRequestThread->clearRepeatingRequests();
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
- maxExpectedDuration);
+ SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
+ } else {
+ res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
+ if (res != OK) {
+ SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
+ maxExpectedDuration);
+ // Continue to close device even in case of error
+ }
}
}
- }
- if (mStatus == STATUS_ERROR) {
- CLOGE("Shutting down in an error state");
- }
+ if (mStatus == STATUS_ERROR) {
+ CLOGE("Shutting down in an error state");
+ }
- if (mStatusTracker != NULL) {
- mStatusTracker->requestExit();
- }
+ if (mStatusTracker != NULL) {
+ mStatusTracker->requestExit();
+ }
- if (mRequestThread != NULL) {
- mRequestThread->requestExit();
- }
+ if (mRequestThread != NULL) {
+ mRequestThread->requestExit();
+ }
- streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
- for (size_t i = 0; i < mOutputStreams.size(); i++) {
- streams.push_back(mOutputStreams[i]);
- }
- if (mInputStream != nullptr) {
- streams.push_back(mInputStream);
+ streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ streams.push_back(mOutputStreams[i]);
+ }
+ if (mInputStream != nullptr) {
+ streams.push_back(mInputStream);
+ }
}
}
-
- // Joining done without holding mLock, otherwise deadlocks may ensue
- // as the threads try to access parent state
+ // Joining done without holding mLock and mInterfaceLock, otherwise deadlocks may ensue
+ // as the threads try to access parent state (b/143513518)
if (mRequestThread != NULL && mStatus != STATUS_ERROR) {
// HAL may be in a bad state, so waiting for request thread
// (which may be stuck in the HAL processCaptureRequest call)
// could be dangerous.
+ // give up mInterfaceLock here and then lock it again. Could this lead
+ // to other deadlocks
mRequestThread->join();
}
-
- if (mStatusTracker != NULL) {
- mStatusTracker->join();
- }
-
- HalInterface* interface;
{
- Mutex::Autolock l(mLock);
- mRequestThread.clear();
- Mutex::Autolock stLock(mTrackerLock);
- mStatusTracker.clear();
- interface = mInterface.get();
- }
+ Mutex::Autolock il(mInterfaceLock);
+ if (mStatusTracker != NULL) {
+ mStatusTracker->join();
+ }
- // Call close without internal mutex held, as the HAL close may need to
- // wait on assorted callbacks,etc, to complete before it can return.
- interface->close();
+ HalInterface* interface;
+ {
+ Mutex::Autolock l(mLock);
+ mRequestThread.clear();
+ Mutex::Autolock stLock(mTrackerLock);
+ mStatusTracker.clear();
+ interface = mInterface.get();
+ }
- flushInflightRequests();
+ // Call close without internal mutex held, as the HAL close may need to
+ // wait on assorted callbacks,etc, to complete before it can return.
+ interface->close();
- {
- Mutex::Autolock l(mLock);
- mInterface->clear();
- mOutputStreams.clear();
- mInputStream.clear();
- mDeletedStreams.clear();
- mBufferManager.clear();
- internalUpdateStatusLocked(STATUS_UNINITIALIZED);
- }
+ flushInflightRequests();
- for (auto& weakStream : streams) {
- sp<Camera3StreamInterface> stream = weakStream.promote();
- if (stream != nullptr) {
- ALOGE("%s: Stream %d leaked! strong reference (%d)!",
- __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ {
+ Mutex::Autolock l(mLock);
+ mInterface->clear();
+ mOutputStreams.clear();
+ mInputStream.clear();
+ mDeletedStreams.clear();
+ mBufferManager.clear();
+ internalUpdateStatusLocked(STATUS_UNINITIALIZED);
+ }
+
+ for (auto& weakStream : streams) {
+ sp<Camera3StreamInterface> stream = weakStream.promote();
+ if (stream != nullptr) {
+ ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+ __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ }
}
}
-
ALOGI("%s: X", __FUNCTION__);
return res;
}
@@ -2165,9 +2168,7 @@
}
void Camera3Device::pauseStateNotify(bool enable) {
- // We must not hold mInterfaceLock here since this function is called from
- // RequestThread::threadLoop and holding mInterfaceLock could lead to
- // deadlocks (http://b/143513518)
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
mPauseStateNotify = enable;
@@ -2744,9 +2745,7 @@
ATRACE_CALL();
bool ret = false;
- // We must not hold mInterfaceLock here since this function is called from
- // RequestThread::threadLoop and holding mInterfaceLock could lead to
- // deadlocks (http://b/143513518)
+ Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index cae34ce..9b0648d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -244,7 +244,6 @@
// A lock to enforce serialization on the input/configure side
// of the public interface.
- // Only locked by public methods inherited from CameraDeviceBase.
// Not locked by methods guarded by mOutputLock, since they may act
// concurrently to the input/configure side of the interface.
// Must be locked before mLock if both will be locked by a method
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
index 16b036f..e906500 100644
--- a/services/mediaextractor/Android.bp
+++ b/services/mediaextractor/Android.bp
@@ -29,9 +29,6 @@
"liblog",
"libavservices_minijail",
],
- header_libs: [
- "bionic_libc_platform_headers",
- ],
target: {
android: {
product_variables: {
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index afb7692..3c4125b 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -28,8 +28,6 @@
#include <android-base/properties.h>
#include <utils/misc.h>
-#include <bionic/reserved_signals.h>
-
// from LOCAL_C_INCLUDES
#include "MediaExtractorService.h"
#include "MediaUtils.h"
@@ -51,10 +49,6 @@
signal(SIGPIPE, SIG_IGN);
- // Do not assist platform profilers (relevant only on debug builds).
- // Otherwise, the signal handler can violate the seccomp policy.
- signal(BIONIC_SIGNAL_PROFILER, SIG_IGN);
-
//b/62255959: this forces libutis.so to dlopen vendor version of libutils.so
//before minijail is on. This is dirty but required since some syscalls such
//as pread64 are used by linker but aren't allowed in the minijail. By