Merge "Send the Audio Usage down from AudioPolicyManager" into main
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index a866dc8..d90f7c9 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -69,12 +69,11 @@
// deadlock if we call any method of ICamera here.
}
-sp<Camera> Camera::connect(int cameraId, const std::string& clientPackageName,
- int targetSdkVersion, int rotationOverride,
+sp<Camera> Camera::connect(int cameraId, int targetSdkVersion, int rotationOverride,
bool forceSlowJpegMode, const AttributionSourceState& clientAttribution,
int32_t devicePolicy)
{
- return CameraBaseT::connect(cameraId, clientPackageName, targetSdkVersion, rotationOverride,
+ return CameraBaseT::connect(cameraId, targetSdkVersion, rotationOverride,
forceSlowJpegMode, clientAttribution, devicePolicy);
}
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index b617c65..774db25 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -161,7 +161,6 @@
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
- const std::string& clientPackageName,
int targetSdkVersion, int rotationOverride,
bool forceSlowJpegMode,
const AttributionSourceState& clientAttribution,
@@ -177,7 +176,7 @@
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
ALOGI("Connect camera (legacy API) - rotationOverride %d, forceSlowJpegMode %d",
rotationOverride, forceSlowJpegMode);
- ret = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, targetSdkVersion,
+ ret = (cs.get()->*fnConnectService)(cl, cameraId, targetSdkVersion,
rotationOverride, forceSlowJpegMode, clientAttribution, devicePolicy,
/*out*/ &c->mCamera);
}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 2df1495..ce6c2d3 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -120,7 +120,6 @@
* Open a camera device through the old camera API.
*
* @param cameraId The ID of the camera to open.
- * @param opPackageName The package name to report for the app-ops.
* @param targetSdkVersion the target sdk level of the application calling this function.
* @param rotationOverride Whether to override the sensor orientation information to
* correspond to portrait: {@link ICameraService#ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT}
@@ -136,7 +135,6 @@
*/
ICamera connect(ICameraClient client,
int cameraId,
- @utf8InCpp String opPackageName,
int targetSdkVersion,
int rotationOverride,
boolean forceSlowJpegMode,
@@ -148,7 +146,6 @@
* Only supported for device HAL versions >= 3.2.
*
* @param cameraId The ID of the camera to open.
- * @param opPackageName The package name to report for the app-ops.
* @param targetSdkVersion the target sdk level of the application calling this function.
* @param rotationOverride Whether to override the sensor orientation information to
* correspond to portrait: {@link ICameraService#ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT}
@@ -163,8 +160,6 @@
*/
ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
@utf8InCpp String cameraId,
- @utf8InCpp String opPackageName,
- @nullable @utf8InCpp String featureId,
int oomScoreOffset,
int targetSdkVersion,
int rotationOverride,
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index fe10e12..e916985 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -247,3 +247,33 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "camera_platform"
+ name: "api1_release_binderlock_before_cameraservice_disconnect"
+ description: "Drop mSerializationLock in Camera1 client when calling into CameraService"
+ bug: "351778072"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "bump_preview_frame_space_priority"
+ description: "Increase the PreviewFrameSpacer thread priority"
+ bug: "355665306"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "dumpsys_request_stream_ids"
+ description: "Add stream id information to last request dumpsys"
+ bug: "357913929"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h
index f533dd8..646b139 100644
--- a/camera/include/camera/Camera.h
+++ b/camera/include/camera/Camera.h
@@ -59,7 +59,7 @@
typedef ::android::hardware::ICameraClient TCamCallbacks;
typedef ::android::binder::Status (::android::hardware::ICameraService::*TCamConnectService)
(const sp<::android::hardware::ICameraClient>&,
- int, const std::string&, int, int, bool, const AttributionSourceState&, int32_t,
+ int, int, int, bool, const AttributionSourceState&, int32_t,
/*out*/
sp<::android::hardware::ICamera>*);
static TCamConnectService fnConnectService;
@@ -81,7 +81,6 @@
// construct a camera client from an existing remote
static sp<Camera> create(const sp<::android::hardware::ICamera>& camera);
static sp<Camera> connect(int cameraId,
- const std::string& clientPackageName,
int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
const AttributionSourceState& clientAttribution,
int32_t devicePolicy = 0);
diff --git a/camera/include/camera/CameraBase.h b/camera/include/camera/CameraBase.h
index aa83362..d98abe4 100644
--- a/camera/include/camera/CameraBase.h
+++ b/camera/include/camera/CameraBase.h
@@ -125,7 +125,6 @@
typedef typename TCamTraits::TCamConnectService TCamConnectService;
static sp<TCam> connect(int cameraId,
- const std::string& clientPackageName,
int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
const AttributionSourceState &clientAttribution,
int32_t devicePolicy);
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 6caaa94..6d29ef5 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -177,14 +177,11 @@
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
- do {
- binder = sm->getService(toString16(kCameraServiceName));
- if (binder != nullptr) {
- break;
- }
- ALOGW("CameraService not published, waiting...");
- usleep(kCameraServicePollDelay);
- } while(true);
+ binder = sm->checkService(String16(kCameraServiceName));
+ if (binder == nullptr) {
+ ALOGE("%s: Could not get CameraService instance.", __FUNCTION__);
+ return nullptr;
+ }
if (mDeathNotifier == nullptr) {
mDeathNotifier = new DeathNotifier(this);
}
@@ -871,11 +868,13 @@
clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
clientAttribution.deviceId = mDeviceContext.deviceId;
+ clientAttribution.packageName = "";
+ clientAttribution.attributionTag = std::nullopt;
// No way to get package name from native.
// Send a zero length package name and let camera service figure it out from UID
binder::Status serviceRet = cs->connectDevice(
- callbacks, cameraId, "", {}, /*oomScoreOffset*/0,
+ callbacks, cameraId, /*oomScoreOffset*/0,
targetSdkVersion, /*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, static_cast<int32_t>(mDeviceContext.policy),
/*out*/&deviceRemote);
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index d8bf6b1..f4124ef 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -99,7 +99,6 @@
private:
sp<hardware::ICameraService> mCameraService;
- const int kCameraServicePollDelay = 500000; // 0.5s
const char* kCameraServiceName = "media.camera";
Mutex mLock;
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index cf6b970..1400121 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -576,9 +576,7 @@
*
* @param session the capture session of interest
*
- * @return <ul><li>
- * {@link ACAMERA_OK} if the method succeeds. captureSequenceId will be filled
- * if it is not NULL.</li>
+ * @return <ul><li>{@link ACAMERA_OK} if the method succeeds.</li>
* <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if session is NULL.</li>
* <li>{@link ACAMERA_ERROR_SESSION_CLOSED} if the capture session has been closed</li>
* <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed</li>
@@ -617,9 +615,7 @@
*
* @param session the capture session of interest
*
- * @return <ul><li>
- * {@link ACAMERA_OK} if the method succeeds. captureSequenceId will be filled
- * if it is not NULL.</li>
+ * @return <ul><li> {@link ACAMERA_OK} if the method succeeds</li>
* <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if session is NULL.</li>
* <li>{@link ACAMERA_ERROR_SESSION_CLOSED} if the capture session has been closed</li>
* <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed</li>
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index e8c2006..d21513c 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -351,6 +351,7 @@
AttributionSourceState clientAttribution;
clientAttribution.deviceId = kDefaultDeviceId;
clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
+ clientAttribution.packageName = "meeeeeeeee!";
res = service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_ALL, clientAttribution,
/*devicePolicy*/0, &numCameras);
EXPECT_TRUE(res.isOk()) << res;
@@ -397,8 +398,8 @@
// Check connect binder calls
sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
sp<hardware::camera2::ICameraDeviceUser> device;
- res = service->connectDevice(callbacks, cameraId, "meeeeeeeee!",
- {}, /*oomScoreOffset*/ 0,
+ res = service->connectDevice(callbacks, cameraId,
+ /*oomScoreOffset*/ 0,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
/*overrideToPortrait*/false, clientAttribution, /*devicePolicy*/0, /*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
@@ -444,8 +445,9 @@
AttributionSourceState clientAttribution;
clientAttribution.deviceId = kDefaultDeviceId;
clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
- binder::Status res = service->connectDevice(callbacks, deviceId, "meeeeeeeee!",
- {}, /*oomScoreOffset*/ 0,
+ clientAttribution.packageName = "meeeeeeeee!";
+ binder::Status res = service->connectDevice(callbacks, deviceId,
+ /*oomScoreOffset*/ 0,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
/*overrideToPortrait*/false, clientAttribution, /*devicePolicy*/0,
/*out*/&device);
diff --git a/camera/tests/CameraZSLTests.cpp b/camera/tests/CameraZSLTests.cpp
index b80ad15..2740d09 100644
--- a/camera/tests/CameraZSLTests.cpp
+++ b/camera/tests/CameraZSLTests.cpp
@@ -216,8 +216,9 @@
clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
+ clientAttribution.packageName = "ZSLTest";
rc = mCameraService->connect(this, cameraId,
- "ZSLTest", /*targetSdkVersion*/__ANDROID_API_FUTURE__,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__,
/*overrideToPortrait*/false, /*forceSlowJpegMode*/false, clientAttribution,
/*devicePolicy*/0, &cameraDevice);
EXPECT_TRUE(rc.isOk());
diff --git a/camera/tests/fuzzer/camera_fuzzer.cpp b/camera/tests/fuzzer/camera_fuzzer.cpp
index 9b6fd49..f46d246 100644
--- a/camera/tests/fuzzer/camera_fuzzer.cpp
+++ b/camera/tests/fuzzer/camera_fuzzer.cpp
@@ -129,15 +129,16 @@
if (mFDP->ConsumeBool()) {
clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
- cameraService->connect(this, mFDP->ConsumeIntegral<int32_t>() /* cameraId */, "CAMERAFUZZ",
+ clientAttribution.packageName = "CAMERAFUZZ";
+ cameraService->connect(this, mFDP->ConsumeIntegral<int32_t>() /* cameraId */,
/*targetSdkVersion*/ __ANDROID_API_FUTURE__,
/*overrideToPortrait*/ false, /*forceSlowJpegMode*/ false,
clientAttribution, /*devicePolicy*/0, &cameraDevice);
} else {
clientAttribution.uid = mFDP->ConsumeIntegral<int8_t>();
clientAttribution.pid = mFDP->ConsumeIntegral<int8_t>();
+ clientAttribution.packageName = mFDP->ConsumeRandomLengthString(kMaxBytes).c_str();
cameraService->connect(this, mFDP->ConsumeIntegral<int32_t>() /* cameraId */,
- mFDP->ConsumeRandomLengthString(kMaxBytes).c_str(),
/*targetSdkVersion*/ mFDP->ConsumeIntegral<int32_t>(),
/*overrideToPortrait*/ mFDP->ConsumeBool(),
/*forceSlowJpegMode*/ mFDP->ConsumeBool(), clientAttribution,
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
index 16ea15e..6e55a16 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
@@ -64,10 +64,6 @@
"libfwdlock-decoder",
],
- whole_static_libs: [
- "libc++fs",
- ],
-
local_include_dirs: ["include"],
relative_install_path: "drm",
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
index 9a06bd2..8e8e57d 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -11,13 +11,13 @@
name: "aidl_clearkey_service_defaults-use-shared-deps",
shared_libs: [
+ "android.hardware.drm-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
- "android.hardware.drm-V1-ndk",
],
static_libs: [
@@ -62,7 +62,11 @@
relative_install_path: "hw",
- cflags: ["-Wall", "-Werror", "-Wthread-safety"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ ],
include_dirs: ["frameworks/av/include"],
@@ -137,18 +141,22 @@
relative_install_path: "hw",
- cflags: ["-Wall", "-Werror", "-Wthread-safety"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ ],
include_dirs: ["frameworks/av/include"],
shared_libs: [
+ "android.hardware.drm-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
- "android.hardware.drm-V1-ndk",
],
static_libs: [
@@ -192,7 +200,7 @@
],
prebuilts: [
"android.hardware.drm-service.clearkey.apex.rc",
- "android.hardware.drm-service.clearkey.xml"
+ "android.hardware.drm-service.clearkey.xml",
],
overrides: [
"android.hardware.drm-service.clearkey",
@@ -233,11 +241,11 @@
],
prebuilts: [
"android.hardware.drm-service-lazy.clearkey.apex.rc",
- "android.hardware.drm-service.clearkey.xml"
+ "android.hardware.drm-service.clearkey.xml",
],
overrides: [
- "android.hardware.drm-service.clearkey",
"android.hardware.drm-service-lazy.clearkey",
+ "android.hardware.drm-service.clearkey",
"com.android.hardware.drm.clearkey",
],
}
diff --git a/media/audio/aconfig/audio.aconfig b/media/audio/aconfig/audio.aconfig
index c3ae59c..9221c04 100644
--- a/media/audio/aconfig/audio.aconfig
+++ b/media/audio/aconfig/audio.aconfig
@@ -83,6 +83,14 @@
}
flag {
+ name: "ring_my_car"
+ namespace: "media_audio"
+ description:
+ "Incoming ringtones will not be muted based on ringer mode when connected to a car"
+ bug: "319515324"
+}
+
+flag {
name: "ringer_mode_affects_alarm"
namespace: "media_audio"
description:
diff --git a/media/audio/aconfig/audioserver.aconfig b/media/audio/aconfig/audioserver.aconfig
index 39fa187..d1c6239 100644
--- a/media/audio/aconfig/audioserver.aconfig
+++ b/media/audio/aconfig/audioserver.aconfig
@@ -65,6 +65,22 @@
}
flag {
+ name: "portid_volume_management"
+ namespace: "media_audio"
+ description:
+ "Allows to manage volume by port id within audio flinger instead of legacy stream type."
+ bug: "317212590"
+}
+
+flag {
+ name: "power_stats"
+ namespace: "media_audio"
+ description:
+ "Add power stats tracking and management."
+ bug: "350114693"
+}
+
+flag {
name: "use_bt_sco_for_media"
namespace: "media_audio"
description:
diff --git a/media/audio/aconfig/soundtrigger.aconfig b/media/audio/aconfig/soundtrigger.aconfig
index e61e6a3..5233119 100644
--- a/media/audio/aconfig/soundtrigger.aconfig
+++ b/media/audio/aconfig/soundtrigger.aconfig
@@ -6,9 +6,18 @@
container: "system"
flag {
- name: "sound_trigger_generic_model_api"
+ name: "generic_model_api"
is_exported: true
- namespace: "soundtrigger"
+ namespace: "media_audio"
description: "Feature flag for adding GenericSoundModel to SystemApi"
bug: "339267254"
}
+
+flag {
+ name: "manager_api"
+ is_exported: true
+ namespace: "media_audio"
+ description: "Feature flag for adding SoundTriggerManager API to SystemApi"
+ bug: "339267254"
+}
+
diff --git a/media/audioaidlconversion/include/media/AidlConversionEffect.h b/media/audioaidlconversion/include/media/AidlConversionEffect.h
index b03d06b..e51bf8b 100644
--- a/media/audioaidlconversion/include/media/AidlConversionEffect.h
+++ b/media/audioaidlconversion/include/media/AidlConversionEffect.h
@@ -72,9 +72,6 @@
MAKE_EXTENSION_PARAMETER_ID(_effect, _tag##Tag, _extId); \
aidl::android::hardware::audio::effect::Parameter _aidlParam; \
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(_id, &_aidlParam))); \
- aidl::android::hardware::audio::effect::VendorExtension _ext = \
- VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD( \
- _aidlParam, _effect, _tag, _effect::vendor, VendorExtension)); \
return VALUE_OR_RETURN_STATUS( \
aidl::android::aidl2legacy_Parameter_EffectParameterWriter(_aidlParam, _param)); \
}
diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp
index ac2fcbe..47b48e3 100644
--- a/media/audioserver/Android.bp
+++ b/media/audioserver/Android.bp
@@ -20,13 +20,6 @@
"-Werror",
],
- header_libs: [
- "audiopolicyservicelocal_headers",
- "libaudiohal_headers",
- "libmedia_headers",
- "libmediametrics_headers",
- ],
-
defaults: [
"latest_android_hardware_audio_core_sounddose_ndk_shared",
"latest_android_media_audio_common_types_cpp_shared",
@@ -40,39 +33,10 @@
"libaudioflinger",
"libaudiopolicyservice",
"libmedialogservice",
- "libnbaio",
],
shared_libs: [
- "libaudioclient",
- "libaudioprocessing",
- "libbinder",
- "libcutils",
- "libhidlbase",
- "liblog",
- "libmedia",
- "libmediautils",
- "libnblog",
- "libpowermanager",
- "libutils",
- "libvibrator",
- ],
-
- // TODO check if we still need all of these include directories
- include_dirs: [
- "external/sonic",
- "frameworks/av/media/libaaudio/include",
- "frameworks/av/media/libaaudio/src",
- "frameworks/av/media/libaaudio/src/binding",
- "frameworks/av/services/audioflinger",
- "frameworks/av/services/audiopolicy",
- "frameworks/av/services/audiopolicy/common/include",
- "frameworks/av/services/audiopolicy/common/managerdefinitions/include",
- "frameworks/av/services/audiopolicy/engine/interface",
- "frameworks/av/services/audiopolicy/service",
- "frameworks/av/services/medialog",
- "frameworks/av/services/oboeservice", // TODO oboeservice is the old folder name for aaudioservice. It will be changed.
-
+ "libhidlbase", // required for threadpool config.
],
init_rc: ["audioserver.rc"],
diff --git a/media/codec2/components/cmds/codec2.cpp b/media/codec2/components/cmds/codec2.cpp
index a17b04e..ca65aa2 100644
--- a/media/codec2/components/cmds/codec2.cpp
+++ b/media/codec2/components/cmds/codec2.cpp
@@ -46,7 +46,6 @@
#include <media/stagefright/Utils.h>
#include <gui/GLConsumer.h>
-#include <gui/IProducerListener.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -91,7 +90,7 @@
std::shared_ptr<Listener> mListener;
std::shared_ptr<C2Component> mComponent;
- sp<IProducerListener> mProducerListener;
+ sp<SurfaceListener> mSurfaceListener;
std::atomic_int mLinearPoolId;
@@ -138,7 +137,7 @@
SimplePlayer::SimplePlayer()
: mListener(new Listener(this)),
- mProducerListener(new StubProducerListener),
+ mSurfaceListener(new StubSurfaceListener),
mLinearPoolId(C2BlockPool::PLATFORM_START),
mComposerClient(new SurfaceComposerClient) {
CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
@@ -164,7 +163,7 @@
mSurface = mControl->getSurface();
CHECK(mSurface != nullptr);
- mSurface->connect(NATIVE_WINDOW_API_CPU, mProducerListener);
+ mSurface->connect(NATIVE_WINDOW_API_CPU, mSurfaceListener);
}
SimplePlayer::~SimplePlayer() {
diff --git a/media/codec2/hal/client/GraphicBufferAllocator.cpp b/media/codec2/hal/client/GraphicBufferAllocator.cpp
index 8f489ec..6a6da0f 100644
--- a/media/codec2/hal/client/GraphicBufferAllocator.cpp
+++ b/media/codec2/hal/client/GraphicBufferAllocator.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "Codec2-GraphicBufferAllocator"
-#include <gui/IProducerListener.h>
#include <media/stagefright/foundation/ADebug.h>
#include <codec2/aidl/GraphicBufferAllocator.h>
@@ -25,25 +24,6 @@
namespace aidl::android::hardware::media::c2::implementation {
-class OnBufferReleasedListener : public ::android::BnProducerListener {
-private:
- uint32_t mGeneration;
- std::weak_ptr<GraphicBufferAllocator> mAllocator;
-public:
- OnBufferReleasedListener(
- uint32_t generation,
- const std::shared_ptr<GraphicBufferAllocator> &allocator)
- : mGeneration(generation), mAllocator(allocator) {}
- virtual ~OnBufferReleasedListener() = default;
- virtual void onBufferReleased() {
- auto p = mAllocator.lock();
- if (p) {
- p->onBufferReleased(mGeneration);
- }
- }
- virtual bool needsReleaseNotify() { return true; }
-};
-
::ndk::ScopedAStatus GraphicBufferAllocator::allocate(
const IGraphicBufferAllocator::Description& in_desc,
IGraphicBufferAllocator::Allocation* _aidl_return) {
@@ -108,15 +88,14 @@
mGraphicsTracker->stop();
}
-const ::android::sp<::android::IProducerListener> GraphicBufferAllocator::createReleaseListener(
- uint32_t generation) {
- return new OnBufferReleasedListener(generation, ref<GraphicBufferAllocator>());
-}
-
void GraphicBufferAllocator::onBufferReleased(uint32_t generation) {
mGraphicsTracker->onReleased(generation);
}
+void GraphicBufferAllocator::onBufferAttached(uint32_t generation) {
+ mGraphicsTracker->onAttached(generation);
+}
+
c2_status_t GraphicBufferAllocator::allocate(
uint32_t width, uint32_t height, ::android::PixelFormat format, uint64_t usage,
AHardwareBuffer **buf, ::android::sp<::android::Fence> *fence) {
diff --git a/media/codec2/hal/client/GraphicsTracker.cpp b/media/codec2/hal/client/GraphicsTracker.cpp
index f80809a..8d9e76e 100644
--- a/media/codec2/hal/client/GraphicsTracker.cpp
+++ b/media/codec2/hal/client/GraphicsTracker.cpp
@@ -673,6 +673,15 @@
ALOGW("BQ might not be ready for dequeueBuffer()");
return C2_BLOCKING;
}
+ bool cacheExpired = false;
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ cacheExpired = (mBufferCache.get() != cache.get());
+ }
+ if (cacheExpired) {
+ ALOGW("a new BQ is configured. dequeueBuffer() error %d", (int)status);
+ return C2_BLOCKING;
+ }
ALOGE("BQ in inconsistent status. dequeueBuffer() error %d", (int)status);
return C2_CORRUPTED;
}
@@ -992,6 +1001,11 @@
{
std::unique_lock<std::mutex> l(mLock);
if (mBufferCache->mGeneration == generation) {
+ if (mBufferCache->mNumAttached > 0) {
+ ALOGV("one onReleased() ignored for each prior onAttached().");
+ mBufferCache->mNumAttached--;
+ return;
+ }
if (!adjustDequeueConfLocked(&updateDequeue)) {
mDequeueable++;
writeIncDequeueableLocked(1);
@@ -1003,4 +1017,12 @@
}
}
+void GraphicsTracker::onAttached(uint32_t generation) {
+ std::unique_lock<std::mutex> l(mLock);
+ if (mBufferCache->mGeneration == generation) {
+ ALOGV("buffer attached");
+ mBufferCache->mNumAttached++;
+ }
+}
+
} // namespace aidl::android::hardware::media::c2::implementation
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 1d2794e..a137dbb 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -2556,6 +2556,19 @@
mOutputBufferQueue->onBufferReleased(generation);
}
+void Codec2Client::Component::onBufferAttachedToOutputSurface(
+ uint32_t generation) {
+ if (mAidlBase) {
+ std::shared_ptr<AidlGraphicBufferAllocator> gba =
+ mGraphicBufferAllocators->current();
+ if (gba) {
+ gba->onBufferAttached(generation);
+ }
+ return;
+ }
+ mOutputBufferQueue->onBufferAttached(generation);
+}
+
void Codec2Client::Component::holdIgbaBlocks(
const std::list<std::unique_ptr<C2Work>>& workList) {
if (!mAidlBase) {
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h b/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
index 902c53f..a797cb7 100644
--- a/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicBufferAllocator.h
@@ -71,18 +71,6 @@
void reset();
/**
- * Create a listener for buffer being released.
- *
- * Surface will register this listener and notify whenever the consumer
- * releases a buffer.
- *
- * @param generation generation # for the BufferQueue.
- * @return IProducerListener can be used when connect# to Surface.
- */
- const ::android::sp<::android::IProducerListener> createReleaseListener(
- uint32_t generation);
-
- /**
* Notifies a buffer being released.
*
* @param generation generation # for the BufferQueue.
@@ -90,6 +78,13 @@
void onBufferReleased(uint32_t generation);
/**
+ * Notifies a buffer being attached to the consumer.
+ *
+ * @param generation generation # for the BufferQueue.
+ */
+ void onBufferAttached(uint32_t generation);
+
+ /**
* Allocates a buffer.
*
* @param width width of the requested buffer.
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
index 762030b..9a4fa12 100644
--- a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
@@ -143,6 +143,18 @@
void onReleased(uint32_t generation);
/**
+ * Notifies when a Buffer is attached to Graphics(consumer side).
+ * If generation does not match to the current, notifications via the interface
+ * will be ignored. (In the case, the notifications are from one of the old surfaces
+ * which is no longer used.)
+ * One onReleased() should be ignored for one onAttached() when both of
+ * them have the same generation as params.
+ *
+ * @param[in] generation generation id for specifying Graphics(BQ)
+ */
+ void onAttached(uint32_t generation);
+
+ /**
* Get waitable fd for events.(allocate is ready, end of life cycle)
*
* @param[out] pipeFd a file descriptor created from pipe2()
@@ -217,9 +229,11 @@
BlockedSlot mBlockedSlots[kNumSlots];
- BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr} {}
+ std::atomic<int> mNumAttached;
+
+ BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr}, mNumAttached{0} {}
BufferCache(uint64_t bqId, uint32_t generation, const sp<IGraphicBufferProducer>& igbp) :
- mBqId{bqId}, mGeneration{generation}, mIgbp{igbp} {}
+ mBqId{bqId}, mGeneration{generation}, mIgbp{igbp}, mNumAttached{0} {}
~BufferCache();
diff --git a/media/codec2/hal/client/include/codec2/hidl/client.h b/media/codec2/hal/client/include/codec2/hidl/client.h
index 5c75a47..413e92e 100644
--- a/media/codec2/hal/client/include/codec2/hidl/client.h
+++ b/media/codec2/hal/client/include/codec2/hidl/client.h
@@ -481,6 +481,10 @@
void onBufferReleasedFromOutputSurface(
uint32_t generation);
+ // Notify a buffer is attached to output surface.
+ void onBufferAttachedToOutputSurface(
+ uint32_t generation);
+
// When the client received \p workList and the blocks inside
// \p workList are IGBA based graphic blocks, specify the owner
// as the current IGBA for the future operations.
diff --git a/media/codec2/hal/client/include/codec2/hidl/output.h b/media/codec2/hal/client/include/codec2/hidl/output.h
index fda34a8..ddb9855 100644
--- a/media/codec2/hal/client/include/codec2/hidl/output.h
+++ b/media/codec2/hal/client/include/codec2/hidl/output.h
@@ -69,6 +69,9 @@
// update the number of dequeueable/allocatable buffers.
void onBufferReleased(uint32_t generation);
+ // Nofify a buffer is attached to the output surface.
+ void onBufferAttached(uint32_t generation);
+
// Retrieve frame event history from the output surface.
void pollForRenderedFrames(FrameEventHistoryDelta* delta);
diff --git a/media/codec2/hal/client/output.cpp b/media/codec2/hal/client/output.cpp
index 36322f5..54d78a0 100644
--- a/media/codec2/hal/client/output.cpp
+++ b/media/codec2/hal/client/output.cpp
@@ -542,6 +542,11 @@
}
}
+void OutputBufferQueue::onBufferAttached(uint32_t generation) {
+ // TODO
+ (void) generation;
+}
+
void OutputBufferQueue::pollForRenderedFrames(FrameEventHistoryDelta* delta) {
if (mIgbp) {
mIgbp->getFrameTimestamps(delta);
diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
index 55bf7a9..b287b91 100644
--- a/media/codec2/hal/common/MultiAccessUnitHelper.cpp
+++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
@@ -327,10 +327,10 @@
newWork->worklets.front()->component = inWork->worklets.front()->component;
std::vector<std::unique_ptr<C2Tuning>> tunings;
for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
- tunings.push_back(std::move(
+ tunings.push_back(
std::unique_ptr<C2Tuning>(
static_cast<C2Tuning*>(
- C2Param::Copy(*(tuning.get())).release()))));
+ C2Param::Copy(*(tuning.get())).release())));
}
newWork->worklets.front()->tunings = std::move(tunings);
}
@@ -344,7 +344,7 @@
<< inputOrdinal.frameIndex.peekull()
<< ") -> newFrameIndex " << newFrameIdx
<<" : input ts " << inputOrdinal.timestamp.peekull();
- sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags)));
+ sliceWork.push_back(cloneInputWork(w, w->input.flags));
if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
sliceWork.back()->input.buffers = std::move(w->input.buffers);
}
@@ -853,4 +853,4 @@
mLargeWork.reset();
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index f8fd425..4a956f5 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -139,6 +139,20 @@
mReorderDepth = -1;
mTimestampDevTest = false;
mMd5Offset = 0;
+ mIsTunneledCodec = false;
+
+ // For C2 codecs that support tunneling by default, the default value of
+ // C2PortTunneledModeTuning::mode should (!= NONE). Otherwise VTS
+ // can assume that the codec can support regular (non-tunneled decode)
+ queried.clear();
+ c2err = mComponent->query(
+ {}, {C2PortTunneledModeTuning::output::PARAM_TYPE}, C2_MAY_BLOCK, &queried);
+ if (c2err == C2_OK && !queried.empty() && queried.front() != nullptr) {
+ C2TunneledModeStruct::mode_t tunneledMode =
+ ((C2PortTunneledModeTuning::output*)queried.front().get())->m.mode;
+ mIsTunneledCodec = (tunneledMode != C2TunneledModeStruct::NONE);
+ }
+
mMd5Enable = false;
mRefMd5 = nullptr;
@@ -308,6 +322,7 @@
bool mEos;
bool mDisableTest;
+ bool mIsTunneledCodec;
bool mMd5Enable;
bool mTimestampDevTest;
uint64_t mTimestampUs;
@@ -612,11 +627,14 @@
bool signalEOS = std::get<3>(GetParam());
surfaceMode_t surfMode = std::get<4>(GetParam());
- mTimestampDevTest = true;
+ // Disable checking timestamp as tunneled codecs doesn't populate
+ // output buffers in C2Work.
+ mTimestampDevTest = !mIsTunneledCodec;
android::Vector<FrameInfo> Info;
- mMd5Enable = true;
+ // Disable md5 checks as tunneled codecs doesn't populate output buffers in C2Work
+ mMd5Enable = !mIsTunneledCodec;
if (!mChksumFile.compare(sResourceDir)) mMd5Enable = false;
uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
@@ -712,7 +730,9 @@
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
- mTimestampDevTest = true;
+ // Disable checking timestamp as tunneled codecs doesn't populate
+ // output buffers in C2Work.
+ mTimestampDevTest = !mIsTunneledCodec;
uint32_t timestampOffset = 0;
uint32_t offset = 0;
android::Vector<FrameInfo> Info;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index ae66a58..0aae23c 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2627,7 +2627,7 @@
mChannel->setInfoBuffer(std::make_shared<C2InfoBuffer>(info));
}
} else {
- ALOGE("Ignoring param key %s as buffer size %d is less than expected "
+ ALOGE("Ignoring param key %s as buffer size %zu is less than expected "
"buffer size %d",
PARAMETER_KEY_QP_OFFSET_MAP, mapSize, expectedMapSize);
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index f0a4180..1348ef0 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1469,6 +1469,17 @@
}
}
+void CCodecBufferChannel::onBufferAttachedToOutputSurface(uint32_t generation) {
+ // Note: Since this is called asynchronously from IProducerListener not
+ // knowing the internal state of CCodec/CCodecBufferChannel,
+ // prevent mComponent from being destroyed by holding the shared reference
+ // during this interface being executed.
+ std::shared_ptr<Codec2Client::Component> comp = mComponent;
+ if (comp) {
+ comp->onBufferAttachedToOutputSurface(generation);
+ }
+}
+
status_t CCodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
ALOGV("[%s] discardBuffer: %p", mName, buffer.get());
bool released = false;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 94a5998..e62742b 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -102,6 +102,7 @@
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
void pollForRenderedBuffers() override;
void onBufferReleasedFromOutputSurface(uint32_t generation) override;
+ void onBufferAttachedToOutputSurface(uint32_t generation) override;
status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
index 1e8dd40..806932c 100644
--- a/media/codec2/vndk/include/C2BqBufferPriv.h
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -44,7 +44,77 @@
*
* If a specific HGBP is configured, the HGBP acts as an allocator for creating graphic blocks.
*
- * TODO: add more ducumentation(graphic block life-cycle, waitable object and workaounds)
+ *
+ * HGBP/IGBP and the BlockPool
+ *
+ * GraphicBuffer(s) from BufferQueue(IGBP/IGBC) are based on slot id.
+ * A created GraphicBuffer occupies a slot(so the GraphicBuffer has a slot-id).
+ * A GraphicBuffer is produced and consumed and recyled based on the slot-id
+ * w.r.t. BufferQueue.
+ *
+ * HGBP::dequeueBuffer() returns a slot id where the slot has an available GraphicBuffer.
+ * If it is necessary, HGBP allocates a new GraphicBuffer to the slot and indicates
+ * that a new buffer is allocated as return flag.
+ * To retrieve the GraphicBuffer, HGBP::requestBuffer() along with the slot id
+ * is required. In order to save HGBP remote calls, the blockpool caches the
+ * allocated GraphicBuffer(s) along with the slot information.
+ *
+ * The blockpool provides C2GraphicBlock upon \fetchGraphicBlock().
+ * The C2GraphicBlock has a native handle, which is extracted from a GraphicBuffer
+ * and then cloned for independent life-cycle with the GraphicBuffer. The GraphicBuffer
+ * is allocated by HGBP::dequeueBuffer() and retrieved by HGBP::requestBuffer()
+ * if there is a HGBP configured.
+ *
+ *
+ * Life-cycle of C2GraphicBlock
+ *
+ * The decoder HAL writes a decoded frame into C2GraphicBlock. Upon
+ * completion, the component sends the block to the client in the remote process
+ * (i.e. to MediaCodec). The remote process renders the frame into the output surface
+ * via IGBP::queueBuffer() (Note: this is not hidlized.).
+ *
+ * If the decoder HAL destroys the C2GraphicBlock without transferring to the
+ * client, the destroy request goes to the BlockPool. Then
+ * the BlockPool free the associated GraphicBuffer from a slot to
+ * HGBP in order to recycle via HGBP::cancelBuffer().
+ *
+ *
+ * Clearing the Cache(GraphicBuffer)
+ *
+ * When the output surface is switched to a new surface, The GraphicBuffers from
+ * the old surface is either migrated or cleared.
+ *
+ * The GraphicBuffer(s) still in use are migrated to a new surface during
+ * configuration via HGBP::attachBuffer(). The GraphicBuffer(s) not in use are
+ * cleared from the cache inside the BlockPool.
+ *
+ * When the surface is switched to a null surface, all the
+ * GraphicBuffers in the cache are cleared.
+ *
+ *
+ * Workaround w.r.t. b/322731059 (Deferring cleaning the cache)
+ *
+ * Some vendor devices have issues with graphic buffer lifecycle management,
+ * where the graphic buffers get released even when the cloned native handles
+ * in the remote process are not closed yet. This issue led to rare crashes
+ * for those devices when the cache is cleared early.
+ *
+ * We workarounded the crash by deferring the cleaning of the cache.
+ * The workaround is not enabled by default, and can be enabled via a
+ * system property as shown below:
+ *
+ * 'debug.codec2.bqpool_dealloc_after_stop' = 1
+ *
+ * Configuring the debug flag will call \::setDeferDeallocationAfterStop()
+ * after the blockpool creation. This will enable the deferring.
+ *
+ * After enabling the deferring, clearing the GraphicBuffer is delayed until
+ * 1) \::clearDeferredBlocks() is called.
+ * Typically after HAL processes stop() request.
+ * 2) Or a new ::fetchGraphicBlock() is called.
+ *
+ * Since the deferring will delay the deallocation, the deferring will result
+ * in more memory consumption during the brief period.
*/
class C2BufferQueueBlockPool : public C2BlockPool {
public:
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 21321b9..e19d526 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -574,7 +574,7 @@
* For privacy, the following usages can not be recorded: AAUDIO_VOICE_COMMUNICATION*,
* AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and {@link #AAUDIO_USAGE_ASSISTANT}.
*
- * On <a href="/reference/android/os/Build.VERSION_CODES#Q">Build.VERSION_CODES</a>,
+ * On <a href="/reference/android/os/Build.VERSION_CODES#Q">Q</a>,
* this means only {@link #AAUDIO_USAGE_MEDIA} and {@link #AAUDIO_USAGE_GAME} may be captured.
*
* See <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_ALL">
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 3e51575..67fc668 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -23,13 +23,6 @@
using namespace aaudio;
-// TODO These defines should be moved to a central place in audio.
-#define SAMPLES_PER_FRAME_MIN 1
-#define SAMPLES_PER_FRAME_MAX FCC_LIMIT
-#define SAMPLE_RATE_HZ_MIN 8000
-// HDMI supports up to 32 channels at 1536000 Hz.
-#define SAMPLE_RATE_HZ_MAX 1600000
-
void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) {
mSamplesPerFrame = other.mSamplesPerFrame;
mSampleRate = other.mSampleRate;
@@ -73,8 +66,8 @@
}
aaudio_result_t AAudioStreamParameters::validate() const {
- if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
- && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
+ if (mSamplesPerFrame != AAUDIO_UNSPECIFIED && (mSamplesPerFrame < CHANNEL_COUNT_MIN_AAUDIO ||
+ mSamplesPerFrame > CHANNEL_COUNT_MAX_AAUDIO)) {
ALOGD("channelCount out of range = %d", mSamplesPerFrame);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
@@ -105,8 +98,8 @@
aaudio_result_t result = isFormatValid (mAudioFormat);
if (result != AAUDIO_OK) return result;
- if (mSampleRate != AAUDIO_UNSPECIFIED
- && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
+ if (mSampleRate != AAUDIO_UNSPECIFIED &&
+ (mSampleRate < SAMPLE_RATE_HZ_MIN_AAUDIO || mSampleRate > SAMPLE_RATE_HZ_MAX_IEC610937)) {
ALOGD("sampleRate out of range = %d", mSampleRate);
return AAUDIO_ERROR_INVALID_RATE;
}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 9bbaf3e..c9d8b35 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -50,15 +50,6 @@
#define AAUDIO_MMAP_POLICY_DEFAULT_AIDL AudioMMapPolicy::NEVER
#define AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT_AIDL AudioMMapPolicy::NEVER
-// These values are for a pre-check before we ask the lower level service to open a stream.
-// So they are just outside the maximum conceivable range of value,
-// on the edge of being ridiculous.
-// TODO These defines should be moved to a central place in audio.
-#define SAMPLES_PER_FRAME_MIN 1
-#define SAMPLES_PER_FRAME_MAX FCC_LIMIT
-#define SAMPLE_RATE_HZ_MIN 8000
-// HDMI supports up to 32 channels at 1536000 Hz.
-#define SAMPLE_RATE_HZ_MAX 1600000
#define FRAMES_PER_DATA_CALLBACK_MIN 1
#define FRAMES_PER_DATA_CALLBACK_MAX (1024 * 1024)
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 3f4fcfd..3602e94 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -322,23 +322,6 @@
return NO_ERROR;
}
-status_t AudioSystem::getStreamVolume(audio_stream_type_t stream, float* volume,
- audio_io_handle_t output) {
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
- const sp<IAudioFlinger> af = get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- *volume = af->streamVolume(stream, output);
- return NO_ERROR;
-}
-
-status_t AudioSystem::getStreamMute(audio_stream_type_t stream, bool* mute) {
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
- const sp<IAudioFlinger> af = get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- *mute = af->streamMute(stream);
- return NO_ERROR;
-}
-
status_t AudioSystem::setMode(audio_mode_t mode) {
if (uint32_t(mode) >= AUDIO_MODE_CNT) return BAD_VALUE;
const sp<IAudioFlinger> af = get_audio_flinger();
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 85cc9bd..161c4d5 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -319,13 +319,6 @@
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mAudioTrackCallback(new AudioTrackCallback())
{
mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
@@ -2408,12 +2401,14 @@
int32_t flags = android_atomic_and(
~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->mFlags);
+ const bool isOffloaded = isOffloaded_l();
+ const bool isOffloadedOrDirect = isOffloadedOrDirect_l();
// Check for track invalidation
if (flags & CBLK_INVALID) {
// for offloaded tracks restoreTrack_l() will just update the sequence and clear
// AudioSystem cache. We should not exit here but after calling the callback so
// that the upper layers can recreate the track
- if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) {
+ if (!isOffloadedOrDirect || (mSequence == mObservedSequence)) {
status_t status __unused = restoreTrack_l("processAudioBuffer");
// FIXME unused status
// after restoration, continue below to make sure that the loop and buffer events
@@ -2583,7 +2578,7 @@
mObservedSequence = sequence;
callback->onNewIAudioTrack();
// for offloaded tracks, just wait for the upper layers to recreate the track
- if (isOffloadedOrDirect()) {
+ if (isOffloadedOrDirect) {
return NS_INACTIVE;
}
}
@@ -2671,7 +2666,7 @@
__func__, mPortId, mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
if (err != NO_ERROR) {
if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR ||
- (isOffloaded() && (err == DEAD_OBJECT))) {
+ (isOffloaded && (err == DEAD_OBJECT))) {
// FIXME bug 25195759
return 1000000;
}
@@ -2757,7 +2752,7 @@
// buffer size and skip the loop entirely.
nsecs_t myns;
- if (audio_has_proportional_frames(mFormat)) {
+ if (!isOffloaded && audio_has_proportional_frames(mFormat)) {
// time to wait based on buffer occupancy
const nsecs_t datans = mRemainingFrames <= avail ? 0 :
framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed);
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index a707909..e0dca2d 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -350,34 +350,6 @@
return statusTFromBinderStatus(mDelegate->setStreamMute(streamAidl, muted));
}
-float AudioFlingerClientAdapter::streamVolume(audio_stream_type_t stream,
- audio_io_handle_t output) const {
- auto result = [&]() -> ConversionResult<float> {
- AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
- legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
- int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
- float aidlRet;
- RETURN_IF_ERROR(statusTFromBinderStatus(
- mDelegate->streamVolume(streamAidl, outputAidl, &aidlRet)));
- return aidlRet;
- }();
- // Failure is ignored.
- return result.value_or(0.f);
-}
-
-bool AudioFlingerClientAdapter::streamMute(audio_stream_type_t stream) const {
- auto result = [&]() -> ConversionResult<bool> {
- AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
- legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
- bool aidlRet;
- RETURN_IF_ERROR(statusTFromBinderStatus(
- mDelegate->streamMute(streamAidl, &aidlRet)));
- return aidlRet;
- }();
- // Failure is ignored.
- return result.value_or(false);
-}
-
status_t AudioFlingerClientAdapter::setMode(audio_mode_t mode) {
AudioMode modeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_mode_t_AudioMode(mode));
return statusTFromBinderStatus(mDelegate->setMode(modeAidl));
@@ -1040,23 +1012,6 @@
return Status::fromStatusT(mDelegate->setStreamMute(streamLegacy, muted));
}
-Status AudioFlingerServerAdapter::streamVolume(AudioStreamType stream, int32_t output,
- float* _aidl_return) {
- audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
- aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
- audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
- aidl2legacy_int32_t_audio_io_handle_t(output));
- *_aidl_return = mDelegate->streamVolume(streamLegacy, outputLegacy);
- return Status::ok();
-}
-
-Status AudioFlingerServerAdapter::streamMute(AudioStreamType stream, bool* _aidl_return) {
- audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
- aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
- *_aidl_return = mDelegate->streamMute(streamLegacy);
- return Status::ok();
-}
-
Status AudioFlingerServerAdapter::setMode(AudioMode mode) {
audio_mode_t modeLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioMode_audio_mode_t(mode));
return Status::fromStatusT(mDelegate->setMode(modeLegacy));
diff --git a/media/libaudioclient/PolicyAidlConversion.cpp b/media/libaudioclient/PolicyAidlConversion.cpp
index 441e329..163a359 100644
--- a/media/libaudioclient/PolicyAidlConversion.cpp
+++ b/media/libaudioclient/PolicyAidlConversion.cpp
@@ -379,6 +379,8 @@
return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
case media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL:
return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
+ case media::AudioPolicyForcedConfig::BT_BLE:
+ return AUDIO_POLICY_FORCE_BT_BLE;
}
return unexpected(BAD_VALUE);
}
@@ -418,6 +420,8 @@
return media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS;
case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
return media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL;
+ case AUDIO_POLICY_FORCE_BT_BLE:
+ return media::AudioPolicyForcedConfig::BT_BLE;
case AUDIO_POLICY_FORCE_CFG_CNT:
break;
}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
index 2255d4c..111bb2f 100644
--- a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
@@ -36,4 +36,5 @@
ENCODED_SURROUND_NEVER = 13,
ENCODED_SURROUND_ALWAYS = 14,
ENCODED_SURROUND_MANUAL = 15,
+ BT_BLE = 16,
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index 4f00f83..29de9c2 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -94,13 +94,11 @@
float getMasterBalance();
/*
- * Set/gets stream type state. This will probably be used by
+ * Set stream type state. This will probably be used by
* the preference panel, mostly.
*/
void setStreamVolume(AudioStreamType stream, float value, int /* audio_io_handle_t */ output);
void setStreamMute(AudioStreamType stream, boolean muted);
- float streamVolume(AudioStreamType stream, int /* audio_io_handle_t */ output);
- boolean streamMute(AudioStreamType stream);
// set audio mode.
void setMode(AudioMode mode);
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index 11955c9..61d5ccd 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -28,53 +28,20 @@
"libcgrouprc_format",
"libfakeservicemanager",
"libjsoncpp",
- "liblog",
- "libmedia_helper",
"libmediametricsservice",
"libprocessgroup",
"shared-file-region-aidl-cpp",
],
shared_libs: [
"android.hardware.audio.common-util",
- "audioclient-types-aidl-cpp",
- "audioflinger-aidl-cpp",
- "audiopolicy-aidl-cpp",
- "audiopolicy-types-aidl-cpp",
- "av-types-aidl-cpp",
- "capture_state_listener-aidl-cpp",
- "effect-aidl-cpp",
- "framework-permission-aidl-cpp",
- "libactivitymanager_aidl",
- "libaudioclient",
- "libaudioclient_aidl_conversion",
"libaudioflinger",
- "libaudiofoundation",
- "libaudiohal",
- "libaudiomanager",
- "libaudiopolicy",
- "libaudiopolicymanagerdefault",
"libaudiopolicyservice",
- "libaudioprocessing",
- "libaudioutils",
"libdl",
- "libheadtracking",
- "libmediametrics",
- "libmediautils",
- "libnbaio",
- "libnblog",
- "libpowermanager",
- "libvibrator",
"libvndksupport",
- "libxml2",
"mediametricsservice-aidl-cpp",
- "packagemanager_aidl-cpp",
],
header_libs: [
- "libaudioflinger_headers",
- "libaudiofoundation_headers",
- "libaudiohal_headers",
"libaudiopolicymanager_interface_headers",
- "libbinder_headers",
"libmedia_headers",
],
fuzz_config: {
@@ -99,6 +66,8 @@
"latest_android_hardware_audio_core_sounddose_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
"libaudioclient_aidl_fuzzer_defaults",
+ "libaudioflinger_dependencies",
+ "libaudiopolicyservice_dependencies",
"service_fuzzer_defaults",
],
}
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index dfdb4cf..4c94974 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -519,12 +519,6 @@
stream = getValue(&mFdp, kStreamtypes);
AudioSystem::getOutputLatency(&latency, stream);
- stream = getValue(&mFdp, kStreamtypes);
- AudioSystem::getStreamVolume(stream, &volume, mFdp.ConsumeIntegral<int32_t>());
-
- stream = getValue(&mFdp, kStreamtypes);
- AudioSystem::getStreamMute(stream, &state);
-
uint32_t samplingRate;
AudioSystem::getSamplingRate(mFdp.ConsumeIntegral<int32_t>(), &samplingRate);
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 9cfd540..67b3dcd 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -124,15 +124,12 @@
static status_t setMasterMute(bool mute);
static status_t getMasterMute(bool* mute);
- // set/get stream volume on specified output
+ // set stream volume on specified output
static status_t setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output);
- static status_t getStreamVolume(audio_stream_type_t stream, float* volume,
- audio_io_handle_t output);
// mute/unmute stream
static status_t setStreamMute(audio_stream_type_t stream, bool mute);
- static status_t getStreamMute(audio_stream_type_t stream, bool* mute);
// set audio mode in audio hardware
static status_t setMode(audio_mode_t mode);
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 211fffa..667e9ae 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -229,10 +229,6 @@
audio_io_handle_t output) = 0;
virtual status_t setStreamMute(audio_stream_type_t stream, bool muted) = 0;
- virtual float streamVolume(audio_stream_type_t stream,
- audio_io_handle_t output) const = 0;
- virtual bool streamMute(audio_stream_type_t stream) const = 0;
-
// set audio mode
virtual status_t setMode(audio_mode_t mode) = 0;
@@ -424,9 +420,6 @@
status_t setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output) override;
status_t setStreamMute(audio_stream_type_t stream, bool muted) override;
- float streamVolume(audio_stream_type_t stream,
- audio_io_handle_t output) const override;
- bool streamMute(audio_stream_type_t stream) const override;
status_t setMode(audio_mode_t mode) override;
status_t setMicMute(bool state) override;
bool getMicMute() const override;
@@ -549,8 +542,6 @@
MASTER_MUTE = media::BnAudioFlingerService::TRANSACTION_masterMute,
SET_STREAM_VOLUME = media::BnAudioFlingerService::TRANSACTION_setStreamVolume,
SET_STREAM_MUTE = media::BnAudioFlingerService::TRANSACTION_setStreamMute,
- STREAM_VOLUME = media::BnAudioFlingerService::TRANSACTION_streamVolume,
- STREAM_MUTE = media::BnAudioFlingerService::TRANSACTION_streamMute,
SET_MODE = media::BnAudioFlingerService::TRANSACTION_setMode,
SET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_setMicMute,
GET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_getMicMute,
@@ -673,9 +664,6 @@
Status setStreamVolume(media::audio::common::AudioStreamType stream,
float value, int32_t output) override;
Status setStreamMute(media::audio::common::AudioStreamType stream, bool muted) override;
- Status streamVolume(media::audio::common::AudioStreamType stream,
- int32_t output, float* _aidl_return) override;
- Status streamMute(media::audio::common::AudioStreamType stream, bool* _aidl_return) override;
Status setMode(media::audio::common::AudioMode mode) override;
Status setMicMute(bool state) override;
Status getMicMute(bool* _aidl_return) override;
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
index 745c7d1..1599839 100644
--- a/media/libaudioclient/tests/audio_test_utils.cpp
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -294,6 +294,7 @@
ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC, &location) ==
OK) {
// Use audio timestamp.
+ std::lock_guard l(mMutex);
timeUs = timeNs / 1000 -
(position - mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
} else {
@@ -322,6 +323,7 @@
} else {
numLostBytes = 0;
}
+ std::lock_guard l(mMutex);
const int64_t timestampUs =
((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
mRecord->getSampleRate();
@@ -335,6 +337,7 @@
if (buffer.size() == 0) {
ALOGW("Nothing is available from AudioRecord callback buffer");
} else {
+ std::lock_guard l(mMutex);
const size_t bufferSize = buffer.size();
const int64_t timestampUs =
((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
@@ -359,17 +362,24 @@
void AudioCapture::onOverrun() {
ALOGV("received event overrun");
- mBufferOverrun = true;
}
void AudioCapture::onMarker(uint32_t markerPosition) {
ALOGV("received Callback at position %d", markerPosition);
- mReceivedCbMarkerAtPosition = markerPosition;
+ {
+ std::lock_guard l(mMutex);
+ mReceivedCbMarkerAtPosition = markerPosition;
+ }
+ mMarkerCondition.notify_all();
}
void AudioCapture::onNewPos(uint32_t markerPosition) {
ALOGV("received Callback at position %d", markerPosition);
- mReceivedCbMarkerCount++;
+ {
+ std::lock_guard l(mMutex);
+ mReceivedCbMarkerCount = mReceivedCbMarkerCount.value_or(0) + 1;
+ }
+ mMarkerCondition.notify_all();
}
void AudioCapture::onNewIAudioRecord() {
@@ -387,20 +397,7 @@
mFlags(flags),
mSessionId(sessionId),
mTransferType(transferType),
- mAttributes(attributes) {
- mFrameCount = 0;
- mNotificationFrames = 0;
- mNumFramesToRecord = 0;
- mNumFramesReceived = 0;
- mNumFramesLost = 0;
- mBufferOverrun = false;
- mMarkerPosition = 0;
- mMarkerPeriod = 0;
- mReceivedCbMarkerAtPosition = -1;
- mReceivedCbMarkerCount = 0;
- mState = REC_NO_INIT;
- mStopRecording = false;
-}
+ mAttributes(attributes) {}
AudioCapture::~AudioCapture() {
if (mOutFileFd > 0) close(mOutFileFd);
@@ -531,25 +528,32 @@
const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
int counter = 0;
size_t nonContig = 0;
- while (mNumFramesReceived < mNumFramesToRecord) {
+ int64_t numFramesReceived;
+ {
+ std::lock_guard l(mMutex);
+ numFramesReceived = mNumFramesReceived;
+ }
+ while (numFramesReceived < mNumFramesToRecord) {
AudioRecord::Buffer recordBuffer;
recordBuffer.frameCount = mNotificationFrames;
status_t status = mRecord->obtainBuffer(&recordBuffer, 1, &nonContig);
if (OK == status) {
const int64_t timestampUs =
- ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ ((1000000LL * numFramesReceived) + (mRecord->getSampleRate() >> 1)) /
mRecord->getSampleRate();
RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
buffer = std::move(buff);
- mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
+ numFramesReceived += recordBuffer.size() / mRecord->frameSize();
mRecord->releaseBuffer(&recordBuffer);
counter = 0;
} else if (WOULD_BLOCK == status) {
// if not received a buffer for MAX_WAIT_TIME_MS, something has gone wrong
- if (counter == maxTries) return TIMED_OUT;
- counter++;
+ if (counter++ == maxTries) status = TIMED_OUT;
}
+ std::lock_guard l(mMutex);
+ mNumFramesReceived = numFramesReceived;
+ if (TIMED_OUT == status) return status;
}
return OK;
}
@@ -577,7 +581,12 @@
status_t AudioCapture::audioProcess() {
RawBuffer buffer;
status_t status = OK;
- while (mNumFramesReceived < mNumFramesToRecord && status == OK) {
+ int64_t numFramesReceived;
+ {
+ std::lock_guard l(mMutex);
+ numFramesReceived = mNumFramesReceived;
+ }
+ while (numFramesReceived < mNumFramesToRecord && status == OK) {
if (mTransferType == AudioRecord::TRANSFER_CALLBACK)
status = obtainBufferCb(buffer);
else
@@ -586,10 +595,52 @@
const char* ptr = static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
write(mOutFileFd, ptr, buffer.mCapacity);
}
+ std::lock_guard l(mMutex);
+ numFramesReceived = mNumFramesReceived;
}
return OK;
}
+uint32_t AudioCapture::getMarkerPeriod() const {
+ std::lock_guard l(mMutex);
+ return mMarkerPeriod;
+}
+
+uint32_t AudioCapture::getMarkerPosition() const {
+ std::lock_guard l(mMutex);
+ return mMarkerPosition;
+}
+
+void AudioCapture::setMarkerPeriod(uint32_t markerPeriod) {
+ std::lock_guard l(mMutex);
+ mMarkerPeriod = markerPeriod;
+}
+
+void AudioCapture::setMarkerPosition(uint32_t markerPosition) {
+ std::lock_guard l(mMutex);
+ mMarkerPosition = markerPosition;
+}
+
+uint32_t AudioCapture::waitAndGetReceivedCbMarkerAtPosition() const {
+ std::unique_lock lock(mMutex);
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
+ mMarkerCondition.wait_for(lock, std::chrono::seconds(3), [this]() {
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
+ return mReceivedCbMarkerAtPosition.has_value();
+ });
+ return mReceivedCbMarkerAtPosition.value_or(~0);
+}
+
+uint32_t AudioCapture::waitAndGetReceivedCbMarkerCount() const {
+ std::unique_lock lock(mMutex);
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
+ mMarkerCondition.wait_for(lock, std::chrono::seconds(3), [this]() {
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
+ return mReceivedCbMarkerCount.has_value();
+ });
+ return mReceivedCbMarkerCount.value_or(0);
+}
+
status_t listAudioPorts(std::vector<audio_port_v7>& portsVec) {
int attempts = 5;
status_t status;
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
index 40c3365..022ecf3 100644
--- a/media/libaudioclient/tests/audio_test_utils.h
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -146,8 +146,8 @@
~AudioCapture();
size_t onMoreData(const AudioRecord::Buffer& buffer) override EXCLUDES(mMutex);
void onOverrun() override;
- void onMarker(uint32_t markerPosition) override;
- void onNewPos(uint32_t newPos) override;
+ void onMarker(uint32_t markerPosition) override EXCLUDES(mMutex);
+ void onNewPos(uint32_t newPos) override EXCLUDES(mMutex);
void onNewIAudioRecord() override;
status_t create();
status_t setRecordDuration(float durationInSec);
@@ -157,20 +157,19 @@
status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
status_t obtainBufferCb(RawBuffer& buffer) EXCLUDES(mMutex);
- status_t obtainBuffer(RawBuffer& buffer);
- status_t audioProcess();
+ status_t obtainBuffer(RawBuffer& buffer) EXCLUDES(mMutex);
+ status_t audioProcess() EXCLUDES(mMutex);
status_t stop() EXCLUDES(mMutex);
+ uint32_t getMarkerPeriod() const EXCLUDES(mMutex);
+ uint32_t getMarkerPosition() const EXCLUDES(mMutex);
+ void setMarkerPeriod(uint32_t markerPeriod) EXCLUDES(mMutex);
+ void setMarkerPosition(uint32_t markerPosition) EXCLUDES(mMutex);
+ uint32_t waitAndGetReceivedCbMarkerAtPosition() const EXCLUDES(mMutex);
+ uint32_t waitAndGetReceivedCbMarkerCount() const EXCLUDES(mMutex);
- uint32_t mFrameCount;
- uint32_t mNotificationFrames;
- int64_t mNumFramesToRecord;
- int64_t mNumFramesReceived;
- int64_t mNumFramesLost;
- uint32_t mMarkerPosition;
- uint32_t mMarkerPeriod;
- uint32_t mReceivedCbMarkerAtPosition;
- uint32_t mReceivedCbMarkerCount;
- bool mBufferOverrun;
+ uint32_t mFrameCount = 0;
+ uint32_t mNotificationFrames = 0;
+ int64_t mNumFramesToRecord = 0;
enum State {
REC_NO_INIT,
@@ -191,14 +190,23 @@
size_t mMaxBytesPerCallback = 2048;
sp<AudioRecord> mRecord;
- State mState;
- bool mStopRecording GUARDED_BY(mMutex);
+ State mState = REC_NO_INIT;
+ bool mStopRecording GUARDED_BY(mMutex) = false;
std::string mFileName;
int mOutFileFd = -1;
mutable std::mutex mMutex;
std::condition_variable mCondition;
std::deque<RawBuffer> mBuffersReceived GUARDED_BY(mMutex);
+
+ mutable std::condition_variable mMarkerCondition;
+ uint32_t mMarkerPeriod GUARDED_BY(mMutex) = 0;
+ uint32_t mMarkerPosition GUARDED_BY(mMutex) = 0;
+ std::optional<uint32_t> mReceivedCbMarkerCount GUARDED_BY(mMutex);
+ std::optional<uint32_t> mReceivedCbMarkerAtPosition GUARDED_BY(mMutex);
+
+ int64_t mNumFramesReceived GUARDED_BY(mMutex) = 0;
+ int64_t mNumFramesLost GUARDED_BY(mMutex) = 0;
};
#endif // AUDIO_TEST_UTILS_H_
diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp
index f4d37bc..199fb8b 100644
--- a/media/libaudioclient/tests/audioeffect_analyser.cpp
+++ b/media/libaudioclient/tests/audioeffect_analyser.cpp
@@ -62,6 +62,15 @@
constexpr int kNPointFFT = 16384;
constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+// frequency used to generate testing tone
+constexpr uint32_t kTestFrequency = 1400;
+
+// Tolerance of audio gain difference in dB, which is 10^(0.1/20) (around 1.0116%) difference in
+// amplitude
+constexpr float kAudioGainDiffTolerancedB = .1f;
+
+const std::string kDataTempPath = "/data/local/tmp";
+
const char* gPackageName = "AudioEffectAnalyser";
static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kCaptureDurationSec,
@@ -177,21 +186,30 @@
return effect;
}
-void computeFilterGainsAtTones(float captureDuration, int nPointFft, std::vector<int>& binOffsets,
- float* inputMag, float* gaindB, const char* res,
- audio_session_t sessionId) {
+void computeFilterGainsAtTones(float captureDuration, int nPointFft, std::vector<int> binOffsets,
+ float* inputMag, float* gaindB, const std::string res,
+ audio_session_t sessionId, const std::string res2 = "",
+ audio_session_t sessionId2 = AUDIO_SESSION_NONE) {
int totalFrameCount = captureDuration * kSamplingFrequency;
auto output = pffft::AlignedVector<float>(totalFrameCount);
auto fftOutput = pffft::AlignedVector<float>(nPointFft);
- PlaybackEnv argsP;
- argsP.mRes = std::string{res};
+ PlaybackEnv argsP, argsP2;
+ argsP.mRes = res;
argsP.mSessionId = sessionId;
CaptureEnv argsR;
argsR.mCaptureDuration = captureDuration;
std::thread playbackThread(&PlaybackEnv::play, &argsP);
+ std::optional<std::thread> playbackThread2;
+ if (res2 != "") {
+ argsP2 = {.mSessionId = sessionId2, .mRes = res2};
+ playbackThread2 = std::thread(&PlaybackEnv::play, &argsP2);
+ }
std::thread captureThread(&CaptureEnv::capture, &argsR);
captureThread.join();
playbackThread.join();
+ if (playbackThread2 != std::nullopt) {
+ playbackThread2->join();
+ }
ASSERT_EQ(OK, argsR.mStatus) << argsR.mMsg;
ASSERT_EQ(OK, argsP.mStatus) << argsP.mMsg;
ASSERT_FALSE(argsR.mDumpFileName.empty()) << "recorded not written to file";
@@ -210,7 +228,11 @@
auto k = binOffsets[i];
auto outputMag = sqrt((fftOutput[k * 2] * fftOutput[k * 2]) +
(fftOutput[k * 2 + 1] * fftOutput[k * 2 + 1]));
- gaindB[i] = 20 * log10(outputMag / inputMag[i]);
+ if (inputMag == nullptr) {
+ gaindB[i] = 20 * log10(outputMag);
+ } else {
+ gaindB[i] = 20 * log10(outputMag / inputMag[i]);
+ }
}
}
@@ -282,7 +304,7 @@
inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
(fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
}
- TemporaryFile tf("/data/local/tmp");
+ TemporaryFile tf(kDataTempPath);
close(tf.release());
std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
fout.write((char*)input.data(), input.size() * sizeof(input[0]));
@@ -386,7 +408,7 @@
inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
(fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
}
- TemporaryFile tf("/data/local/tmp");
+ TemporaryFile tf(kDataTempPath);
close(tf.release());
std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
fout.write((char*)input.data(), input.size() * sizeof(input[0]));
@@ -396,7 +418,7 @@
memset(gainWithOutFilter, 0, sizeof(gainWithOutFilter));
ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, binOffsets,
inputMag, gainWithOutFilter, tf.path,
- AUDIO_SESSION_OUTPUT_MIX));
+ AUDIO_SESSION_NONE));
float diffA = gainWithOutFilter[0] - gainWithOutFilter[1];
float prevGain = -100.f;
for (auto strength = 150; strength < 1000; strength += strengthSupported ? 150 : 1000) {
@@ -421,6 +443,56 @@
}
}
+// assert the silent audio session with effect does not override the output audio
+TEST(AudioEffectTest, SilentAudioEffectSessionNotOverrideOutput) {
+ audio_session_t sessionId =
+ (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+ sp<AudioEffect> bassboost = createEffect(SL_IID_BASSBOOST, sessionId);
+ if ((bassboost->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
+ GTEST_SKIP() << "effect processed output inaccessible, skipping test";
+ }
+ ASSERT_EQ(OK, bassboost->initCheck());
+ ASSERT_EQ(NO_ERROR, bassboost->setEnabled(true));
+
+ const auto bin = roundToFreqCenteredToFftBin(kBinWidth, kTestFrequency);
+ const int binIndex = std::get<0 /* index */>(bin);
+ const int binFrequency = std::get<1 /* freq */>(bin);
+
+ const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
+ // input for effect module
+ auto silentAudio = pffft::AlignedVector<float>(totalFrameCount);
+ auto input = pffft::AlignedVector<float>(totalFrameCount);
+ generateMultiTone({binFrequency}, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
+ input.data(), totalFrameCount);
+ TemporaryFile tf(kDataTempPath);
+ close(tf.release());
+ std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
+ fout.write((char*)input.data(), input.size() * sizeof(input[0]));
+ fout.close();
+
+ // play non-silent audio file on AUDIO_SESSION_NONE
+ float audioGain, audioPlusSilentEffectGain;
+ ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, {binIndex},
+ nullptr, &audioGain, tf.path,
+ AUDIO_SESSION_NONE));
+ EXPECT_FALSE(std::isinf(audioGain)) << "output gain should not be -inf";
+
+ TemporaryFile silentFile(kDataTempPath);
+ close(silentFile.release());
+ std::ofstream fSilent(silentFile.path, std::ios::out | std::ios::binary);
+ fSilent.write((char*)silentAudio.data(), silentAudio.size() * sizeof(silentAudio[0]));
+ fSilent.close();
+ // play non-silent audio file on AUDIO_SESSION_NONE and silent audio on sessionId, expect
+ // the new output gain to be almost same as last playback
+ ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(
+ kCaptureDurationSec, kNPointFFT, {binIndex}, nullptr, &audioPlusSilentEffectGain,
+ tf.path, AUDIO_SESSION_NONE, silentFile.path, sessionId));
+ EXPECT_FALSE(std::isinf(audioPlusSilentEffectGain))
+ << "output might have been overwritten in effect accumulate mode";
+ EXPECT_NEAR(audioGain, audioPlusSilentEffectGain, kAudioGainDiffTolerancedB)
+ << " output gain should almost same with one more silent audio stream";
+}
+
int main(int argc, char** argv) {
android::ProcessState::self()->startThreadPool();
::testing::InitGoogleTest(&argc, argv);
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
index 59d0c6a..bedeff9 100644
--- a/media/libaudioclient/tests/audioeffect_tests.cpp
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -70,8 +70,8 @@
return effect;
}
-status_t isEffectExistsOnAudioSession(const effect_uuid_t* type, const effect_uuid_t* uuid,
- int priority, audio_session_t sessionId) {
+status_t createAndInitCheckEffect(const effect_uuid_t* type, const effect_uuid_t* uuid,
+ int priority, audio_session_t sessionId) {
sp<AudioEffect> effect = createEffect(type, uuid, priority, sessionId);
return effect->initCheck();
}
@@ -272,10 +272,9 @@
EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
- EXPECT_EQ(NO_ERROR,
- isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
- kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ EXPECT_EQ(NO_ERROR, createAndInitCheckEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
<< "Effect should not have been added. " << type;
EXPECT_EQ(OK, capture->audioProcess());
EXPECT_EQ(OK, capture->stop());
@@ -296,9 +295,9 @@
capture->getAudioRecordHandle()))
<< "Effect should have been default on record. " << type;
EXPECT_EQ(ALREADY_EXISTS,
- isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
- kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ createAndInitCheckEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
<< "Effect should have been added. " << type;
EXPECT_EQ(OK, capture->audioProcess());
EXPECT_EQ(OK, capture->stop());
@@ -313,10 +312,9 @@
EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
capture->getAudioRecordHandle()))
<< "Effect should not have been default on record. " << type;
- EXPECT_EQ(NO_ERROR,
- isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
- kDefaultInputEffectPriority - 1,
- capture->getAudioRecordHandle()->getSessionId()))
+ EXPECT_EQ(NO_ERROR, createAndInitCheckEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
<< "Effect should not have been added. " << type;
EXPECT_EQ(OK, capture->audioProcess());
EXPECT_EQ(OK, capture->stop());
@@ -421,8 +419,8 @@
EXPECT_EQ(NO_ERROR, playback->create());
EXPECT_EQ(NO_ERROR, playback->start());
EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
- isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
+ createAndInitCheckEffect(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
<< "Effect should not have been added. " << mTypeStr;
EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
playback->stop();
@@ -445,8 +443,8 @@
EXPECT_EQ(NO_ERROR, playback->start());
// If effect chosen is not compatible with the session, then effect won't be applied
EXPECT_EQ(compatCheck ? ALREADY_EXISTS : NO_INIT,
- isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
+ createAndInitCheckEffect(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
<< "Effect should have been added. " << mTypeStr;
EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
if (mSelectFastMode) {
@@ -467,8 +465,8 @@
EXPECT_EQ(NO_ERROR, playback->create());
EXPECT_EQ(NO_ERROR, playback->start());
EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
- isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
- playback->getAudioTrackHandle()->getSessionId()))
+ createAndInitCheckEffect(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
<< "Effect should not have been added. " << mTypeStr;
EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
playback->stop();
@@ -502,8 +500,8 @@
EXPECT_EQ(NO_ERROR, playback->create());
EXPECT_EQ(NO_ERROR, playback->start());
- EXPECT_EQ(ALREADY_EXISTS, isEffectExistsOnAudioSession(
- &mType, &mUuid, kDefaultOutputEffectPriority - 1, sessionId))
+ EXPECT_EQ(ALREADY_EXISTS,
+ createAndInitCheckEffect(&mType, &mUuid, kDefaultOutputEffectPriority - 1, sessionId))
<< "Effect should have been added. " << mTypeStr;
if (mSelectFastMode) {
EXPECT_EQ(mIsFastCompatibleEffect ? AUDIO_OUTPUT_FLAG_FAST : 0,
@@ -556,8 +554,8 @@
ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
EXPECT_EQ(NO_ERROR, playback->create());
EXPECT_EQ(NO_ERROR, playback->start());
- EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
- kDefaultOutputEffectPriority - 1, sessionId))
+ ASSERT_EQ(ALREADY_EXISTS, createAndInitCheckEffect(selectedEffectType, selectedEffectUuid,
+ kDefaultOutputEffectPriority - 1, sessionId))
<< "Effect should have been added. " << type;
EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
playback->stop();
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index 9908f33..f2fee8b 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -102,7 +102,10 @@
}
void TearDown() override {
- if (mAC) ASSERT_EQ(OK, mAC->stop());
+ if (mAC) {
+ ASSERT_EQ(OK, mAC->stop());
+ mAC.clear();
+ }
}
};
@@ -168,33 +171,33 @@
}
TEST_F(AudioRecordTest, TestGetSetMarker) {
- mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
- EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition))
+ mAC->setMarkerPosition((mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setMarkerPosition(mAC->getMarkerPosition()))
<< "setMarkerPosition() failed";
uint32_t marker;
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker))
<< "getMarkerPosition() failed";
EXPECT_EQ(OK, mAC->start()) << "start recording failed";
EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
- // TODO(b/348658586): Properly synchronize callback updates with the test thread.
- EXPECT_EQ(marker, mAC->mMarkerPosition)
+ EXPECT_EQ(marker, mAC->getMarkerPosition())
<< "configured marker and received marker are different";
- EXPECT_EQ(mAC->mReceivedCbMarkerAtPosition, mAC->mMarkerPosition)
+ EXPECT_EQ(mAC->waitAndGetReceivedCbMarkerAtPosition(), mAC->getMarkerPosition())
<< "configured marker and received cb marker are different";
}
TEST_F(AudioRecordTest, TestGetSetMarkerPeriodical) {
- mAC->mMarkerPeriod = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
- EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPeriod))
+ mAC->setMarkerPeriod((mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->getMarkerPeriod()))
<< "setPositionUpdatePeriod() failed";
uint32_t marker;
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker))
<< "getPositionUpdatePeriod() failed";
EXPECT_EQ(OK, mAC->start()) << "start recording failed";
EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
- // TODO(b/348658586): Properly synchronize callback updates with the test thread.
- EXPECT_EQ(marker, mAC->mMarkerPeriod) << "configured marker and received marker are different";
- EXPECT_EQ(mAC->mReceivedCbMarkerCount, mAC->mNumFramesToRecord / mAC->mMarkerPeriod)
+ EXPECT_EQ(marker, mAC->getMarkerPeriod())
+ << "configured marker and received marker are different";
+ EXPECT_EQ(mAC->waitAndGetReceivedCbMarkerCount(),
+ mAC->mNumFramesToRecord / mAC->getMarkerPeriod())
<< "configured marker and received cb marker are different";
}
@@ -221,12 +224,12 @@
EXPECT_EQ(mSessionId, mAC->getAudioRecordHandle()->getSessionId());
if (mTransferType != AudioRecord::TRANSFER_CALLBACK) {
uint32_t marker;
- mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ mAC->setMarkerPosition((mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1));
EXPECT_EQ(INVALID_OPERATION,
- mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition));
+ mAC->getAudioRecordHandle()->setMarkerPosition(mAC->getMarkerPosition()));
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker));
EXPECT_EQ(INVALID_OPERATION,
- mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPosition));
+ mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->getMarkerPosition()));
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker));
}
EXPECT_EQ(OK, mAC->start()) << "start recording failed";
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
index db998cd..742ca48 100644
--- a/media/libaudioclient/tests/audiosystem_tests.cpp
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -199,20 +199,6 @@
EXPECT_EQ(origBalance, tstBalance);
}
-TEST_F(AudioSystemTest, GetStreamVolume) {
- ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
- float origStreamVol;
- const auto [pbAudioIo, _] = mCbPlayback->getLastPortAndDevice();
- EXPECT_EQ(NO_ERROR,
- AudioSystem::getStreamVolume(AUDIO_STREAM_MUSIC, &origStreamVol, pbAudioIo));
-}
-
-TEST_F(AudioSystemTest, GetStreamMute) {
- ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
- bool origMuteState;
- EXPECT_EQ(NO_ERROR, AudioSystem::getStreamMute(AUDIO_STREAM_MUSIC, &origMuteState));
-}
-
TEST_F(AudioSystemTest, StartAndStopAudioSource) {
std::vector<struct audio_port_v7> ports;
audio_port_config sourcePortConfig;
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
index 639c7aa..75e2c11 100644
--- a/media/libaudiohal/Android.bp
+++ b/media/libaudiohal/Android.bp
@@ -45,6 +45,8 @@
"liberror_headers",
"libmediautils_headers",
],
+
+ export_include_dirs: ["include"],
}
cc_library_shared {
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
index 64cc7ed..2753906 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
@@ -42,6 +42,8 @@
using ::aidl::android::hardware::audio::effect::Descriptor;
using ::aidl::android::hardware::audio::effect::IFactory;
using ::aidl::android::hardware::audio::effect::Processing;
+using ::aidl::android::media::audio::common::AudioDevice;
+using ::aidl::android::media::audio::common::AudioDeviceAddress;
using ::aidl::android::media::audio::common::AudioSource;
using ::aidl::android::media::audio::common::AudioStreamType;
using ::aidl::android::media::audio::common::AudioUuid;
@@ -174,9 +176,6 @@
if (uuid == nullptr || effect == nullptr) {
return BAD_VALUE;
}
- if (sessionId == AUDIO_SESSION_DEVICE && ioId == AUDIO_IO_HANDLE_NONE) {
- return INVALID_OPERATION;
- }
ALOGV("%s session %d ioId %d", __func__, sessionId, ioId);
AudioUuid aidlUuid =
@@ -284,7 +283,8 @@
auto getConfigProcessingWithAidlProcessing =
[&](const auto& aidlProcess, std::vector<effectsConfig::InputStream>& preprocess,
- std::vector<effectsConfig::OutputStream>& postprocess) {
+ std::vector<effectsConfig::OutputStream>& postprocess,
+ std::vector<effectsConfig::DeviceEffects>& deviceprocess) {
if (aidlProcess.type.getTag() == Processing::Type::streamType) {
AudioStreamType aidlType =
aidlProcess.type.template get<Processing::Type::streamType>();
@@ -316,6 +316,25 @@
effectsConfig::InputStream stream = {.type = type.value(),
.effects = std::move(effects)};
preprocess.emplace_back(stream);
+ } else if (aidlProcess.type.getTag() == Processing::Type::device) {
+ AudioDevice aidlDevice =
+ aidlProcess.type.template get<Processing::Type::device>();
+ std::vector<std::shared_ptr<const effectsConfig::Effect>> effects;
+ std::transform(aidlProcess.ids.begin(), aidlProcess.ids.end(),
+ std::back_inserter(effects), getConfigEffectWithDescriptor);
+ audio_devices_t type;
+ char address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ status_t status = ::aidl::android::aidl2legacy_AudioDevice_audio_device(
+ aidlDevice, &type, address);
+ if (status != NO_ERROR) {
+ ALOGE("%s device effect has invalid device type / address", __func__);
+ return;
+ }
+ effectsConfig::DeviceEffects device = {
+ {.type = type, .effects = std::move(effects)},
+ .address = address,
+ };
+ deviceprocess.emplace_back(device);
}
};
@@ -323,17 +342,21 @@
[&]() -> std::shared_ptr<const effectsConfig::Processings> {
std::vector<effectsConfig::InputStream> preprocess;
std::vector<effectsConfig::OutputStream> postprocess;
+ std::vector<effectsConfig::DeviceEffects> deviceprocess;
for (const auto& processing : mAidlProcessings) {
- getConfigProcessingWithAidlProcessing(processing, preprocess, postprocess);
+ getConfigProcessingWithAidlProcessing(processing, preprocess, postprocess,
+ deviceprocess);
}
- if (0 == preprocess.size() && 0 == postprocess.size()) {
+ if (0 == preprocess.size() && 0 == postprocess.size() &&
+ 0 == deviceprocess.size()) {
return nullptr;
}
return std::make_shared<const effectsConfig::Processings>(
effectsConfig::Processings({.preprocess = std::move(preprocess),
- .postprocess = std::move(postprocess)}));
+ .postprocess = std::move(postprocess),
+ .deviceprocess = std::move(deviceprocess)}));
}());
return processings;
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 38e1ea4..918f886 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -421,8 +421,16 @@
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
- return sendCommand(makeHalCommand<HalCommand::Tag::pause>(), reply,
- true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
+
+ if (const auto state = getState(); isInPlayOrRecordState(state)) {
+ return sendCommand(
+ makeHalCommand<HalCommand::Tag::pause>(), reply,
+ true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
+ } else {
+ ALOGD("%s: already stream in one of the PAUSED kind of states, current state: %s", __func__,
+ toString(state).c_str());
+ return OK;
+ }
}
status_t StreamHalAidl::resume(StreamDescriptor::Reply* reply) {
@@ -477,8 +485,19 @@
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
- return sendCommand(makeHalCommand<HalCommand::Tag::flush>(), reply,
- true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
+
+ if (const auto state = getState(); isInPausedState(state)) {
+ return sendCommand(
+ makeHalCommand<HalCommand::Tag::flush>(), reply,
+ true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
+ } else if (isInPlayOrRecordState(state)) {
+ ALOGE("%s: found stream in non-flushable state: %s", __func__, toString(state).c_str());
+ return INVALID_OPERATION;
+ } else {
+ ALOGD("%s: already stream in one of the flushable state: current state: %s", __func__,
+ toString(state).c_str());
+ return OK;
+ }
}
status_t StreamHalAidl::exit() {
@@ -800,6 +819,14 @@
}
status_t StreamOutHalAidl::drain(bool earlyNotify) {
+ if (!mStream) return NO_INIT;
+
+ if (const auto state = getState(); isInDrainedState(state)) {
+ ALOGD("%p %s stream already in %s", this, __func__, toString(state).c_str());
+ if (mContext.isAsynchronous()) onDrainReady();
+ return OK;
+ }
+
return StreamHalAidl::drain(earlyNotify);
}
diff --git a/media/libaudiohal/impl/StreamHalAidl.h b/media/libaudiohal/impl/StreamHalAidl.h
index 9cb2cff..baf4ac0 100644
--- a/media/libaudiohal/impl/StreamHalAidl.h
+++ b/media/libaudiohal/impl/StreamHalAidl.h
@@ -215,6 +215,46 @@
~StreamHalAidl() override;
+ ::aidl::android::hardware::audio::core::StreamDescriptor::State getState() {
+ std::lock_guard l(mLock);
+ return mLastReply.state;
+ }
+
+ bool isInDrainedState(
+ const ::aidl::android::hardware::audio::core::StreamDescriptor::State state) {
+ if (state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::IDLE ||
+ state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::STANDBY) {
+ // drain equivalent states
+ return true;
+ }
+ return false;
+ }
+
+ bool isInPlayOrRecordState(
+ const ::aidl::android::hardware::audio::core::StreamDescriptor::State state) {
+ if (state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::ACTIVE ||
+ state ==
+ ::aidl::android::hardware::audio::core::StreamDescriptor::State::TRANSFERRING ||
+ state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::DRAINING) {
+ // play or record equivalent states
+ return true;
+ }
+ return false;
+ }
+
+ bool isInPausedState(
+ const ::aidl::android::hardware::audio::core::StreamDescriptor::State& state) {
+ if (state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::PAUSED ||
+ state ==
+ ::aidl::android::hardware::audio::core::StreamDescriptor::State::DRAIN_PAUSED ||
+ state == ::aidl::android::hardware::audio::core::StreamDescriptor::State::
+ TRANSFER_PAUSED) {
+ // pause equivalent states
+ return true;
+ }
+ return false;
+ }
+
status_t getLatency(uint32_t *latency);
// Always returns non-negative values.
@@ -268,10 +308,6 @@
result.format = config.format;
return result;
}
- ::aidl::android::hardware::audio::core::StreamDescriptor::State getState() {
- std::lock_guard l(mLock);
- return mLastReply.state;
- }
// Note: Since `sendCommand` takes mLock while holding mCommandReplyLock, never call
// it with `mLock` being held.
status_t sendCommand(
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f9ae2d4..7ef9ff2 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -585,7 +585,7 @@
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
- t->mHapticScale = {/*level=*/os::HapticLevel::NONE };
+ t->mHapticScale = os::HapticScale::none();
t->mHapticMaxAmplitude = NAN;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
diff --git a/media/libeffects/downmix/aidl/DownmixContext.cpp b/media/libeffects/downmix/aidl/DownmixContext.cpp
index 593e16f..3a55361 100644
--- a/media/libeffects/downmix/aidl/DownmixContext.cpp
+++ b/media/libeffects/downmix/aidl/DownmixContext.cpp
@@ -100,11 +100,6 @@
return RetCode::SUCCESS;
}
-void DownmixContext::reset() {
- disable();
- resetBuffer();
-}
-
IEffect::Status DownmixContext::downmixProcess(float* in, float* out, int samples) {
IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0};
diff --git a/media/libeffects/downmix/aidl/DownmixContext.h b/media/libeffects/downmix/aidl/DownmixContext.h
index a381d7f..1be1508 100644
--- a/media/libeffects/downmix/aidl/DownmixContext.h
+++ b/media/libeffects/downmix/aidl/DownmixContext.h
@@ -32,9 +32,8 @@
public:
DownmixContext(int statusDepth, const Parameter::Common& common);
~DownmixContext();
- RetCode enable();
- RetCode disable();
- void reset();
+ RetCode enable() override;
+ RetCode disable() override;
RetCode setDmType(Downmix::Type type) {
mType = type;
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.cpp b/media/libeffects/downmix/aidl/EffectDownmix.cpp
index 883d41d..10c7c4f 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.cpp
+++ b/media/libeffects/downmix/aidl/EffectDownmix.cpp
@@ -71,26 +71,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus DownmixImpl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->reset();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus DownmixImpl::setParameterSpecific(const Parameter::Specific& specific) {
RETURN_IF(Parameter::Specific::downmix != specific.getTag(), EX_ILLEGAL_ARGUMENT,
"EffectNotSupported");
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.h b/media/libeffects/downmix/aidl/EffectDownmix.h
index b7d621a..cea6d1b 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.h
+++ b/media/libeffects/downmix/aidl/EffectDownmix.h
@@ -31,7 +31,6 @@
DownmixImpl() = default;
~DownmixImpl() { cleanUp(); }
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) override;
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
index 836e034..8324473 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
@@ -244,27 +244,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus DynamicsProcessingImpl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- return ndk::ScopedAStatus::ok();
- case CommandId::STOP:
- mContext->disable();
- return ndk::ScopedAStatus::ok();
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- return ndk::ScopedAStatus::ok();
- default:
- // Need this default handling for vendor extendable CommandId::VENDOR_COMMAND_*
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
-}
-
bool DynamicsProcessingImpl::isParamInRange(const Parameter::Specific& specific) {
auto& dp = specific.get<Parameter::Specific::dynamicsProcessing>();
return DynamicsProcessingRanges::isParamInRange(dp, kRanges);
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
index e850ba4..b34cdcf 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
@@ -36,7 +36,6 @@
ndk::ScopedAStatus open(const Parameter::Common& common,
const std::optional<Parameter::Specific>& specific,
OpenEffectReturn* ret) override;
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) override;
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
index ada301b..fd4e615 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
@@ -48,10 +48,11 @@
return RetCode::SUCCESS;
}
-void DynamicsProcessingContext::reset() {
+RetCode DynamicsProcessingContext::reset() {
if (mDpFreq != nullptr) {
- mDpFreq.reset();
+ mDpFreq->reset();
}
+ return RetCode::SUCCESS;
}
RetCode DynamicsProcessingContext::setCommon(const Parameter::Common& common) {
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
index ce657db..15c6811 100644
--- a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
@@ -37,9 +37,9 @@
public:
DynamicsProcessingContext(int statusDepth, const Parameter::Common& common);
~DynamicsProcessingContext() = default;
- RetCode enable();
- RetCode disable();
- void reset();
+ RetCode enable() override;
+ RetCode disable() override;
+ RetCode reset() override;
// override EffectContext::setCommon to update mChannelCount
RetCode setCommon(const Parameter::Common& common) override;
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index f60d616..0c7ea7f 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -35,12 +35,15 @@
#include <audio_utils/format.h>
#include <audio_utils/safe_math.h>
#include <system/audio.h>
+#include <system/audio_effects/audio_effects_utils.h>
static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
static constexpr float DEFAULT_DISTORTION_OUTPUT_GAIN = 1.5f;
+using android::effect::utils::EffectParamReader;
+
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
@@ -307,25 +310,32 @@
return 0;
}
-int HapticGenerator_SetParameter(struct HapticGeneratorContext *context,
- int32_t param,
- uint32_t size,
- void *value) {
- switch (param) {
+int HapticGenerator_SetParameter(struct HapticGeneratorContext *context, effect_param_t* param) {
+ if (param == nullptr) {
+ ALOGE("%s invalid effect_param_t is nullptr", __func__);
+ return -EINVAL;
+ }
+ int32_t paramType;
+ EffectParamReader reader(*param);
+ reader.readFromParameter(¶mType);
+
+ switch (paramType) {
case HG_PARAM_HAPTIC_INTENSITY: {
- if (value == nullptr || size != (uint32_t) (2 * sizeof(int) + sizeof(float))) {
+ if (param->vsize != (sizeof(int32_t) + sizeof(os::HapticScale))) {
+ ALOGE("%s invalid haptic intensity param size %s", __func__, reader.toString().c_str());
return -EINVAL;
}
- const int id = *(int *) value;
- const os::HapticLevel hapticLevel = static_cast<os::HapticLevel>(*((int *) value + 1));
- const float adaptiveScaleFactor = (*((float *) value + 2));
- const os::HapticScale hapticScale = {hapticLevel, adaptiveScaleFactor};
- ALOGD("Updating haptic scale, hapticLevel=%d, adaptiveScaleFactor=%f",
- static_cast<int>(hapticLevel), adaptiveScaleFactor);
+ int32_t paramId;
+ os::HapticScale hapticScale;
+ if (reader.readFromValue(¶mId) != OK || reader.readFromValue(&hapticScale) != OK) {
+ ALOGE("%s error reading haptic intensity %s", __func__, reader.toString().c_str());
+ return -EINVAL;
+ }
+ ALOGD("Updating haptic scale, %s", hapticScale.toString().c_str());
if (hapticScale.isScaleMute()) {
- context->param.id2HapticScale.erase(id);
+ context->param.id2HapticScale.erase(paramId);
} else {
- context->param.id2HapticScale.emplace(id, hapticScale);
+ context->param.id2HapticScale.emplace(paramId, hapticScale);
}
context->param.maxHapticScale = hapticScale;
for (const auto&[id, scale] : context->param.id2HapticScale) {
@@ -336,12 +346,17 @@
break;
}
case HG_PARAM_VIBRATOR_INFO: {
- if (value == nullptr || size != 3 * sizeof(float)) {
+ if (param->vsize != (3 * sizeof(float))) {
+ ALOGE("%s invalid vibrator info param size %s", __func__, reader.toString().c_str());
return -EINVAL;
}
- const float resonantFrequency = *(float*) value;
- const float qFactor = *((float *) value + 1);
- const float maxAmplitude = *((float *) value + 2);
+ float resonantFrequency, qFactor, maxAmplitude;
+ if (reader.readFromValue(&resonantFrequency) != OK ||
+ reader.readFromValue(&qFactor) != OK ||
+ reader.readFromValue(&maxAmplitude) != OK) {
+ ALOGE("%s error reading vibrator info %s", __func__, reader.toString().c_str());
+ return -EINVAL;
+ }
context->param.resonantFrequency =
audio_utils::safe_isnan(resonantFrequency) ? DEFAULT_RESONANT_FREQUENCY
: resonantFrequency;
@@ -369,7 +384,7 @@
HapticGenerator_Reset(context);
} break;
default:
- ALOGW("Unknown param: %d", param);
+ ALOGW("Unknown param: %d", paramType);
return -EINVAL;
}
@@ -573,8 +588,7 @@
return -EINVAL;
}
effect_param_t *cmd = (effect_param_t *) cmdData;
- *(int *) replyData = HapticGenerator_SetParameter(
- context, *(int32_t *) cmd->data, cmd->vsize, cmd->data + sizeof(int32_t));
+ *(int *) replyData = HapticGenerator_SetParameter(context, cmd);
}
break;
diff --git a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
index b803ee4..55e07fb 100644
--- a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
@@ -70,26 +70,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus HapticGeneratorImpl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->reset();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus HapticGeneratorImpl::setParameterSpecific(const Parameter::Specific& specific) {
RETURN_IF(Parameter::Specific::hapticGenerator != specific.getTag(), EX_ILLEGAL_ARGUMENT,
"EffectNotSupported");
diff --git a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
index a775f06..8bae024 100644
--- a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
@@ -30,7 +30,6 @@
HapticGeneratorImpl() = default;
~HapticGeneratorImpl() { cleanUp(); }
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) override;
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
index 9b2f443..6e9e216 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
@@ -71,7 +71,7 @@
return RetCode::SUCCESS;
}
-void HapticGeneratorContext::reset() {
+RetCode HapticGeneratorContext::reset() {
for (auto& filter : mProcessorsRecord.filters) {
filter->clear();
}
@@ -81,6 +81,7 @@
for (auto& distortion : mProcessorsRecord.distortions) {
distortion->clear();
}
+ return RetCode::SUCCESS;
}
RetCode HapticGeneratorContext::setHgHapticScales(
@@ -176,7 +177,9 @@
runProcessingChain(mInputBuffer.data(), mOutputBuffer.data(), mFrameCount);
::android::os::scaleHapticData(
hapticOutBuffer, hapticSampleCount,
- {static_cast<::android::os::HapticLevel>(mParams.mMaxVibratorScale)} /* scale */,
+ // TODO(b/356406686): add the new HapticScale fields to the AIDL interface.
+ ::android::os::HapticScale(
+ static_cast<::android::os::HapticLevel>(mParams.mMaxVibratorScale)),
mParams.mVibratorInfo.maxAmplitude /* limit */);
// For haptic data, the haptic playback thread will copy the data from effect input
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
index 8a736e3..cf38e47 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
@@ -65,9 +65,9 @@
public:
HapticGeneratorContext(int statusDepth, const Parameter::Common& common);
~HapticGeneratorContext();
- RetCode enable();
- RetCode disable();
- void reset();
+ RetCode enable() override;
+ RetCode disable() override;
+ RetCode reset() override;
RetCode setHgHapticScales(const std::vector<HapticGenerator::HapticScale>& hapticScales);
std::vector<HapticGenerator::HapticScale> getHgHapticScales() const;
diff --git a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.cpp
index f89606e..592fd60 100644
--- a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.cpp
+++ b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.cpp
@@ -70,27 +70,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus LoudnessEnhancerImpl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus LoudnessEnhancerImpl::setParameterSpecific(const Parameter::Specific& specific) {
RETURN_IF(Parameter::Specific::loudnessEnhancer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
"EffectNotSupported");
diff --git a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
index 98bdc6b..1e050f3 100644
--- a/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
+++ b/media/libeffects/loudness/aidl/EffectLoudnessEnhancer.h
@@ -30,7 +30,6 @@
LoudnessEnhancerImpl() = default;
~LoudnessEnhancerImpl() { cleanUp(); }
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) override;
diff --git a/media/libeffects/loudness/aidl/LoudnessEnhancerContext.cpp b/media/libeffects/loudness/aidl/LoudnessEnhancerContext.cpp
index d8bcfc0..ac8b14a 100644
--- a/media/libeffects/loudness/aidl/LoudnessEnhancerContext.cpp
+++ b/media/libeffects/loudness/aidl/LoudnessEnhancerContext.cpp
@@ -43,17 +43,13 @@
return RetCode::SUCCESS;
}
-void LoudnessEnhancerContext::reset() {
- float targetAmp = pow(10, mGain / 2000.0f); // mB to linear amplification
+RetCode LoudnessEnhancerContext::setLeGain(int gainMb) {
+ float targetAmp = pow(10, gainMb / 2000.0f); // mB to linear amplification
if (mCompressor != nullptr) {
// Get samplingRate from input
mCompressor->Initialize(targetAmp, mCommon.input.base.sampleRate);
}
-}
-
-RetCode LoudnessEnhancerContext::setLeGain(int gainMb) {
mGain = gainMb;
- reset(); // apply parameter update
return RetCode::SUCCESS;
}
diff --git a/media/libeffects/loudness/aidl/LoudnessEnhancerContext.h b/media/libeffects/loudness/aidl/LoudnessEnhancerContext.h
index 192b212..67ccd24 100644
--- a/media/libeffects/loudness/aidl/LoudnessEnhancerContext.h
+++ b/media/libeffects/loudness/aidl/LoudnessEnhancerContext.h
@@ -34,9 +34,8 @@
LoudnessEnhancerContext(int statusDepth, const Parameter::Common& common);
~LoudnessEnhancerContext() = default;
- RetCode enable();
- RetCode disable();
- void reset();
+ RetCode enable() override;
+ RetCode disable() override;
RetCode setLeGain(int gainMb);
int getLeGain() const { return mGain; }
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index 044c8dd..e5ab40d 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -35,9 +35,9 @@
void deInit();
lvm::BundleEffectType getBundleType() const { return mType; }
- RetCode enable();
+ RetCode enable() override;
RetCode enableOperatingMode();
- RetCode disable();
+ RetCode disable() override;
RetCode disableOperatingMode();
bool isDeviceSupportedBassBoost(
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
index 70c276d..2a81673 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
@@ -428,27 +428,6 @@
return RetCode::SUCCESS;
}
-ndk::ScopedAStatus EffectBundleAidl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
// Processing method running in EffectWorker thread.
IEffect::Status EffectBundleAidl::effectProcessImpl(float* in, float* out, int sampleToProcess) {
IEffect::Status status = {EX_NULL_POINTER, 0, 0};
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
index 429e941..479579b 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
@@ -49,8 +49,6 @@
IEffect::Status effectProcessImpl(float* in, float* out, int samples)
REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
-
std::string getEffectName() override { return *mEffectName; }
private:
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
index 4d369b1..201c659 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.cpp
@@ -361,27 +361,6 @@
return RetCode::SUCCESS;
}
-ndk::ScopedAStatus EffectReverb::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
// Processing method running in EffectWorker thread.
IEffect::Status EffectReverb::effectProcessImpl(float* in, float* out, int sampleToProcess) {
IEffect::Status status = {EX_NULL_POINTER, 0, 0};
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
index e0771a1..4acac1d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/EffectReverb.h
@@ -42,8 +42,6 @@
IEffect::Status effectProcessImpl(float* in, float* out, int samples)
REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
-
std::string getEffectName() override { return *mEffectName; }
private:
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.h b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.h
index 44391f2..f55eac5 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.h
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.h
@@ -51,8 +51,8 @@
RetCode init();
void deInit();
- RetCode enable();
- RetCode disable();
+ RetCode enable() override;
+ RetCode disable() override;
bool isAuxiliary();
bool isPreset();
diff --git a/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp b/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
index 87d267b..4bc34e7 100644
--- a/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
+++ b/media/libeffects/preprocessing/aidl/EffectPreProcessing.cpp
@@ -417,27 +417,6 @@
return RetCode::SUCCESS;
}
-ndk::ScopedAStatus EffectPreProcessing::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
// Processing method running in EffectWorker thread.
IEffect::Status EffectPreProcessing::effectProcessImpl(float* in, float* out, int sampleToProcess) {
IEffect::Status status = {EX_NULL_POINTER, 0, 0};
diff --git a/media/libeffects/preprocessing/aidl/EffectPreProcessing.h b/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
index 9ce5597..31f5737 100644
--- a/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
+++ b/media/libeffects/preprocessing/aidl/EffectPreProcessing.h
@@ -43,8 +43,6 @@
IEffect::Status effectProcessImpl(float* in, float* out, int samples)
REQUIRES(mImplMutex) override;
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
-
std::string getEffectName() override { return *mEffectName; }
private:
diff --git a/media/libeffects/preprocessing/aidl/PreProcessingContext.h b/media/libeffects/preprocessing/aidl/PreProcessingContext.h
index 11a2bea..1b9b77b 100644
--- a/media/libeffects/preprocessing/aidl/PreProcessingContext.h
+++ b/media/libeffects/preprocessing/aidl/PreProcessingContext.h
@@ -45,8 +45,8 @@
PreProcessingEffectType getPreProcessingType() const { return mType; }
- RetCode enable();
- RetCode disable();
+ RetCode enable() override;
+ RetCode disable() override;
RetCode setCommon(const Parameter::Common& common) override;
void updateConfigs(const Parameter::Common& common);
diff --git a/media/libeffects/visualizer/aidl/Visualizer.cpp b/media/libeffects/visualizer/aidl/Visualizer.cpp
index 9b493d4..f4b9b25 100644
--- a/media/libeffects/visualizer/aidl/Visualizer.cpp
+++ b/media/libeffects/visualizer/aidl/Visualizer.cpp
@@ -85,27 +85,6 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus VisualizerImpl::commandImpl(CommandId command) {
- RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
- switch (command) {
- case CommandId::START:
- mContext->enable();
- break;
- case CommandId::STOP:
- mContext->disable();
- break;
- case CommandId::RESET:
- mContext->disable();
- mContext->resetBuffer();
- break;
- default:
- LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- "commandIdNotSupported");
- }
- return ndk::ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus VisualizerImpl::setParameterSpecific(const Parameter::Specific& specific) {
RETURN_IF(Parameter::Specific::visualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
"EffectNotSupported");
@@ -222,6 +201,7 @@
RetCode VisualizerImpl::releaseContext() {
if (mContext) {
mContext->disable();
+ mContext->reset();
mContext->resetBuffer();
}
return RetCode::SUCCESS;
diff --git a/media/libeffects/visualizer/aidl/Visualizer.h b/media/libeffects/visualizer/aidl/Visualizer.h
index 3180972..f25b78d 100644
--- a/media/libeffects/visualizer/aidl/Visualizer.h
+++ b/media/libeffects/visualizer/aidl/Visualizer.h
@@ -32,7 +32,6 @@
VisualizerImpl() = default;
~VisualizerImpl() { cleanUp(); }
- ndk::ScopedAStatus commandImpl(CommandId command) REQUIRES(mImplMutex) override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) override;
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.cpp b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
index 1e08674..a368e52 100644
--- a/media/libeffects/visualizer/aidl/VisualizerContext.cpp
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
@@ -57,7 +57,7 @@
#endif
mChannelCount = channelCount;
mCommon = common;
- std::fill(mCaptureBuf.begin(), mCaptureBuf.end(), 0x80);
+ reset();
return RetCode::SUCCESS;
}
@@ -77,8 +77,9 @@
return RetCode::SUCCESS;
}
-void VisualizerContext::reset() {
+RetCode VisualizerContext::reset() {
std::fill(mCaptureBuf.begin(), mCaptureBuf.end(), 0x80);
+ return RetCode::SUCCESS;
}
RetCode VisualizerContext::setCaptureSamples(int samples) {
@@ -109,7 +110,6 @@
mDownstreamLatency = latency;
return RetCode::SUCCESS;
}
-
int VisualizerContext::getDownstreamLatency() {
return mDownstreamLatency;
}
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.h b/media/libeffects/visualizer/aidl/VisualizerContext.h
index 9715e20..d4abbd3 100644
--- a/media/libeffects/visualizer/aidl/VisualizerContext.h
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.h
@@ -36,10 +36,10 @@
RetCode initParams(const Parameter::Common& common);
- RetCode enable();
- RetCode disable();
+ RetCode enable() override;
+ RetCode disable() override;
// keep all parameters and reset buffer.
- void reset();
+ RetCode reset() override;
RetCode setCaptureSamples(int32_t captureSize);
int32_t getCaptureSamples();
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index e5323a6..8a962c6 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -255,6 +255,45 @@
}
cc_library_shared {
+ name: "libmedia_codeclist_capabilities",
+
+ srcs: [
+ "AudioCapabilities.cpp",
+ "CodecCapabilities.cpp",
+ "CodecCapabilitiesUtils.cpp",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
+
+cc_library_shared {
name: "libmedia_codeclist",
srcs: [
@@ -270,6 +309,7 @@
"android.hardware.media.omx@1.0",
"libbinder",
"liblog",
+ "libmedia_codeclist_capabilities",
"libstagefright_foundation",
"libutils",
],
@@ -278,10 +318,6 @@
"android.media.codec-aconfig-cc",
],
- include_dirs: [
- "system/libhidl/transport/token/1.0/utils/include",
- ],
-
export_include_dirs: [
"include",
],
diff --git a/media/libmedia/AudioCapabilities.cpp b/media/libmedia/AudioCapabilities.cpp
new file mode 100644
index 0000000..e8cf517
--- /dev/null
+++ b/media/libmedia/AudioCapabilities.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioCapabilities"
+
+#include <android-base/strings.h>
+#include <android-base/properties.h>
+
+#include <media/AudioCapabilities.h>
+#include <media/CodecCapabilities.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+namespace android {
+
+const Range<int>& AudioCapabilities::getBitrateRange() const {
+ return mBitrateRange;
+}
+
+const std::vector<int>& AudioCapabilities::getSupportedSampleRates() const {
+ return mSampleRates;
+}
+
+const std::vector<Range<int>>&
+ AudioCapabilities::getSupportedSampleRateRanges() const {
+ return mSampleRateRanges;
+}
+
+int AudioCapabilities::getMaxInputChannelCount() const {
+ int overallMax = 0;
+ for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
+ int lmax = mInputChannelRanges[i].upper();
+ if (lmax > overallMax) {
+ overallMax = lmax;
+ }
+ }
+ return overallMax;
+}
+
+int AudioCapabilities::getMinInputChannelCount() const {
+ int overallMin = MAX_INPUT_CHANNEL_COUNT;
+ for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
+ int lmin = mInputChannelRanges[i].lower();
+ if (lmin < overallMin) {
+ overallMin = lmin;
+ }
+ }
+ return overallMin;
+}
+
+const std::vector<Range<int>>&
+ AudioCapabilities::getInputChannelCountRanges() const {
+ return mInputChannelRanges;
+}
+
+// static
+std::shared_ptr<AudioCapabilities> AudioCapabilities::Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+ std::shared_ptr<AudioCapabilities> caps(new AudioCapabilities());
+ caps->init(mediaType, profLevs, format);
+ return caps;
+}
+
+void AudioCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format) {
+ mMediaType = mediaType;
+ mProfileLevels = profLevs;
+ mError = 0;
+
+ initWithPlatformLimits();
+ applyLevelLimits();
+ parseFromInfo(format);
+}
+
+void AudioCapabilities::initWithPlatformLimits() {
+ mBitrateRange = Range<int>(0, INT_MAX);
+ mInputChannelRanges.push_back(Range<int>(1, MAX_INPUT_CHANNEL_COUNT));
+
+ const int minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
+ const int maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
+ mSampleRateRanges.push_back(Range<int>(minSampleRate, maxSampleRate));
+}
+
+bool AudioCapabilities::supports(int sampleRate, int inputChannels) {
+ // channels and sample rates are checked orthogonally
+ if (inputChannels != 0
+ && !std::any_of(mInputChannelRanges.begin(), mInputChannelRanges.end(),
+ [inputChannels](const Range<int> &a) { return a.contains(inputChannels); })) {
+ return false;
+ }
+ if (sampleRate != 0
+ && !std::any_of(mSampleRateRanges.begin(), mSampleRateRanges.end(),
+ [sampleRate](const Range<int> &a) { return a.contains(sampleRate); })) {
+ return false;
+ }
+ return true;
+}
+
+bool AudioCapabilities::isSampleRateSupported(int sampleRate) {
+ return supports(sampleRate, 0);
+}
+
+void AudioCapabilities::limitSampleRates(std::vector<int> rates) {
+ std::vector<Range<int>> sampleRateRanges;
+ std::sort(rates.begin(), rates.end());
+ for (int rate : rates) {
+ if (supports(rate, 0 /* channels */)) {
+ sampleRateRanges.push_back(Range<int>(rate, rate));
+ }
+ }
+ mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, sampleRateRanges);
+ createDiscreteSampleRates();
+}
+
+void AudioCapabilities::createDiscreteSampleRates() {
+ mSampleRates.clear();
+ for (int i = 0; i < mSampleRateRanges.size(); i++) {
+ mSampleRates.push_back(mSampleRateRanges[i].lower());
+ }
+}
+
+void AudioCapabilities::limitSampleRates(std::vector<Range<int>> rateRanges) {
+ sortDistinctRanges(&rateRanges);
+ mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
+ // check if all values are discrete
+ for (Range<int> range: mSampleRateRanges) {
+ if (range.lower() != range.upper()) {
+ mSampleRates.clear();
+ return;
+ }
+ }
+ createDiscreteSampleRates();
+}
+
+void AudioCapabilities::applyLevelLimits() {
+ std::vector<int> sampleRates;
+ std::optional<Range<int>> sampleRateRange;
+ std::optional<Range<int>> bitRates;
+ int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+
+ // const char *mediaType = mMediaType.c_str();
+ if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MPEG)) {
+ sampleRates = {
+ 8000, 11025, 12000,
+ 16000, 22050, 24000,
+ 32000, 44100, 48000 };
+ bitRates = Range<int>(8000, 320000);
+ maxChannels = 2;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(4750, 12200);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)) {
+ sampleRates = { 16000 };
+ bitRates = Range<int>(6600, 23850);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
+ sampleRates = {
+ 7350, 8000,
+ 11025, 12000, 16000,
+ 22050, 24000, 32000,
+ 44100, 48000, 64000,
+ 88200, 96000 };
+ bitRates = Range<int>(8000, 510000);
+ maxChannels = 48;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_VORBIS)) {
+ bitRates = Range<int>(32000, 500000);
+ sampleRateRange = Range<int>(8000, 192000);
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_OPUS)) {
+ bitRates = Range<int>(6000, 510000);
+ sampleRates = { 8000, 12000, 16000, 24000, 48000 };
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_RAW)) {
+ sampleRateRange = Range<int>(1, 192000);
+ bitRates = Range<int>(1, 10000000);
+ maxChannels = MAX_NUM_CHANNELS;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
+ sampleRateRange = Range<int>(1, 655350);
+ // lossless codec, so bitrate is ignored
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
+ || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(64000, 64000);
+ // platform allows multiple channels for this format
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(13000, 13000);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC3)) {
+ maxChannels = 6;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3)) {
+ maxChannels = 16;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3_JOC)) {
+ sampleRates = { 48000 };
+ bitRates = Range<int>(32000, 6144000);
+ maxChannels = 16;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC4)) {
+ sampleRates = { 44100, 48000, 96000, 192000 };
+ bitRates = Range<int>(16000, 2688000);
+ maxChannels = 24;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS)) {
+ sampleRates = { 44100, 48000 };
+ bitRates = Range<int>(96000, 1524000);
+ maxChannels = 6;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_HD)) {
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mProfile) {
+ case DTS_HDProfileLBR:
+ sampleRates = { 22050, 24000, 44100, 48000 };
+ bitRates = Range<int>(32000, 768000);
+ break;
+ case DTS_HDProfileHRA:
+ case DTS_HDProfileMA:
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
+ mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ }
+ }
+ maxChannels = 8;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_UHD)) {
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mProfile) {
+ case DTS_UHDProfileP2:
+ sampleRates = { 48000 };
+ bitRates = Range<int>(96000, 768000);
+ maxChannels = 10;
+ break;
+ case DTS_UHDProfileP1:
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ maxChannels = 32;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
+ mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ maxChannels = 32;
+ }
+ }
+ } else {
+ ALOGW("Unsupported mediaType %s", mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNSUPPORTED;
+ }
+
+ // restrict ranges
+ if (!sampleRates.empty()) {
+ limitSampleRates(sampleRates);
+ } else if (sampleRateRange) {
+ std::vector<Range<int>> rateRanges = { sampleRateRange.value() };
+ limitSampleRates(rateRanges);
+ }
+
+ Range<int> channelRange = Range<int>(1, maxChannels);
+ std::vector<Range<int>> inputChannels = { channelRange };
+ applyLimits(inputChannels, bitRates);
+}
+
+void AudioCapabilities::applyLimits(
+ const std::vector<Range<int>> &inputChannels,
+ const std::optional<Range<int>> &bitRates) {
+ // clamp & make a local copy
+ std::vector<Range<int>> inputChannelsCopy(inputChannels.size());
+ for (int i = 0; i < inputChannels.size(); i++) {
+ int lower = inputChannels[i].clamp(1);
+ int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ inputChannelsCopy[i] = Range<int>(lower, upper);
+ }
+
+ // sort, intersect with existing, & save channel list
+ sortDistinctRanges(&inputChannelsCopy);
+ mInputChannelRanges = intersectSortedDistinctRanges(inputChannelsCopy, mInputChannelRanges);
+
+ if (bitRates) {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+}
+
+void AudioCapabilities::parseFromInfo(const sp<AMessage> &format) {
+ int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ std::vector<Range<int>> channels = { Range<int>(1, maxInputChannels) };
+ std::optional<Range<int>> bitRates = POSITIVE_INTEGERS;
+
+ AString rateAString;
+ if (format->findString("sample-rate-ranges", &rateAString)) {
+ std::vector<std::string> rateStrings = base::Split(std::string(rateAString.c_str()), ",");
+ std::vector<Range<int>> rateRanges;
+ for (std::string rateString : rateStrings) {
+ std::optional<Range<int>> rateRange = ParseIntRange(rateString);
+ if (!rateRange) {
+ continue;
+ }
+ rateRanges.push_back(rateRange.value());
+ }
+ limitSampleRates(rateRanges);
+ }
+
+ // we will prefer channel-ranges over max-channel-count
+ AString valueStr;
+ if (format->findString("channel-ranges", &valueStr)) {
+ std::vector<std::string> channelStrings = base::Split(std::string(valueStr.c_str()), ",");
+ std::vector<Range<int>> channelRanges;
+ for (std::string channelString : channelStrings) {
+ std::optional<Range<int>> channelRange = ParseIntRange(channelString);
+ if (!channelRange) {
+ continue;
+ }
+ channelRanges.push_back(channelRange.value());
+ }
+ channels = channelRanges;
+ } else if (format->findString("channel-range", &valueStr)) {
+ std::optional<Range<int>> oneRange = ParseIntRange(std::string(valueStr.c_str()));
+ if (oneRange) {
+ channels = { oneRange.value() };
+ }
+ } else if (format->findString("max-channel-count", &valueStr)) {
+ maxInputChannels = std::atoi(valueStr.c_str());
+ if (maxInputChannels == 0) {
+ channels = { Range<int>(0, 0) };
+ } else {
+ channels = { Range<int>(1, maxInputChannels) };
+ }
+ } else if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
+ maxInputChannels = 0;
+ channels = { Range<int>(0, 0) };
+ }
+
+ if (format->findString("bitrate-range", &valueStr)) {
+ std::optional<Range<int>> parsedBitrate = ParseIntRange(valueStr.c_str());
+ if (parsedBitrate) {
+ bitRates = bitRates.value().intersect(parsedBitrate.value());
+ }
+ }
+
+ applyLimits(channels, bitRates);
+}
+
+void AudioCapabilities::getDefaultFormat(sp<AMessage> &format) {
+ // report settings that have only a single choice
+ if (mBitrateRange.lower() == mBitrateRange.upper()) {
+ format->setInt32(KEY_BIT_RATE, mBitrateRange.lower());
+ }
+ if (getMaxInputChannelCount() == 1) {
+ // mono-only format
+ format->setInt32(KEY_CHANNEL_COUNT, 1);
+ }
+ if (!mSampleRates.empty() && mSampleRates.size() == 1) {
+ format->setInt32(KEY_SAMPLE_RATE, mSampleRates[0]);
+ }
+}
+
+bool AudioCapabilities::supportsFormat(const sp<AMessage> &format) {
+ int32_t sampleRate;
+ format->findInt32(KEY_SAMPLE_RATE, &sampleRate);
+ int32_t channels;
+ format->findInt32(KEY_CHANNEL_COUNT, &channels);
+
+ if (!supports(sampleRate, channels)) {
+ return false;
+ }
+
+ if (!CodecCapabilities::SupportsBitrate(mBitrateRange, format)) {
+ return false;
+ }
+
+ // nothing to do for:
+ // KEY_CHANNEL_MASK: codecs don't get this
+ // KEY_IS_ADTS: required feature for all AAC decoders
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/CodecCapabilities.cpp b/media/libmedia/CodecCapabilities.cpp
new file mode 100644
index 0000000..5bed1c4
--- /dev/null
+++ b/media/libmedia/CodecCapabilities.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CodecCapabilities"
+
+#include <utils/Log.h>
+#include <media/CodecCapabilities.h>
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+bool CodecCapabilities::SupportsBitrate(Range<int> bitrateRange,
+ const sp<AMessage> &format) {
+ // consider max bitrate over average bitrate for support
+ int32_t maxBitrate = 0;
+ format->findInt32(KEY_MAX_BIT_RATE, &maxBitrate);
+ int32_t bitrate = 0;
+ format->findInt32(KEY_BIT_RATE, &bitrate);
+
+ if (bitrate == 0) {
+ bitrate = maxBitrate;
+ } else if (maxBitrate != 0) {
+ bitrate = std::max(bitrate, maxBitrate);
+ }
+
+ if (bitrate > 0) {
+ return bitrateRange.contains(bitrate);
+ }
+
+ return true;
+}
+
+const std::string& CodecCapabilities::getMediaType() {
+ return mMediaType;
+}
+
+const std::vector<ProfileLevel>& CodecCapabilities::getProfileLevels() {
+ return mProfileLevels;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/CodecCapabilitiesUtils.cpp b/media/libmedia/CodecCapabilitiesUtils.cpp
new file mode 100644
index 0000000..edfc9be
--- /dev/null
+++ b/media/libmedia/CodecCapabilitiesUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CodecCapabilitiesUtils"
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <cmath>
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <media/CodecCapabilitiesUtils.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+
+namespace android {
+
+std::optional<Range<int>> ParseIntRange(const std::string &str) {
+ if (str.empty()) {
+ ALOGW("could not parse empty integer range");
+ return std::nullopt;
+ }
+ int lower, upper;
+ std::regex regex("([0-9]+)-([0-9]+)");
+ std::smatch match;
+ if (std::regex_match(str, match, regex)) {
+ lower = std::atoi(match[1].str().c_str());
+ upper = std::atoi(match[2].str().c_str());
+ } else if (std::atoi(str.c_str()) != 0) {
+ lower = upper = std::atoi(str.c_str());
+ } else {
+ ALOGW("could not parse integer range: %s", str.c_str());
+ return std::nullopt;
+ }
+ return std::make_optional<Range<int>>(lower, upper);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index c45c5c3..d5d1a09 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -18,8 +18,6 @@
#define LOG_TAG "MediaCodecInfo"
#include <utils/Log.h>
-#include <media/IOMX.h>
-
#include <media/MediaCodecInfo.h>
#include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libmedia/include/media/AudioCapabilities.h b/media/libmedia/include/media/AudioCapabilities.h
new file mode 100644
index 0000000..2bc3335
--- /dev/null
+++ b/media/libmedia/include/media/AudioCapabilities.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_CAPABILITIES_H_
+
+#define AUDIO_CAPABILITIES_H_
+
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <system/audio.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct AudioCapabilities {
+ /**
+ * Create AudioCapabilities.
+ */
+ static std::shared_ptr<AudioCapabilities> Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format);
+
+ /**
+ * Returns the range of supported bitrates in bits/second.
+ */
+ const Range<int>& getBitrateRange() const;
+
+ /**
+ * Returns the array of supported sample rates if the codec
+ * supports only discrete values. Otherwise, it returns an empty array.
+ * The array is sorted in ascending order.
+ */
+ const std::vector<int>& getSupportedSampleRates() const;
+
+ /**
+ * Returns the array of supported sample rate ranges. The
+ * array is sorted in ascending order, and the ranges are
+ * distinct.
+ */
+ const std::vector<Range<int>>& getSupportedSampleRateRanges() const;
+
+ /**
+ * Returns the maximum number of input channels supported.
+ * The returned value should be between 1 and 255.
+ *
+ * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
+ * for any number of input channels between 1 and this maximum value.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#S},
+ * the implied lower limit of 1 channel is no longer valid.
+ * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
+ * superseded by {@link #getInputChannelCountRanges},
+ * which returns an array of ranges of channels.
+ * The {@link #getMaxInputChannelCount} method will return the highest value
+ * in the ranges returned by {@link #getInputChannelCountRanges}
+ */
+ int getMaxInputChannelCount() const;
+
+ /**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * This returns the lowest channel count in the ranges returned by
+ * {@link #getInputChannelCountRanges}.
+ */
+ int getMinInputChannelCount() const;
+
+ /**
+ * Returns an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ *
+ * This supersedes the {@link #getMaxInputChannelCount} method.
+ *
+ * For many codecs, this will be a single range [1..N], for some N.
+ *
+ * The returned array cannot be empty.
+ */
+ const std::vector<Range<int>>& getInputChannelCountRanges() const;
+
+ /**
+ * Query whether the sample rate is supported by the codec.
+ */
+ bool isSampleRateSupported(int sampleRate);
+
+ /* For internal use only. Not exposed as a public API */
+ void getDefaultFormat(sp<AMessage> &format);
+
+ /* For internal use only. Not exposed as a public API */
+ bool supportsFormat(const sp<AMessage> &format);
+
+private:
+ static constexpr int MAX_INPUT_CHANNEL_COUNT = 30;
+ static constexpr uint32_t MAX_NUM_CHANNELS = FCC_LIMIT;
+
+ int mError;
+ std::string mMediaType;
+ std::vector<ProfileLevel> mProfileLevels;
+
+ Range<int> mBitrateRange;
+
+ std::vector<int> mSampleRates;
+ std::vector<Range<int>> mSampleRateRanges;
+ std::vector<Range<int>> mInputChannelRanges;
+
+ /* no public constructor */
+ AudioCapabilities() {}
+ void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format);
+ void initWithPlatformLimits();
+ bool supports(int sampleRate, int inputChannels);
+ void limitSampleRates(std::vector<int> rates);
+ void createDiscreteSampleRates();
+ void limitSampleRates(std::vector<Range<int>> rateRanges);
+ void applyLevelLimits();
+ void applyLimits(const std::vector<Range<int>> &inputChannels,
+ const std::optional<Range<int>> &bitRates);
+ void parseFromInfo(const sp<AMessage> &format);
+
+ friend struct CodecCapabilities;
+};
+
+} // namespace android
+
+#endif // AUDIO_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/include/media/CodecCapabilities.h b/media/libmedia/include/media/CodecCapabilities.h
new file mode 100644
index 0000000..9d1c4ea
--- /dev/null
+++ b/media/libmedia/include/media/CodecCapabilities.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC_CAPABILITIES_H_
+
+#define CODEC_CAPABILITIES_H_
+
+#include <media/AudioCapabilities.h>
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct CodecCapabilities {
+
+ static bool SupportsBitrate(Range<int> bitrateRange,
+ const sp<AMessage> &format);
+
+ /**
+ * Returns the media type for which this codec-capability object was created.
+ */
+ const std::string& getMediaType();
+
+ /**
+ * Returns the supported profile levels.
+ */
+ const std::vector<ProfileLevel>& getProfileLevels();
+
+private:
+ std::string mMediaType;
+ std::vector<ProfileLevel> mProfileLevels;
+
+ std::shared_ptr<AudioCapabilities> mAudioCaps;
+};
+
+} // namespace android
+
+#endif // CODEC_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/include/media/CodecCapabilitiesUtils.h b/media/libmedia/include/media/CodecCapabilitiesUtils.h
new file mode 100644
index 0000000..2bf822a
--- /dev/null
+++ b/media/libmedia/include/media/CodecCapabilitiesUtils.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC_CAPABILITIES__UTILS_H_
+
+#define CODEC_CAPABILITIES__UTILS_H_
+
+#include <algorithm>
+#include <cmath>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+
+namespace android {
+
+struct ProfileLevel {
+ uint32_t mProfile;
+ uint32_t mLevel;
+ bool operator <(const ProfileLevel &o) const {
+ return mProfile < o.mProfile || (mProfile == o.mProfile && mLevel < o.mLevel);
+ }
+};
+
+/**
+ * Immutable class for describing the range of two numeric values.
+ *
+ * To make it immutable, all data are private and all functions are const.
+ *
+ * From frameworks/base/core/java/android/util/Range.java
+ */
+template<typename T>
+struct Range {
+ Range() : lower_(), upper_() {}
+
+ Range(T l, T u) : lower_(l), upper_(u) {}
+
+ constexpr bool empty() const { return lower_ > upper_; }
+
+ T lower() const { return lower_; }
+
+ T upper() const { return upper_; }
+
+ // Check if a value is in the range.
+ bool contains(T value) const {
+ return lower_ <= value && upper_ >= value;
+ }
+
+ bool contains(Range<T> range) const {
+ return (range.lower_ >= lower_) && (range.upper_ <= upper_);
+ }
+
+ // Clamp a value in the range
+ T clamp(T value) const{
+ if (value < lower_) {
+ return lower_;
+ } else if (value > upper_) {
+ return upper_;
+ } else {
+ return value;
+ }
+ }
+
+ // Return the intersected range
+ Range<T> intersect(Range<T> range) const {
+ if (lower_ >= range.lower() && range.upper() >= upper_) {
+ // range includes this
+ return *this;
+ } else if (range.lower() >= lower_ && range.upper() <= upper_) {
+ // this includes range
+ return range;
+ } else {
+ // if ranges are disjoint returns an empty Range(lower > upper)
+ Range<T> result = Range<T>(std::max(lower_, range.lower_),
+ std::min(upper_, range.upper_));
+ if (result.empty()) {
+ ALOGE("Failed to intersect 2 ranges as they are disjoint");
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Returns the intersection of this range and the inclusive range
+ * specified by {@code [lower, upper]}.
+ * <p>
+ * See {@link #intersect(Range)} for more details.</p>
+ *
+ * @param lower a non-{@code null} {@code T} reference
+ * @param upper a non-{@code null} {@code T} reference
+ * @return the intersection of this range and the other range
+ *
+ * @throws NullPointerException if {@code lower} or {@code upper} was {@code null}
+ * @throws IllegalArgumentException if the ranges are disjoint.
+ */
+ Range<T> intersect(T lower, T upper) {
+ return Range(std::max(lower_, lower), std::min(upper_, upper));
+ }
+
+private:
+ T lower_;
+ T upper_;
+};
+
+static const Range<int> POSITIVE_INTEGERS = Range<int>(1, INT_MAX);
+
+// found stuff that is not supported by framework (=> this should not happen)
+constexpr int ERROR_CAPABILITIES_UNRECOGNIZED = (1 << 0);
+// found profile/level for which we don't have capability estimates
+constexpr int ERROR_CAPABILITIES_UNSUPPORTED = (1 << 1);
+// have not found any profile/level for which we don't have capability estimate
+// constexpr int ERROR_NONE_SUPPORTED = (1 << 2);
+
+/**
+ * Sorts distinct (non-intersecting) range array in ascending order.
+ * From frameworks/base/media/java/android/media/Utils.java
+ */
+template<typename T>
+void sortDistinctRanges(std::vector<Range<T>> *ranges) {
+ std::sort(ranges->begin(), ranges->end(),
+ [](Range<T> r1, Range<T> r2) {
+ if (r1.upper() < r2.lower()) {
+ return true;
+ } else if (r1.lower() > r2.upper()) {
+ return false;
+ } else {
+ ALOGE("sample rate ranges must be distinct.");
+ return false;
+ }
+ });
+}
+
+/**
+ * Returns the intersection of two sets of non-intersecting ranges
+ * From frameworks/base/media/java/android/media/Utils.java
+ * @param one a sorted set of non-intersecting ranges in ascending order
+ * @param another another sorted set of non-intersecting ranges in ascending order
+ * @return the intersection of the two sets, sorted in ascending order
+ */
+template<typename T>
+std::vector<Range<T>> intersectSortedDistinctRanges(
+ const std::vector<Range<T>> &one, const std::vector<Range<T>> &another) {
+ std::vector<Range<T>> result;
+ int ix = 0;
+ for (Range<T> range : another) {
+ while (ix < one.size() && one[ix].upper() < range.lower()) {
+ ++ix;
+ }
+ while (ix < one.size() && one[ix].upper() < range.upper()) {
+ result.push_back(range.intersect(one[ix]));
+ ++ix;
+ }
+ if (ix == one.size()) {
+ break;
+ }
+ if (one[ix].lower() <= range.upper()) {
+ result.push_back(range.intersect(one[ix]));
+ }
+ }
+ return result;
+}
+
+// parse string into int range
+std::optional<Range<int>> ParseIntRange(const std::string &str);
+
+} // namespace android
+
+#endif // CODEC_CAPABILITIES__UTILS_H_
\ No newline at end of file
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index 88a2dc4..72aca98 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -20,6 +20,8 @@
#include <android-base/macros.h>
#include <binder/Parcel.h>
+#include <media/CodecCapabilities.h>
+#include <media/CodecCapabilitiesUtils.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
@@ -43,13 +45,10 @@
struct MediaCodecListWriter;
struct MediaCodecInfo : public RefBase {
- struct ProfileLevel {
- uint32_t mProfile;
- uint32_t mLevel;
- bool operator <(const ProfileLevel &o) const {
- return mProfile < o.mProfile || (mProfile == o.mProfile && mLevel < o.mLevel);
- }
- };
+
+ // Moved to CodecCapabilitiesUtils.h
+ // Map MediaCodecInfo::ProfileLevel to android::ProfileLevel to maintain compatibility.
+ typedef ::android::ProfileLevel ProfileLevel;
struct CapabilitiesWriter;
diff --git a/media/libmedia/tests/codeccapabilities/Android.bp b/media/libmedia/tests/codeccapabilities/Android.bp
new file mode 100644
index 0000000..79eb71a
--- /dev/null
+++ b/media/libmedia/tests/codeccapabilities/Android.bp
@@ -0,0 +1,36 @@
+cc_test {
+ name: "CodecCapabilitiesTest",
+ team: "trendy_team_media_codec_framework",
+
+ test_suites: [
+ "general-tests",
+ ],
+ gtest: true,
+
+ srcs: [
+ "CodecCapabilitiesTest.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libmedia_codeclist", // available >= R
+ "libmedia_codeclist_capabilities",
+ "libstagefright",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
new file mode 100644
index 0000000..89c9739
--- /dev/null
+++ b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CodecCapabilitiesTest"
+
+#include <utils/Log.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+
+#include <media/CodecCapabilities.h>
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/MediaCodecInfo.h>
+
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+
+using namespace android;
+
+class AudioCapsAacTest : public testing::Test {
+protected:
+ AudioCapsAacTest() {
+ std::string mediaType = MIMETYPE_AUDIO_AAC;
+
+ sp<AMessage> details = new AMessage;
+ details->setString("bitrate-range", "8000-960000");
+ details->setString("max-channel-count", "8");
+ details->setString("sample-rate-ranges",
+ "7350,8000,11025,12000,16000,22050,24000,32000,44100,48000");
+
+ std::vector<ProfileLevel> profileLevel{
+ ProfileLevel(2, 0),
+ ProfileLevel(5, 0),
+ ProfileLevel(29, 0),
+ ProfileLevel(23, 0),
+ ProfileLevel(39, 0),
+ ProfileLevel(20, 0),
+ ProfileLevel(42, 0),
+ };
+
+ audioCaps = AudioCapabilities::Create(mediaType, profileLevel, details);
+ }
+
+ std::shared_ptr<AudioCapabilities> audioCaps;
+};
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_Bitrate) {
+ const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+ EXPECT_EQ(bitrateRange.lower(), 8000) << "bitrate range1 does not match. lower: "
+ << bitrateRange.lower();
+ EXPECT_EQ(bitrateRange.upper(), 510000) << "bitrate range1 does not match. upper: "
+ << bitrateRange.upper();
+}
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_InputChannelCount) {
+ int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ EXPECT_EQ(maxInputChannelCount, 8);
+ int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ EXPECT_EQ(minInputChannelCount, 1);
+}
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_SupportedSampleRates) {
+ const std::vector<int>& sampleRates = audioCaps->getSupportedSampleRates();
+ EXPECT_EQ(sampleRates, std::vector<int>({7350, 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000}));
+
+ EXPECT_FALSE(audioCaps->isSampleRateSupported(6000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(8000))
+ << "isSampleRateSupported returned false for supported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(12000))
+ << "isSampleRateSupported returned false for supported sample rate";
+ EXPECT_FALSE(audioCaps->isSampleRateSupported(44000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(48000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+}
+
+class AudioCapsRawTest : public testing::Test {
+protected:
+ AudioCapsRawTest() {
+ std::string mediaType = MIMETYPE_AUDIO_RAW;
+
+ sp<AMessage> details = new AMessage;
+ details->setString("bitrate-range", "1-10000000");
+ details->setString("channel-ranges", "1,2,3,4,5,6,7,8,9,10,11,12");
+ details->setString("sample-rate-ranges", "8000-192000");
+
+ std::vector<ProfileLevel> profileLevel;
+
+ audioCaps = AudioCapabilities::Create(mediaType, profileLevel, details);
+ }
+
+ std::shared_ptr<AudioCapabilities> audioCaps;
+};
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_Bitrate) {
+ const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+ EXPECT_EQ(bitrateRange.lower(), 1);
+ EXPECT_EQ(bitrateRange.upper(), 10000000);
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCount) {
+ int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ EXPECT_EQ(maxInputChannelCount, 12);
+ int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ EXPECT_EQ(minInputChannelCount, 1);
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCountRanges) {
+ const std::vector<Range<int>>& inputChannelCountRanges
+ = audioCaps->getInputChannelCountRanges();
+ std::vector<Range<int>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
+ {6,6}, {7,7}, {8,8}, {9,9}, {10,10}, {11,11}, {12,12}});
+ ASSERT_EQ(inputChannelCountRanges.size(), expectedOutput.size());
+ for (int i = 0; i < inputChannelCountRanges.size(); i++) {
+ EXPECT_EQ(inputChannelCountRanges.at(i).lower(), expectedOutput.at(i).lower());
+ EXPECT_EQ(inputChannelCountRanges.at(i).upper(), expectedOutput.at(i).upper());
+ }
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_SupportedSampleRates) {
+ const std::vector<Range<int>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
+ EXPECT_EQ(sampleRateRanges.size(), 1);
+ EXPECT_EQ(sampleRateRanges.at(0).lower(), 8000);
+ EXPECT_EQ(sampleRateRanges.at(0).upper(), 192000);
+
+ EXPECT_EQ(audioCaps->isSampleRateSupported(7000), false);
+ EXPECT_EQ(audioCaps->isSampleRateSupported(10000), true);
+ EXPECT_EQ(audioCaps->isSampleRateSupported(193000), false);
+}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index f0e1b9e..dce6ba8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1334,10 +1334,10 @@
// cause out-of-memory due to large input buffer size. And audio recording
// probably doesn't make sense in the scenario, since the slow-down factor
// is probably huge (eg. mSampleRate=48K, mCaptureFps=240, mFrameRate=1).
- const static int32_t SAMPLE_RATE_HZ_MAX = 192000;
+ const static int32_t kSampleRateHzMax = 192000;
sourceSampleRate =
(mSampleRate * mCaptureFps + mFrameRate / 2) / mFrameRate;
- if (sourceSampleRate < mSampleRate || sourceSampleRate > SAMPLE_RATE_HZ_MAX) {
+ if (sourceSampleRate < mSampleRate || sourceSampleRate > kSampleRateHzMax) {
ALOGE("source sample rate out of range! "
"(mSampleRate %d, mCaptureFps %.2f, mFrameRate %d",
mSampleRate, mCaptureFps, mFrameRate);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3d4e955..3c8b809 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -102,6 +102,10 @@
switch (pcmEncoding) {
case kAudioEncodingPcmFloat:
return AUDIO_FORMAT_PCM_FLOAT;
+ case kAudioEncodingPcm32bit:
+ return AUDIO_FORMAT_PCM_32_BIT;
+ case kAudioEncodingPcm24bitPacked:
+ return AUDIO_FORMAT_PCM_24_BIT_PACKED;
case kAudioEncodingPcm16bit:
return AUDIO_FORMAT_PCM_16_BIT;
case kAudioEncodingPcm8bit:
@@ -2025,7 +2029,12 @@
if (offloadingAudio()) {
AString mime;
CHECK(format->findString("mime", &mime));
- status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str());
+ status_t err = OK;
+ if (audioFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ // If there is probably no pcm-encoding in the format message, try to get the format by
+ // its mimetype.
+ err = mapMimeToAudioFormat(audioFormat, mime.c_str());
+ }
if (err != OK) {
ALOGE("Couldn't map mime \"%s\" to a valid "
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index ad42813..16e267b 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -50,7 +50,7 @@
using namespace hardware::cas::native::V1_0;
using DrmBufferType = hardware::drm::V1_0::BufferType;
using BufferInfo = ACodecBufferChannel::BufferInfo;
-using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
+using BufferInfoIterator = std::vector<BufferInfo>::const_iterator;
ACodecBufferChannel::~ACodecBufferChannel() {
if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
@@ -59,7 +59,7 @@
}
static BufferInfoIterator findClientBuffer(
- const std::shared_ptr<const std::vector<const BufferInfo>> &array,
+ const std::shared_ptr<const std::vector<BufferInfo>> &array,
const sp<MediaCodecBuffer> &buffer) {
return std::find_if(
array->begin(), array->end(),
@@ -67,7 +67,7 @@
}
static BufferInfoIterator findBufferId(
- const std::shared_ptr<const std::vector<const BufferInfo>> &array,
+ const std::shared_ptr<const std::vector<BufferInfo>> &array,
IOMX::buffer_id bufferId) {
return std::find_if(
array->begin(), array->end(),
@@ -97,7 +97,7 @@
}
status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mInputBuffers));
BufferInfoIterator it = findClientBuffer(array, buffer);
if (it == array->end()) {
@@ -138,7 +138,7 @@
if (!hasCryptoOrDescrambler() || mDealer == nullptr) {
return -ENOSYS;
}
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mInputBuffers));
BufferInfoIterator it = findClientBuffer(array, buffer);
if (it == array->end()) {
@@ -352,7 +352,7 @@
size_t numSubSamples,
const sp<MediaCodecBuffer> &buffer,
AString* errorDetailMsg) {
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mInputBuffers));
BufferInfoIterator it = findClientBuffer(array, buffer);
if (it == array->end()) {
@@ -473,7 +473,7 @@
status_t ACodecBufferChannel::renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mOutputBuffers));
BufferInfoIterator it = findClientBuffer(array, buffer);
if (it == array->end()) {
@@ -495,7 +495,7 @@
}
status_t ACodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mInputBuffers));
bool input = true;
BufferInfoIterator it = findClientBuffer(array, buffer);
@@ -517,7 +517,7 @@
}
void ACodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
- std::shared_ptr<const std::vector<const BufferInfo>> inputBuffers(
+ std::shared_ptr<const std::vector<BufferInfo>> inputBuffers(
std::atomic_load(&mInputBuffers));
array->clear();
for (const BufferInfo &elem : *inputBuffers) {
@@ -526,7 +526,7 @@
}
void ACodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
- std::shared_ptr<const std::vector<const BufferInfo>> outputBuffers(
+ std::shared_ptr<const std::vector<BufferInfo>> outputBuffers(
std::atomic_load(&mOutputBuffers));
array->clear();
for (const BufferInfo &elem : *outputBuffers) {
@@ -583,7 +583,7 @@
mDecryptDestination = mDealer->allocate(destinationBufferSize);
}
}
- std::vector<const BufferInfo> inputBuffers;
+ std::vector<BufferInfo> inputBuffers;
for (const BufferAndId &elem : array) {
sp<IMemory> sharedEncryptedBuffer;
if (hasCryptoOrDescrambler()) {
@@ -593,22 +593,22 @@
}
std::atomic_store(
&mInputBuffers,
- std::make_shared<const std::vector<const BufferInfo>>(inputBuffers));
+ std::make_shared<const std::vector<BufferInfo>>(inputBuffers));
}
void ACodecBufferChannel::setOutputBufferArray(const std::vector<BufferAndId> &array) {
- std::vector<const BufferInfo> outputBuffers;
+ std::vector<BufferInfo> outputBuffers;
for (const BufferAndId &elem : array) {
outputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, nullptr);
}
std::atomic_store(
&mOutputBuffers,
- std::make_shared<const std::vector<const BufferInfo>>(outputBuffers));
+ std::make_shared<const std::vector<BufferInfo>>(outputBuffers));
}
void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {
ALOGV("fillThisBuffer #%d", bufferId);
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mInputBuffers));
BufferInfoIterator it = findBufferId(array, bufferId);
@@ -629,7 +629,7 @@
IOMX::buffer_id bufferId,
OMX_U32 omxFlags) {
ALOGV("drainThisBuffer #%d", bufferId);
- std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::shared_ptr<const std::vector<BufferInfo>> array(
std::atomic_load(&mOutputBuffers));
BufferInfoIterator it = findBufferId(array, bufferId);
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 02a5e6c..26b8d0c 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -154,8 +154,9 @@
clientAttribution.pid = clientPid;
clientAttribution.uid = clientUid;
clientAttribution.deviceId = kDefaultDeviceId;
+ clientAttribution.packageName = clientName;
- mCamera = Camera::connect(cameraId, clientName, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
+ mCamera = Camera::connect(cameraId, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
/*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_NONE,
/*forceSlowJpegMode*/false, clientAttribution);
if (mCamera == 0) return -EBUSY;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e918b5e..15188b0 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -72,6 +72,9 @@
static const int64_t kMaxMetadataSize = 0x4000000LL; // 64MB max per-frame metadata size
static const int64_t kMaxCttsOffsetTimeUs = 30 * 60 * 1000000LL; // 30 minutes
static const size_t kESDSScratchBufferSize = 10; // kMaxAtomSize in Mpeg4Extractor 64MB
+// Allow up to 100 milli second, which is safely above the maximum delay observed in manual testing
+// between posting from setNextFd and handling it
+static const int64_t kFdCondWaitTimeoutNs = 100000000;
static const char kMetaKey_Version[] = "com.android.version";
static const char kMetaKey_Manufacturer[] = "com.android.manufacturer";
@@ -1262,9 +1265,13 @@
return OK;
}
+ // Wait for the signal only if the new file is not available.
if (mNextFd == -1) {
- ALOGW("No FileDescriptor for next recording");
- return INVALID_OPERATION;
+ status_t res = mFdCond.waitRelative(mLock, kFdCondWaitTimeoutNs);
+ if (res != OK) {
+ ALOGW("No FileDescriptor for next recording");
+ return INVALID_OPERATION;
+ }
}
mSwitchPending = true;
@@ -2433,6 +2440,7 @@
return INVALID_OPERATION;
}
mNextFd = dup(fd);
+ mFdCond.signal();
return OK;
}
@@ -4886,8 +4894,15 @@
int32_t mediaTime = (mFirstSampleStartOffsetUs * mTimeScale + 5E5) / 1E6;
int32_t firstSampleOffsetTicks =
(mFirstSampleStartOffsetUs * mvhdTimeScale + 5E5) / 1E6;
- // samples before 0 don't count in for duration, hence subtract firstSampleOffsetTicks.
- addOneElstTableEntry(tkhdDurationTicks - firstSampleOffsetTicks, mediaTime, 1, 0);
+ if (tkhdDurationTicks >= firstSampleOffsetTicks) {
+ // samples before 0 don't count in for duration, hence subtract
+ // firstSampleOffsetTicks.
+ addOneElstTableEntry(tkhdDurationTicks - firstSampleOffsetTicks, mediaTime, 1, 0);
+ } else {
+ ALOGW("The track header duration %" PRId64
+ " is smaller than the first sample offset %" PRId64,
+ mTrackDurationUs, mFirstSampleStartOffsetUs);
+ }
} else {
// Track starting at zero.
ALOGV("No edit list entry required for this track");
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 0401e82..b380ade 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -487,7 +487,7 @@
.id = getId(mClient),
.name = mCodecName,
.importance = mImportance};
- return std::move(clientInfo);
+ return clientInfo;
}
private:
@@ -840,7 +840,7 @@
const sp<AMessage> mNotify;
};
-class OnBufferReleasedListener : public ::android::BnProducerListener{
+class OnBufferReleasedListener : public ::android::SurfaceListener{
private:
uint32_t mGeneration;
std::weak_ptr<BufferChannelBase> mBufferChannel;
@@ -852,6 +852,13 @@
}
}
+ void notifyBufferAttached() {
+ auto p = mBufferChannel.lock();
+ if (p) {
+ p->onBufferAttachedToOutputSurface(mGeneration);
+ }
+ }
+
public:
explicit OnBufferReleasedListener(
uint32_t generation,
@@ -864,11 +871,22 @@
notifyBufferReleased();
}
+ void onBuffersDiscarded([[maybe_unused]] const std::vector<sp<GraphicBuffer>>& buffers)
+ override { }
+
void onBufferDetached([[maybe_unused]] int slot) override {
notifyBufferReleased();
}
bool needsReleaseNotify() override { return true; }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ void onBufferAttached() override {
+ notifyBufferAttached();
+ }
+
+ bool needsAttachNotify() override { return true; }
+#endif
};
class BufferCallback : public CodecBase::BufferCallback {
@@ -6722,7 +6740,7 @@
// to this surface after disconnect/connect, and those free frames would inherit the new
// generation number. Disconnecting after setting a unique generation prevents this.
nativeWindowDisconnect(surface.get(), "connectToSurface(reconnect)");
- sp<IProducerListener> listener =
+ sp<SurfaceListener> listener =
new OnBufferReleasedListener(*generation, mBufferChannel);
err = surfaceConnectWithListener(
surface, listener, "connectToSurface(reconnect-with-listener)");
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
index 714e312..74432a6 100644
--- a/media/libstagefright/SurfaceUtils.cpp
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -335,7 +335,7 @@
}
status_t surfaceConnectWithListener(
- const sp<Surface> &surface, sp<IProducerListener> listener, const char *reason) {
+ const sp<Surface> &surface, sp<SurfaceListener> listener, const char *reason) {
ALOGD("connecting to surface %p, reason %s", surface.get(), reason);
status_t err = surface->connect(NATIVE_WINDOW_API_MEDIA, listener);
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index 946d533..46a5183 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -155,8 +155,8 @@
// obtained. Inside BufferInfo, mBufferId and mSharedEncryptedBuffer are
// immutable objects. We write internal states of mClient/CodecBuffer when
// the caller has given up the reference, so that access is also safe.
- std::shared_ptr<const std::vector<const BufferInfo>> mInputBuffers;
- std::shared_ptr<const std::vector<const BufferInfo>> mOutputBuffers;
+ std::shared_ptr<const std::vector<BufferInfo>> mInputBuffers;
+ std::shared_ptr<const std::vector<BufferInfo>> mOutputBuffers;
sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index fcd17b9..f42e315 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -236,7 +236,7 @@
// Start of members protected by mBatchLock
std::deque<uint32_t> mInflightBatchSizes;
std::vector<native_handle_t*> mInflightReturnedHandles;
- std::vector<const sp<IMemory>> mInflightReturnedMemorys;
+ std::vector<sp<IMemory>> mInflightReturnedMemorys;
// End of members protected by mBatchLock
void releaseQueuedFrames();
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index bffb294..c6087b0 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -517,6 +517,15 @@
};
/**
+ * Notify a buffer is attached to output surface.
+ *
+ * @param generation MediaCodec's surface specifier
+ */
+ virtual void onBufferAttachedToOutputSurface(uint32_t /*generation*/) {
+ // default: no-op
+ };
+
+ /**
* Discard a buffer to the underlying CodecBase object.
*
* TODO: remove once this operation can be handled by just clearing the
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 054a4b8..ee75129 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -144,6 +144,7 @@
std::mutex mFallocMutex;
bool mPreAllocFirstTime; // Pre-allocate space for file and track headers only once per file.
uint64_t mPrevAllTracksTotalMetaDataSizeEstimate;
+ Condition mFdCond;
List<Track *> mTracks;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 4e4aa75..b1cf665 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -562,6 +562,30 @@
}
}
+inline constexpr int32_t DTS_HDProfileHRA = 0x1;
+inline constexpr int32_t DTS_HDProfileLBR = 0x2;
+inline constexpr int32_t DTS_HDProfileMA = 0x4;
+
+inline static const char *asString_Dts_HDProfile(int32_t i, const char *def = "??") {
+ switch (i) {
+ case DTS_HDProfileHRA: return "HRA";
+ case DTS_HDProfileLBR: return "LBR";
+ case DTS_HDProfileMA: return "MA";
+ default: return def;
+ }
+}
+
+inline constexpr int32_t DTS_UHDProfileP1 = 0x1;
+inline constexpr int32_t DTS_UHDProfileP2 = 0x2;
+
+inline static const char *asString_Dts_UHDProfile(int32_t i, const char *def = "??") {
+ switch (i) {
+ case DTS_UHDProfileP1: return "P1";
+ case DTS_UHDProfileP2: return "P2";
+ default: return def;
+ }
+}
+
inline constexpr int32_t BITRATE_MODE_CBR = 2;
inline constexpr int32_t BITRATE_MODE_CBR_FD = 3;
inline constexpr int32_t BITRATE_MODE_CQ = 0;
@@ -729,8 +753,13 @@
inline constexpr char MIMETYPE_AUDIO_FLAC[] = "audio/flac";
inline constexpr char MIMETYPE_AUDIO_MSGSM[] = "audio/gsm";
inline constexpr char MIMETYPE_AUDIO_AC3[] = "audio/ac3";
+inline constexpr char MIMETYPE_AUDIO_AC4[] = "audio/ac4";
inline constexpr char MIMETYPE_AUDIO_EAC3[] = "audio/eac3";
+inline constexpr char MIMETYPE_AUDIO_EAC3_JOC[] = "audio/eac3-joc";
inline constexpr char MIMETYPE_AUDIO_SCRAMBLED[] = "audio/scrambled";
+inline constexpr char MIMETYPE_AUDIO_DTS[] = "audio/vnd.dts";
+inline constexpr char MIMETYPE_AUDIO_DTS_HD[] = "audio/vnd.dts.hd";
+inline constexpr char MIMETYPE_AUDIO_DTS_UHD[] = "audio/vnd.dts.uhd";
inline constexpr char MIMETYPE_IMAGE_ANDROID_HEIC[] = "image/vnd.android.heic";
diff --git a/media/libstagefright/include/media/stagefright/SurfaceUtils.h b/media/libstagefright/include/media/stagefright/SurfaceUtils.h
index eccb413..882a5ab 100644
--- a/media/libstagefright/include/media/stagefright/SurfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/SurfaceUtils.h
@@ -27,7 +27,7 @@
namespace android {
struct HDRStaticInfo;
-class IProducerListener;
+class SurfaceListener;
/**
* Configures |nativeWindow| for given |width|x|height|, pixel |format|, |rotation| and |usage|.
@@ -45,7 +45,7 @@
status_t nativeWindowConnect(ANativeWindow *surface, const char *reason);
status_t nativeWindowDisconnect(ANativeWindow *surface, const char *reason);
status_t surfaceConnectWithListener(const sp<Surface> &surface,
- sp<IProducerListener> listener, const char *reason);
+ sp<SurfaceListener> listener, const char *reason);
/**
* Disable buffer dropping behavior of BufferQueue if target sdk of application
diff --git a/media/libstagefright/renderfright/gl/ProgramCache.cpp b/media/libstagefright/renderfright/gl/ProgramCache.cpp
index 350f0b7..ad6dd03 100644
--- a/media/libstagefright/renderfright/gl/ProgramCache.cpp
+++ b/media/libstagefright/renderfright/gl/ProgramCache.cpp
@@ -683,7 +683,7 @@
fs << "uniform mat4 inputTransformMatrix;";
fs << R"__SHADER__(
highp vec3 InputTransform(const highp vec3 color) {
- return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ return vec3(inputTransformMatrix * vec4(color, 1.0));
}
)__SHADER__";
} else {
diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp
index ca862b0..151ce7c 100644
--- a/media/libstagefright/webm/WebmWriter.cpp
+++ b/media/libstagefright/webm/WebmWriter.cpp
@@ -290,7 +290,7 @@
// Max file duration limit is set
if (mMaxFileDurationLimitUs != 0) {
if (bitRate > 0) {
- int64_t size2 = ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000);
+ int64_t size2 = ((mMaxFileDurationLimitUs / 1000) * bitRate * 6) / 8000000;
if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
// When both file size and duration limits are set,
// we use the smaller limit of the two.
diff --git a/media/module/bufferpool/2.0/AccessorImpl.cpp b/media/module/bufferpool/2.0/AccessorImpl.cpp
index b9483bf..3d7f0c7 100644
--- a/media/module/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/module/bufferpool/2.0/AccessorImpl.cpp
@@ -923,7 +923,7 @@
std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
std::mutex &mutex,
std::condition_variable &cv) {
- std::list<const std::weak_ptr<Accessor::Impl>> evictList;
+ std::list<std::weak_ptr<Accessor::Impl>> evictList;
while (true) {
int expired = 0;
int evicted = 0;
diff --git a/media/module/codecs/amrnb/common/include/abs_s.h b/media/module/codecs/amrnb/common/include/abs_s.h
deleted file mode 100644
index e92eaf4..0000000
--- a/media/module/codecs/amrnb/common/include/abs_s.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Pathname: ./gsm-amr/c/include/abs_s.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for abs_s function.
-
- Description: Updated template to make it build for Symbian.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the abs_s function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef ABS_S_H
-#define ABS_S_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
- Word16 abs_s(Word16 var1);
-
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ABS_S_H */
-
-
diff --git a/media/module/codecs/amrnb/common/include/basic_op_c_equivalent.h b/media/module/codecs/amrnb/common/include/basic_op_c_equivalent.h
index 8f0867a..8817621 100644
--- a/media/module/codecs/amrnb/common/include/basic_op_c_equivalent.h
+++ b/media/module/codecs/amrnb/common/include/basic_op_c_equivalent.h
@@ -120,15 +120,11 @@
{
Word32 L_sum;
- L_sum = L_var1 + L_var2;
-
- if ((L_var1 ^ L_var2) >= 0)
+ if (__builtin_add_overflow(L_var1, L_var2, &L_sum))
{
- if ((L_sum ^ L_var1) < 0)
- {
- L_sum = (L_var1 < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
+ // saturating...
+ L_sum = (L_var1 < 0) ? MIN_32 : MAX_32;
+ *pOverflow = 1;
}
return (L_sum);
@@ -160,15 +156,11 @@
{
Word32 L_diff;
- L_diff = L_var1 - L_var2;
-
- if ((L_var1 ^ L_var2) < 0)
+ if (__builtin_sub_overflow(L_var1, L_var2, &L_diff))
{
- if ((L_diff ^ L_var1) & MIN_32)
- {
- L_diff = (L_var1 < 0L) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
+ // saturating...
+ L_diff = (L_var1 < 0L) ? MIN_32 : MAX_32;
+ *pOverflow = 1;
}
return (L_diff);
@@ -204,16 +196,12 @@
result = (Word32) var1 * var2;
if (result != (Word32) 0x40000000L)
{
- L_sum = (result << 1) + L_var3;
-
/* Check if L_sum and L_var_3 share the same sign */
- if ((L_var3 ^ result) > 0)
+ if (__builtin_add_overflow((result << 1), L_var3, &L_sum))
{
- if ((L_sum ^ L_var3) < 0)
- {
- L_sum = (L_var3 < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
+ // saturating...
+ L_sum = (L_var3 < 0) ? MIN_32 : MAX_32;
+ *pOverflow = 1;
}
}
else
@@ -345,14 +333,10 @@
product32 = ((Word32) L_var1_hi * L_var2_lo) >> 15;
/* L_product = L_mac (L_product, result, 1, pOverflow); */
- L_sum = L_product + (product32 << 1);
-
- if ((L_product ^ product32) > 0)
+ if (__builtin_add_overflow(L_product, (product32 << 1), &L_sum))
{
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- }
+ // saturating...
+ L_sum = (L_product < 0) ? MIN_32 : MAX_32;
}
L_product = L_sum;
@@ -361,14 +345,10 @@
product32 = ((Word32) L_var1_lo * L_var2_hi) >> 15;
/* L_product = L_mac (L_product, result, 1, pOverflow); */
- L_sum = L_product + (product32 << 1);
-
- if ((L_product ^ product32) > 0)
+ if (__builtin_add_overflow(L_product, (product32 << 1), &L_sum))
{
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- }
+ // saturating...
+ L_sum = (L_product < 0) ? MIN_32 : MAX_32;
}
return (L_sum);
}
@@ -416,15 +396,11 @@
result = ((Word32)L_var1_lo * var2) >> 15;
- L_sum = L_product + (result << 1);
-
- if ((L_product ^ result) > 0)
+ if (__builtin_add_overflow(L_product, (result << 1), &L_sum))
{
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
+ // saturating...
+ L_sum = (L_product < 0) ? MIN_32 : MAX_32;
+ *pOverflow = 1;
}
return (L_sum);
diff --git a/media/module/codecs/amrnb/common/include/l_add.h b/media/module/codecs/amrnb/common/include/l_add.h
deleted file mode 100644
index 136b914..0000000
--- a/media/module/codecs/amrnb/common/include/l_add.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/l_add.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_add function.
-
- Description: Changed function prototype declaration. A pointer to the overflow
- flag is being passed in as a parameter instead of using global
- data.
-
- Description: Updated template. Changed paramter name from overflow to
- pOverflow
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Providing support for ARM and Linux-ARM assembly instructions.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_add function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_ADD_H
-#define L_ADD_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 L_add(register Word32 L_var1, register Word32 L_var2, Flag *pOverflow)
- {
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
- __asm
- {
- QADD result, L_var1, L_var2
- }
- return(result);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- __inline Word32 L_add(register Word32 L_var1, register Word32 L_var2, Flag *pOverflow)
- {
- register Word32 ra = L_var1;
- register Word32 rb = L_var2;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("qadd %0, %1, %2"
- : "=r"(result)
- : "r"(ra), "r"(rb)
- );
- return (result);
-
- }
-
-#else /* C EQUIVALENT */
-
-
- static inline Word32 L_add(register Word32 L_var1, register Word32 L_var2, Flag *pOverflow)
- {
- Word32 L_sum;
-
- L_sum = L_var1 + L_var2;
-
- if ((L_var1 ^ L_var2) >= 0)
- {
- if ((L_sum ^ L_var1) < 0)
- {
- L_sum = (L_var1 < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
- }
-
- return (L_sum);
- }
-
-#endif
-
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_ADD_H_ */
diff --git a/media/module/codecs/amrnb/common/include/l_add_c.h b/media/module/codecs/amrnb/common/include/l_add_c.h
deleted file mode 100644
index 3585a3c..0000000
--- a/media/module/codecs/amrnb/common/include/l_add_c.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/l_add_c.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_add_c function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag and carry flag is passed into the
- function. Updated template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_add_c function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_ADD_C_H
-#define L_ADD_C_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
- Word32 L_add_c(Word32 L_var1, Word32 L_var2, Flag *pOverflow, Flag *pCarry);
-
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_ADD_C_H_ */
-
-
diff --git a/media/module/codecs/amrnb/common/include/l_mac.h b/media/module/codecs/amrnb/common/include/l_mac.h
deleted file mode 100644
index b4af3aa..0000000
--- a/media/module/codecs/amrnb/common/include/l_mac.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
- Filename: /audio/gsm_amr/c/include/l_mac.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_mac function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: 1. Updated the function to include ARM and Linux-ARM assembly
- instructions.
- 2. Added OSCL_UNUSED_ARG(pOverflow) to remove compiler warnings.
-
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_mac function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_MAC_H
-#define L_MAC_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 L_mac(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 result;
- Word32 L_sum;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm {SMULBB result, var1, var2}
- __asm {QDADD L_sum, L_var3, result}
- return (L_sum);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- static inline Word32 L_mac(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 ra = L_var3;
- register Word32 rb = var1;
- register Word32 rc = var2;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(result)
- : "r"(rb), "r"(rc)
- );
-
- asm volatile("qdadd %0, %1, %2"
- : "=r"(rc)
- : "r"(ra), "r"(result)
- );
-
- return (rc);
- }
-
-#else /* C_EQUIVALENT */
-
- __inline Word32 L_mac(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 result;
- Word32 L_sum;
- result = (Word32) var1 * var2;
- if (result != (Word32) 0x40000000L)
- {
- L_sum = (result << 1) + L_var3;
-
- /* Check if L_sum and L_var_3 share the same sign */
- if ((L_var3 ^ result) > 0)
- {
- if ((L_sum ^ L_var3) < 0)
- {
- L_sum = (L_var3 < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
- }
- }
- else
- {
- *pOverflow = 1;
- L_sum = MAX_32;
- }
- return (L_sum);
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_MAC_H_ */
-
-
diff --git a/media/module/codecs/amrnb/common/include/l_msu.h b/media/module/codecs/amrnb/common/include/l_msu.h
deleted file mode 100644
index 3bafb00..0000000
--- a/media/module/codecs/amrnb/common/include/l_msu.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
- Filename: /audio/gsm_amr/c/include/l_msu.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_msu function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Providing support for ARM and Linux-ARM assembly instructions.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_msu function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_MSU_H
-#define L_MSU_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-#include "l_mult.h"
-#include "l_sub.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 L_msu(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 product;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm
- {
- SMULBB product, var1, var2
- QDSUB result, L_var3, product
- }
-
- return (result);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- __inline Word32 L_msu(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 ra = L_var3;
- register Word32 rb = var1;
- register Word32 rc = var2;
- Word32 product;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(product)
- : "r"(rb), "r"(rc)
- );
-
- asm volatile("qdsub %0, %1, %2"
- : "=r"(result)
- : "r"(ra), "r"(product)
- );
-
- return (result);
- }
-
-#else /* C EQUIVALENT */
-
- static inline Word32 L_msu(Word32 L_var3, Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 result;
-
- result = L_mult(var1, var2, pOverflow);
- result = L_sub(L_var3, result, pOverflow);
-
- return (result);
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_MSU_H_ */
diff --git a/media/module/codecs/amrnb/common/include/l_mult.h b/media/module/codecs/amrnb/common/include/l_mult.h
deleted file mode 100644
index 061df60..0000000
--- a/media/module/codecs/amrnb/common/include/l_mult.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/l_mult.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_mult function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Providing support for ARM and Linux-ARM assembly instructions.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_mult function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_MULT_H
-#define L_MULT_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 L_mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 result;
- Word32 product;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm
- {
- SMULBB product, var1, var2
- QADD result, product, product
- }
-
- return (result);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- __inline Word32 L_mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 ra = var1;
- register Word32 rb = var2;
- Word32 result;
- Word32 product;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(product)
- : "r"(ra), "r"(rb)
- );
-
- asm volatile("qadd %0, %1, %2"
- : "=r"(result)
- : "r"(product), "r"(product)
- );
-
- return(result);
- }
-
-#else /* C EQUIVALENT */
-
- static inline Word32 L_mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 L_product;
-
- L_product = (Word32) var1 * var2;
-
- if (L_product != (Word32) 0x40000000L)
- {
- L_product <<= 1; /* Multiply by 2 */
- }
- else
- {
- *pOverflow = 1;
- L_product = MAX_32;
- }
-
- return (L_product);
- }
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_MULT_H */
-
diff --git a/media/module/codecs/amrnb/common/include/l_shl.h b/media/module/codecs/amrnb/common/include/l_shl.h
deleted file mode 100644
index 7b9fdb1..0000000
--- a/media/module/codecs/amrnb/common/include/l_shl.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/l_shl.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_shl function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_shl function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_SHL_H
-#define L_SHL_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
- Word32 L_shl(Word32 L_var1, Word16 var2, Flag *pOverflow);
-
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_SHL_H_ */
-
-
-
diff --git a/media/module/codecs/amrnb/common/include/l_shr.h b/media/module/codecs/amrnb/common/include/l_shr.h
deleted file mode 100644
index ef22073..0000000
--- a/media/module/codecs/amrnb/common/include/l_shr.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
- Filename: /audio/gsm_amr/c/include/l_shr.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_shr function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_shr function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_SHR_H
-#define L_SHR_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
- Word32 L_shr(Word32 L_var1, Word16 var2, Flag *pOverflow);
-
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_SHR_H_ */
-
-
-
diff --git a/media/module/codecs/amrnb/common/include/l_sub.h b/media/module/codecs/amrnb/common/include/l_sub.h
deleted file mode 100644
index 97d7538..0000000
--- a/media/module/codecs/amrnb/common/include/l_sub.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/l_sub.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for L_sub function.
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Providing support for ARM and Linux-ARM assembly instructions.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the L_sub function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef L_SUB_H
-#define L_SUB_H
-
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 L_sub(Word32 L_var1, Word32 L_var2, Flag *pOverflow)
- {
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm
- {
- QSUB result, L_var1, L_var2
- }
-
- return(result);
-
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- __inline Word32 L_sub(Word32 L_var1, Word32 L_var2, Flag *pOverflow)
- {
- register Word32 ra = L_var1;
- register Word32 rb = L_var2;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("qsub %0, %1, %2"
- : "=r"(result)
- : "r"(ra), "r"(rb)
- );
-
- return (result);
- }
-
-#else /* C EQUIVALENT */
-
- static inline Word32 L_sub(register Word32 L_var1, register Word32 L_var2,
- register Flag *pOverflow)
- {
- Word32 L_diff;
-
- L_diff = L_var1 - L_var2;
-
- if ((L_var1 ^ L_var2) < 0)
- {
- if ((L_diff ^ L_var1) & MIN_32)
- {
- L_diff = (L_var1 < 0L) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
- }
-
- return (L_diff);
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _L_SUB_H_ */
-
-
diff --git a/media/module/codecs/amrnb/common/include/mpy_32.h b/media/module/codecs/amrnb/common/include/mpy_32.h
deleted file mode 100644
index 03f36b2..0000000
--- a/media/module/codecs/amrnb/common/include/mpy_32.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/mpy_32.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Updated function prototype declaration to reflect new interface.
- A pointer to overflow flag is passed into the function. Updated
- template.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Updated the function to include ARM and Linux-ARM assembly
- instructions.
-
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the Mpy_32 function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef MPY_32_H
-#define MPY_32_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 Mpy_32(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 L_var2_hi,
- Word16 L_var2_lo,
- Flag *pOverflow)
-
- {
- /*----------------------------------------------------------------------------
- ; Define all local variables
- ----------------------------------------------------------------------------*/
- Word32 L_product;
- Word32 L_sum;
- Word32 product32;
-
- OSCL_UNUSED_ARG(pOverflow);
- /*----------------------------------------------------------------------------
- ; Function body here
- ----------------------------------------------------------------------------*/
- /* L_product = L_mult (L_var1_hi, L_var2_hi, pOverflow);*/
-
- __asm {SMULBB L_product, L_var1_hi, L_var2_hi}
- __asm {QDADD L_product, 0, L_product}
- __asm {SMULBB product32, L_var1_hi, L_var2_lo}
- product32 >>= 15;
- __asm {QDADD L_sum, L_product, product32}
- L_product = L_sum;
- __asm {SMULBB product32, L_var1_lo, L_var2_hi}
- product32 >>= 15;
- __asm {QDADD L_sum, L_product, product32}
- return (L_sum);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- static inline Word32 Mpy_32(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 L_var2_hi,
- Word16 L_var2_lo,
- Flag *pOverflow)
- {
- register Word32 product32;
- register Word32 L_sum;
- register Word32 L_product, result;
- register Word32 ra = L_var1_hi;
- register Word32 rb = L_var1_lo;
- register Word32 rc = L_var2_hi;
- register Word32 rd = L_var2_lo;
-
-
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(L_product)
- : "r"(ra), "r"(rc)
- );
- asm volatile("mov %0, #0"
- : "=r"(result)
- );
-
- asm volatile("qdadd %0, %1, %2"
- : "=r"(L_sum)
- : "r"(result), "r"(L_product)
- );
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(product32)
- : "r"(ra), "r"(rd)
- );
-
- asm volatile("mov %0, %1, ASR #15"
- : "=r"(ra)
- : "r"(product32)
- );
- asm volatile("qdadd %0, %1, %2"
- : "=r"(L_product)
- : "r"(L_sum), "r"(ra)
- );
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(product32)
- : "r"(rb), "r"(rc)
- );
-
- asm volatile("mov %0, %1, ASR #15"
- : "=r"(rb)
- : "r"(product32)
- );
-
- asm volatile("qdadd %0, %1, %2"
- : "=r"(L_sum)
- : "r"(L_product), "r"(rb)
- );
-
- return (L_sum);
- }
-
-#else /* C_EQUIVALENT */
-
- __inline Word32 Mpy_32(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 L_var2_hi,
- Word16 L_var2_lo,
- Flag *pOverflow)
- {
- Word32 L_product;
- Word32 L_sum;
- Word32 product32;
-
- OSCL_UNUSED_ARG(pOverflow);
- L_product = (Word32) L_var1_hi * L_var2_hi;
-
- if (L_product != (Word32) 0x40000000L)
- {
- L_product <<= 1;
- }
- else
- {
- L_product = MAX_32;
- }
-
- /* result = mult (L_var1_hi, L_var2_lo, pOverflow); */
- product32 = ((Word32) L_var1_hi * L_var2_lo) >> 15;
-
- /* L_product = L_mac (L_product, result, 1, pOverflow); */
- L_sum = L_product + (product32 << 1);
-
- if ((L_product ^ product32) > 0)
- {
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- }
- }
-
- L_product = L_sum;
-
- /* result = mult (L_var1_lo, L_var2_hi, pOverflow); */
- product32 = ((Word32) L_var1_lo * L_var2_hi) >> 15;
-
- /* L_product = L_mac (L_product, result, 1, pOverflow); */
- L_sum = L_product + (product32 << 1);
-
- if ((L_product ^ product32) > 0)
- {
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- }
- }
-
- /*----------------------------------------------------------------------------
- ; Return nothing or data or data pointer
- ----------------------------------------------------------------------------*/
- return (L_sum);
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _MPY_32_H_ */
diff --git a/media/module/codecs/amrnb/common/include/mpy_32_16.h b/media/module/codecs/amrnb/common/include/mpy_32_16.h
deleted file mode 100644
index 7eaa741..0000000
--- a/media/module/codecs/amrnb/common/include/mpy_32_16.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/mpy_32_16.h
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the Mpy_32_16 function.
-
-------------------------------------------------------------------------------
-*/
-
-/*----------------------------------------------------------------------------
-; CONTINUE ONLY IF NOT ALREADY DEFINED
-----------------------------------------------------------------------------*/
-#ifndef MPY_32_16_H
-#define MPY_32_16_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5) /* Instructions for ARM Assembly on ADS*/
-
- __inline Word32 Mpy_32_16(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 var2,
- Flag *pOverflow)
- {
-
- Word32 L_product;
- Word32 L_sum;
- Word32 result;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm {SMULBB L_product, L_var1_hi, var2}
- __asm {QDADD L_product, 0, L_product}
- __asm {SMULBB result, L_var1_lo, var2}
- result >>= 15;
- __asm {QDADD L_sum, L_product, result}
- return (L_sum);
- }
-
-#elif defined(PV_ARM_GCC_V5) /* Instructions for ARM-linux cross-compiler*/
-
- static inline Word32 Mpy_32_16(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 var2,
- Flag *pOverflow)
- {
-
- register Word32 ra = L_var1_hi;
- register Word32 rb = L_var1_lo;
- register Word32 rc = var2;
- Word32 result, L_product;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(L_product)
- : "r"(ra), "r"(rc)
- );
- asm volatile("mov %0, #0"
- : "=r"(result)
- );
-
- asm volatile("qdadd %0, %1, %2"
- : "=r"(L_product)
- : "r"(result), "r"(L_product)
- );
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(result)
- : "r"(rb), "r"(rc)
- );
-
- asm volatile("mov %0, %1, ASR #15"
- : "=r"(ra)
- : "r"(result)
- );
- asm volatile("qdadd %0, %1, %2"
- : "=r"(result)
- : "r"(L_product), "r"(ra)
- );
-
- return (result);
- }
-
-#else /* C_EQUIVALENT */
- __inline Word32 Mpy_32_16(Word16 L_var1_hi,
- Word16 L_var1_lo,
- Word16 var2,
- Flag *pOverflow)
- {
-
- Word32 L_product;
- Word32 L_sum;
- Word32 result;
- L_product = (Word32) L_var1_hi * var2;
-
- if (L_product != (Word32) 0x40000000L)
- {
- L_product <<= 1;
- }
- else
- {
- *pOverflow = 1;
- L_product = MAX_32;
- }
-
- result = ((Word32)L_var1_lo * var2) >> 15;
-
- L_sum = L_product + (result << 1);
-
- if ((L_product ^ result) > 0)
- {
- if ((L_sum ^ L_product) < 0)
- {
- L_sum = (L_product < 0) ? MIN_32 : MAX_32;
- *pOverflow = 1;
- }
- }
- return (L_sum);
-
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _MPY_32_16_H_ */
-
-
diff --git a/media/module/codecs/amrnb/common/include/mult.h b/media/module/codecs/amrnb/common/include/mult.h
deleted file mode 100644
index 6927eba..0000000
--- a/media/module/codecs/amrnb/common/include/mult.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/*
-
- Filename: /audio/gsm_amr/c/include/mult.h
-
-------------------------------------------------------------------------------
- REVISION HISTORY
-
- Description: Created separate header file for mult function.
-
- Description: Changed prototype of the mult() function. Instead of using global
- a pointer to overflow flag is now passed into the function.
-
- Description: Updated copyright information.
- Updated variable name from "overflow" to "pOverflow" to match
- with original function declaration.
-
- Description: Moved _cplusplus #ifdef after Include section.
-
- Description: Providing support for ARM and Linux-ARM assembly instructions.
-
- Who: Date:
- Description:
-
-------------------------------------------------------------------------------
- INCLUDE DESCRIPTION
-
- This file contains all the constant definitions and prototype definitions
- needed by the mult function.
-
-------------------------------------------------------------------------------
-*/
-
-#ifndef MULT_H
-#define MULT_H
-
-/*----------------------------------------------------------------------------
-; INCLUDES
-----------------------------------------------------------------------------*/
-
-#include "basicop_malloc.h"
-
-/*--------------------------------------------------------------------------*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*----------------------------------------------------------------------------
- ; MACROS
- ; Define module specific macros here
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; DEFINES
- ; Include all pre-processor statements here.
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; EXTERNAL VARIABLES REFERENCES
- ; Declare variables used in this module but defined elsewhere
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; SIMPLE TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; ENUMERATED TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; STRUCTURES TYPEDEF'S
- ----------------------------------------------------------------------------*/
-
- /*----------------------------------------------------------------------------
- ; GLOBAL FUNCTION DEFINITIONS
- ; Function Prototype declaration
- ----------------------------------------------------------------------------*/
-#if defined(PV_ARM_V5)
-
- __inline Word16 mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- Word32 product;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- __asm
- {
- SMULBB product, var1, var2
- MOV product, product, ASR #15
- CMP product, 0x7FFF
- MOVGE product, 0x7FFF
- }
-
- return ((Word16) product);
- }
-
-#elif defined(PV_ARM_GCC_V5)
-
- __inline Word16 mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 ra = var1;
- register Word32 rb = var2;
- Word32 product;
- Word32 temp = 0x7FFF;
-
- OSCL_UNUSED_ARG(pOverflow);
-
- asm volatile("smulbb %0, %1, %2"
- : "=r"(product)
- : "r"(ra), "r"(rb)
- );
- asm volatile("mov %0, %1, ASR #15"
- : "=r"(product)
- : "r"(product)
- );
- asm volatile("cmp %0, %1"
- : "=r"(product)
- : "r"(temp)
- );
- asm volatile("movge %0, %1"
- : "=r"(product)
- : "r"(temp)
- );
-
- return ((Word16) product);
- }
-
-#else /* C EQUIVALENT */
-
- static inline Word16 mult(Word16 var1, Word16 var2, Flag *pOverflow)
- {
- register Word32 product;
-
- product = ((Word32) var1 * var2) >> 15;
-
- /* Saturate result (if necessary). */
- /* var1 * var2 >0x00007fff is the only case */
- /* that saturation occurs. */
-
- if (product > 0x00007fffL)
- {
- *pOverflow = 1;
- product = (Word32) MAX_16;
- }
-
-
- /* Return the product as a 16 bit value by type casting Word32 to Word16 */
-
- return ((Word16) product);
- }
-
-#endif
- /*----------------------------------------------------------------------------
- ; END
- ----------------------------------------------------------------------------*/
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _MULT_H_ */
-
diff --git a/media/module/codecs/amrnb/common/include/n_proc.h b/media/module/codecs/amrnb/common/include/n_proc.h
deleted file mode 100644
index e5738c1..0000000
--- a/media/module/codecs/amrnb/common/include/n_proc.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 1998-2009 PacketVideo
- *
- * 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.
- * -------------------------------------------------------------------
- */
-/****************************************************************************************
-Portions of this file are derived from the following 3GPP standard:
-
- 3GPP TS 26.073
- ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
- Available from http://www.3gpp.org
-
-(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
-Permission to distribute, modify and use this file under the standard license
-terms listed above has been obtained from the copyright holder.
-****************************************************************************************/
-/* $Id $ */
-
-void proc_head(char *mes);
diff --git a/media/module/codecs/amrnb/enc/src/g_pitch.cpp b/media/module/codecs/amrnb/enc/src/g_pitch.cpp
index 5b80e2a..6f686fa 100644
--- a/media/module/codecs/amrnb/enc/src/g_pitch.cpp
+++ b/media/module/codecs/amrnb/enc/src/g_pitch.cpp
@@ -376,15 +376,11 @@
{
L_temp = ((Word32) * (p_xn++) * *(p_y1++));
s1 = s;
- s = s1 + L_temp;
- if ((s1 ^ L_temp) > 0)
+ if (__builtin_add_overflow(s1, L_temp, &s))
{
- if ((s1 ^ s) < 0)
- {
- *pOverflow = 1;
- break;
- }
+ *pOverflow = 1;
+ break;
}
}
diff --git a/media/module/codecs/amrwb/enc/inc/basic_op.h b/media/module/codecs/amrwb/enc/inc/basic_op.h
index 80ad7f1..8e740b4 100644
--- a/media/module/codecs/amrwb/enc/inc/basic_op.h
+++ b/media/module/codecs/amrwb/enc/inc/basic_op.h
@@ -569,13 +569,10 @@
static_vo Word32 L_add (Word32 L_var1, Word32 L_var2)
{
Word32 L_var_out;
- L_var_out = L_var1 + L_var2;
- if (((L_var1 ^ L_var2) & MIN_32) == 0)
+ if (__builtin_add_overflow(L_var1, L_var2, &L_var_out))
{
- if ((L_var_out ^ L_var1) & MIN_32)
- {
- L_var_out = (L_var1 < 0) ? MIN_32 : MAX_32;
- }
+ // saturating...
+ L_var_out = (L_var1 < 0) ? MIN_32 : MAX_32;
}
return (L_var_out);
}
@@ -616,13 +613,10 @@
static_vo Word32 L_sub (Word32 L_var1, Word32 L_var2)
{
Word32 L_var_out;
- L_var_out = L_var1 - L_var2;
- if (((L_var1 ^ L_var2) & MIN_32) != 0)
+ if (__builtin_sub_overflow(L_var1, L_var2, &L_var_out))
{
- if ((L_var_out ^ L_var1) & MIN_32)
- {
- L_var_out = (L_var1 < 0L) ? MIN_32 : MAX_32;
- }
+ // saturating...
+ L_var_out = (L_var1 < 0L) ? MIN_32 : MAX_32;
}
return (L_var_out);
}
diff --git a/media/module/codecserviceregistrant/Android.bp b/media/module/codecserviceregistrant/Android.bp
index becb98a..56cd8b8 100644
--- a/media/module/codecserviceregistrant/Android.bp
+++ b/media/module/codecserviceregistrant/Android.bp
@@ -55,6 +55,8 @@
"com.android.media.swcodec",
],
+ export_include_dirs: ["include"],
+
srcs: [
"CodecServiceRegistrant.cpp",
],
diff --git a/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp b/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
index 6df9dc8..42fd94e 100644
--- a/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
+++ b/media/module/codecserviceregistrant/CodecServiceRegistrant.cpp
@@ -40,7 +40,7 @@
#include <codec2/aidl/ComponentStore.h>
#include <codec2/aidl/ParamTypes.h>
-#include <media/CodecServiceRegistrant.h>
+#include <codecserviceregistrant/CodecServiceRegistrant.h>
namespace /* unnamed */ {
@@ -775,13 +775,12 @@
return nullptr;
}
-extern "C" void RegisterCodecServices() {
+/**
+ * This function encapsulates the core logic required to register codec services,
+ * separated from threadpool management to avoid timeouts when called by the fuzzer.
+ */
+static void RegisterCodecServicesWithExistingThreadpool() {
const bool aidlSelected = c2_aidl::utils::IsSelected();
- constexpr int kThreadCount = 64;
- ABinderProcess_setThreadPoolMaxThreadCount(kThreadCount);
- ABinderProcess_startThreadPool();
- ::android::hardware::configureRpcThreadpool(kThreadCount, false);
-
LOG(INFO) << "Creating software Codec2 service...";
std::shared_ptr<C2ComponentStore> store =
android::GetCodec2PlatformComponentStore();
@@ -880,8 +879,16 @@
if (registered) {
LOG(INFO) << "Software Codec2 service created and registered.";
}
+}
+
+extern "C" void RegisterCodecServices() {
+ constexpr int kThreadCount = 64;
+ ABinderProcess_setThreadPoolMaxThreadCount(kThreadCount);
+ ABinderProcess_startThreadPool();
+ ::android::hardware::configureRpcThreadpool(kThreadCount, false);
+
+ RegisterCodecServicesWithExistingThreadpool();
ABinderProcess_joinThreadPool();
::android::hardware::joinRpcThreadpool();
}
-
diff --git a/media/module/codecserviceregistrant/fuzzer/codecServiceRegistrant_fuzzer.cpp b/media/module/codecserviceregistrant/fuzzer/codecServiceRegistrant_fuzzer.cpp
index 4868e0c..0baf1ca 100644
--- a/media/module/codecserviceregistrant/fuzzer/codecServiceRegistrant_fuzzer.cpp
+++ b/media/module/codecserviceregistrant/fuzzer/codecServiceRegistrant_fuzzer.cpp
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include <codecserviceregistrant/CodecServiceRegistrant.h>
+
#include "../CodecServiceRegistrant.cpp"
#include "fuzzer/FuzzedDataProvider.h"
#include <C2Config.h>
@@ -166,9 +169,9 @@
void CodecServiceRegistrantFuzzer::process(const uint8_t *data, size_t size) {
mFDP = new FuzzedDataProvider(data, size);
invokeH2C2ComponentStore();
- /** RegisterCodecServices is called here to improve code coverage */
- /** as currently it is not called by codecServiceRegistrant */
- RegisterCodecServices();
+ /** RegisterCodecServicesWithExistingThreadpool() is called here to improve
+ * code coverage as currently it is not called in codecServiceRegistrant.cpp */
+ RegisterCodecServicesWithExistingThreadpool();
delete mFDP;
}
diff --git a/media/libmedia/include/media/CodecServiceRegistrant.h b/media/module/codecserviceregistrant/include/codecserviceregistrant/CodecServiceRegistrant.h
similarity index 77%
rename from media/libmedia/include/media/CodecServiceRegistrant.h
rename to media/module/codecserviceregistrant/include/codecserviceregistrant/CodecServiceRegistrant.h
index e0af781..1c6f71f 100644
--- a/media/libmedia/include/media/CodecServiceRegistrant.h
+++ b/media/module/codecserviceregistrant/include/codecserviceregistrant/CodecServiceRegistrant.h
@@ -18,6 +18,13 @@
#define CODEC_SERVICE_REGISTRANT_H_
+/**
+ * This function starts the threadpool, calls the registration logic
+ * encapsulated in RegisterCodecServicesWithExistingThreadpool(), and
+ * then stops the threadpool.
+ */
+extern "C" void RegisterCodecServices();
+
typedef void (*RegisterCodecServicesFunc)();
#endif // CODEC_SERVICE_REGISTRANT_H_
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index b3707c8..f247f8c 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -523,11 +523,10 @@
}
[this, &track] {
- int64_t duration;
+ int64_t duration = track->mMdhdDurationUs;
int32_t samplerate;
// Only for audio track.
- if (track->elst_needs_processing && mHeaderTimescale != 0 &&
- AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
+ if (track->elst_needs_processing && mHeaderTimescale != 0 && duration != 0 &&
AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
// Elst has to be processed only the first time this function is called.
track->elst_needs_processing = false;
@@ -1645,7 +1644,10 @@
(long long) duration, (long long) mLastTrack->timescale);
return ERROR_MALFORMED;
}
- AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, durationUs);
+ // Store this track's mdhd duration to calculate the padding.
+ mLastTrack->mMdhdDurationUs = (int64_t)durationUs;
+ } else {
+ mLastTrack->mMdhdDurationUs = 0;
}
uint8_t lang[2];
@@ -3907,17 +3909,18 @@
}
int32_t id;
+ int64_t duration;
if (version == 1) {
// we can get ctime value from U64_AT(&buffer[4])
// we can get mtime value from U64_AT(&buffer[12])
id = U32_AT(&buffer[20]);
- // we can get duration value from U64_AT(&buffer[28])
+ duration = U64_AT(&buffer[28]);
} else if (version == 0) {
// we can get ctime value from U32_AT(&buffer[4])
// we can get mtime value from U32_AT(&buffer[8])
id = U32_AT(&buffer[12]);
- // we can get duration value from U32_AT(&buffer[20])
+ duration = U32_AT(&buffer[20]);
} else {
return ERROR_UNSUPPORTED;
}
@@ -3926,6 +3929,15 @@
return ERROR_MALFORMED;
AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_TRACK_ID, id);
+ if (duration != 0 && mHeaderTimescale != 0) {
+ long double durationUs = ((long double)duration * 1000000) / mHeaderTimescale;
+ if (durationUs < 0 || durationUs > INT64_MAX) {
+ ALOGE("cannot represent %lld * 1000000 / %lld in 64 bits",
+ (long long) duration, (long long) mHeaderTimescale);
+ return ERROR_MALFORMED;
+ }
+ AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, durationUs);
+ }
size_t matrixOffset = dynSize + 16;
int32_t a00 = U32_AT(&buffer[matrixOffset]);
diff --git a/media/module/extractors/mp4/include/MPEG4Extractor.h b/media/module/extractors/mp4/include/MPEG4Extractor.h
index 542a3e6..59626f6 100644
--- a/media/module/extractors/mp4/include/MPEG4Extractor.h
+++ b/media/module/extractors/mp4/include/MPEG4Extractor.h
@@ -96,7 +96,7 @@
uint8_t *mTx3gBuffer;
size_t mTx3gSize, mTx3gFilled;
-
+ int64_t mMdhdDurationUs;
Track() {
next = NULL;
diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h
index 197e202..def142c 100644
--- a/media/ndk/include/media/NdkMediaDataSource.h
+++ b/media/ndk/include/media/NdkMediaDataSource.h
@@ -49,16 +49,16 @@
/*
* AMediaDataSource's callbacks will be invoked on an implementation-defined thread
* or thread pool. No guarantees are provided about which thread(s) will be used for
- * callbacks. For example, |close| can be invoked from a different thread than the
- * thread invoking |readAt|. As such, the Implementations of AMediaDataSource callbacks
+ * callbacks. For example, `close` can be invoked from a different thread than the
+ * thread invoking `readAt`. As such, the Implementations of AMediaDataSource callbacks
* must be threadsafe.
*/
/**
- * Called to request data from the given |offset|.
+ * Called to request data from the given `offset`.
*
- * Implementations should should write up to |size| bytes into
- * |buffer|, and return the number of bytes written.
+ * Implementations should should write up to `size` bytes into
+ * `buffer`, and return the number of bytes written.
*
* Return 0 if size is zero (thus no bytes are read).
*
@@ -78,9 +78,9 @@
* Called to close the data source, unblock reads, and release associated
* resources.
*
- * The NDK media framework guarantees that after the first |close| is
+ * The NDK media framework guarantees that after the first `close` is
* called, no future callbacks will be invoked on the data source except
- * for |close| itself.
+ * for `close` itself.
*
* Closing a data source allows readAt calls that were blocked waiting
* for I/O data to return promptly.
@@ -101,7 +101,7 @@
/**
* Called to get an estimate of the number of bytes that can be read from this data source
- * starting at |offset| without blocking for I/O.
+ * starting at `offset` without blocking for I/O.
*
* Return -1 when such an estimate is not possible.
*/
@@ -111,10 +111,10 @@
* Create new media data source. Returns NULL if memory allocation
* for the new data source object fails.
*
- * Set the |uri| from which the data source will read,
+ * Set the `uri` from which the data source will read,
* plus additional http headers when initiating the request.
*
- * Headers will contain corresponding items from |key_values|
+ * Headers will contain corresponding items from `key_values`
* in the following fashion:
*
* key_values[0]:key_values[1]
diff --git a/media/psh_utils/Android.bp b/media/psh_utils/Android.bp
new file mode 100644
index 0000000..4662db8
--- /dev/null
+++ b/media/psh_utils/Android.bp
@@ -0,0 +1,47 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+// libraries that are included whole_static for test apps
+ndk_libs = [
+ "android.hardware.health-V3-ndk",
+ "android.hardware.power.stats-V1-ndk",
+]
+
+// Power, System, Health utils
+cc_library {
+ name: "libpshutils",
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ srcs: [
+ "HealthStats.cpp",
+ "HealthStatsProvider.cpp",
+ "PowerStats.cpp",
+ "PowerStatsCollector.cpp",
+ "PowerStatsProvider.cpp",
+ ],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+ shared: {
+ shared_libs: ndk_libs,
+ },
+ static: {
+ whole_static_libs: ndk_libs,
+ },
+}
diff --git a/media/psh_utils/HealthStats.cpp b/media/psh_utils/HealthStats.cpp
new file mode 100644
index 0000000..5e767f6
--- /dev/null
+++ b/media/psh_utils/HealthStats.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <psh_utils/HealthStats.h>
+
+namespace android::media::psh_utils {
+
+template <typename T>
+const T& choose_voltage(const T& a, const T& b) {
+ return std::max(a, b); // we use max here, could use avg.
+}
+
+std::string HealthStats::toString() const {
+ std::string result;
+ const float batteryVoltage = batteryVoltageMillivolts * 1e-3f; // Volts
+ const float charge = batteryChargeCounterUah * (3600 * 1e-6); // Joules = Amp-Second
+ result.append("{Net Battery V: ")
+ .append(std::to_string(batteryVoltage))
+ .append(" J: ")
+ .append(std::to_string(charge))
+ .append("}");
+ return result;
+}
+
+std::string HealthStats::normalizedEnergy(double timeSec) const {
+ std::string result;
+ const float batteryVoltage = batteryVoltageMillivolts * 1e-3f; // Volts
+ const float charge = -batteryChargeCounterUah * (3600 * 1e-6f); // Joules = Amp-Second
+ const float watts = charge * batteryVoltage / timeSec;
+ result.append("{Net Battery V: ")
+ .append(std::to_string(batteryVoltage))
+ .append(" J: ")
+ .append(std::to_string(charge))
+ .append(" W: ")
+ .append(std::to_string(watts))
+ .append("}");
+ return result;
+}
+
+HealthStats HealthStats::operator+=(const HealthStats& other) {
+ batteryVoltageMillivolts = choose_voltage(
+ batteryVoltageMillivolts, other.batteryVoltageMillivolts);
+ batteryFullChargeUah = std::max(batteryFullChargeUah, other.batteryFullChargeUah);
+ batteryChargeCounterUah += other.batteryChargeCounterUah;
+ return *this;
+}
+
+HealthStats HealthStats::operator-=(const HealthStats& other) {
+ batteryVoltageMillivolts = choose_voltage(
+ batteryVoltageMillivolts, other.batteryVoltageMillivolts);
+ batteryFullChargeUah = std::max(batteryFullChargeUah, other.batteryFullChargeUah);
+ batteryChargeCounterUah -= other.batteryChargeCounterUah;
+ return *this;
+}
+
+HealthStats HealthStats::operator+(const HealthStats& other) const {
+ HealthStats result = *this;
+ result += other;
+ return result;
+}
+
+HealthStats HealthStats::operator-(const HealthStats& other) const {
+ HealthStats result = *this;
+ result -= other;
+ return result;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/HealthStatsProvider.cpp b/media/psh_utils/HealthStatsProvider.cpp
new file mode 100644
index 0000000..744daad
--- /dev/null
+++ b/media/psh_utils/HealthStatsProvider.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PowerStatsProvider.h"
+#include <aidl/android/hardware/health/IHealth.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+
+using ::aidl::android::hardware::health::HealthInfo;
+using ::aidl::android::hardware::health::IHealth;
+
+namespace android::media::psh_utils {
+
+static auto getHealthService() {
+ [[clang::no_destroy]] static constinit std::mutex m;
+ [[clang::no_destroy]] static constinit
+ std::shared_ptr<IHealth> healthService;
+
+ std::lock_guard l(m);
+ if (healthService) {
+ return healthService;
+ }
+ const auto serviceName =
+ std::string(IHealth::descriptor).append("/default");
+ healthService = IHealth::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
+ return healthService;
+}
+
+status_t HealthStatsDataProvider::fill(PowerStats* stat) const {
+ if (stat == nullptr) return BAD_VALUE;
+ HealthStats& stats = stat->health_stats;
+ auto healthService = getHealthService();
+ if (healthService == nullptr) {
+ LOG(ERROR) << "unable to get health AIDL service";
+ return NO_INIT;
+ }
+ HealthInfo healthInfo;
+ if (!healthService->getHealthInfo(&healthInfo).isOk()) {
+ LOG(ERROR) << __func__ << ": unable to get health info";
+ return INVALID_OPERATION;
+ }
+
+ stats.batteryVoltageMillivolts = healthInfo.batteryVoltageMillivolts;
+ stats.batteryFullChargeUah = healthInfo.batteryFullChargeUah;
+ stats.batteryChargeCounterUah = healthInfo.batteryChargeCounterUah;
+ return NO_ERROR;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/PowerStats.cpp b/media/psh_utils/PowerStats.cpp
new file mode 100644
index 0000000..f8f87c5
--- /dev/null
+++ b/media/psh_utils/PowerStats.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
+#include <psh_utils/PowerStats.h>
+
+namespace android::media::psh_utils {
+
+// Determine the best start time from a and b, which is
+// min(a, b) if both exist, otherwise the one that exists.
+template <typename T>
+const T& choose_best_start_time(const T& a, const T& b) {
+ if (a) {
+ return b ? std::min(a, b) : a;
+ } else {
+ return b;
+ }
+}
+
+// subtract two time differences.
+template <typename T, typename U>
+const T sub_time_diff(const T& diff_a, const T& diff_b, const U& abs_c, const U& abs_d) {
+ if (diff_a) {
+ return diff_b ? (diff_a - diff_b) : diff_a;
+ } else if (diff_b) {
+ return diff_b;
+ } else { // no difference exists, use absolute time.
+ return abs_c - abs_d;
+ }
+}
+
+std::string PowerStats::Metadata::toString() const {
+ return std::string("start_time_since_boot_ms: ").append(
+ std::to_string(start_time_since_boot_ms))
+ .append(" start_time_monotonic_ms: ").append(std::to_string(start_time_monotonic_ms))
+ .append(audio_utils_time_string_from_ns(start_time_epoch_ms * 1'000'000).time)
+ .append(" duration_ms: ").append(std::to_string(duration_ms))
+ .append(" duration_monotonic_ms: ").append(std::to_string(duration_monotonic_ms));
+}
+
+PowerStats::Metadata PowerStats::Metadata::operator+=(const Metadata& other) {
+ start_time_since_boot_ms = choose_best_start_time(
+ start_time_since_boot_ms, other.start_time_since_boot_ms);
+ start_time_epoch_ms = choose_best_start_time(
+ start_time_epoch_ms, other.start_time_epoch_ms);
+ start_time_monotonic_ms = choose_best_start_time(
+ start_time_monotonic_ms, other.start_time_monotonic_ms);
+ duration_ms += other.duration_ms;
+ duration_monotonic_ms += other.duration_monotonic_ms;
+ return *this;
+}
+
+PowerStats::Metadata PowerStats::Metadata::operator-=(const Metadata& other) {
+ // here we calculate duration, if it makes sense.
+ duration_ms = sub_time_diff(duration_ms, other.duration_ms,
+ start_time_since_boot_ms, other.start_time_since_boot_ms);
+ duration_monotonic_ms = sub_time_diff(
+ duration_monotonic_ms, other.duration_monotonic_ms,
+ start_time_monotonic_ms, other.start_time_monotonic_ms);
+ start_time_since_boot_ms = choose_best_start_time(
+ start_time_since_boot_ms, other.start_time_since_boot_ms);
+ start_time_epoch_ms = choose_best_start_time(
+ start_time_epoch_ms, other.start_time_epoch_ms);
+ start_time_monotonic_ms = choose_best_start_time(
+ start_time_monotonic_ms, other.start_time_monotonic_ms);
+ return *this;
+}
+
+PowerStats::Metadata PowerStats::Metadata::operator+(const Metadata& other) const {
+ Metadata result = *this;
+ result += other;
+ return result;
+}
+
+PowerStats::Metadata PowerStats::Metadata::operator-(const Metadata& other) const {
+ Metadata result = *this;
+ result -= other;
+ return result;
+}
+
+std::string PowerStats::StateResidency::toString() const {
+ return std::string(entity_name).append(state_name)
+ .append(" ").append(std::to_string(time_ms))
+ .append(" ").append(std::to_string(entry_count));
+}
+
+PowerStats::StateResidency PowerStats::StateResidency::operator+=(const StateResidency& other) {
+ if (entity_name.empty()) entity_name = other.entity_name;
+ if (state_name.empty()) state_name = other.state_name;
+ time_ms += other.time_ms;
+ entry_count += other.entry_count;
+ return *this;
+}
+
+PowerStats::StateResidency PowerStats::StateResidency::operator-=(const StateResidency& other) {
+ if (entity_name.empty()) entity_name = other.entity_name;
+ if (state_name.empty()) state_name = other.state_name;
+ time_ms -= other.time_ms;
+ entry_count -= other.entry_count;
+ return *this;
+}
+
+PowerStats::StateResidency PowerStats::StateResidency::operator+(
+ const StateResidency& other) const {
+ StateResidency result = *this;
+ result += other;
+ return result;
+}
+
+PowerStats::StateResidency PowerStats::StateResidency::operator-(
+ const StateResidency& other) const {
+ StateResidency result = *this;
+ result -= other;
+ return result;
+}
+
+std::string PowerStats::RailEnergy::toString() const {
+ return std::string(subsystem_name)
+ .append(rail_name)
+ .append(" ")
+ .append(std::to_string(energy_uws));
+}
+
+PowerStats::RailEnergy PowerStats::RailEnergy::operator+=(const RailEnergy& other) {
+ if (subsystem_name.empty()) subsystem_name = other.subsystem_name;
+ if (rail_name.empty()) rail_name = other.rail_name;
+ energy_uws += other.energy_uws;
+ return *this;
+}
+
+PowerStats::RailEnergy PowerStats::RailEnergy::operator-=(const RailEnergy& other) {
+ if (subsystem_name.empty()) subsystem_name = other.subsystem_name;
+ if (rail_name.empty()) rail_name = other.rail_name;
+ energy_uws -= other.energy_uws;
+ return *this;
+}
+
+PowerStats::RailEnergy PowerStats::RailEnergy::operator+(const RailEnergy& other) const {
+ RailEnergy result = *this;
+ result += other;
+ return result;
+}
+
+PowerStats::RailEnergy PowerStats::RailEnergy::operator-(const RailEnergy& other) const {
+ RailEnergy result = *this;
+ result -= other;
+ return result;
+}
+
+std::string PowerStats::toString(const std::string& prefix) const {
+ std::string result;
+ result.append(prefix).append(metadata.toString()).append("\n");
+ result.append(prefix).append(health_stats.toString()).append("\n");
+ for (const auto &residency: power_entity_state_residency) {
+ result.append(prefix).append(residency.toString()).append("\n");
+ }
+ for (const auto &energy: rail_energy) {
+ result.append(prefix).append(energy.toString()).append("\n");
+ }
+ return result;
+}
+
+std::string PowerStats::normalizedEnergy(const std::string& prefix) const {
+ if (metadata.duration_ms == 0) return {};
+
+ std::string result(prefix);
+ result.append(audio_utils_time_string_from_ns(
+ metadata.start_time_epoch_ms * 1'000'000).time);
+ result.append(" duration_boottime: ")
+ .append(std::to_string(metadata.duration_ms * 1e-3f))
+ .append(" duration_monotonic: ")
+ .append(std::to_string(metadata.duration_monotonic_ms * 1e-3f))
+ .append("\n");
+ if (health_stats.isValid()) {
+ result.append(prefix)
+ .append(health_stats.normalizedEnergy(metadata.duration_ms * 1e-3f)).append("\n");
+ }
+
+ // energy_uws is converted to ave W using recip time in us.
+ const float recipTime = 1e-3 / metadata.duration_ms;
+ int64_t total_energy = 0;
+ for (const auto& energy: rail_energy) {
+ total_energy += energy.energy_uws;
+ result.append(prefix).append(energy.subsystem_name)
+ .append(energy.rail_name)
+ .append(" ")
+ .append(std::to_string(energy.energy_uws * 1e-6))
+ .append(" ")
+ .append(std::to_string(energy.energy_uws * recipTime))
+ .append("\n");
+ }
+ if (total_energy != 0) {
+ result.append(prefix).append("total J and ave W: ")
+ .append(std::to_string(total_energy * 1e-6))
+ .append(" ")
+ .append(std::to_string(total_energy * recipTime))
+ .append("\n");
+ }
+ return result;
+}
+
+// seconds, joules, watts
+std::tuple<float, float, float> PowerStats::energyFrom(const std::string& railMatcher) const {
+ if (metadata.duration_ms == 0) return {};
+
+ // energy_uws is converted to ave W using recip time in us.
+ const float recipTime = 1e-3 / metadata.duration_ms;
+ int64_t total_energy = 0;
+ for (const auto& energy: rail_energy) {
+ if (energy.subsystem_name.find(railMatcher) != std::string::npos
+ || energy.rail_name.find(railMatcher) != std::string::npos) {
+ total_energy += energy.energy_uws;
+ }
+ }
+ return {metadata.duration_ms * 1e-3, total_energy * 1e-6, total_energy * recipTime};
+}
+
+PowerStats PowerStats::operator+=(const PowerStats& other) {
+ metadata += other.metadata;
+ health_stats += other.health_stats;
+ if (power_entity_state_residency.empty()) {
+ power_entity_state_residency = other.power_entity_state_residency;
+ } else {
+ for (size_t i = 0; i < power_entity_state_residency.size(); ++i) {
+ power_entity_state_residency[i] += other.power_entity_state_residency[i];
+ }
+ }
+ if (rail_energy.empty()) {
+ rail_energy = other.rail_energy;
+ } else {
+ for (size_t i = 0; i < rail_energy.size(); ++i) {
+ rail_energy[i] += other.rail_energy[i];
+ }
+ }
+ return *this;
+}
+
+PowerStats PowerStats::operator-=(const PowerStats& other) {
+ metadata -= other.metadata;
+ health_stats -= other.health_stats;
+ if (power_entity_state_residency.empty()) {
+ power_entity_state_residency = other.power_entity_state_residency;
+ } else {
+ for (size_t i = 0; i < power_entity_state_residency.size(); ++i) {
+ power_entity_state_residency[i] -= other.power_entity_state_residency[i];
+ }
+ }
+ if (rail_energy.empty()) {
+ rail_energy = other.rail_energy;
+ } else {
+ for (size_t i = 0; i < rail_energy.size(); ++i) {
+ rail_energy[i] -= other.rail_energy[i];
+ }
+ }
+ return *this;
+}
+
+PowerStats PowerStats::operator+(const PowerStats& other) const {
+ PowerStats result = *this;
+ result += other;
+ return result;
+}
+
+PowerStats PowerStats::operator-(const PowerStats& other) const {
+ PowerStats result = *this;
+ result -= other;
+ return result;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/PowerStatsCollector.cpp b/media/psh_utils/PowerStatsCollector.cpp
new file mode 100644
index 0000000..6e02993
--- /dev/null
+++ b/media/psh_utils/PowerStatsCollector.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <psh_utils/PowerStatsCollector.h>
+#include "PowerStatsProvider.h"
+#include <utils/Timers.h>
+
+namespace android::media::psh_utils {
+
+PowerStatsCollector::PowerStatsCollector() {
+ addProvider(std::make_unique<PowerEntityResidencyDataProvider>());
+ addProvider(std::make_unique<RailEnergyDataProvider>());
+ addProvider(std::make_unique<HealthStatsDataProvider>());
+}
+
+/* static */
+PowerStatsCollector& PowerStatsCollector::getCollector() {
+ [[clang::no_destroy]] static PowerStatsCollector psc;
+ return psc;
+}
+
+std::shared_ptr<const PowerStats> PowerStatsCollector::getStats(int64_t toleranceNs) {
+ // Check if there is a cached PowerStats result available.
+ // As toleranceNs may be different between callers, it may be that some callers
+ // are blocked on mMutexExclusiveFill for a new stats result, while other callers
+ // may find the current cached result acceptable (within toleranceNs).
+ if (toleranceNs > 0) {
+ auto result = checkLastStats(toleranceNs);
+ if (result) return result;
+ }
+
+ // Take the mMutexExclusiveFill to ensure only one thread is filling.
+ std::lock_guard lg1(mMutexExclusiveFill);
+ // As obtaining a new PowerStats snapshot might take some time,
+ // check again to see if another waiting thread filled the cached result for us.
+ if (toleranceNs > 0) {
+ auto result = checkLastStats(toleranceNs);
+ if (result) return result;
+ }
+ auto result = std::make_shared<PowerStats>();
+ (void)fill(result.get());
+ std::lock_guard lg2(mMutex);
+ mLastFetchNs = systemTime(SYSTEM_TIME_BOOTTIME);
+ mLastFetchStats = result;
+ return result;
+}
+
+std::shared_ptr<const PowerStats> PowerStatsCollector::checkLastStats(int64_t toleranceNs) const {
+ if (toleranceNs > 0) {
+ // see if we can return an old result.
+ std::lock_guard lg(mMutex);
+ if (mLastFetchStats && systemTime(SYSTEM_TIME_BOOTTIME) - mLastFetchNs < toleranceNs) {
+ return mLastFetchStats;
+ }
+ }
+ return {};
+}
+
+void PowerStatsCollector::addProvider(std::unique_ptr<PowerStatsProvider>&& powerStatsProvider) {
+ mPowerStatsProviders.emplace_back(std::move(powerStatsProvider));
+}
+
+int PowerStatsCollector::fill(PowerStats* stats) const {
+ if (!stats) {
+ LOG(ERROR) << __func__ << ": bad args; stat is null";
+ return 1;
+ }
+
+ for (const auto& provider : mPowerStatsProviders) {
+ if (provider->fill(stats) != 0) {
+ LOG(ERROR) << __func__ << ": a data provider failed";
+ continue;
+ }
+ }
+
+ // boot time follows wall clock time, but starts from boot.
+ stats->metadata.start_time_since_boot_ms = systemTime(SYSTEM_TIME_BOOTTIME) / 1'000'000;
+
+ // wall clock time
+ stats->metadata.start_time_epoch_ms = systemTime(SYSTEM_TIME_REALTIME) / 1'000'000;
+
+ // monotonic time follows boot time, but does not include any time suspended.
+ stats->metadata.start_time_monotonic_ms = systemTime() / 1'000'000;
+ return 0;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/PowerStatsProvider.cpp b/media/psh_utils/PowerStatsProvider.cpp
new file mode 100644
index 0000000..94df933
--- /dev/null
+++ b/media/psh_utils/PowerStatsProvider.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PowerStatsProvider.h"
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <unordered_map>
+#include <aidl/android/hardware/power/stats/IPowerStats.h>
+
+using ::aidl::android::hardware::power::stats::IPowerStats;
+
+namespace android::media::psh_utils {
+
+static auto getPowerStatsService() {
+ [[clang::no_destroy]] static constinit std::mutex m;
+ [[clang::no_destroy]] static constinit
+ std::shared_ptr<IPowerStats> powerStats;
+
+ std::lock_guard l(m);
+ if (powerStats) {
+ return powerStats;
+ }
+ const auto serviceName =
+ std::string(IPowerStats::descriptor)
+ .append("/default");
+ powerStats = IPowerStats::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
+ return powerStats;
+}
+
+status_t RailEnergyDataProvider::fill(PowerStats *stat) const {
+ if (stat == nullptr) return BAD_VALUE;
+ auto powerStatsService = getPowerStatsService();
+ if (powerStatsService == nullptr) {
+ LOG(ERROR) << "unable to get power.stats AIDL service";
+ return NO_INIT;
+ }
+
+ std::unordered_map<int32_t, ::aidl::android::hardware::power::stats::Channel> channelMap;
+ {
+ std::vector<::aidl::android::hardware::power::stats::Channel> channels;
+ if (!powerStatsService->getEnergyMeterInfo(&channels).isOk()) {
+ LOG(ERROR) << "unable to get energy meter info";
+ return INVALID_OPERATION;
+ }
+ for (auto& channel : channels) {
+ channelMap.emplace(channel.id, std::move(channel));
+ }
+ }
+
+ std::vector<::aidl::android::hardware::power::stats::EnergyMeasurement> measurements;
+ if (!powerStatsService->readEnergyMeter({}, &measurements).isOk()) {
+ LOG(ERROR) << "unable to get energy measurements";
+ return INVALID_OPERATION;
+ }
+
+ for (const auto& measurement : measurements) {
+ stat->rail_energy.emplace_back(
+ channelMap.at(measurement.id).subsystem,
+ channelMap.at(measurement.id).name,
+ measurement.energyUWs);
+ }
+
+ // Sort entries first by subsystem_name, then by rail_name.
+ // Sorting is needed to make interval processing efficient.
+ std::sort(stat->rail_energy.begin(), stat->rail_energy.end(),
+ [](const auto& a, const auto& b) {
+ if (a.subsystem_name != b.subsystem_name) {
+ return a.subsystem_name < b.subsystem_name;
+ }
+ return a.rail_name < b.rail_name;
+ });
+
+ return NO_ERROR;
+}
+
+status_t PowerEntityResidencyDataProvider::fill(PowerStats* stat) const {
+ if (stat == nullptr) return BAD_VALUE;
+ auto powerStatsService = getPowerStatsService();
+ if (powerStatsService == nullptr) {
+ LOG(ERROR) << "unable to get power.stats AIDL service";
+ return NO_INIT;
+ }
+
+ // these are based on entityId
+ std::unordered_map<int32_t, std::string> entityNames;
+ std::unordered_map<int32_t, std::unordered_map<int32_t, std::string>> stateNames;
+ std::vector<int32_t> powerEntityIds; // ids to use
+
+ {
+ std::vector<::aidl::android::hardware::power::stats::PowerEntity> entities;
+ if (!powerStatsService->getPowerEntityInfo(&entities).isOk()) {
+ LOG(ERROR) << __func__ << ": unable to get entity info";
+ return INVALID_OPERATION;
+ }
+
+ std::vector<std::string> powerEntityNames;
+ for (const auto& entity : entities) {
+ std::unordered_map<int32_t, std::string> states;
+ for (const auto& state : entity.states) {
+ states.emplace(state.id, state.name);
+ }
+
+ if (std::find(powerEntityNames.begin(), powerEntityNames.end(), entity.name) !=
+ powerEntityNames.end()) {
+ powerEntityIds.emplace_back(entity.id);
+ }
+ entityNames.emplace(entity.id, std::move(entity.name));
+ stateNames.emplace(entity.id, std::move(states));
+ }
+ }
+
+ std::vector<::aidl::android::hardware::power::stats::StateResidencyResult> results;
+ if (!powerStatsService->getStateResidency(powerEntityIds, &results).isOk()) {
+ LOG(ERROR) << __func__ << ": Unable to get state residency";
+ return INVALID_OPERATION;
+ }
+
+ for (const auto& result : results) {
+ for (const auto& curStateResidency : result.stateResidencyData) {
+ stat->power_entity_state_residency.emplace_back(
+ entityNames.at(result.id),
+ stateNames.at(result.id).at(curStateResidency.id),
+ static_cast<uint64_t>(curStateResidency.totalTimeInStateMs),
+ static_cast<uint64_t>(curStateResidency.totalStateEntryCount));
+ }
+ }
+
+ // Sort entries first by entity_name, then by state_name.
+ // Sorting is needed to make interval processing efficient.
+ std::sort(stat->power_entity_state_residency.begin(),
+ stat->power_entity_state_residency.end(),
+ [](const auto& a, const auto& b) {
+ if (a.entity_name != b.entity_name) {
+ return a.entity_name < b.entity_name;
+ }
+ return a.state_name < b.state_name;
+ });
+ return NO_ERROR;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/PowerStatsProvider.h b/media/psh_utils/PowerStatsProvider.h
new file mode 100644
index 0000000..5f5a506
--- /dev/null
+++ b/media/psh_utils/PowerStatsProvider.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <psh_utils/PowerStatsCollector.h>
+#include <iostream>
+
+namespace android::media::psh_utils {
+
+class RailEnergyDataProvider : public PowerStatsProvider {
+public:
+ status_t fill(PowerStats* stat) const override;
+};
+
+class PowerEntityResidencyDataProvider : public PowerStatsProvider {
+public:
+ status_t fill(PowerStats* stat) const override;
+};
+
+class HealthStatsDataProvider : public PowerStatsProvider {
+public:
+ status_t fill(PowerStats* stat) const override;
+};
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/benchmarks/Android.bp b/media/psh_utils/benchmarks/Android.bp
new file mode 100644
index 0000000..20efaa9
--- /dev/null
+++ b/media/psh_utils/benchmarks/Android.bp
@@ -0,0 +1,51 @@
+package {
+ default_team: "trendy_team_android_media_audio_framework",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_benchmark {
+ name: "audio_powerstats_benchmark",
+
+ srcs: ["audio_powerstats_benchmark.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libaudioutils",
+ "libpshutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
+
+cc_benchmark {
+ name: "audio_powerstatscollector_benchmark",
+
+ srcs: ["audio_powerstatscollector_benchmark.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libaudioutils",
+ "libpshutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/media/psh_utils/benchmarks/audio_powerstats_benchmark.cpp b/media/psh_utils/benchmarks/audio_powerstats_benchmark.cpp
new file mode 100644
index 0000000..d3f815c
--- /dev/null
+++ b/media/psh_utils/benchmarks/audio_powerstats_benchmark.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_powerstat_benchmark"
+#include <cutils/properties.h>
+#include <utils/Log.h>
+
+#include <psh_utils/PerformanceFixture.h>
+
+#include <algorithm>
+#include <android-base/strings.h>
+#include <random>
+#include <thread>
+#include <vector>
+
+float result = 0;
+
+using android::media::psh_utils::CoreClass;
+using android::media::psh_utils::CORE_LITTLE;
+using android::media::psh_utils::CORE_MID;
+using android::media::psh_utils::CORE_BIG;
+
+enum Direction {
+ DIRECTION_FORWARD,
+ DIRECTION_BACKWARD,
+ DIRECTION_RANDOM,
+};
+
+std::string toString(Direction direction) {
+ switch (direction) {
+ case DIRECTION_FORWARD: return "DIRECTION_FORWARD";
+ case DIRECTION_BACKWARD: return "DIRECTION_BACKWARD";
+ case DIRECTION_RANDOM: return "DIRECTION_RANDOM";
+ default: return "DIRECTION_UNKNOWN";
+ }
+}
+
+enum Content {
+ CONTENT_ZERO,
+ CONTENT_RANDOM,
+};
+
+std::string toString(Content content) {
+ switch (content) {
+ case CONTENT_ZERO: return "CONTENT_ZERO";
+ case CONTENT_RANDOM: return "CONTENT_RANDOM";
+ default: return "CONTENT_UNKNOWN";
+ }
+}
+
+class MemoryFixture : public android::media::psh_utils::PerformanceFixture {
+public:
+ void SetUp(benchmark::State& state) override {
+ mCount = state.range(0) / (sizeof(uint32_t) + sizeof(float));
+ state.SetComplexityN(mCount * 2); // 2 access per iteration.
+
+ // create src distribution
+ mSource.resize(mCount);
+ const auto content = static_cast<Content>(state.range(3));
+ if (content == CONTENT_RANDOM) {
+ std::minstd_rand gen(mCount);
+ std::uniform_real_distribution<float> dis(-1.f, 1.f);
+ for (size_t i = 0; i < mCount; i++) {
+ mSource[i] = dis(gen);
+ }
+ }
+
+ // create direction
+ mIndex.resize(mCount);
+ const auto direction = static_cast<Direction>(state.range(2));
+ switch (direction) {
+ case DIRECTION_BACKWARD:
+ for (size_t i = 0; i < mCount; i++) {
+ mIndex[i] = i; // it is also possible to go in the reverse direction
+ }
+ break;
+ case DIRECTION_FORWARD:
+ case DIRECTION_RANDOM:
+ for (size_t i = 0; i < mCount; i++) {
+ mIndex[i] = i; // it is also possible to go in the reverse direction
+ }
+ if (direction == DIRECTION_RANDOM) {
+ std::random_device rd;
+ std::mt19937 g(rd());
+ std::shuffle(mIndex.begin(), mIndex.end(), g);
+ }
+ break;
+ }
+
+ // set up the profiler
+ const auto coreClass = static_cast<CoreClass>(state.range(1));
+
+ // It would be best if we could override SetName() but it is too late at this point,
+ // so we set the state label here for clarity.
+ state.SetLabel(toString(coreClass).append("/")
+ .append(toString(direction)).append("/")
+ .append(toString(content)));
+
+ if (property_get_bool("persist.audio.benchmark_profile", false)) {
+ startProfiler(coreClass);
+ }
+ }
+ size_t mCount = 0;
+ std::vector<uint32_t> mIndex;
+ std::vector<float> mSource;
+};
+
+BENCHMARK_DEFINE_F(MemoryFixture, CacheAccess)(benchmark::State &state) {
+ float accum = 0;
+ while (state.KeepRunning()) {
+ for (size_t i = 0; i < mCount; ++i) {
+ accum += mSource[mIndex[i]];
+ }
+ benchmark::ClobberMemory();
+ }
+ result += accum; // not optimized
+}
+
+BENCHMARK_REGISTER_F(MemoryFixture, CacheAccess)->ArgsProduct({
+ benchmark::CreateRange(64, 64<<20, /* multi = */2),
+ {CORE_LITTLE, CORE_MID, CORE_BIG},
+ {DIRECTION_FORWARD, DIRECTION_RANDOM},
+ {CONTENT_RANDOM},
+});
+
+BENCHMARK_MAIN();
diff --git a/media/psh_utils/benchmarks/audio_powerstatscollector_benchmark.cpp b/media/psh_utils/benchmarks/audio_powerstatscollector_benchmark.cpp
new file mode 100644
index 0000000..9e581bc
--- /dev/null
+++ b/media/psh_utils/benchmarks/audio_powerstatscollector_benchmark.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_token_benchmark"
+#include <utils/Log.h>
+
+#include <psh_utils/PowerStatsCollector.h>
+
+#include <benchmark/benchmark.h>
+
+/*
+ Pixel 8 Pro
+------------------------------------------------------------------------------------------
+ Benchmark Time CPU Iteration
+------------------------------------------------------------------------------------------
+audio_powerstatscollector_benchmark:
+ #BM_StatsToleranceMs/0 1.2005660120994434E8 ns 2532739.72 ns 100
+ #BM_StatsToleranceMs/50 1281.095987079007 ns 346.0322183913503 ns 2022168
+ #BM_StatsToleranceMs/100 459.9668862534226 ns 189.47902626735942 ns 2891307
+ #BM_StatsToleranceMs/200 233.8438662484292 ns 149.84041813854736 ns 4407343
+ #BM_StatsToleranceMs/500 184.42197142314103 ns 144.86896036787098 ns 7295167
+*/
+
+// We check how expensive it is to query stats depending
+// on the tolerance to reuse the cached values.
+// A tolerance of 0 means we always fetch stats.
+static void BM_StatsToleranceMs(benchmark::State& state) {
+ auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
+ const int64_t toleranceNs = state.range(0) * 1'000'000;
+ while (state.KeepRunning()) {
+ collector.getStats(toleranceNs);
+ benchmark::ClobberMemory();
+ }
+}
+
+// Here we test various time tolerances (given in milliseconds here)
+BENCHMARK(BM_StatsToleranceMs)->Arg(0)->Arg(50)->Arg(100)->Arg(200)->Arg(500);
+
+BENCHMARK_MAIN();
diff --git a/media/psh_utils/include/psh_utils/HealthStats.h b/media/psh_utils/include/psh_utils/HealthStats.h
new file mode 100644
index 0000000..d7a8d1a
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/HealthStats.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android::media::psh_utils {
+
+// From hardware/interfaces/health/aidl/android/hardware/health/HealthInfo.aidl
+
+struct HealthStats {
+ /**
+ * Instantaneous battery voltage in millivolts (mV).
+ *
+ * Historically, the unit of this field is microvolts (µV), but all
+ * clients and implementations uses millivolts in practice, making it
+ * the de-facto standard.
+ */
+ double batteryVoltageMillivolts;
+ /**
+ * Battery charge value when it is considered to be "full" in µA-h
+ */
+ double batteryFullChargeUah;
+ /**
+ * Instantaneous battery capacity in µA-h
+ */
+ double batteryChargeCounterUah;
+
+ std::string normalizedEnergy(double time) const;
+
+ bool isValid() const { return batteryVoltageMillivolts > 0; }
+
+ // Returns {seconds, joules, watts} from battery counters
+ std::tuple<float, float, float> energyFrom(const std::string& s) const;
+ std::string toString() const;
+
+ HealthStats operator+=(const HealthStats& other);
+ HealthStats operator-=(const HealthStats& other);
+ HealthStats operator+(const HealthStats& other) const;
+ HealthStats operator-(const HealthStats& other) const;
+ bool operator==(const HealthStats& other) const = default;
+};
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/include/psh_utils/PerformanceFixture.h b/media/psh_utils/include/psh_utils/PerformanceFixture.h
new file mode 100644
index 0000000..092a508
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/PerformanceFixture.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <audio_utils/threads.h>
+#include <benchmark/benchmark.h>
+#include <psh_utils/PowerStats.h>
+#include <psh_utils/PowerStatsCollector.h>
+
+#include <future>
+
+namespace android::media::psh_utils {
+
+enum CoreClass {
+ CORE_LITTLE,
+ CORE_MID,
+ CORE_BIG,
+};
+
+inline std::string toString(CoreClass coreClass) {
+ switch (coreClass) {
+ case CORE_LITTLE: return "LITTLE";
+ case CORE_MID: return "MID";
+ case CORE_BIG: return "BIG";
+ default: return "UNKNOWN";
+ }
+}
+
+/**
+ * A benchmark fixture is used to specify benchmarks that have a custom SetUp() and
+ * TearDown(). This is **required** for performance testing, as a typical benchmark
+ * method **may be called several times** during a run.
+ *
+ * A fixture ensures that SetUp() and TearDown() and the resulting statistics accumulation
+ * is done only once. Note: BENCHMARK(BM_func)->Setup(DoSetup)->Teardown(DoTeardown)
+ * does something similar, but it requires some singleton to contain the state properly.
+ */
+class PerformanceFixture : public benchmark::Fixture {
+public:
+ // call this to start the profiling
+ virtual void startProfiler(CoreClass coreClass) {
+ mCores = android::audio_utils::get_number_cpus();
+ if (mCores == 0) return;
+ mCoreClass = coreClass;
+ std::array<unsigned, 3> coreSelection{0U, mCores / 2 + 1, mCores - 1};
+ mCore = coreSelection[std::min((size_t)coreClass, std::size(coreSelection) - 1)];
+
+ auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
+ mStartStats = collector.getStats();
+
+ const pid_t tid = gettid(); // us.
+
+ // Possibly change priority to improve benchmarking
+ // android::audio_utils::set_thread_priority(gettid(), 98);
+
+ android::audio_utils::set_thread_affinity(0 /* pid */, 1 << mCore);
+ }
+
+ void TearDown(benchmark::State &state) override {
+ const auto N = state.complexity_length_n();
+ state.counters["N"] = benchmark::Counter(N,
+ benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::OneK::kIs1024);
+ if (mStartStats) {
+ auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
+ const auto stopStats = collector.getStats();
+ android::media::psh_utils::PowerStats diff = *stopStats - *mStartStats;
+ auto cpuEnergy = diff.energyFrom("CPU");
+ auto memEnergy = diff.energyFrom("MEM");
+
+ constexpr float kMwToW = 1e-3;
+ state.counters["WCPU"] = benchmark::Counter(std::get<2>(cpuEnergy) * kMwToW,
+ benchmark::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000);
+ state.counters["WMem"] = benchmark::Counter(std::get<2>(memEnergy) * kMwToW,
+ benchmark::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000);
+ state.counters["JCPU"] = benchmark::Counter(
+ std::get<1>(cpuEnergy) / N / state.iterations(), benchmark::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000);
+ state.counters["JMem"] = benchmark::Counter(
+ std::get<1>(memEnergy) / N / state.iterations(), benchmark::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000);
+ }
+ }
+
+protected:
+ // these are only initialized upon startProfiler.
+ unsigned mCores = 0;
+ int mCore = 0;
+ CoreClass mCoreClass = CORE_LITTLE;
+ std::shared_ptr<const android::media::psh_utils::PowerStats> mStartStats;
+};
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/include/psh_utils/PowerStats.h b/media/psh_utils/include/psh_utils/PowerStats.h
new file mode 100644
index 0000000..ae48606
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/PowerStats.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "HealthStats.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android::media::psh_utils {
+
+// See powerstats_util.proto and powerstats_util.pb.h
+
+struct PowerStats {
+ struct Metadata {
+ // Represents the start time measured in milliseconds since boot of the
+ // interval or point in time when stats were gathered.
+ uint64_t start_time_since_boot_ms;
+
+ // Represents the start time measured in milliseconds since epoch of the
+ // interval or point in time when stats were gathered.
+ uint64_t start_time_epoch_ms;
+
+ // In monotonic clock.
+ uint64_t start_time_monotonic_ms;
+
+ // If PowerStats represent an interval, the duration field will be set will
+ // the millisecond duration of stats collection. It will be unset for point
+ // stats.
+ // This is in boottime.
+ uint64_t duration_ms;
+
+ // This is in monotonic time, and does not include suspend.
+ uint64_t duration_monotonic_ms;
+
+ std::string toString() const;
+
+ Metadata operator+=(const Metadata& other);
+ Metadata operator-=(const Metadata& other);
+ Metadata operator+(const Metadata& other) const;
+ Metadata operator-(const Metadata& other) const;
+ bool operator==(const Metadata& other) const = default;
+ };
+
+ struct StateResidency {
+ std::string entity_name;
+ std::string state_name;
+ uint64_t time_ms;
+ uint64_t entry_count;
+
+ std::string toString() const;
+
+ StateResidency operator+=(const StateResidency& other);
+ StateResidency operator-=(const StateResidency& other);
+ StateResidency operator+(const StateResidency& other) const;
+ StateResidency operator-(const StateResidency& other) const;
+ bool operator==(const StateResidency& other) const = default;
+ };
+
+ struct RailEnergy {
+ std::string subsystem_name;
+ std::string rail_name;
+ uint64_t energy_uws;
+
+ std::string toString() const;
+ RailEnergy operator+=(const RailEnergy& other);
+ RailEnergy operator-=(const RailEnergy& other);
+ RailEnergy operator+(const RailEnergy& other) const;
+ RailEnergy operator-(const RailEnergy& other) const;
+ bool operator==(const RailEnergy& other) const = default;
+ };
+
+ HealthStats health_stats;
+
+ std::string normalizedEnergy(const std::string& prefix = {}) const;
+
+ // Returns {seconds, joules, watts} from all rails containing a matching string.
+ std::tuple<float, float, float> energyFrom(const std::string& railMatcher) const;
+ std::string toString(const std::string& prefix = {}) const;
+
+ PowerStats operator+=(const PowerStats& other);
+ PowerStats operator-=(const PowerStats& other);
+ PowerStats operator+(const PowerStats& other) const;
+ PowerStats operator-(const PowerStats& other) const;
+ bool operator==(const PowerStats& other) const = default;
+
+ Metadata metadata{};
+ // These are sorted by name.
+ std::vector<StateResidency> power_entity_state_residency;
+ std::vector<RailEnergy> rail_energy;
+};
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/include/psh_utils/PowerStatsCollector.h b/media/psh_utils/include/psh_utils/PowerStatsCollector.h
new file mode 100644
index 0000000..e3f8ea8
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/PowerStatsCollector.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "PowerStats.h"
+#include <android-base/thread_annotations.h>
+#include <memory>
+#include <utils/Errors.h> // status_t
+
+namespace android::media::psh_utils {
+
+// Internal providers that fill up the PowerStats state object.
+class PowerStatsProvider {
+public:
+ virtual ~PowerStatsProvider() = default;
+ virtual status_t fill(PowerStats* stat) const = 0;
+};
+
+class PowerStatsCollector {
+public:
+ // singleton getter
+ static PowerStatsCollector& getCollector();
+
+ // Returns a snapshot of the state.
+ // If toleranceNs > 0, we permit the use of a stale snapshot taken within that tolerance.
+ std::shared_ptr<const PowerStats> getStats(int64_t toleranceNs = 0)
+ EXCLUDES(mMutex, mMutexExclusiveFill);
+
+private:
+ PowerStatsCollector(); // use the singleton getter
+
+ // Returns non-empty PowerStats if we have a previous stats snapshot within toleranceNs.
+ std::shared_ptr<const PowerStats> checkLastStats(int64_t toleranceNs) const EXCLUDES(mMutex);
+ int fill(PowerStats* stats) const;
+ void addProvider(std::unique_ptr<PowerStatsProvider>&& powerStatsProvider);
+
+ mutable std::mutex mMutexExclusiveFill;
+ mutable std::mutex mMutex;
+ // addProvider is called in the ctor, so effectively const.
+ std::vector<std::unique_ptr<PowerStatsProvider>> mPowerStatsProviders;
+ int64_t mLastFetchNs GUARDED_BY(mMutex) = 0;
+ std::shared_ptr<const PowerStats> mLastFetchStats GUARDED_BY(mMutex);
+};
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/tests/Android.bp b/media/psh_utils/tests/Android.bp
new file mode 100644
index 0000000..74589f8
--- /dev/null
+++ b/media/psh_utils/tests/Android.bp
@@ -0,0 +1,26 @@
+package {
+ default_team: "trendy_team_media_framework_audio",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+ name: "powerstats_collector_tests",
+ srcs: [
+ "powerstats_collector_tests.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libpshutils",
+ ],
+}
diff --git a/media/psh_utils/tests/powerstats_collector_tests.cpp b/media/psh_utils/tests/powerstats_collector_tests.cpp
new file mode 100644
index 0000000..35c264a
--- /dev/null
+++ b/media/psh_utils/tests/powerstats_collector_tests.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <psh_utils/PowerStatsCollector.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::media::psh_utils;
+
+template <typename T>
+void inRange(const T& a, const T& b, const T& c) {
+ ASSERT_GE(a, std::min(b, c));
+ ASSERT_LE(a, std::max(b, c));
+}
+
+TEST(powerstat_collector_tests, basic) {
+ auto& psc = PowerStatsCollector::getCollector();
+
+ // This test is used for debugging the string through logcat, we validate a non-empty string.
+ auto powerStats = psc.getStats();
+ ALOGD("%s: %s", __func__, powerStats->toString().c_str());
+ EXPECT_FALSE(powerStats->toString().empty());
+}
+
+TEST(powerstat_collector_tests, metadata) {
+ PowerStats ps1, ps2;
+
+ constexpr uint64_t kDurationMs1 = 5;
+ constexpr uint64_t kDurationMs2 = 10;
+ ps1.metadata.duration_ms = kDurationMs1;
+ ps2.metadata.duration_ms = kDurationMs2;
+
+ constexpr uint64_t kDurationMonotonicMs1 = 3;
+ constexpr uint64_t kDurationMonotonicMs2 = 9;
+ ps1.metadata.duration_monotonic_ms = kDurationMonotonicMs1;
+ ps2.metadata.duration_monotonic_ms = kDurationMonotonicMs2;
+
+ constexpr uint64_t kStartTimeSinceBootMs1 = 1616;
+ constexpr uint64_t kStartTimeEpochMs1 = 1121;
+ constexpr uint64_t kStartTimeMonotonicMs1 = 1525;
+ constexpr uint64_t kStartTimeSinceBootMs2 = 2616;
+ constexpr uint64_t kStartTimeEpochMs2 = 2121;
+ constexpr uint64_t kStartTimeMonotonicMs2 = 2525;
+
+ ps1.metadata.start_time_since_boot_ms = kStartTimeSinceBootMs1;
+ ps1.metadata.start_time_epoch_ms = kStartTimeEpochMs1;
+ ps1.metadata.start_time_monotonic_ms = kStartTimeMonotonicMs1;
+ ps2.metadata.start_time_since_boot_ms = kStartTimeSinceBootMs2;
+ ps2.metadata.start_time_epoch_ms = kStartTimeEpochMs2;
+ ps2.metadata.start_time_monotonic_ms = kStartTimeMonotonicMs2;
+
+ PowerStats ps3 = ps1 + ps2;
+ PowerStats ps4 = ps2 + ps1;
+ EXPECT_EQ(ps3, ps4);
+ EXPECT_EQ(kDurationMs1 + kDurationMs2,
+ ps3.metadata.duration_ms);
+ EXPECT_EQ(kDurationMonotonicMs1 + kDurationMonotonicMs2,
+ ps3.metadata.duration_monotonic_ms);
+
+ EXPECT_NO_FATAL_FAILURE(inRange(ps3.metadata.start_time_since_boot_ms,
+ kStartTimeSinceBootMs1, kStartTimeSinceBootMs2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps3.metadata.start_time_epoch_ms,
+ kStartTimeEpochMs1, kStartTimeEpochMs2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps3.metadata.start_time_monotonic_ms,
+ kStartTimeMonotonicMs1, kStartTimeMonotonicMs2));
+
+ PowerStats ps5 = ps2 - ps1;
+ EXPECT_EQ(kDurationMs2 - kDurationMs1,
+ ps5.metadata.duration_ms);
+ EXPECT_EQ(kDurationMonotonicMs2 - kDurationMonotonicMs1,
+ ps5.metadata.duration_monotonic_ms);
+
+ EXPECT_NO_FATAL_FAILURE(inRange(ps5.metadata.start_time_since_boot_ms,
+ kStartTimeSinceBootMs1, kStartTimeSinceBootMs2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps5.metadata.start_time_epoch_ms,
+ kStartTimeEpochMs1, kStartTimeEpochMs2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps5.metadata.start_time_monotonic_ms,
+ kStartTimeMonotonicMs1, kStartTimeMonotonicMs2));
+}
+
+TEST(powerstat_collector_tests, state_residency) {
+ PowerStats ps1, ps2;
+
+ constexpr uint64_t kTimeMs1 = 5;
+ constexpr uint64_t kTimeMs2 = 10;
+ constexpr uint64_t kEntryCount1 = 15;
+ constexpr uint64_t kEntryCount2 = 18;
+
+ ps1.power_entity_state_residency.emplace_back(
+ PowerStats::StateResidency{"", "", kTimeMs1, kEntryCount1});
+ ps2.power_entity_state_residency.emplace_back(
+ PowerStats::StateResidency{"", "", kTimeMs2, kEntryCount2});
+
+ PowerStats ps3 = ps1 + ps2;
+ PowerStats ps4 = ps2 + ps1;
+ EXPECT_EQ(ps3, ps4);
+ EXPECT_EQ(kTimeMs1 + kTimeMs2,
+ ps3.power_entity_state_residency[0].time_ms);
+ EXPECT_EQ(kEntryCount1 + kEntryCount2,
+ ps3.power_entity_state_residency[0].entry_count);
+
+ PowerStats ps5 = ps2 - ps1;
+ EXPECT_EQ(kTimeMs2 - kTimeMs1,
+ ps5.power_entity_state_residency[0].time_ms);
+ EXPECT_EQ(kEntryCount2 - kEntryCount1,
+ ps5.power_entity_state_residency[0].entry_count);
+}
+
+TEST(powerstat_collector_tests, rail_energy) {
+ PowerStats ps1, ps2;
+
+ constexpr uint64_t kEnergyUws1 = 5;
+ constexpr uint64_t kEnergyUws2 = 10;
+
+ ps1.rail_energy.emplace_back(
+ PowerStats::RailEnergy{"", "", kEnergyUws1});
+ ps2.rail_energy.emplace_back(
+ PowerStats::RailEnergy{"", "", kEnergyUws2});
+
+ PowerStats ps3 = ps1 + ps2;
+ PowerStats ps4 = ps2 + ps1;
+ EXPECT_EQ(ps3, ps4);
+ EXPECT_EQ(kEnergyUws1 + kEnergyUws2,
+ ps3.rail_energy[0].energy_uws);
+
+ PowerStats ps5 = ps2 - ps1;
+ EXPECT_EQ(kEnergyUws2 - kEnergyUws1,
+ ps5.rail_energy[0].energy_uws);
+}
+
+TEST(powerstat_collector_tests, health_stats) {
+ PowerStats ps1, ps2;
+
+ constexpr double kBatteryChargeCounterUah1 = 21;
+ constexpr double kBatteryChargeCounterUah2 = 25;
+ ps1.health_stats.batteryChargeCounterUah = kBatteryChargeCounterUah1;
+ ps2.health_stats.batteryChargeCounterUah = kBatteryChargeCounterUah2;
+
+ constexpr double kBatteryFullChargeUah1 = 32;
+ constexpr double kBatteryFullChargeUah2 = 33;
+ ps1.health_stats.batteryFullChargeUah = kBatteryFullChargeUah1;
+ ps2.health_stats.batteryFullChargeUah = kBatteryFullChargeUah2;
+
+ constexpr double kBatteryVoltageMillivolts1 = 42;
+ constexpr double kBatteryVoltageMillivolts2 = 43;
+ ps1.health_stats.batteryVoltageMillivolts = kBatteryVoltageMillivolts1;
+ ps2.health_stats.batteryVoltageMillivolts = kBatteryVoltageMillivolts2;
+
+ PowerStats ps3 = ps1 + ps2;
+ PowerStats ps4 = ps2 + ps1;
+ EXPECT_EQ(ps3, ps4);
+ EXPECT_EQ(kBatteryChargeCounterUah1 + kBatteryChargeCounterUah2,
+ ps3.health_stats.batteryChargeCounterUah);
+
+ EXPECT_NO_FATAL_FAILURE(inRange(ps3.health_stats.batteryFullChargeUah,
+ kBatteryFullChargeUah1, kBatteryFullChargeUah2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps3.health_stats.batteryVoltageMillivolts,
+ kBatteryVoltageMillivolts1, kBatteryVoltageMillivolts2));
+
+ PowerStats ps5 = ps2 - ps1;
+ EXPECT_EQ(kBatteryChargeCounterUah2 - kBatteryChargeCounterUah1,
+ ps5.health_stats.batteryChargeCounterUah);
+
+ EXPECT_NO_FATAL_FAILURE(inRange(ps5.health_stats.batteryFullChargeUah,
+ kBatteryFullChargeUah1, kBatteryFullChargeUah2));
+ EXPECT_NO_FATAL_FAILURE(inRange(ps5.health_stats.batteryVoltageMillivolts,
+ kBatteryVoltageMillivolts1, kBatteryVoltageMillivolts2));
+}
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index ec68de7..658191e 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <audio_utils/clock.h>
+#include <cutils/properties.h>
#include <mediautils/EventLog.h>
#include <mediautils/FixedString.h>
#include <mediautils/MethodStatistics.h>
@@ -36,6 +37,46 @@
namespace android::mediautils {
+
+// Note: The sum of kDefaultTimeOutDurationMs and kDefaultSecondChanceDurationMs
+// should be no less than 2 seconds, otherwise spurious timeouts
+// may occur with system suspend.
+static constexpr int kDefaultTimeoutDurationMs = 3000;
+
+// Due to suspend abort not incrementing the monotonic clock,
+// we allow another second chance timeout after the first timeout expires.
+//
+// The total timeout is therefore kDefaultTimeoutDuration + kDefaultSecondChanceDuration,
+// and the result is more stable when the monotonic clock increments during suspend.
+//
+static constexpr int kDefaultSecondChanceDurationMs = 2000;
+
+/* static */
+TimeCheck::Duration TimeCheck::getDefaultTimeoutDuration() {
+ static constinit std::atomic<int> defaultTimeoutDurationMs{};
+ auto defaultMs = defaultTimeoutDurationMs.load(std::memory_order_relaxed);
+ if (defaultMs == 0) {
+ defaultMs = property_get_int32(
+ "audio.timecheck.timeout_duration_ms", kDefaultTimeoutDurationMs);
+ if (defaultMs < 1) defaultMs = kDefaultTimeoutDurationMs;
+ defaultTimeoutDurationMs.store(defaultMs, std::memory_order_relaxed);
+ }
+ return std::chrono::milliseconds(defaultMs);
+}
+
+/* static */
+TimeCheck::Duration TimeCheck::getDefaultSecondChanceDuration() {
+ static constinit std::atomic<int> defaultSecondChanceDurationMs{};
+ auto defaultMs = defaultSecondChanceDurationMs.load(std::memory_order_relaxed);
+ if (defaultMs == 0) {
+ defaultMs = property_get_int32(
+ "audio.timecheck.second_chance_duration_ms", kDefaultSecondChanceDurationMs);
+ if (defaultMs < 1) defaultMs = kDefaultSecondChanceDurationMs;
+ defaultSecondChanceDurationMs.store(defaultMs, std::memory_order_relaxed);
+ }
+ return std::chrono::milliseconds(defaultMs);
+}
+
// This function appropriately signals a pid to dump a backtrace if we are
// running on device (and the HAL exists). If we are not running on an Android
// device, there is no HAL to signal (so we do nothing).
@@ -182,23 +223,25 @@
/* static */
std::string TimeCheck::analyzeTimeouts(
- float requestedTimeoutMs, float elapsedSteadyMs, float elapsedSystemMs) {
+ float requestedTimeoutMs, float secondChanceMs,
+ float elapsedSteadyMs, float elapsedSystemMs) {
// Track any OS clock issues with suspend.
// It is possible that the elapsedSystemMs is much greater than elapsedSteadyMs if
// a suspend occurs; however, we always expect the timeout ms should always be slightly
// less than the elapsed steady ms regardless of whether a suspend occurs or not.
- std::string s("Timeout ms ");
- s.append(std::to_string(requestedTimeoutMs))
- .append(" elapsed steady ms ").append(std::to_string(elapsedSteadyMs))
- .append(" elapsed system ms ").append(std::to_string(elapsedSystemMs));
+ const float totalTimeoutMs = requestedTimeoutMs + secondChanceMs;
+ std::string s = std::format(
+ "Timeout ms {:.2f} ({:.2f} + {:.2f})"
+ " elapsed steady ms {:.4f} elapsed system ms {:.4f}",
+ totalTimeoutMs, requestedTimeoutMs, secondChanceMs, elapsedSteadyMs, elapsedSystemMs);
// Is there something unusual?
static constexpr float TOLERANCE_CONTEXT_SWITCH_MS = 200.f;
- if (requestedTimeoutMs > elapsedSteadyMs || requestedTimeoutMs > elapsedSystemMs) {
+ if (totalTimeoutMs > elapsedSteadyMs || totalTimeoutMs > elapsedSystemMs) {
s.append("\nError: early expiration - "
- "requestedTimeoutMs should be less than elapsed time");
+ "totalTimeoutMs should be less than elapsed time");
}
if (elapsedSteadyMs > elapsedSystemMs + TOLERANCE_CONTEXT_SWITCH_MS) {
@@ -206,13 +249,13 @@
}
// This has been found in suspend stress testing.
- if (elapsedSteadyMs > requestedTimeoutMs + TOLERANCE_CONTEXT_SWITCH_MS) {
+ if (elapsedSteadyMs > totalTimeoutMs + TOLERANCE_CONTEXT_SWITCH_MS) {
s.append("\nWarning: steady time significantly exceeds timeout "
"- possible thread stall or aborted suspend");
}
// This has been found in suspend stress testing.
- if (elapsedSystemMs > requestedTimeoutMs + TOLERANCE_CONTEXT_SWITCH_MS) {
+ if (elapsedSystemMs > totalTimeoutMs + TOLERANCE_CONTEXT_SWITCH_MS) {
s.append("\nInformation: system time significantly exceeds timeout "
"- possible suspend");
}
@@ -282,7 +325,7 @@
.append(tag)
.append(" scheduled ").append(formatTime(startSystemTime))
.append(" on thread ").append(std::to_string(tid)).append("\n")
- .append(analyzeTimeouts(requestedTimeoutMs + secondChanceMs,
+ .append(analyzeTimeouts(requestedTimeoutMs, secondChanceMs,
elapsedSteadyMs, elapsedSystemMs)).append("\n")
.append(halPids).append("\n")
.append(snapshotAnalysis.toString());
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index f1d572f..3e8d35d 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -42,19 +42,29 @@
// float elapsedMs (the elapsed time to this event).
using OnTimerFunc = std::function<void(bool /* timeout */, float /* elapsedMs */ )>;
- // The default timeout is chosen to be less than system server watchdog timeout
- // Note: kDefaultTimeOutMs should be no less than 2 seconds, otherwise spurious timeouts
- // may occur with system suspend.
- static constexpr TimeCheck::Duration kDefaultTimeoutDuration = std::chrono::milliseconds(3000);
+ /**
+ * Returns the default timeout to use for TimeCheck.
+ *
+ * The default timeout of 3000ms (kDefaultTimeoutDurationMs) is chosen to be less than
+ * the system server watchdog timeout, and can be changed by the sysprop
+ * audio.timecheck.timeout_duration_ms.
+ * A second chance wait may be set to extend the check.
+ */
+ static TimeCheck::Duration getDefaultTimeoutDuration();
- // Due to suspend abort not incrementing the monotonic clock,
- // we allow another second chance timeout after the first timeout expires.
- //
- // The total timeout is therefore kDefaultTimeoutDuration + kDefaultSecondChanceDuration,
- // and the result is more stable when the monotonic clock increments during suspend.
- //
- static constexpr TimeCheck::Duration kDefaultSecondChanceDuration =
- std::chrono::milliseconds(2000);
+ /**
+ * Returns the second chance timeout to use for TimeCheck.
+ *
+ * Due to suspend abort not incrementing the monotonic clock,
+ * we allow another second chance timeout after the first timeout expires.
+ * The second chance timeout default of 2000ms (kDefaultSecondChanceDurationMs)
+ * may be changed by the sysprop audio.timecheck.second_chance_duration_ms.
+ *
+ * The total timeout is therefore
+ * getDefaultTimeoutDuration() + getDefaultSecondChanceDuration(),
+ * and the result is more stable when the monotonic clock increments during suspend.
+ */
+ static TimeCheck::Duration getDefaultSecondChanceDuration();
/**
* TimeCheck is a RAII object which will notify a callback
@@ -130,7 +140,8 @@
// Returns a string that represents the timeout vs elapsed time,
// and diagnostics if there are any potential issues.
static std::string analyzeTimeouts(
- float timeoutMs, float elapsedSteadyMs, float elapsedSystemMs);
+ float timeoutMs, float secondChanceMs,
+ float elapsedSteadyMs, float elapsedSystemMs);
static TimerThread& getTimeCheckThread();
static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 2abf682..bf2915a 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -141,9 +141,15 @@
cc_defaults {
name: "libaudioflinger_dependencies",
+ header_libs: [
+ "libaudiohal_headers", // required for AudioFlinger
+ ],
+
shared_libs: [
+ "audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
+ "audiopermissioncontroller",
"av-types-aidl-cpp",
"com.android.media.audio-aconfig-cc",
"effect-aidl-cpp",
@@ -177,11 +183,6 @@
"libvibrator",
"packagemanager_aidl-cpp",
],
-
- static_libs: [
- "libaudiospdif",
- "libmedialogservice",
- ],
}
cc_library {
@@ -213,23 +214,21 @@
],
static_libs: [
- "audiopermissioncontroller",
+ "libaudiospdif",
"libcpustats",
- "libpermission",
+ "libmedialogservice",
],
header_libs: [
- "audiopermissioncontroller_headers",
"audiopolicyservicelocal_headers",
"libaaudio_headers",
- "libaudioclient_headers",
- "libaudiohal_headers",
- "libaudioutils_headers",
"libmedia_headers",
],
export_header_lib_headers: ["audiopolicyservicelocal_headers"],
+ export_include_dirs: ["."],
+
export_shared_lib_headers: [
"libpermission",
],
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7a4c643..e59ff1e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -187,8 +187,6 @@
BINDER_METHOD_ENTRY(masterMute) \
BINDER_METHOD_ENTRY(setStreamVolume) \
BINDER_METHOD_ENTRY(setStreamMute) \
-BINDER_METHOD_ENTRY(streamVolume) \
-BINDER_METHOD_ENTRY(streamMute) \
BINDER_METHOD_ENTRY(setMode) \
BINDER_METHOD_ENTRY(setMicMute) \
BINDER_METHOD_ENTRY(getMicMute) \
@@ -1748,37 +1746,6 @@
return NO_ERROR;
}
-float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const
-{
- status_t status = checkStreamType(stream);
- if (status != NO_ERROR) {
- return 0.0f;
- }
- if (output == AUDIO_IO_HANDLE_NONE) {
- return 0.0f;
- }
-
- audio_utils::lock_guard lock(mutex());
- sp<VolumeInterface> volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface == NULL) {
- return 0.0f;
- }
-
- return volumeInterface->streamVolume(stream);
-}
-
-bool AudioFlinger::streamMute(audio_stream_type_t stream) const
-{
- status_t status = checkStreamType(stream);
- if (status != NO_ERROR) {
- return true;
- }
-
- audio_utils::lock_guard lock(mutex());
- return streamMute_l(stream);
-}
-
-
void AudioFlinger::broadcastParametersToRecordThreads_l(const String8& keyValuePairs)
{
for (size_t i = 0; i < mRecordThreads.size(); i++) {
@@ -4610,7 +4577,7 @@
// TODO(b/184194057): Use the vibrator information from the vibrator that will be used
// for the HapticGenerator.
const std::optional<media::AudioVibratorInfo> defaultVibratorInfo =
- std::move(getDefaultVibratorInfo_l());
+ getDefaultVibratorInfo_l();
if (defaultVibratorInfo) {
// Only set the vibrator info when it is a valid one.
audio_utils::lock_guard _cl(chain->mutex());
@@ -5245,8 +5212,8 @@
} else {
getIAudioFlingerStatistics().event(code, elapsedMs);
}
- }, mediautils::TimeCheck::kDefaultTimeoutDuration,
- mediautils::TimeCheck::kDefaultSecondChanceDuration,
+ }, mediautils::TimeCheck::getDefaultTimeoutDuration(),
+ mediautils::TimeCheck::getDefaultSecondChanceDuration(),
true /* crashOnTimeout */);
return delegate();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ca6f718..9513327 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -96,10 +96,6 @@
status_t setStreamMute(audio_stream_type_t stream, bool muted) final
EXCLUDES_AudioFlinger_Mutex;
- float streamVolume(audio_stream_type_t stream,
- audio_io_handle_t output) const final EXCLUDES_AudioFlinger_Mutex;
- bool streamMute(audio_stream_type_t stream) const final EXCLUDES_AudioFlinger_Mutex;
-
status_t setMode(audio_mode_t mode) final EXCLUDES_AudioFlinger_Mutex;
status_t setMicMute(bool state) final EXCLUDES_AudioFlinger_Mutex;
@@ -340,7 +336,9 @@
audio_output_flags_t flags,
audio_attributes_t attributes) final REQUIRES(mutex());
const DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*>&
- getAudioHwDevs_l() const final REQUIRES(mutex()) { return mAudioHwDevs; }
+ getAudioHwDevs_l() const final REQUIRES(mutex(), hardwareMutex()) {
+ return mAudioHwDevs;
+ }
void updateDownStreamPatches_l(const struct audio_patch* patch,
const std::set<audio_io_handle_t>& streams) final REQUIRES(mutex());
void updateOutDevicesForRecordThreads_l(const DeviceDescriptorBaseVector& devices) final
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index feae97e..7cb9329 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -71,10 +71,15 @@
void DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
+ // Keep a reference on disconnected handle to delay destruction without lock held.
+ std::vector<sp<IAfEffectHandle>> disconnectedHandles{};
audio_utils::lock_guard _l(mutex());
for (auto& effectProxies : mDeviceEffects) {
for (auto& effect : effectProxies.second) {
- effect->onReleasePatch(handle);
+ sp<IAfEffectHandle> disconnectedHandle = effect->onReleasePatch(handle);
+ if (disconnectedHandle != nullptr) {
+ disconnectedHandles.push_back(std::move(disconnectedHandle));
+ }
}
}
}
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 3af51d5..287d838 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -108,7 +108,6 @@
}
audio_io_handle_t io() const final { return AUDIO_IO_HANDLE_NONE; }
- bool shouldDispatchAddRemoveToHal(bool isAdded __unused) const final { return true; }
bool isOutput() const final { return false; }
bool isOffload() const final { return false; }
bool isOffloadOrDirect() const final { return false; }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index bceba4b..ec8d135 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -39,6 +39,7 @@
#include <mediautils/MethodStatistics.h>
#include <mediautils/ServiceUtilities.h>
#include <mediautils/TimeCheck.h>
+#include <system/audio_effects/audio_effects_utils.h>
#include <system/audio_effects/effect_aec.h>
#include <system/audio_effects/effect_downmix.h>
#include <system/audio_effects/effect_dynamicsprocessing.h>
@@ -70,6 +71,7 @@
namespace android {
using aidl_utils::statusTFromBinderStatus;
+using android::effect::utils::EffectParamWriter;
using audioflinger::EffectConfiguration;
using binder::Status;
@@ -617,10 +619,11 @@
}
+// return true if any effect started or stopped
bool EffectModule::updateState_l() {
audio_utils::lock_guard _l(mutex());
- bool started = false;
+ bool startedOrStopped = false;
switch (mState) {
case RESTART:
reset_l();
@@ -635,7 +638,7 @@
}
if (start_ll() == NO_ERROR) {
mState = ACTIVE;
- started = true;
+ startedOrStopped = true;
} else {
mState = IDLE;
}
@@ -655,6 +658,7 @@
// turn off sequence.
if (--mDisableWaitCnt == 0) {
reset_l();
+ startedOrStopped = true;
mState = IDLE;
}
break;
@@ -669,7 +673,7 @@
break;
}
- return started;
+ return startedOrStopped;
}
void EffectModule::process()
@@ -1040,11 +1044,25 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- if (!getCallback()->shouldDispatchAddRemoveToHal(/* isAdded= */ true)) {
+ if (mCurrentHalStream == getCallback()->io()) {
return;
}
- (void)getCallback()->addEffectToHal(mEffectInterface);
+ status_t status = getCallback()->addEffectToHal(mEffectInterface);
+ if (status == NO_ERROR) {
+ mCurrentHalStream = getCallback()->io();
+ }
+ }
+}
+
+void HwAccDeviceEffectModule::addEffectToHal_l()
+{
+ if (mAddedToHal) {
+ return;
+ }
+ status_t status = getCallback()->addEffectToHal(mEffectInterface);
+ if (status == NO_ERROR) {
+ mAddedToHal = true;
}
}
@@ -1141,14 +1159,25 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- if (!getCallback()->shouldDispatchAddRemoveToHal(/* isAdded= */ false)) {
- return (getCallback()->io() == AUDIO_IO_HANDLE_NONE) ? NO_ERROR : INVALID_OPERATION;
+ if (mCurrentHalStream != getCallback()->io()) {
+ return (mCurrentHalStream == AUDIO_IO_HANDLE_NONE) ? NO_ERROR : INVALID_OPERATION;
}
getCallback()->removeEffectFromHal(mEffectInterface);
+ mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
}
return NO_ERROR;
}
+status_t HwAccDeviceEffectModule::removeEffectFromHal_l()
+{
+ if (!mAddedToHal) {
+ return NO_ERROR;
+ }
+ getCallback()->removeEffectFromHal(mEffectInterface);
+ mAddedToHal = false;
+ return NO_ERROR;
+}
+
// round up delta valid if value and divisor are positive.
template <typename T>
static T roundUpDelta(const T &value, const T &divisor) {
@@ -1369,12 +1398,12 @@
((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_MONITOR)) {
- status = setVolumeInternal(left, right, controller);
+ status = setVolumeInternal_ll(left, right, controller);
}
return status;
}
-status_t EffectModule::setVolumeInternal(
+status_t EffectModule::setVolumeInternal_ll(
uint32_t *left, uint32_t *right, bool controller) {
if (mVolume.has_value() && *left == mVolume.value()[0] && *right == mVolume.value()[1] &&
!controller) {
@@ -1558,16 +1587,20 @@
return INVALID_OPERATION;
}
- std::vector<uint8_t> request(sizeof(effect_param_t) + 3 * sizeof(uint32_t) + sizeof(float));
- effect_param_t *param = (effect_param_t*) request.data();
- param->psize = sizeof(int32_t);
- param->vsize = sizeof(int32_t) * 2 + sizeof(float);
- *(int32_t*)param->data = HG_PARAM_HAPTIC_INTENSITY;
- int32_t* hapticScalePtr = reinterpret_cast<int32_t*>(param->data + sizeof(int32_t));
- hapticScalePtr[0] = id;
- hapticScalePtr[1] = static_cast<int32_t>(hapticScale.getLevel());
- float* adaptiveScaleFactorPtr = reinterpret_cast<float*>(param->data + 3 * sizeof(int32_t));
- *adaptiveScaleFactorPtr = hapticScale.getAdaptiveScaleFactor();
+ size_t psize = sizeof(int32_t); // HG_PARAM_HAPTIC_INTENSITY
+ size_t vsize = sizeof(int32_t) + sizeof(os::HapticScale); // id + hapticScale
+ std::vector<uint8_t> request(sizeof(effect_param_t) + psize + vsize);
+ effect_param_t *effectParam = (effect_param_t*) request.data();
+ effectParam->psize = psize;
+ effectParam->vsize = vsize;
+
+ int32_t intensityParam = static_cast<int32_t>(HG_PARAM_HAPTIC_INTENSITY);
+ EffectParamWriter writer(*effectParam);
+ writer.writeToParameter(&intensityParam);
+ writer.writeToValue(&id);
+ writer.writeToValue(&hapticScale);
+ writer.finishValueWrite();
+
std::vector<uint8_t> response;
status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
if (status == NO_ERROR) {
@@ -1586,17 +1619,21 @@
return INVALID_OPERATION;
}
- const size_t paramCount = 3;
- std::vector<uint8_t> request(
- sizeof(effect_param_t) + sizeof(int32_t) + paramCount * sizeof(float));
- effect_param_t *param = (effect_param_t*) request.data();
- param->psize = sizeof(int32_t);
- param->vsize = paramCount * sizeof(float);
- *(int32_t*)param->data = HG_PARAM_VIBRATOR_INFO;
- float* vibratorInfoPtr = reinterpret_cast<float*>(param->data + sizeof(int32_t));
- vibratorInfoPtr[0] = vibratorInfo.resonantFrequency;
- vibratorInfoPtr[1] = vibratorInfo.qFactor;
- vibratorInfoPtr[2] = vibratorInfo.maxAmplitude;
+ size_t psize = sizeof(int32_t); // HG_PARAM_VIBRATOR_INFO
+ size_t vsize = 3 * sizeof(float); // resonantFrequency + qFactor + maxAmplitude
+ std::vector<uint8_t> request(sizeof(effect_param_t) + psize + vsize);
+ effect_param_t *effectParam = (effect_param_t*) request.data();
+ effectParam->psize = psize;
+ effectParam->vsize = vsize;
+
+ int32_t infoParam = static_cast<int32_t>(HG_PARAM_VIBRATOR_INFO);
+ EffectParamWriter writer(*effectParam);
+ writer.writeToParameter(&infoParam);
+ writer.writeToValue(&vibratorInfo.resonantFrequency);
+ writer.writeToValue(&vibratorInfo.qFactor);
+ writer.writeToValue(&vibratorInfo.maxAmplitude);
+ writer.finishValueWrite();
+
std::vector<uint8_t> response;
status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
if (status == NO_ERROR) {
@@ -1733,6 +1770,9 @@
const sp<media::IEffectClient>& effectClient,
int32_t priority, bool notifyFramesProcessed)
{
+ if (client == nullptr && effectClient == nullptr) {
+ return sp<InternalEffectHandle>::make(effect, notifyFramesProcessed);
+ }
return sp<EffectHandle>::make(
effect, client, effectClient, priority, notifyFramesProcessed);
}
@@ -1740,12 +1780,14 @@
EffectHandle::EffectHandle(const sp<IAfEffectBase>& effect,
const sp<Client>& client,
const sp<media::IEffectClient>& effectClient,
- int32_t priority, bool notifyFramesProcessed)
- : BnEffect(),
+ int32_t priority, bool notifyFramesProcessed,
+ bool isInternal,
+ audio_utils::MutexOrder mutexOrder)
+ : BnEffect(), mMutex(mutexOrder),
mEffect(effect), mEffectClient(media::EffectClientAsyncProxy::makeIfNeeded(effectClient)),
mClient(client), mCblk(nullptr),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false),
- mNotifyFramesProcessed(notifyFramesProcessed)
+ mNotifyFramesProcessed(notifyFramesProcessed), mIsInternal(isInternal)
{
ALOGV("constructor %p client %p", this, client.get());
setMinSchedulerPolicy(SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
@@ -1912,7 +1954,7 @@
void EffectHandle::disconnect(bool unpinIfLast)
{
- audio_utils::lock_guard _l(mutex());
+ audio_utils::unique_lock _l(mutex());
ALOGV("disconnect(%s) %p", unpinIfLast ? "true" : "false", this);
if (mDisconnected) {
if (unpinIfLast) {
@@ -1924,11 +1966,19 @@
{
sp<IAfEffectBase> effect = mEffect.promote();
if (effect != 0) {
+ // Unlock e.g. for device effect: may need to acquire AudioFlinger lock
+ // Also Internal Effect Handle would require Proxy lock (and vice versa).
+ if (isInternal()) {
+ _l.unlock();
+ }
if (effect->disconnectHandle(this, unpinIfLast) > 0) {
ALOGW("%s Effect handle %p disconnected after thread destruction",
__func__, this);
}
effect->updatePolicyState();
+ if (isInternal()) {
+ _l.lock();
+ }
}
}
@@ -2308,6 +2358,9 @@
}
bool doResetVolume = false;
for (size_t i = 0; i < size; i++) {
+ // reset volume when any effect just started or stopped.
+ // resetVolume_l will check if the volume controller effect in the chain needs update and
+ // apply the correct volume
doResetVolume = mEffects[i]->updateState_l() || doResetVolume;
}
if (doResetVolume) {
@@ -2661,6 +2714,9 @@
true /* effect chain volume controller */);
mNewLeftVolume = newLeft;
mNewRightVolume = newRight;
+ ALOGD("%s sessionId %d volume controller effect %s set (%d, %d), ret (%d, %d)", __func__,
+ mSessionId, mEffects[ctrlIdx]->desc().name, mLeftVolume, mRightVolume, newLeft,
+ newRight);
}
// then indicate volume to all other effects in chain.
// Pass altered volume to effects before volume controller
@@ -3120,16 +3176,12 @@
return result;
}
result = st->addEffect(effect);
- if (result == OK) {
- mCurrentHalStream = t->id();
- }
ALOGE_IF(result != OK, "Error when adding effect: %d", result);
return result;
}
status_t EffectChain::EffectCallback::removeEffectFromHal(
const sp<EffectHalInterface>& effect) {
- mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
status_t result = NO_INIT;
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
@@ -3144,11 +3196,6 @@
return result;
}
-bool EffectChain::EffectCallback::shouldDispatchAddRemoveToHal(bool isAdded) const {
- const bool currentHalStreamMatchesThreadId = (io() == mCurrentHalStream);
- return isAdded != currentHalStreamMatchesThreadId;
-}
-
audio_io_handle_t EffectChain::EffectCallback::io() const {
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
@@ -3510,19 +3557,17 @@
ALOGV("%s reusing HAL effect", __func__);
} else {
mDevicePort = *port;
- mHalEffect = new EffectModule(mMyCallback,
- const_cast<effect_descriptor_t *>(&mDescriptor),
- mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE,
- false /* pinned */, port->id);
+ mHalEffect = sp<HwAccDeviceEffectModule>::make(mMyCallback,
+ const_cast<effect_descriptor_t *>(&mDescriptor), mMyCallback->newEffectId(),
+ port->id);
+ mHalEffect->configure_l();
if (audio_is_input_device(mDevice.mType)) {
mHalEffect->setInputDevice(mDevice);
} else {
mHalEffect->setDevices({mDevice});
}
- mHalEffect->configure_l();
}
- *handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/,
- mNotifyFramesProcessed);
+ *handle = sp<InternalEffectHandle>::make(mHalEffect, mNotifyFramesProcessed);
status = (*handle)->initCheck();
if (status == OK) {
status = mHalEffect->addHandle((*handle).get());
@@ -3569,15 +3614,16 @@
return status;
}
-void DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
- sp<IAfEffectHandle> effect;
+sp<IAfEffectHandle> DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
+ sp<IAfEffectHandle> disconnectedHandle;
{
audio_utils::lock_guard _l(proxyMutex());
if (mEffectHandles.find(patchHandle) != mEffectHandles.end()) {
- effect = mEffectHandles.at(patchHandle);
+ disconnectedHandle = std::move(mEffectHandles.at(patchHandle));
mEffectHandles.erase(patchHandle);
}
}
+ return disconnectedHandle;
}
@@ -3741,14 +3787,11 @@
if (proxy == nullptr) {
return NO_INIT;
}
- status_t ret = proxy->addEffectToHal(effect);
- mAddedToHal = (ret == OK);
- return ret;
+ return proxy->addEffectToHal(effect);
}
status_t DeviceEffectProxy::ProxyCallback::removeEffectFromHal(
const sp<EffectHalInterface>& effect) {
- mAddedToHal = false;
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return NO_INIT;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 549cff2..9ecf89e 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -179,7 +179,7 @@
// the attached track(s) to accumulate their auxiliary channel.
class EffectModule : public IAfEffectModule, public EffectBase {
public:
- EffectModule(const sp<EffectCallbackInterface>& callabck,
+ EffectModule(const sp<EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -228,7 +228,7 @@
REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex;
bool isOffloaded_l() const final
REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex;
- void addEffectToHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+ void addEffectToHal_l() override REQUIRES(audio_utils::EffectChain_Mutex);
void release_l(const std::string& from = "") final REQUIRES(audio_utils::EffectChain_Mutex);
sp<IAfEffectModule> asEffectModule() final { return this; }
@@ -250,6 +250,9 @@
void dump(int fd, const Vector<String16>& args) const final;
+protected:
+ sp<EffectHalInterface> mEffectInterface; // Effect module HAL
+
private:
// Maximum time allocated to effect engines to complete the turn off sequence
@@ -259,18 +262,18 @@
status_t start_ll() REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
status_t stop_ll() REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
- status_t removeEffectFromHal_l() REQUIRES(audio_utils::EffectChain_Mutex);
+ status_t removeEffectFromHal_l() override REQUIRES(audio_utils::EffectChain_Mutex);
status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);
effect_buffer_access_e requiredEffectBufferAccessMode() const {
return mConfig.inputCfg.buffer.raw == mConfig.outputCfg.buffer.raw
? EFFECT_BUFFER_ACCESS_WRITE : EFFECT_BUFFER_ACCESS_ACCUMULATE;
}
- status_t setVolumeInternal(uint32_t* left, uint32_t* right,
- bool controller /* the volume controller effect of the chain */);
+ status_t setVolumeInternal_ll(uint32_t* left, uint32_t* right,
+ bool controller /* the volume controller effect of the chain */)
+ REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
effect_config_t mConfig; // input and output audio configuration
- sp<EffectHalInterface> mEffectInterface; // Effect module HAL
sp<EffectBufferHalInterface> mInBuffer; // Buffers for interacting with HAL
sp<EffectBufferHalInterface> mOutBuffer;
status_t mStatus; // initialization status
@@ -279,6 +282,8 @@
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mOffloaded; // effect is currently offloaded to the audio DSP
+ // effect has been added to this HAL input stream
+ audio_io_handle_t mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
bool mIsOutput; // direction of the AF thread
bool mSupportsFloat; // effect supports float processing
@@ -290,12 +295,12 @@
template <typename MUTEX>
class AutoLockReentrant {
public:
- AutoLockReentrant(MUTEX& mutex, pid_t allowedTid)
+ AutoLockReentrant(MUTEX& mutex, pid_t allowedTid) ACQUIRE(audio_utils::EffectBase_Mutex)
: mMutex(gettid() == allowedTid ? nullptr : &mutex)
{
if (mMutex != nullptr) mMutex->lock();
}
- ~AutoLockReentrant() {
+ ~AutoLockReentrant() RELEASE(audio_utils::EffectBase_Mutex) {
if (mMutex != nullptr) mMutex->unlock();
}
private:
@@ -312,7 +317,19 @@
// here is used to indicate the volume to apply before this effect.
std::optional<std::vector<uint32_t>> mReturnedVolume;
// TODO: b/315995877, remove this debugging string after root cause
- std::string mEffectInterfaceDebug;
+ std::string mEffectInterfaceDebug GUARDED_BY(audio_utils::EffectChain_Mutex);
+};
+
+class HwAccDeviceEffectModule : public EffectModule {
+public:
+ HwAccDeviceEffectModule(const sp<EffectCallbackInterface>& callback, effect_descriptor_t *desc,
+ int id, audio_port_handle_t deviceId) :
+ EffectModule(callback, desc, id, AUDIO_SESSION_DEVICE, /* pinned */ false, deviceId) {}
+ void addEffectToHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+
+private:
+ status_t removeEffectFromHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+ bool mAddedToHal = false;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -327,7 +344,8 @@
EffectHandle(const sp<IAfEffectBase>& effect,
const sp<Client>& client,
const sp<media::IEffectClient>& effectClient,
- int32_t priority, bool notifyFramesProcessed);
+ int32_t priority, bool notifyFramesProcessed, bool isInternal = false,
+ audio_utils::MutexOrder mutexOrder = audio_utils::MutexOrder::kEffectHandle_Mutex);
~EffectHandle() override;
status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) final;
@@ -347,6 +365,11 @@
int32_t* _aidl_return) final;
const sp<Client>& client() const final { return mClient; }
+ /**
+ * Checks if the handle is internal, aka created by AudioFlinger for internal needs (e.g.
+ * device effect HAL handle or device effect thread handle).
+ */
+ virtual bool isInternal() const { return mIsInternal; }
sp<android::media::IEffect> asIEffect() final {
return sp<android::media::IEffect>::fromExisting(this);
@@ -384,15 +407,18 @@
void dumpToBuffer(char* buffer, size_t size) const final;
+protected:
+ // protects IEffect method calls
+ mutable audio_utils::mutex mMutex;
private:
DISALLOW_COPY_AND_ASSIGN(EffectHandle);
- audio_utils::mutex& mutex() const RETURN_CAPABILITY(android::audio_utils::EffectHandle_Mutex) {
+ virtual audio_utils::mutex& mutex() const
+ RETURN_CAPABILITY(android::audio_utils::EffectHandle_Mutex) {
return mMutex;
}
- // protects IEffect method calls
- mutable audio_utils::mutex mMutex{audio_utils::MutexOrder::kEffectHandle_Mutex};
+
const wp<IAfEffectBase> mEffect; // pointer to controlled EffectModule
const sp<media::IEffectClient> mEffectClient; // callback interface for client notifications
/*const*/ sp<Client> mClient; // client for shared memory allocation, see
@@ -408,6 +434,28 @@
bool mDisconnected; // Set to true by disconnect()
const bool mNotifyFramesProcessed; // true if the client callback event
// EVENT_FRAMES_PROCESSED must be generated
+ const bool mIsInternal;
+};
+
+/**
+ * There are 2 types of effects:
+ * -Session Effect: handle is directly called from the client, without AudioFlinger lock.
+ * -Device Effect: a device effect proxy is aggregating a collection of internal effect handles that
+ * controls the same effect added on all audio patches involving the device effect selected port
+ * requested either by a client or by AudioPolicyEffects. These internal effect handles do not have
+ * client. Sequence flow implies a different locking order, hence the lock is specialied.
+ */
+class InternalEffectHandle : public EffectHandle {
+public:
+ InternalEffectHandle(const sp<IAfEffectBase>& effect, bool notifyFramesProcessed) :
+ EffectHandle(effect, /* client= */ nullptr, /* effectClient= */ nullptr,
+ /* priority= */ 0, notifyFramesProcessed, /* isInternal */ true,
+ audio_utils::MutexOrder::kDeviceEffectHandle_Mutex) {}
+
+ virtual audio_utils::mutex& mutex() const
+ RETURN_CAPABILITY(android::audio_utils::DeviceEffectHandle_Mutex) {
+ return mMutex;
+ }
};
// the EffectChain class represents a group of effects associated to one audio session.
@@ -594,7 +642,6 @@
status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
bool updateOrphanEffectChains(const sp<IAfEffectBase>& effect) override;
- bool shouldDispatchAddRemoveToHal(bool isAdded) const override;
audio_io_handle_t io() const override;
bool isOutput() const override;
bool isOffload() const override;
@@ -652,8 +699,6 @@
mediautils::atomic_wp<IAfThreadBase> mThread;
sp<IAfThreadCallback> mAfThreadCallback;
IAfThreadBase::type_t mThreadType = IAfThreadBase::MIXER;
- // effect has been added to this HAL input stream
- audio_io_handle_t mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
};
DISALLOW_COPY_AND_ASSIGN(EffectChain);
@@ -714,7 +759,7 @@
uint32_t mRightVolume; // previous volume on right channel
uint32_t mNewLeftVolume; // new volume on left channel
uint32_t mNewRightVolume; // new volume on right channel
- product_strategy_t mStrategy; // strategy for this effect chain
+ product_strategy_t mStrategy = PRODUCT_STRATEGY_NONE; // strategy for this effect chain
// mSuspendedEffects lists all effects currently suspended in the chain.
// Use effect type UUID timelow field as key. There is no real risk of identical
// timeLow fields among effect type UUIDs.
@@ -748,7 +793,7 @@
status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle, audio_patch_handle_t newPatchHandle,
const IAfPatchPanel::Patch& patch) final;
- void onReleasePatch(audio_patch_handle_t patchHandle) final;
+ sp<IAfEffectHandle> onReleasePatch(audio_patch_handle_t patchHandle) final;
size_t removeEffect(const sp<IAfEffectModule>& effect) final;
@@ -785,9 +830,6 @@
}
audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
- bool shouldDispatchAddRemoveToHal(bool isAdded) const override {
- return isAdded != mAddedToHal;
- }
bool isOutput() const override;
bool isOffload() const override { return false; }
bool isOffloadOrDirect() const override { return false; }
@@ -800,7 +842,10 @@
audio_channel_mask_t outChannelMask() const override;
uint32_t outChannelCount() const override;
audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
- size_t frameCount() const override { return 0; }
+ /**
+ * frameCount cannot be zero.
+ */
+ size_t frameCount() const override { return 1; }
uint32_t latency() const override { return 0; }
status_t addEffectToHal(const sp<EffectHalInterface>& effect) override;
@@ -812,7 +857,7 @@
void checkSuspendOnEffectEnabled(const sp<IAfEffectBase>& effect __unused,
bool enabled __unused, bool threadLocked __unused) override {}
void resetVolume_l() override REQUIRES(audio_utils::EffectChain_Mutex) {}
- product_strategy_t strategy() const override { return static_cast<product_strategy_t>(0); }
+ product_strategy_t strategy() const override { return PRODUCT_STRATEGY_NONE; }
int32_t activeTrackCnt() const override { return 0; }
void onEffectEnable(const sp<IAfEffectBase>& effect __unused) override;
void onEffectDisable(const sp<IAfEffectBase>& effect __unused) override;
@@ -828,7 +873,6 @@
private:
const wp<DeviceEffectProxy> mProxy;
const sp<DeviceEffectManagerCallback> mManagerCallback;
- bool mAddedToHal = false;
};
status_t checkPort(const IAfPatchPanel::Patch& patch,
diff --git a/services/audioflinger/IAfEffect.h b/services/audioflinger/IAfEffect.h
index 98a0fcb..3452e94 100644
--- a/services/audioflinger/IAfEffect.h
+++ b/services/audioflinger/IAfEffect.h
@@ -46,7 +46,6 @@
public:
// Trivial methods usually implemented with help from ThreadBase
virtual audio_io_handle_t io() const = 0;
- virtual bool shouldDispatchAddRemoveToHal(bool isAdded) const = 0;
virtual bool isOutput() const = 0;
virtual bool isOffload() const = 0;
virtual bool isOffloadOrDirect() const = 0;
@@ -154,7 +153,7 @@
public:
static sp<IAfEffectModule> create(
- const sp<EffectCallbackInterface>& callabck,
+ const sp<EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -190,11 +189,13 @@
virtual status_t sendMetadata_ll(const std::vector<playback_track_metadata_v7_t>& metadata)
REQUIRES(audio_utils::ThreadBase_Mutex,
audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex = 0;
+ // return true if there was a state change from STARTING to ACTIVE, or STOPPED to IDLE, effect
+ // chain will do a volume reset in these two cases
+ virtual bool updateState_l()
+ REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex = 0;
private:
virtual void process() = 0;
- virtual bool updateState_l()
- REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex = 0;
virtual void reset_l() REQUIRES(audio_utils::EffectChain_Mutex) = 0;
virtual status_t configure_l() REQUIRES(audio_utils::EffectChain_Mutex) = 0;
virtual status_t init_l()
@@ -213,6 +214,7 @@
virtual status_t stop_l() = 0;
virtual void addEffectToHal_l() = 0;
+ virtual status_t removeEffectFromHal_l() = 0;
virtual void release_l(const std::string& from) = 0;
};
@@ -398,7 +400,14 @@
virtual status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle,
audio_patch_handle_t newPatchHandle,
const IAfPatchPanel::Patch& patch) = 0;
- virtual void onReleasePatch(audio_patch_handle_t patchHandle) = 0;
+ /**
+ * Checks (and release) of the effect handle is linked with the given release patch handle.
+ *
+ * @param patchHandle handle of the released patch
+ * @return a reference on the effect handle released if any, nullptr otherwise.
+ * It allows to delay the destruction of the handle.
+ */
+ virtual sp<IAfEffectHandle> onReleasePatch(audio_patch_handle_t patchHandle) = 0;
virtual void dump2(int fd, int spaces) const = 0; // TODO(b/291319101) naming?
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2dcbbce..583552a 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -165,6 +165,9 @@
// maximum time to wait in sendConfigEvent_l() for a status to be received
static const nsecs_t kConfigEventTimeoutNs = seconds(2);
+// longer timeout for create audio patch to account for specific scenarii
+// with Bluetooth devices
+static const nsecs_t kCreatePatchEventTimeoutNs = seconds(4);
// minimum sleep time for the mixer thread loop when tracks are active but in underrun
static const uint32_t kMinThreadSleepTimeUs = 5000;
@@ -731,9 +734,11 @@
mutex().unlock();
{
audio_utils::unique_lock _l(event->mutex());
+ nsecs_t timeoutNs = event->mType == CFG_EVENT_CREATE_AUDIO_PATCH ?
+ kCreatePatchEventTimeoutNs : kConfigEventTimeoutNs;
while (event->mWaitStatus) {
if (event->mCondition.wait_for(
- _l, std::chrono::nanoseconds(kConfigEventTimeoutNs), getTid())
+ _l, std::chrono::nanoseconds(timeoutNs), getTid())
== std::cv_status::timeout) {
event->mStatus = TIMED_OUT;
event->mWaitStatus = false;
@@ -1699,7 +1704,7 @@
// TODO(b/184194057): Use the vibrator information from the vibrator that will be used
// for the HapticGenerator.
const std::optional<media::AudioVibratorInfo> defaultVibratorInfo =
- std::move(mAfThreadCallback->getDefaultVibratorInfo_l());
+ mAfThreadCallback->getDefaultVibratorInfo_l();
if (defaultVibratorInfo) {
audio_utils::lock_guard _cl(chain->mutex());
// Only set the vibrator info when it is a valid one.
@@ -2920,7 +2925,7 @@
// TODO(b/184194780): Use the vibrator information from the vibrator that will be
// used to play this track.
audio_utils::lock_guard _l(mAfThreadCallback->mutex());
- vibratorInfo = std::move(mAfThreadCallback->getDefaultVibratorInfo_l());
+ vibratorInfo = mAfThreadCallback->getDefaultVibratorInfo_l();
}
mutex().lock();
track->setHapticScale(hapticScale);
@@ -5204,7 +5209,7 @@
// audio to FastMixer
fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
- fastTrack->mHapticScale = {/*level=*/os::HapticLevel::NONE };
+ fastTrack->mHapticScale = os::HapticScale::none();
fastTrack->mHapticMaxAmplitude = NAN;
fastTrack->mGeneration++;
state->mFastTracksGen++;
@@ -7146,7 +7151,7 @@
uint32_t DirectOutputThread::activeSleepTimeUs() const
{
uint32_t time;
- if (audio_has_proportional_frames(mFormat)) {
+ if (audio_has_proportional_frames(mFormat) && mType != OFFLOAD) {
time = PlaybackThread::activeSleepTimeUs();
} else {
time = kDirectMinSleepTimeUs;
@@ -7157,7 +7162,7 @@
uint32_t DirectOutputThread::idleSleepTimeUs() const
{
uint32_t time;
- if (audio_has_proportional_frames(mFormat)) {
+ if (audio_has_proportional_frames(mFormat) && mType != OFFLOAD) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
} else {
time = kDirectMinSleepTimeUs;
@@ -7168,7 +7173,7 @@
uint32_t DirectOutputThread::suspendSleepTimeUs() const
{
uint32_t time;
- if (audio_has_proportional_frames(mFormat)) {
+ if (audio_has_proportional_frames(mFormat) && mType != OFFLOAD) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
} else {
time = kDirectMinSleepTimeUs;
@@ -7185,7 +7190,7 @@
// no delay on outputs with HW A/V sync
if (usesHwAvSync()) {
mStandbyDelayNs = 0;
- } else if ((mType == OFFLOAD) && !audio_has_proportional_frames(mFormat)) {
+ } else if (mType == OFFLOAD) {
mStandbyDelayNs = kOffloadStandbyDelayNs;
} else {
mStandbyDelayNs = microseconds(mActiveSleepTimeUs*2);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 654b841..10a77ef 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -379,10 +379,10 @@
return isOutput() ? outDeviceTypes_l() : DeviceTypeSet({inDeviceType_l()});
}
- const AudioDeviceTypeAddrVector& outDeviceTypeAddrs() const final {
+ const AudioDeviceTypeAddrVector& outDeviceTypeAddrs() const final REQUIRES(mutex()) {
return mOutDeviceTypeAddrs;
}
- const AudioDeviceTypeAddr& inDeviceTypeAddr() const final {
+ const AudioDeviceTypeAddr& inDeviceTypeAddr() const final REQUIRES(mutex()) {
return mInDeviceTypeAddr;
}
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index 04d5362..ee6af4c 100644
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -38,10 +38,6 @@
static const uint32_t SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY = 5000;
-// For mixed output and inputs, the policy will use max mixer sampling rates.
-// Do not limit sampling rate otherwise
-#define SAMPLE_RATE_HZ_MAX 192000
-
// Used when a client opens a capture stream, without specifying a desired sample rate.
#define SAMPLE_RATE_HZ_DEFAULT 48000
diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp
index e8b04ce..051e975 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.bp
+++ b/services/audiopolicy/common/managerdefinitions/Android.bp
@@ -42,6 +42,7 @@
"libaudioclient_aidl_conversion",
"libaudiofoundation",
"libaudiopolicy",
+ "libaudioutils",
"libbase",
"libcutils",
"libhidlbase",
@@ -56,9 +57,6 @@
"libmedia",
"libmedia_helper",
],
- static_libs: [
- "libaudioutils",
- ],
header_libs: [
"libaudiopolicycommon",
"libaudiopolicymanager_interface_headers",
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index c502fc2..7002e63 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -282,6 +282,11 @@
const AudioProfileVector& getSupportedProfiles() { return mSupportedProfiles; }
+ /**
+ * @brief checks if all devices in device vector are attached to the HwModule or not
+ * @return true if all the devices in device vector are attached, otherwise false
+ */
+ bool areAllDevicesAttached() const;
// Return a string to describe the DeviceVector. The sensitive information will only be
// added to the string if `includeSensitiveInfo` is true.
std::string toString(bool includeSensitiveInfo = false) const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index b92cd70..f7b9b33 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -99,21 +99,20 @@
* @return ioHandle if found, AUDIO_IO_HANDLE_NONE otherwise.
*/
audio_io_handle_t getIoForSession(audio_session_t sessionId,
- const effect_uuid_t *effectType = nullptr) const;
- bool hasOrphansForSession(audio_session_t sessionId) const;
- EffectDescriptorCollection getOrphanEffectsForSession(audio_session_t sessionId) const;
- void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
+ const effect_uuid_t* effectType = nullptr) const;
/**
- * @brief Checks if there is at least one orphan effect with given sessionId and effect type
- * uuid.
+ * @brief Checks if there is at least one orphan effect with given sessionId and optional effect
+ * type uuid.
* @param sessionId Session ID.
- * @param effectType Effect type UUID, the implementation will be same as hasOrphansForSession
- * if null.
+ * @param effectType Optional effect type UUID pointer to effect_uuid_t, nullptr by default.
* @return True if there is an orphan effect for given sessionId and type UUID, false otherwise.
*/
- bool hasOrphanEffectsForSessionAndType(audio_session_t sessionId,
- const effect_uuid_t* effectType) const;
+ bool hasOrphansForSession(audio_session_t sessionId,
+ const effect_uuid_t* effectType = nullptr) const;
+
+ EffectDescriptorCollection getOrphanEffectsForSession(audio_session_t sessionId) const;
+ void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
private:
status_t setEffectEnabled(const sp<EffectDescriptor> &effectDesc, bool enabled);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index a8663fa..6fef215 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -524,6 +524,14 @@
StreamTypeVector streams = streamTypes;
if (!AudioOutputDescriptor::setVolume(
volumeDb, muted, vs, streamTypes, deviceTypes, delayMs, force, isVoiceVolSrc)) {
+ if (hasStream(streamTypes, AUDIO_STREAM_BLUETOOTH_SCO)) {
+ VolumeSource callVolSrc = getVoiceSource();
+ if (callVolSrc != VOLUME_SOURCE_NONE && volumeDb != getCurVolume(callVolSrc)) {
+ setCurVolume(callVolSrc, volumeDb, true);
+ mClientInterface->setStreamVolume(
+ AUDIO_STREAM_VOICE_CALL, Volume::DbToAmpl(volumeDb), mIoHandle, delayMs);
+ }
+ }
return false;
}
if (streams.empty()) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 9f7b8fc..46a04de 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -541,4 +541,14 @@
return filteredDevices;
}
+bool DeviceVector::areAllDevicesAttached() const
+{
+ for (const auto &device : *this) {
+ if (!device->isAttached()) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 7971b61..090da6c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -210,27 +210,13 @@
}
}
-bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId) const
-{
+bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId,
+ const effect_uuid_t* effectType) const {
for (size_t i = 0; i < size(); ++i) {
sp<EffectDescriptor> effect = valueAt(i);
- if (effect->mSession == sessionId && effect->mIsOrphan) {
- return true;
- }
- }
- return false;
-}
-
-bool EffectDescriptorCollection::hasOrphanEffectsForSessionAndType(
- audio_session_t sessionId, const effect_uuid_t* effectType) const {
- if (effectType == nullptr) {
- return hasOrphansForSession(sessionId);
- }
-
- for (size_t i = 0; i < size(); ++i) {
- sp<EffectDescriptor> effect = valueAt(i);
- if (effect->mIsOrphan && effect->mSession == sessionId &&
- memcmp(&effect->mDesc.type, effectType, sizeof(effect_uuid_t)) == 0) {
+ if (effect->mSession == sessionId && effect->mIsOrphan &&
+ (effectType == nullptr ||
+ memcmp(&effect->mDesc.type, effectType, sizeof(effect_uuid_t)) == 0)) {
return true;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/PolicyAudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/PolicyAudioPort.cpp
index ae99191..cd54626 100644
--- a/services/audiopolicy/common/managerdefinitions/src/PolicyAudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/PolicyAudioPort.cpp
@@ -20,6 +20,7 @@
#include "PolicyAudioPort.h"
#include "HwModule.h"
#include <policy.h>
+#include <system/audio.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 7666a2b..9fafe2e 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -143,7 +143,8 @@
}
break;
case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
- if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
+ if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_BT_BLE
+ && config != AUDIO_POLICY_FORCE_NONE) {
ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
return BAD_VALUE;
}
@@ -402,6 +403,40 @@
}
}
}
+
+ // if LEA headset is connected and we are told to use it, play ringtone over
+ // speaker and BT LEA
+ if (!availableOutputDevices.getDevicesFromTypes(getAudioDeviceOutAllBleSet()).isEmpty()) {
+ DeviceVector devices2;
+ devices2 = availableOutputDevices.getFirstDevicesFromTypes({
+ AUDIO_DEVICE_OUT_BLE_HEADSET, AUDIO_DEVICE_OUT_BLE_SPEAKER});
+ // Use ONLY Bluetooth LEA output when ringing in vibration mode
+ if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
+ && (strategy == STRATEGY_ENFORCED_AUDIBLE))) {
+ if (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING)
+ == AUDIO_POLICY_FORCE_BT_BLE) {
+ if (!devices2.isEmpty()) {
+ devices = devices2;
+ break;
+ }
+ }
+ }
+ // Use both Bluetooth LEA and phone default output when ringing in normal mode
+ if (audio_is_ble_out_device(getPreferredDeviceTypeForLegacyStrategy(
+ availableOutputDevices, STRATEGY_PHONE))) {
+ if (strategy == STRATEGY_SONIFICATION) {
+ devices.replaceDevicesByType(
+ AUDIO_DEVICE_OUT_SPEAKER,
+ availableOutputDevices.getDevicesFromType(
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE));
+ }
+ if (!devices2.isEmpty()) {
+ devices.add(devices2);
+ break;
+ }
+ }
+ }
+
// The second device used for sonification is the same as the device used by media strategy
FALLTHROUGH_INTENDED;
diff --git a/services/audiopolicy/fuzzer/Android.bp b/services/audiopolicy/fuzzer/Android.bp
index 8cee613..30d4403 100644
--- a/services/audiopolicy/fuzzer/Android.bp
+++ b/services/audiopolicy/fuzzer/Android.bp
@@ -28,39 +28,18 @@
cc_fuzz {
name: "audiopolicy_fuzzer",
+ defaults: [
+ "libaudiopolicyservice_dependencies",
+ ],
srcs: [
"audiopolicy_fuzzer.cpp",
],
- include_dirs: [
- "frameworks/av/services/audiopolicy",
- ],
- shared_libs: [
- "android.hardware.audio.common-util",
- "capture_state_listener-aidl-cpp",
- "framework-permission-aidl-cpp",
- "libaudioclient",
- "libaudiofoundation",
- "libaudiopolicy",
- "libaudiopolicycomponents",
- "libaudiopolicymanagerdefault",
- "libbase",
- "libbinder",
- "libcutils",
- "libdl",
- "libhidlbase",
- "liblog",
- "libmedia_helper",
- "libmediametrics",
- "libutils",
- "libxml2",
- ],
static_libs: [
"android.hardware.audio.common@7.0-enums",
],
- header_libs: [
- "libaudiopolicycommon",
- "libaudiopolicyengine_interface_headers",
- "libaudiopolicymanager_interface_headers",
+ include_dirs: [
+ "frameworks/av/services/audiopolicy", // include path outside of libaudiopolicyservice
+ "frameworks/av/services/audiopolicy/engine/interface", // for /tests/AudioPolicyTestManager.h:
],
data: [":audiopolicyfuzzer_configuration_files"],
fuzz_config: {
diff --git a/services/audiopolicy/fuzzer/aidl/Android.bp b/services/audiopolicy/fuzzer/aidl/Android.bp
index a91ecd7..680f76d 100644
--- a/services/audiopolicy/fuzzer/aidl/Android.bp
+++ b/services/audiopolicy/fuzzer/aidl/Android.bp
@@ -23,37 +23,15 @@
cc_defaults {
name: "audiopolicy_aidl_fuzzer_defaults",
shared_libs: [
- "audiopolicy-aidl-cpp",
- "audiopolicy-types-aidl-cpp",
- "framework-permission-aidl-cpp",
- "libactivitymanager_aidl",
- "libaudioclient",
"libaudioflinger",
- "libaudiohal",
- "libaudiopolicy",
- "libaudiopolicymanagerdefault",
"libaudiopolicyservice",
- "libaudioprocessing",
- "libhidlbase",
- "liblog",
- "libmediautils",
- "libnbaio",
- "libnblog",
- "libpowermanager",
- "libvibrator",
- "packagemanager_aidl-cpp",
- ],
- static_libs: [
- "audiopermissioncontroller",
- "libaudiomockhal",
- "libfakeservicemanager",
"libmediaplayerservice",
],
+ static_libs: [
+ "libaudiomockhal",
+ "libfakeservicemanager",
+ ],
header_libs: [
- "libaudioflinger_headers",
- "libaudiohal_headers",
- "libaudiopolicymanager_interface_headers",
- "libbinder_headers",
"libmedia_headers",
],
fuzz_config: {
@@ -78,6 +56,8 @@
"latest_android_hardware_audio_core_ndk_shared",
"latest_android_hardware_audio_core_sounddose_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
+ "libaudioflinger_dependencies",
+ "libaudiopolicyservice_dependencies",
"service_fuzzer_defaults",
],
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 5b736ca..a2363af 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -375,6 +375,7 @@
checkLeBroadcastRoutes(wasLeUnicastActive, nullptr, 0);
mpClientInterface->onAudioPortListUpdate();
+ ALOGV("%s() completed for device: %s", __func__, device->toString().c_str());
return NO_ERROR;
} // end if is output device
@@ -390,6 +391,8 @@
return INVALID_OPERATION;
}
+ ALOGV("%s() connecting device %s", __func__, device->toString().c_str());
+
if (mAvailableInputDevices.add(device) < 0) {
return NO_MEMORY;
}
@@ -462,6 +465,7 @@
}
mpClientInterface->onAudioPortListUpdate();
+ ALOGV("%s() completed for device: %s", __func__, device->toString().c_str());
return NO_ERROR;
} // end if is input device
@@ -770,7 +774,7 @@
}
muteWaitMs = setOutputDevices(__func__, mPrimaryOutput, rxDevices, true, delayMs);
} else { // create RX path audio patch
- connectTelephonyRxAudioSource();
+ connectTelephonyRxAudioSource(delayMs);
// If the TX device is on the primary HW module but RX device is
// on other HW module, SinkMetaData of telephony input should handle it
// assuming the device uses audio HAL V5.0 and above
@@ -807,7 +811,7 @@
return false;
}
-void AudioPolicyManager::connectTelephonyRxAudioSource()
+void AudioPolicyManager::connectTelephonyRxAudioSource(uint32_t delayMs)
{
const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
@@ -835,7 +839,7 @@
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
status_t status = startAudioSourceInternal(&source, &aa, &portId, 0 /*uid*/,
- true /*internal*/, true /*isCallRx*/);
+ true /*internal*/, true /*isCallRx*/, delayMs);
ALOGE_IF(status != OK, "%s: failed to start audio source (%d)", __func__, status);
mCallRxSourceClient = mAudioSources.valueFor(portId);
ALOGV("%s portdID %d between source %s and sink %s", __func__, portId,
@@ -1593,7 +1597,7 @@
(config->channel_mask == desc->getChannelMask()) &&
(session == desc->mDirectClientSession)) {
desc->mDirectOpenCount++;
- ALOGV("%s reusing direct output %d for session %d", __func__,
+ ALOGI("%s reusing direct output %d for session %d", __func__,
mOutputs.keyAt(i), session);
*output = mOutputs.keyAt(i);
return NO_ERROR;
@@ -1603,17 +1607,23 @@
if (!profile->canOpenNewIo()) {
if (!com::android::media::audioserver::direct_track_reprioritization()) {
+ ALOGW("%s profile %s can't open new output maxOpenCount reached", __func__,
+ profile->getName().c_str());
return NAME_NOT_FOUND;
} else if ((profile->getFlags() & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) != 0) {
// MMAP gracefully handles lack of an exclusive track resource by mixing
// above the audio framework. For AAudio to know that the limit is reached,
// return an error.
+ ALOGW("%s profile %s can't open new mmap output maxOpenCount reached", __func__,
+ profile->getName().c_str());
return NAME_NOT_FOUND;
} else {
// Close outputs on this profile, if available, to free resources for this request
for (int i = 0; i < mOutputs.size() && !profile->canOpenNewIo(); i++) {
const auto desc = mOutputs.valueAt(i);
if (desc->mProfile == profile) {
+ ALOGV("%s closeOutput %d to prioritize session %d on profile %s", __func__,
+ desc->mIoHandle, session, profile->getName().c_str());
closeOutput(desc->mIoHandle);
}
}
@@ -1622,6 +1632,8 @@
// Unable to close streams to find free resources for this request
if (!profile->canOpenNewIo()) {
+ ALOGW("%s profile %s can't open new output maxOpenCount reached", __func__,
+ profile->getName().c_str());
return NAME_NOT_FOUND;
}
@@ -2178,8 +2190,7 @@
// matching criteria values in priority order for best matching output so far
std::vector<uint32_t> bestMatchCriteria(8, 0);
- const bool hasOrphanHaptic =
- mEffects.hasOrphanEffectsForSessionAndType(sessionId, FX_IID_HAPTICGENERATOR);
+ const bool hasOrphanHaptic = mEffects.hasOrphansForSession(sessionId, FX_IID_HAPTICGENERATOR);
const uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);
const uint32_t hapticChannelCount = audio_channel_count_from_out_mask(
channelMask & AUDIO_CHANNEL_HAPTIC_ALL);
@@ -3500,6 +3511,11 @@
bool enabled,
audio_stream_type_t streamToDriveAbs)
{
+ if (!enabled) {
+ mAbsoluteVolumeDrivingStreams.erase(deviceType);
+ return NO_ERROR;
+ }
+
audio_attributes_t attributesToDriveAbs = mEngine->getAttributesForStreamType(streamToDriveAbs);
if (attributesToDriveAbs == AUDIO_ATTRIBUTES_INITIALIZER) {
ALOGW("%s: no attributes for stream %s, bailing out", __func__,
@@ -3507,12 +3523,7 @@
return BAD_VALUE;
}
- if (enabled) {
- mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
- } else {
- mAbsoluteVolumeDrivingStreams.erase(deviceType);
- }
-
+ mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
return NO_ERROR;
}
@@ -3543,8 +3554,8 @@
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());
+ ALOGV("%s: stream %s attributes=%s, index %d , device 0x%X", __func__,
+ toString(stream).c_str(), toString(attributes).c_str(), index, device);
return setVolumeIndexForAttributes(attributes, index, device);
}
@@ -3722,8 +3733,8 @@
bool hasVoice = hasVoiceStream(volumeCurves.getStreamTypes());
if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoice && index == 0)) ||
(index > volumeCurves.getVolumeIndexMax())) {
- ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index,
- volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax());
+ ALOGE("%s: wrong index %d min=%d max=%d, device 0x%X", __FUNCTION__, index,
+ volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax(), device);
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
@@ -5888,13 +5899,14 @@
audio_port_handle_t *portId,
uid_t uid) {
return startAudioSourceInternal(source, attributes, portId, uid,
- false /*internal*/, false /*isCallRx*/);
+ false /*internal*/, false /*isCallRx*/, 0 /*delayMs*/);
}
status_t AudioPolicyManager::startAudioSourceInternal(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
- uid_t uid, bool internal, bool isCallRx)
+ uid_t uid, bool internal, bool isCallRx,
+ uint32_t delayMs)
{
ALOGV("%s", __FUNCTION__);
*portId = AUDIO_PORT_HANDLE_NONE;
@@ -5929,14 +5941,15 @@
mEngine->getProductStrategyForAttributes(*attributes),
toVolumeSource(*attributes), internal, isCallRx, false);
- status_t status = connectAudioSource(sourceDesc);
+ status_t status = connectAudioSource(sourceDesc, delayMs);
if (status == NO_ERROR) {
mAudioSources.add(*portId, sourceDesc);
}
return status;
}
-status_t AudioPolicyManager::connectAudioSource(const sp<SourceClientDescriptor>& sourceDesc)
+status_t AudioPolicyManager::connectAudioSource(const sp<SourceClientDescriptor>& sourceDesc,
+ uint32_t delayMs)
{
ALOGV("%s handle %d", __FUNCTION__, sourceDesc->portId());
@@ -5962,7 +5975,7 @@
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
return connectAudioSourceToSink(
- sourceDesc, sinkDevice, patchBuilder.patch(), handle, mUidCached, 0 /*delayMs*/);
+ sourceDesc, sinkDevice, patchBuilder.patch(), handle, mUidCached, delayMs);
}
status_t AudioPolicyManager::stopAudioSource(audio_port_handle_t portId)
@@ -6349,10 +6362,11 @@
// mode is not requested.
if (config != nullptr && *config != AUDIO_CONFIG_INITIALIZER) {
- static const bool stereo_spatialization_enabled =
+ static const bool stereo_spatialization_prop_enabled =
property_get_bool("ro.audio.stereo_spatialization_enabled", false);
const bool channel_mask_spatialized =
- (stereo_spatialization_enabled && com_android_media_audio_stereo_spatialization())
+ (stereo_spatialization_prop_enabled
+ && com_android_media_audio_stereo_spatialization())
? audio_channel_mask_contains_stereo(config->channel_mask)
: audio_is_channel_mask_spatialized(config->channel_mask);
if (!channel_mask_spatialized) {
@@ -6400,7 +6414,7 @@
// means haptic use cases (either the client channelmask includes haptic bits, or created a
// HapticGenerator effect for this session) are not supported.
return clientHapticChannel == 0 &&
- !mEffects.hasOrphanEffectsForSessionAndType(sessionId, FX_IID_HAPTICGENERATOR);
+ !mEffects.hasOrphansForSession(sessionId, FX_IID_HAPTICGENERATOR);
}
}
@@ -6692,6 +6706,14 @@
if (!mConfig->getOutputDevices().contains(supportedDevice)) {
continue;
}
+
+ if (outProfile->isMmap() && !outProfile->hasDynamicAudioProfile()
+ && availProfileDevices.areAllDevicesAttached()) {
+ ALOGV("%s skip opening output for mmap profile %s", __func__,
+ outProfile->getTagName().c_str());
+ continue;
+ }
+
sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
mpClientInterface);
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
@@ -6752,6 +6774,14 @@
__func__, inProfile->getTagName().c_str());
continue;
}
+
+ if (inProfile->isMmap() && !inProfile->hasDynamicAudioProfile()
+ && availProfileDevices.areAllDevicesAttached()) {
+ ALOGV("%s skip opening input for mmap profile %s", __func__,
+ inProfile->getTagName().c_str());
+ continue;
+ }
+
sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
inProfile, mpClientInterface, false /*isPreemptor*/);
@@ -6759,12 +6789,12 @@
status_t status = inputDesc->open(nullptr,
availProfileDevices.itemAt(0),
AUDIO_SOURCE_MIC,
- AUDIO_INPUT_FLAG_NONE,
+ (audio_input_flags_t) inProfile->getFlags(),
&input);
if (status != NO_ERROR) {
- ALOGW("Cannot open input stream for device %s on hw module %s",
- availProfileDevices.toString().c_str(),
- hwModule->getName());
+ ALOGW("%s: Cannot open input stream for device %s for profile %s on hw module %s",
+ __func__, availProfileDevices.toString().c_str(),
+ inProfile->getTagName().c_str(), hwModule->getName());
continue;
}
for (const auto &device : availProfileDevices) {
@@ -6872,8 +6902,8 @@
sp<IOProfile> profile = hwModule->getOutputProfiles()[j];
if (profile->supportsDevice(device)) {
profiles.add(profile);
- ALOGV("checkOutputsForDevice(): adding profile %zu from module %s",
- j, hwModule->getName());
+ ALOGV("%s(): adding profile %s from module %s",
+ __func__, profile->getTagName().c_str(), hwModule->getName());
}
}
}
@@ -6906,7 +6936,11 @@
if (j != outputs.size()) {
continue;
}
-
+ if (profile->isMmap() && !profile->hasDynamicAudioProfile()) {
+ ALOGV("%s skip opening output for mmap profile %s",
+ __func__, profile->getTagName().c_str());
+ continue;
+ }
if (!profile->canOpenNewIo()) {
ALOGW("Max Output number %u already opened for this profile %s",
profile->maxOpenCount, profile->getTagName().c_str());
@@ -6967,9 +7001,8 @@
if (!profile->supportsDevice(device)) {
continue;
}
- ALOGV("checkOutputsForDevice(): "
- "clearing direct output profile %zu on module %s",
- j, hwModule->getName());
+ ALOGV("%s(): clearing direct output profile %s on module %s",
+ __func__, profile->getTagName().c_str(), hwModule->getName());
profile->clearAudioProfiles();
if (!profile->hasDynamicAudioProfile()) {
continue;
@@ -7024,8 +7057,8 @@
if (profile->supportsDevice(device)) {
profiles.add(profile);
- ALOGV("checkInputsForDevice(): adding profile %zu from module %s",
- profile_index, hwModule->getName());
+ ALOGV("%s : adding profile %s from module %s", __func__,
+ profile->getTagName().c_str(), hwModule->getName());
}
}
}
@@ -7057,15 +7090,22 @@
continue;
}
+ if (profile->isMmap() && !profile->hasDynamicAudioProfile()) {
+ ALOGV("%s skip opening input for mmap profile %s",
+ __func__, profile->getTagName().c_str());
+ continue;
+ }
if (!profile->canOpenNewIo()) {
- ALOGW("Max Input number %u already opened for this profile %s",
- profile->maxOpenCount, profile->getTagName().c_str());
+ ALOGW("%s Max Input number %u already opened for this profile %s",
+ __func__, profile->maxOpenCount, profile->getTagName().c_str());
continue;
}
desc = new AudioInputDescriptor(profile, mpClientInterface, false /*isPreemptor*/);
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status = desc->open(nullptr, device, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE, &input);
+ ALOGV("%s opening input for profile %s", __func__, profile->getTagName().c_str());
+ status = desc->open(nullptr, device, AUDIO_SOURCE_MIC,
+ (audio_input_flags_t) profile->getFlags(), &input);
if (status == NO_ERROR) {
const String8& address = String8(device->address().c_str());
@@ -7076,7 +7116,8 @@
}
updateAudioProfiles(device, input, profile);
if (!profile->hasValidAudioProfile()) {
- ALOGW("checkInputsForDevice() direct input missing param");
+ ALOGW("%s direct input missing param for profile %s", __func__,
+ profile->getTagName().c_str());
desc->close();
input = AUDIO_IO_HANDLE_NONE;
}
@@ -7087,18 +7128,20 @@
} // endif input != 0
if (input == AUDIO_IO_HANDLE_NONE) {
- ALOGW("%s could not open input for device %s", __func__,
- device->toString().c_str());
+ ALOGW("%s could not open input for device %s on profile %s", __func__,
+ device->toString().c_str(), profile->getTagName().c_str());
profiles.removeAt(profile_index);
profile_index--;
} else {
if (audio_device_is_digital(device->type())) {
device->importAudioPortAndPickAudioProfile(profile);
}
- ALOGV("checkInputsForDevice(): adding input %d", input);
+ ALOGV("%s: adding input %d for profile %s", __func__,
+ input, profile->getTagName().c_str());
if (checkCloseInput(desc)) {
- ALOGV("%s closing input %d", __func__, input);
+ ALOGV("%s: closing input %d for profile %s", __func__,
+ input, profile->getTagName().c_str());
closeInput(input);
}
}
@@ -7117,8 +7160,8 @@
profile_index++) {
sp<IOProfile> profile = hwModule->getInputProfiles()[profile_index];
if (profile->supportsDevice(device)) {
- ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %s",
- profile_index, hwModule->getName());
+ ALOGV("%s: clearing direct input profile %s on module %s", __func__,
+ profile->getTagName().c_str(), hwModule->getName());
profile->clearAudioProfiles();
}
}
@@ -7310,7 +7353,7 @@
if (sourceDesc != nullptr && followsSameRouting(attr, sourceDesc->attributes())
&& sourceDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE
&& !sourceDesc->isCallRx() && !sourceDesc->isInternal()) {
- connectAudioSource(sourceDesc);
+ connectAudioSource(sourceDesc, 0 /*delayMs*/);
}
}
}
@@ -7417,7 +7460,7 @@
}
sp<SourceClientDescriptor> source = getSourceForAttributesOnOutput(srcOut, attr);
if (source != nullptr && !source->isCallRx() && !source->isInternal()) {
- connectAudioSource(source);
+ connectAudioSource(source, 0 /*delayMs*/);
}
}
@@ -8176,7 +8219,7 @@
VolumeSource vsToDriveAbs = toVolumeSource(groupToDriveAbs);
if (vsToDriveAbs == volumeSource) {
// attenuation is applied by the abs volume controller
- return volumeDbMax;
+ return (index != 0) ? volumeDbMax : volumeDb;
} else {
IVolumeCurves &curvesAbs = getVolumeCurves(vsToDriveAbs);
int indexAbs = curvesAbs.getVolumeIndex({volumeDevice});
@@ -8436,11 +8479,19 @@
bool& isBtScoVolSrc,
const char* caller) {
const VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
- const VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
+ isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
+
const bool isScoRequested = isScoRequestedForComm();
const bool isHAUsed = isHearingAidUsedForComm();
- isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
+ if (com_android_media_audio_replace_stream_bt_sco()) {
+ ALOGV("%s stream bt sco is replaced, no volume consistency check for calls", __func__);
+ isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource) &&
+ (isScoRequested || isHAUsed);
+ return true;
+ }
+
+ const VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (btScoVolSrc == volumeSource);
if ((callVolSrc != btScoVolSrc) &&
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 9fc5db5..c0a5012 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -707,7 +707,7 @@
void updateCallAndOutputRouting(bool forceVolumeReeval = true, uint32_t delayMs = 0,
bool skipDelays = false);
- void connectTelephonyRxAudioSource();
+ void connectTelephonyRxAudioSource(uint32_t delayMs);
void disconnectTelephonyAudioSource(sp<SourceClientDescriptor> &clientDesc);
@@ -932,7 +932,8 @@
status_t hasPrimaryOutput() const { return mPrimaryOutput != 0; }
- status_t connectAudioSource(const sp<SourceClientDescriptor>& sourceDesc);
+ status_t connectAudioSource(const sp<SourceClientDescriptor>& sourceDesc,
+ uint32_t delayMs);
status_t disconnectAudioSource(const sp<SourceClientDescriptor>& sourceDesc);
status_t connectAudioSourceToSink(const sp<SourceClientDescriptor>& sourceDesc,
@@ -975,7 +976,8 @@
audio_port_handle_t *portId,
uid_t uid,
bool internal,
- bool isCallRx);
+ bool isCallRx,
+ uint32_t delayMs);
const uid_t mUidCached; // AID_AUDIOSERVER
sp<const AudioPolicyConfig> mConfig;
EngineInstance mEngine; // Audio Policy Engine instance
diff --git a/services/audiopolicy/permission/NativePermissionController.cpp b/services/audiopolicy/permission/NativePermissionController.cpp
index 8659f2c..5743076 100644
--- a/services/audiopolicy/permission/NativePermissionController.cpp
+++ b/services/audiopolicy/permission/NativePermissionController.cpp
@@ -44,11 +44,6 @@
return "audioserver";
case AID_CAMERASERVER:
return "cameraserver";
- // These packages are not handled by AppOps, but labeling may be useful for us
- case AID_RADIO:
- return "telephony";
- case AID_BLUETOOTH:
- return "bluetooth";
default:
return std::nullopt;
}
@@ -129,10 +124,12 @@
BinderResult<bool> NativePermissionController::validateUidPackagePair(
uid_t uid, const std::string& packageName) const {
+ if (uid == AID_ROOT || uid == AID_SYSTEM) return true;
uid = uid % AID_USER_OFFSET;
const auto fixed_package_opt = getFixedPackageName(uid);
if (fixed_package_opt.has_value()) {
- return packageName == fixed_package_opt.value();
+ return (uid == AID_ROOT || uid == AID_SYSTEM) ? true :
+ packageName == fixed_package_opt.value();
}
std::lock_guard l{m_};
if (!is_package_populated_) {
@@ -148,6 +145,7 @@
BinderResult<bool> NativePermissionController::checkPermission(PermissionEnum perm,
uid_t uid) const {
+ if (uid == AID_ROOT || uid == AID_SYSTEM || uid == getuid()) return true;
std::lock_guard l{m_};
const auto& uids = permission_map_[static_cast<size_t>(perm)];
if (!uids.empty()) {
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index 84458f9..e157808 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -11,12 +11,16 @@
cc_defaults {
name: "libaudiopolicyservice_dependencies",
- header_libs: ["audiopolicyservicelocal_headers"],
+ include_dirs: [
+ "frameworks/av/services/audiopolicy", // include path outside of libaudiopolicyservice
+ ],
shared_libs: [
"android.media.audiopolicy-aconfig-cc",
+ "audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
+ "audiopermissioncontroller",
"audiopolicy-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
@@ -35,6 +39,7 @@
"libaudioutils",
"libbinder",
"libcutils",
+ "libeffectsconfig",
"libhardware_legacy",
"libheadtracking",
"libheadtracking-binding",
@@ -52,13 +57,6 @@
"packagemanager_aidl-cpp",
"spatializer-aidl-cpp",
],
-
- static_libs: [
- "audio-permission-aidl-cpp",
- "audiopermissioncontroller",
- "libaudiopolicycomponents",
- "libeffectsconfig",
- ],
}
cc_library {
@@ -84,10 +82,6 @@
"frameworks/av/services/audioflinger",
],
- static_libs: [
- "framework-permission-aidl-cpp", // TODO remove when unnnecessary
- ],
-
header_libs: [
"audiopolicyservicelocal_headers",
"libaudiohal_headers",
@@ -97,21 +91,14 @@
"libaudioutils_headers",
],
+ export_include_dirs: ["."],
+
cflags: [
"-Wall",
"-Werror",
"-Wthread-safety",
"-fvisibility=hidden",
],
-
- export_shared_lib_headers: [
- "framework-permission-aidl-cpp",
- "libactivitymanager_aidl",
- "libaudiousecasevalidation",
- "libheadtracking",
- "libheadtracking-binding",
- "libsensorprivacy",
- ],
}
cc_library_headers {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 768cd07..e598897 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -23,6 +23,8 @@
#include <android/content/AttributionSourceState.h>
#include <android_media_audiopolicy.h>
+#include <com_android_media_audio.h>
+#include <error/expected_utils.h>
#include <media/AidlConversion.h>
#include <media/AudioPolicy.h>
#include <media/AudioValidator.h>
@@ -44,13 +46,30 @@
if (!_tmp.isOk()) return _tmp; \
}
+#define CHECK_PERM(expr1, expr2) \
+ VALUE_OR_RETURN_STATUS(getPermissionProvider().checkPermission((expr1), (expr2)))
+
#define MAX_ITEMS_PER_LIST 1024
namespace android {
namespace audiopolicy_flags = android::media::audiopolicy;
using binder::Status;
using aidl_utils::binderStatusFromStatusT;
+using com::android::media::audio::audioserver_permissions;
using com::android::media::permission::NativePermissionController;
+using com::android::media::permission::PermissionEnum::ACCESS_ULTRASOUND;
+using com::android::media::permission::PermissionEnum::CALL_AUDIO_INTERCEPTION;
+using com::android::media::permission::PermissionEnum::CAPTURE_AUDIO_HOTWORD;
+using com::android::media::permission::PermissionEnum::CAPTURE_VOICE_COMMUNICATION_OUTPUT;
+using com::android::media::permission::PermissionEnum::CAPTURE_AUDIO_OUTPUT;
+using com::android::media::permission::PermissionEnum::CAPTURE_MEDIA_OUTPUT;
+using com::android::media::permission::PermissionEnum::CAPTURE_TUNER_AUDIO_INPUT;
+using com::android::media::permission::PermissionEnum::MODIFY_AUDIO_ROUTING;
+using com::android::media::permission::PermissionEnum::MODIFY_AUDIO_SETTINGS;
+using com::android::media::permission::PermissionEnum::MODIFY_DEFAULT_AUDIO_EFFECTS;
+using com::android::media::permission::PermissionEnum::MODIFY_PHONE_STATE;
+using com::android::media::permission::PermissionEnum::RECORD_AUDIO;
+using com::android::media::permission::PermissionEnum::WRITE_SECURE_SETTINGS;
using content::AttributionSourceState;
using media::audio::common::AudioConfig;
using media::audio::common::AudioConfigBase;
@@ -86,31 +105,37 @@
!= std::end(mSupportedSystemUsages);
}
-status_t AudioPolicyService::validateUsage(const audio_attributes_t& attr) {
+Status AudioPolicyService::validateUsage(const audio_attributes_t& attr) {
return validateUsage(attr, getCallingAttributionSource());
}
-status_t AudioPolicyService::validateUsage(const audio_attributes_t& attr,
+Status AudioPolicyService::validateUsage(const audio_attributes_t& attr,
const AttributionSourceState& attributionSource) {
if (isSystemUsage(attr.usage)) {
if (isSupportedSystemUsage(attr.usage)) {
if (attr.usage == AUDIO_USAGE_CALL_ASSISTANT
&& ((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0)) {
- if (!callAudioInterceptionAllowed(attributionSource)) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(CALL_AUDIO_INTERCEPTION, attributionSource.uid)
+ : callAudioInterceptionAllowed(attributionSource))) {
ALOGE("%s: call audio interception not allowed for attribution source: %s",
__func__, attributionSource.toString().c_str());
- return PERMISSION_DENIED;
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Call audio interception not allowed");
}
- } else if (!modifyAudioRoutingAllowed(attributionSource)) {
+ } else if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, attributionSource.uid)
+ : modifyAudioRoutingAllowed(attributionSource))) {
ALOGE("%s: modify audio routing not allowed for attribution source: %s",
__func__, attributionSource.toString().c_str());
- return PERMISSION_DENIED;
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Modify audio routing not allowed");
}
} else {
- return BAD_VALUE;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
}
- return NO_ERROR;
+ return Status::ok();
}
@@ -137,7 +162,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
@@ -191,7 +218,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -215,7 +244,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(state) >= AUDIO_MODE_CNT) {
@@ -265,7 +296,9 @@
return binderStatusFromStatusT(NO_INIT);
}
- if (!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -355,7 +388,7 @@
RETURN_IF_BINDER_ERROR(
binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
- RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr, attributionSource)));
+ RETURN_IF_BINDER_ERROR(validateUsage(attr, attributionSource));
ALOGV("%s()", __func__);
audio_utils::lock_guard _l(mMutex);
@@ -364,14 +397,22 @@
aidl2legacy_int32_t_uid_t(attributionSource.uid)))) {
attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_NO_MEDIA_PROJECTION);
}
+ const bool bypassInterruptionAllowed = audioserver_permissions() ? (
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, attributionSource.uid) ||
+ CHECK_PERM(MODIFY_PHONE_STATE, attributionSource.uid) ||
+ CHECK_PERM(WRITE_SECURE_SETTINGS, attributionSource.uid))
+ : bypassInterruptionPolicyAllowed(attributionSource);
+
if (((attr.flags & (AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE)) != 0)
- && !bypassInterruptionPolicyAllowed(attributionSource)) {
+ && !bypassInterruptionAllowed) {
attr.flags = static_cast<audio_flags_mask_t>(
attr.flags & ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE));
}
if (attr.content_type == AUDIO_CONTENT_TYPE_ULTRASOUND) {
- if (!accessUltrasoundAllowed(attributionSource)) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(ACCESS_ULTRASOUND, attributionSource.uid)
+ : accessUltrasoundAllowed(attributionSource))) {
ALOGE("%s: permission denied: ultrasound not allowed for uid %d pid %d",
__func__, attributionSource.uid, attributionSource.pid);
return binderStatusFromStatusT(PERMISSION_DENIED);
@@ -400,18 +441,24 @@
break;
case AudioPolicyInterface::API_OUTPUT_TELEPHONY_TX:
if (((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0)
- && !callAudioInterceptionAllowed(attributionSource)) {
+ && !(audioserver_permissions() ?
+ CHECK_PERM(CALL_AUDIO_INTERCEPTION, attributionSource.uid)
+ : callAudioInterceptionAllowed(attributionSource))) {
ALOGE("%s() permission denied: call redirection not allowed for uid %d",
__func__, attributionSource.uid);
result = PERMISSION_DENIED;
- } else if (!modifyPhoneStateAllowed(attributionSource)) {
+ } else if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_PHONE_STATE, attributionSource.uid)
+ : modifyPhoneStateAllowed(attributionSource))) {
ALOGE("%s() permission denied: modify phone state not allowed for uid %d",
__func__, attributionSource.uid);
result = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_OUT_MIX_PLAYBACK:
- if (!modifyAudioRoutingAllowed(attributionSource)) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, attributionSource.uid)
+ : modifyAudioRoutingAllowed(attributionSource))) {
ALOGE("%s() permission denied: modify audio routing not allowed for uid %d",
__func__, attributionSource.uid);
result = PERMISSION_DENIED;
@@ -430,7 +477,7 @@
sp<AudioPlaybackClient> client =
new AudioPlaybackClient(attr, output, attributionSource, session,
- portId, selectedDeviceId, stream, isSpatialized);
+ portId, selectedDeviceId, stream, isSpatialized, config.channel_mask);
mAudioPlaybackClients.add(portId, client);
_aidl_return->output = VALUE_OR_RETURN_BINDER_STATUS(
@@ -630,8 +677,7 @@
return binderStatusFromStatusT(BAD_VALUE);
}
- RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr,
- attributionSource)));
+ RETURN_IF_BINDER_ERROR(validateUsage(attr, attributionSource));
uint32_t virtualDeviceId = kDefaultVirtualDeviceId;
@@ -644,7 +690,10 @@
// type is API_INPUT_MIX_EXT_POLICY_REROUTE and by AudioService if a media projection
// is used and input type is API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK
// - ECHO_REFERENCE source is controlled by captureAudioOutputAllowed()
- if (!(recordingAllowed(attributionSource, inputSource)
+ const auto isRecordingAllowed = audioserver_permissions() ?
+ CHECK_PERM(RECORD_AUDIO, attributionSource.uid) :
+ recordingAllowed(attributionSource, inputSource);
+ if (!(isRecordingAllowed
|| inputSource == AUDIO_SOURCE_FM_TUNER
|| inputSource == AUDIO_SOURCE_REMOTE_SUBMIX
|| inputSource == AUDIO_SOURCE_ECHO_REFERENCE)) {
@@ -653,8 +702,12 @@
return binderStatusFromStatusT(PERMISSION_DENIED);
}
- bool canCaptureOutput = captureAudioOutputAllowed(attributionSource);
- bool canInterceptCallAudio = callAudioInterceptionAllowed(attributionSource);
+ bool canCaptureOutput = audioserver_permissions() ?
+ CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid)
+ : captureAudioOutputAllowed(attributionSource);
+ bool canInterceptCallAudio = audioserver_permissions() ?
+ CHECK_PERM(CALL_AUDIO_INTERCEPTION, attributionSource.uid)
+ : callAudioInterceptionAllowed(attributionSource);
bool isCallAudioSource = inputSource == AUDIO_SOURCE_VOICE_UPLINK
|| inputSource == AUDIO_SOURCE_VOICE_DOWNLINK
|| inputSource == AUDIO_SOURCE_VOICE_CALL;
@@ -668,11 +721,15 @@
}
if (inputSource == AUDIO_SOURCE_FM_TUNER
&& !canCaptureOutput
- && !captureTunerAudioInputAllowed(attributionSource)) {
+ && !(audioserver_permissions() ?
+ CHECK_PERM(CAPTURE_TUNER_AUDIO_INPUT, attributionSource.uid)
+ : captureTunerAudioInputAllowed(attributionSource))) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
- bool canCaptureHotword = captureHotwordAllowed(attributionSource);
+ bool canCaptureHotword = audioserver_permissions() ?
+ CHECK_PERM(CAPTURE_AUDIO_HOTWORD, attributionSource.uid)
+ : captureHotwordAllowed(attributionSource);
if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -687,7 +744,9 @@
}
if (attr.source == AUDIO_SOURCE_ULTRASOUND) {
- if (!accessUltrasoundAllowed(attributionSource)) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(ACCESS_ULTRASOUND, attributionSource.uid)
+ : accessUltrasoundAllowed(attributionSource))) {
ALOGE("%s: permission denied: ultrasound not allowed for uid %d pid %d",
__func__, attributionSource.uid, attributionSource.pid);
return binderStatusFromStatusT(PERMISSION_DENIED);
@@ -733,14 +792,29 @@
status = PERMISSION_DENIED;
}
break;
- case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
- if (!(modifyAudioRoutingAllowed(attributionSource)
+ case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: {
+ bool modAudioRoutingAllowed;
+ if (audioserver_permissions()) {
+ auto result = getPermissionProvider().checkPermission(
+ MODIFY_AUDIO_ROUTING, attributionSource.uid);
+ if (!result.ok()) {
+ ALOGE("%s permission provider error: %s", __func__,
+ result.error().toString8().c_str());
+ status = aidl_utils::statusTFromBinderStatus(result.error());
+ break;
+ }
+ modAudioRoutingAllowed = result.value();
+ } else {
+ modAudioRoutingAllowed = modifyAudioRoutingAllowed(attributionSource);
+ }
+ if (!(modAudioRoutingAllowed
|| ((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0
&& canInterceptCallAudio))) {
ALOGE("%s permission denied for remote submix capture", __func__);
status = PERMISSION_DENIED;
}
break;
+ }
case AudioPolicyInterface::API_INPUT_INVALID:
default:
LOG_ALWAYS_FATAL("%s encountered an invalid input type %d",
@@ -1035,7 +1109,9 @@
if (mAudioPolicyManager == nullptr) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(streamToDriveAbs) >= AUDIO_STREAM_PUBLIC_CNT) {
@@ -1059,7 +1135,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
@@ -1083,7 +1161,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
@@ -1133,7 +1213,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
audio_utils::lock_guard _l(mMutex);
@@ -1439,7 +1521,9 @@
sp<AudioPolicyEffects>audioPolicyEffects;
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
- if (!modifyDefaultAudioEffectsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_DEFAULT_AUDIO_EFFECTS, IPCThreadState::self()->getCallingUid())
+ : modifyDefaultAudioEffectsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(audioPolicyEffects->addSourceDefaultEffect(
@@ -1465,7 +1549,9 @@
sp<AudioPolicyEffects> audioPolicyEffects;
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
- if (!modifyDefaultAudioEffectsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_DEFAULT_AUDIO_EFFECTS, IPCThreadState::self()->getCallingUid())
+ : modifyDefaultAudioEffectsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(audioPolicyEffects->addStreamDefaultEffect(
@@ -1480,7 +1566,9 @@
aidl2legacy_int32_t_audio_unique_id_t(idAidl));
sp<AudioPolicyEffects>audioPolicyEffects;
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
- if (!modifyDefaultAudioEffectsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_DEFAULT_AUDIO_EFFECTS, IPCThreadState::self()->getCallingUid())
+ : modifyDefaultAudioEffectsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
return binderStatusFromStatusT(audioPolicyEffects->removeSourceDefaultEffect(id));
@@ -1492,7 +1580,9 @@
aidl2legacy_int32_t_audio_unique_id_t(idAidl));
sp<AudioPolicyEffects>audioPolicyEffects;
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
- if (!modifyDefaultAudioEffectsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_DEFAULT_AUDIO_EFFECTS, IPCThreadState::self()->getCallingUid())
+ : modifyDefaultAudioEffectsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
return binderStatusFromStatusT(audioPolicyEffects->removeStreamDefaultEffect(id));
@@ -1510,7 +1600,9 @@
std::back_inserter(systemUsages), aidl2legacy_AudioUsage_audio_usage_t)));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -1569,7 +1661,7 @@
return binderStatusFromStatusT(NO_INIT);
}
- RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attributes)));
+ RETURN_IF_BINDER_ERROR(validateUsage(attributes));
audio_utils::lock_guard _l(mMutex);
*_aidl_return = mAudioPolicyManager->isDirectOutputSupported(config, attributes);
@@ -1644,7 +1736,9 @@
RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(AudioValidator::validateAudioPatch(patch)));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1663,7 +1757,9 @@
audio_patch_handle_t handle = VALUE_OR_RETURN_BINDER_STATUS(
aidl2legacy_int32_t_audio_patch_handle_t(handleAidl));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1711,7 +1807,9 @@
binderStatusFromStatusT(AudioValidator::validateAudioPortConfig(config)));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1774,7 +1872,9 @@
// loopback|render only need a MediaProjection (checked in caller AudioService.java)
bool needModifyAudioRouting = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
return !is_mix_loopback_render(mix.mRouteFlags); });
- if (needModifyAudioRouting && !modifyAudioRoutingAllowed()) {
+ if (needModifyAudioRouting && !(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -1790,12 +1890,16 @@
const AttributionSourceState attributionSource = getCallingAttributionSource();
- if (needCaptureMediaOutput && !captureMediaOutputAllowed(attributionSource)) {
+ if (needCaptureMediaOutput && !(audioserver_permissions() ?
+ CHECK_PERM(CAPTURE_MEDIA_OUTPUT, attributionSource.uid)
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (needCaptureVoiceCommunicationOutput &&
- !captureVoiceCommunicationOutputAllowed(attributionSource)) {
+ !(audioserver_permissions() ?
+ CHECK_PERM(CAPTURE_VOICE_COMMUNICATION_OUTPUT, attributionSource.uid)
+ : captureVoiceCommunicationOutputAllowed(attributionSource))) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
@@ -1852,7 +1956,9 @@
aidl2legacy_AudioDeviceTypeAddress));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1866,7 +1972,9 @@
uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1885,7 +1993,9 @@
aidl2legacy_AudioDeviceTypeAddress));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1899,7 +2009,9 @@
int userId = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(userIdAidl));
audio_utils::lock_guard _l(mMutex);
- if(!modifyAudioRoutingAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_ROUTING, IPCThreadState::self()->getCallingUid())
+ : modifyAudioRoutingAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
@@ -1927,7 +2039,7 @@
return binderStatusFromStatusT(NO_INIT);
}
- RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attributes)));
+ RETURN_IF_BINDER_ERROR(validateUsage(attributes));
// startAudioSource should be created as the calling uid
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -1956,7 +2068,9 @@
if (mAudioPolicyManager == NULL) {
return binderStatusFromStatusT(NO_INIT);
}
- if (!settingsAllowed()) {
+ if (!(audioserver_permissions() ?
+ CHECK_PERM(MODIFY_AUDIO_SETTINGS, IPCThreadState::self()->getCallingUid())
+ : settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
audio_utils::lock_guard _l(mMutex);
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index cc67481..cbc0b41 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -590,12 +590,13 @@
if (status == NO_ERROR && currentOutput == newOutput) {
return;
}
- size_t numActiveTracks = countActiveClientsOnOutput_l(newOutput);
+ std::vector<audio_channel_mask_t> activeTracksMasks =
+ getActiveTracksMasks_l(newOutput);
mMutex.unlock();
// It is OK to call detachOutput() is none is already attached.
mSpatializer->detachOutput();
if (status == NO_ERROR && newOutput != AUDIO_IO_HANDLE_NONE) {
- status = mSpatializer->attachOutput(newOutput, numActiveTracks);
+ status = mSpatializer->attachOutput(newOutput, activeTracksMasks);
}
mMutex.lock();
if (status != NO_ERROR) {
@@ -613,17 +614,17 @@
}
}
-size_t AudioPolicyService::countActiveClientsOnOutput_l(
+std::vector<audio_channel_mask_t> AudioPolicyService::getActiveTracksMasks_l(
audio_io_handle_t output, bool spatializedOnly) {
- size_t count = 0;
+ std::vector<audio_channel_mask_t> activeTrackMasks;
for (size_t i = 0; i < mAudioPlaybackClients.size(); i++) {
auto client = mAudioPlaybackClients.valueAt(i);
if (client->io == output && client->active
&& (!spatializedOnly || client->isSpatialized)) {
- count++;
+ activeTrackMasks.push_back(client->channelMask);
}
}
- return count;
+ return activeTrackMasks;
}
void AudioPolicyService::onUpdateActiveSpatializerTracks_l() {
@@ -639,12 +640,12 @@
return;
}
audio_io_handle_t output = mSpatializer->getOutput();
- size_t activeClients;
+ std::vector<audio_channel_mask_t> activeTracksMasks;
{
audio_utils::lock_guard _l(mMutex);
- activeClients = countActiveClientsOnOutput_l(output);
+ activeTracksMasks = getActiveTracksMasks_l(output);
}
- mSpatializer->updateActiveTracks(activeClients);
+ mSpatializer->updateActiveTracks(activeTracksMasks);
}
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -1415,8 +1416,8 @@
} else {
getIAudioPolicyServiceStatistics().event(code, elapsedMs);
}
- }, mediautils::TimeCheck::kDefaultTimeoutDuration,
- mediautils::TimeCheck::kDefaultSecondChanceDuration,
+ }, mediautils::TimeCheck::getDefaultTimeoutDuration(),
+ mediautils::TimeCheck::getDefaultSecondChanceDuration(),
true /* crashOnTimeout */);
switch (code) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index df293bc..92c162f 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -452,8 +452,8 @@
app_state_t apmStatFromAmState(int amState);
bool isSupportedSystemUsage(audio_usage_t usage);
- status_t validateUsage(const audio_attributes_t& attr);
- status_t validateUsage(const audio_attributes_t& attr,
+ binder::Status validateUsage(const audio_attributes_t& attr);
+ binder::Status validateUsage(const audio_attributes_t& attr,
const AttributionSourceState& attributionSource);
void updateUidStates();
@@ -977,13 +977,15 @@
const audio_io_handle_t io, AttributionSourceState attributionSource,
const audio_session_t session, audio_port_handle_t portId,
audio_port_handle_t deviceId, audio_stream_type_t stream,
- bool isSpatialized) :
+ bool isSpatialized, audio_channel_mask_t channelMask) :
AudioClient(attributes, io, attributionSource, session, portId,
- deviceId), stream(stream), isSpatialized(isSpatialized) {}
+ deviceId), stream(stream), isSpatialized(isSpatialized),
+ channelMask(channelMask) {}
~AudioPlaybackClient() override = default;
const audio_stream_type_t stream;
const bool isSpatialized;
+ const audio_channel_mask_t channelMask;
};
void getPlaybackClientAndEffects(audio_port_handle_t portId,
@@ -1014,14 +1016,14 @@
void unloadAudioPolicyManager();
/**
- * Returns the number of active audio tracks on the specified output mixer.
+ * Returns the channel masks for active audio tracks on the specified output mixer.
* The query can be specified to only include spatialized audio tracks or consider
* all tracks.
* @param output the I/O handle of the output mixer to consider
* @param spatializedOnly true if only spatialized tracks should be considered
- * @return the number of active tracks.
+ * @return a list of channel masks for all active tracks matching the condition.
*/
- size_t countActiveClientsOnOutput_l(
+ std::vector<audio_channel_mask_t> getActiveTracksMasks_l(
audio_io_handle_t output, bool spatializedOnly = true) REQUIRES(mMutex);
mutable audio_utils::mutex mMutex{audio_utils::MutexOrder::kAudioPolicyService_Mutex};
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index c98f8df..9cc3b8f 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -936,7 +936,8 @@
});
}
-status_t Spatializer::attachOutput(audio_io_handle_t output, size_t numActiveTracks) {
+status_t Spatializer::attachOutput(audio_io_handle_t output,
+ const std::vector<audio_channel_mask_t>& activeTracksMasks) {
bool outputChanged = false;
sp<media::INativeSpatializerCallback> callback;
@@ -944,7 +945,7 @@
audio_utils::lock_guard lock(mMutex);
ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
mLocalLog.log("%s with output %d tracks %zu (mOutput %d)", __func__, (int)output,
- numActiveTracks, (int)mOutput);
+ activeTracksMasks.size(), (int)mOutput);
if (mOutput != AUDIO_IO_HANDLE_NONE) {
LOG_ALWAYS_FATAL_IF(mEngine == nullptr, "%s output set without FX engine", __func__);
// remove FX instance
@@ -969,7 +970,7 @@
outputChanged = mOutput != output;
mOutput = output;
- mNumActiveTracks = numActiveTracks;
+ mActiveTracksMasks = activeTracksMasks;
AudioSystem::addSupportedLatencyModesCallback(this);
std::vector<audio_latency_mode_t> latencyModes;
@@ -1008,7 +1009,8 @@
{
audio_utils::lock_guard lock(mMutex);
- mLocalLog.log("%s with output %d tracks %zu", __func__, (int)mOutput, mNumActiveTracks);
+ mLocalLog.log("%s with output %d num tracks %zu",
+ __func__, (int)mOutput, mActiveTracksMasks.size());
ALOGV("%s mOutput %d", __func__, (int)mOutput);
if (mOutput == AUDIO_IO_HANDLE_NONE) {
return output;
@@ -1051,11 +1053,13 @@
}
}
-void Spatializer::updateActiveTracks(size_t numActiveTracks) {
+void Spatializer::updateActiveTracks(
+ const std::vector<audio_channel_mask_t>& activeTracksMasks) {
audio_utils::lock_guard lock(mMutex);
- if (mNumActiveTracks != numActiveTracks) {
- mLocalLog.log("%s from %zu to %zu", __func__, mNumActiveTracks, numActiveTracks);
- mNumActiveTracks = numActiveTracks;
+ if (mActiveTracksMasks != activeTracksMasks) {
+ mLocalLog.log("%s from %zu to %zu",
+ __func__, mActiveTracksMasks.size(), activeTracksMasks.size());
+ mActiveTracksMasks = activeTracksMasks;
checkEngineState_l();
checkSensorsState_l();
}
@@ -1114,7 +1118,7 @@
if (mPoseController != nullptr) {
// TODO(b/253297301, b/255433067) reenable low latency condition check
// for Head Tracking after Bluetooth HAL supports it correctly.
- if (mNumActiveTracks > 0 && mLevel != Spatialization::Level::NONE
+ if (shouldUseHeadTracking_l() && mLevel != Spatialization::Level::NONE
&& mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
&& mHeadSensor != SpatializerPoseController::INVALID_SENSOR) {
if (supportsLowLatencyMode) {
@@ -1146,9 +1150,28 @@
}
}
+
+/* static */
+bool Spatializer::containsImmersiveChannelMask(
+ const std::vector<audio_channel_mask_t>& masks)
+{
+ for (auto mask : masks) {
+ if (audio_is_channel_mask_spatialized(mask)) {
+ return true;
+ }
+ }
+ // Only non-immersive channel masks, e.g. AUDIO_CHANNEL_OUT_STEREO, are present.
+ return false;
+}
+
+bool Spatializer::shouldUseHeadTracking_l() const {
+ // Headtracking only available on immersive channel masks.
+ return containsImmersiveChannelMask(mActiveTracksMasks);
+}
+
void Spatializer::checkEngineState_l() {
if (mEngine != nullptr) {
- if (mLevel != Spatialization::Level::NONE && mNumActiveTracks > 0) {
+ if (mLevel != Spatialization::Level::NONE && mActiveTracksMasks.size() > 0) {
mEngine->setEnabled(true);
setEffectParameter_l(SPATIALIZER_PARAM_LEVEL,
std::vector<Spatialization::Level>{mLevel});
@@ -1237,7 +1260,8 @@
base::StringAppendF(&ss, "\n%smSupportsHeadTracking: %s\n", prefixSpace.c_str(),
mSupportsHeadTracking ? "true" : "false");
// 2. Settings (Output, tracks)
- base::StringAppendF(&ss, "%smNumActiveTracks: %zu\n", prefixSpace.c_str(), mNumActiveTracks);
+ base::StringAppendF(&ss, "%sNum Active Tracks: %zu\n",
+ prefixSpace.c_str(), mActiveTracksMasks.size());
base::StringAppendF(&ss, "%sOutputStreamHandle: %d\n", prefixSpace.c_str(), (int)mOutput);
// 3. Sensors, Effect information.
@@ -1248,8 +1272,12 @@
mDisplayOrientation);
// 4. Show flag or property state.
+ static const bool stereo_spatialization_prop_enabled =
+ property_get_bool("ro.audio.stereo_spatialization_enabled", false);
+ const bool stereo_spatialization = com_android_media_audio_stereo_spatialization()
+ && stereo_spatialization_prop_enabled;
base::StringAppendF(&ss, "%sStereo Spatialization: %s\n", prefixSpace.c_str(),
- com_android_media_audio_stereo_spatialization() ? "true" : "false");
+ stereo_spatialization ? "true" : "false");
ss.append(prefixSpace + "CommandLog:\n");
ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index c5f159c..5ea3258 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -185,7 +185,8 @@
/** Called by audio policy service when the special output mixer dedicated to spatialization
* is opened and the spatializer engine must be created.
*/
- status_t attachOutput(audio_io_handle_t output, size_t numActiveTracks);
+ status_t attachOutput(audio_io_handle_t output,
+ const std::vector<audio_channel_mask_t>& activeTracksMasks);
/** Called by audio policy service when the special output mixer dedicated to spatialization
* is closed and the spatializer engine must be release.
*/
@@ -199,7 +200,7 @@
mOutput = output;
}
- void updateActiveTracks(size_t numActiveTracks);
+ void updateActiveTracks(const std::vector<audio_channel_mask_t>& activeTracksMasks);
/** Gets the channel mask, sampling rate and format set for the spatializer input. */
audio_config_base_t getAudioInConfig() const;
@@ -227,6 +228,16 @@
void onSupportedLatencyModesChangedMsg(
audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes);
+ // Made public for test only
+ /**
+ * Returns true if there exists an immersive channel mask in the vector.
+ *
+ * Example of a non-immersive channel mask such as AUDIO_CHANNEL_OUT_STEREO
+ * versus an immersive channel mask such as AUDIO_CHANNEL_OUT_5POINT1.
+ */
+ static bool containsImmersiveChannelMask(
+ const std::vector<audio_channel_mask_t>& masks);
+
private:
Spatializer(effect_descriptor_t engineDescriptor,
SpatializerPolicyCallback *callback);
@@ -462,6 +473,11 @@
*/
audio_latency_mode_t selectHeadtrackingConnectionMode_l() REQUIRES(mMutex);
+ /**
+ * Indicates if current conditions are compatible with head tracking.
+ */
+ bool shouldUseHeadTracking_l() const REQUIRES(mMutex);
+
/** Effect engine descriptor */
const effect_descriptor_t mEngineDescriptor;
/** Callback interface to parent audio policy service */
@@ -539,7 +555,7 @@
sp<ALooper> mLooper;
sp<EngineCallbackHandler> mHandler;
- size_t mNumActiveTracks GUARDED_BY(mMutex) = 0;
+ std::vector<audio_channel_mask_t> mActiveTracksMasks GUARDED_BY(mMutex);
std::vector<audio_latency_mode_t> mSupportedLatencyModes GUARDED_BY(mMutex);
/** preference order for low latency modes according to persist.bluetooth.hid.transport */
std::vector<audio_latency_mode_t> mOrderedLowLatencyModes;
diff --git a/services/audiopolicy/tests/spatializer_tests.cpp b/services/audiopolicy/tests/spatializer_tests.cpp
index 73bef43..0b40f32 100644
--- a/services/audiopolicy/tests/spatializer_tests.cpp
+++ b/services/audiopolicy/tests/spatializer_tests.cpp
@@ -33,6 +33,38 @@
using media::audio::common::HeadTracking;
using media::audio::common::Spatialization;
+// Test Spatializer Helper Methods
+
+TEST(Spatializer, containsImmersiveChannelMask) {
+ // Regardless of the implementation, we expect the following
+ // behavior.
+
+ // Pure non-immersive
+ EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_STEREO }));
+ EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO }));
+ EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_MONO }));
+
+ // Pure immersive
+ EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_5POINT1 }));
+ EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_7POINT1 }));
+ EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_7POINT1,
+ AUDIO_CHANNEL_OUT_22POINT2 }));
+
+ // Mixed immersive/non-immersive
+ EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_7POINT1POINT4 }));
+ EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+ { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_CHANNEL_OUT_7POINT1 }));
+}
+
class TestSpatializerPolicyCallback :
public SpatializerPolicyCallback {
public:
@@ -68,7 +100,7 @@
mSpatializer->setOutput(AUDIO_IO_HANDLE_NONE);
mSpatializer->setDesiredHeadTrackingMode(HeadTracking::Mode::DISABLED);
mSpatializer->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
- mSpatializer->updateActiveTracks(0);
+ mSpatializer->updateActiveTracks({});
}
static constexpr audio_io_handle_t sTestOutput= 1977;
@@ -174,12 +206,12 @@
ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
// requested latency mode must be low if at least one spatialized tracks is active
- mSpatializer->updateActiveTracks(1);
+ mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_LOW);
// requested latency mode must be free after stopping the last spatialized tracks
- mSpatializer->updateActiveTracks(0);
+ mSpatializer->updateActiveTracks({});
requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
}
@@ -202,7 +234,7 @@
// requested latency mode must be low software if at least one spatialized tracks is active
// and the only supported low latency mode is low software
- mSpatializer->updateActiveTracks(1);
+ mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE);
@@ -225,7 +257,7 @@
}
// requested latency mode must be free after stopping the last spatialized tracks
- mSpatializer->updateActiveTracks(0);
+ mSpatializer->updateActiveTracks({});
requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
}
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index d9d8a3d..d21241b 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -22,13 +22,9 @@
#include <utils/Trace.h>
#include <cutils/properties.h>
-#include "camera/CameraMetadata.h"
#include "CameraFlashlight.h"
-#include "gui/IGraphicBufferConsumer.h"
-#include "gui/BufferQueue.h"
+#include "camera/CameraMetadata.h"
#include "camera/camera2/CaptureRequest.h"
-#include "device3/Camera3Device.h"
-
namespace android {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 13cffa7..f94300e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1542,7 +1542,7 @@
s << ", " << std::to_string(i);
}
}
- return std::move(s.str());
+ return s.str();
}
int32_t CameraService::mapToInterface(TorchModeStatus status) {
@@ -1611,13 +1611,16 @@
std::string cameraIdStr = std::to_string(cameraId);
Status ret = Status::ok();
sp<Client> tmp = nullptr;
+
+ logConnectionAttempt(getCallingPid(), kServiceName, cameraIdStr, API_1);
+
if (!(ret = connectHelper<ICameraClient,Client>(
sp<ICameraClient>{nullptr}, cameraIdStr, cameraId,
kServiceName, /*systemNativeClient*/ false, {}, uid, USE_CALLING_PID,
API_1, /*shimUpdateOnly*/ true, /*oomScoreOffset*/ 0,
/*targetSdkVersion*/ __ANDROID_API_FUTURE__,
/*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT,
- /*forceSlowJpegMode*/false, cameraIdStr, /*out*/ tmp)
+ /*forceSlowJpegMode*/false, cameraIdStr, /*isNonSystemNdk*/ false, /*out*/ tmp)
).isOk()) {
ALOGE("%s: Error initializing shim metadata: %s", __FUNCTION__, ret.toString8().c_str());
}
@@ -1687,17 +1690,14 @@
}
Status CameraService::validateConnectLocked(const std::string& cameraId,
- const std::string& clientName8, /*inout*/int& clientUid, /*inout*/int& clientPid,
- /*out*/int& originalClientPid) const {
+ const std::string& clientName8, /*inout*/int& clientUid, /*inout*/int& clientPid) const {
#ifdef __BRILLO__
UNUSED(clientName8);
UNUSED(clientUid);
UNUSED(clientPid);
- UNUSED(originalClientPid);
#else
- Status allowed = validateClientPermissionsLocked(cameraId, clientName8, clientUid, clientPid,
- originalClientPid);
+ Status allowed = validateClientPermissionsLocked(cameraId, clientName8, clientUid, clientPid);
if (!allowed.isOk()) {
return allowed;
}
@@ -1735,8 +1735,7 @@
}
Status CameraService::validateClientPermissionsLocked(const std::string& cameraId,
- const std::string& clientName, int& clientUid, int& clientPid,
- /*out*/int& originalClientPid) const {
+ const std::string& clientName, int& clientUid, int& clientPid) const {
int callingPid = getCallingPid();
int callingUid = getCallingUid();
@@ -1819,12 +1818,10 @@
"is enabled", clientName.c_str(), clientPid, clientUid, cameraId.c_str());
}
+ userid_t clientUserId = multiuser_get_user_id(clientUid);
+
// Only use passed in clientPid to check permission. Use calling PID as the client PID that's
// connected to camera service directly.
- originalClientPid = clientPid;
- clientPid = callingPid;
-
- userid_t clientUserId = multiuser_get_user_id(clientUid);
// For non-system clients : Only allow clients who are being used by the current foreground
// device user, unless calling from our own process.
@@ -1845,11 +1842,11 @@
if (isHeadlessSystemUserMode()
&& (clientUserId == USER_SYSTEM)
&& !hasPermissionsForCameraHeadlessSystemUser(cameraId, callingPid, callingUid)) {
- ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid);
+ ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", callingPid, clientUid);
return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
"Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" as Headless System \
User without camera headless system user permission",
- clientName.c_str(), clientPid, clientUid, cameraId.c_str());
+ clientName.c_str(), callingPid, clientUid, cameraId.c_str());
}
}
@@ -2134,7 +2131,6 @@
Status CameraService::connect(
const sp<ICameraClient>& cameraClient,
int api1CameraId,
- const std::string& clientPackageName,
int targetSdkVersion,
int rotationOverride,
bool forceSlowJpegMode,
@@ -2154,15 +2150,22 @@
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
+ std::string clientPackageNameMaybe = clientAttribution.packageName.value_or("");
+ bool isNonSystemNdk = clientPackageNameMaybe.size() == 0;
+ std::string clientPackageName = resolvePackageName(clientAttribution.uid,
+ clientPackageNameMaybe);
+ logConnectionAttempt(clientAttribution.pid, clientPackageName, cameraIdStr, API_1);
+
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, cameraIdStr, api1CameraId,
- clientPackageName, /*systemNativeClient*/ false, {}, clientAttribution.uid,
- clientAttribution.pid, API_1,
+ clientPackageName, /*systemNativeClient*/ false, {},
+ clientAttribution.uid, clientAttribution.pid, API_1,
/*shimUpdateOnly*/ false, /*oomScoreOffset*/ 0, targetSdkVersion,
- rotationOverride, forceSlowJpegMode, cameraIdStr, /*out*/client);
+ rotationOverride, forceSlowJpegMode, cameraIdStr, isNonSystemNdk, /*out*/client);
if (!ret.isOk()) {
- logRejected(cameraIdStr, getCallingPid(), clientPackageName, toStdString(ret.toString8()));
+ logRejected(cameraIdStr, getCallingPid(), clientAttribution.packageName.value_or(""),
+ toStdString(ret.toString8()));
return ret;
}
@@ -2239,8 +2242,6 @@
Status CameraService::connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
const std::string& unresolvedCameraId,
- const std::string& clientPackageName,
- const std::optional<std::string>& clientFeatureId,
int oomScoreOffset, int targetSdkVersion,
int rotationOverride, const AttributionSourceState& clientAttribution, int32_t devicePolicy,
/*out*/
@@ -2249,13 +2250,13 @@
RunThreadWithRealtimePriority priorityBump;
Status ret = Status::ok();
sp<CameraDeviceClient> client = nullptr;
- std::string clientPackageNameAdj = clientPackageName;
+ std::string clientPackageNameMaybe = clientAttribution.packageName.value_or("");
int callingPid = getCallingPid();
int callingUid = getCallingUid();
bool systemNativeClient = false;
- if (callerHasSystemUid() && (clientPackageNameAdj.size() == 0)) {
+ if (callerHasSystemUid() && (clientPackageNameMaybe.size() == 0)) {
std::string systemClient = fmt::sprintf("client.pid<%d>", callingPid);
- clientPackageNameAdj = systemClient;
+ clientPackageNameMaybe = systemClient;
systemNativeClient = true;
}
@@ -2269,10 +2270,15 @@
}
std::string cameraId = cameraIdOptional.value();
+ bool isNonSystemNdk = clientPackageNameMaybe.size() == 0;
+ std::string clientPackageName = resolvePackageName(clientAttribution.uid,
+ clientPackageNameMaybe);
+ logConnectionAttempt(clientAttribution.pid, clientPackageName, cameraId, API_2);
+
if (oomScoreOffset < 0) {
std::string msg =
fmt::sprintf("Cannot increase the priority of a client %s pid %d for "
- "camera id %s", clientPackageNameAdj.c_str(), callingPid,
+ "camera id %s", clientPackageName.c_str(), callingPid,
cameraId.c_str());
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.c_str());
@@ -2299,19 +2305,19 @@
&& !isTrustedCallingUid(callingUid)) {
std::string msg = fmt::sprintf("Cannot change the priority of a client %s pid %d for "
"camera id %s without SYSTEM_CAMERA permissions",
- clientPackageNameAdj.c_str(), callingPid, cameraId.c_str());
+ clientPackageName.c_str(), callingPid, cameraId.c_str());
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(ERROR_PERMISSION_DENIED, msg.c_str());
}
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb,
- cameraId, /*api1CameraId*/-1, clientPackageNameAdj, systemNativeClient, clientFeatureId,
- clientAttribution.uid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false,
- oomScoreOffset, targetSdkVersion, rotationOverride, /*forceSlowJpegMode*/false,
- unresolvedCameraId, /*out*/client);
+ cameraId, /*api1CameraId*/-1, clientPackageName, systemNativeClient,
+ clientAttribution.attributionTag, clientAttribution.uid, USE_CALLING_PID, API_2,
+ /*shimUpdateOnly*/ false, oomScoreOffset, targetSdkVersion, rotationOverride,
+ /*forceSlowJpegMode*/false, unresolvedCameraId, isNonSystemNdk, /*out*/client);
if (!ret.isOk()) {
- logRejected(cameraId, callingPid, clientPackageNameAdj, toStdString(ret.toString8()));
+ logRejected(cameraId, callingPid, clientPackageName, toStdString(ret.toString8()));
return ret;
}
@@ -2371,7 +2377,7 @@
return false;
}
-std::string CameraService::getPackageNameFromUid(int clientUid) {
+std::string CameraService::getPackageNameFromUid(int clientUid) const {
std::string packageName("");
sp<IPermissionController> permCtrl;
@@ -2416,38 +2422,44 @@
return packageName;
}
-template<class CALLBACK, class CLIENT>
-Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const std::string& cameraId,
- int api1CameraId, const std::string& clientPackageNameMaybe, bool systemNativeClient,
- const std::optional<std::string>& clientFeatureId, int clientUid, int clientPid,
- apiLevel effectiveApiLevel, bool shimUpdateOnly, int oomScoreOffset, int targetSdkVersion,
- int rotationOverride, bool forceSlowJpegMode,
- const std::string& originalCameraId, /*out*/sp<CLIENT>& device) {
- binder::Status ret = binder::Status::ok();
-
- bool isNonSystemNdk = false;
- std::string clientPackageName;
- int packageUid = (clientUid == USE_CALLING_UID) ?
- getCallingUid() : clientUid;
- if (clientPackageNameMaybe.size() <= 0) {
- // NDK calls don't come with package names, but we need one for various cases.
- // Generally, there's a 1:1 mapping between UID and package name, but shared UIDs
- // do exist. For all authentication cases, all packages under the same UID get the
- // same permissions, so picking any associated package name is sufficient. For some
- // other cases, this may give inaccurate names for clients in logs.
- isNonSystemNdk = true;
- clientPackageName = getPackageNameFromUid(packageUid);
- } else {
- clientPackageName = clientPackageNameMaybe;
- }
-
- int originalClientPid = 0;
-
+void CameraService::logConnectionAttempt(int clientPid, const std::string& clientPackageName,
+ const std::string& cameraId, apiLevel effectiveApiLevel) const {
int packagePid = (clientPid == USE_CALLING_PID) ?
getCallingPid() : clientPid;
ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) and "
"Camera API version %d", packagePid, clientPackageName.c_str(), cameraId.c_str(),
static_cast<int>(effectiveApiLevel));
+}
+
+std::string CameraService::resolvePackageName(int clientUid,
+ const std::string& clientPackageNameMaybe) const {
+ if (clientPackageNameMaybe.size() <= 0) {
+ int packageUid = (clientUid == USE_CALLING_UID) ?
+ getCallingUid() : clientUid;
+ // NDK calls don't come with package names, but we need one for various cases.
+ // Generally, there's a 1:1 mapping between UID and package name, but shared UIDs
+ // do exist. For all authentication cases, all packages under the same UID get the
+ // same permissions, so picking any associated package name is sufficient. For some
+ // other cases, this may give inaccurate names for clients in logs.
+ return getPackageNameFromUid(packageUid);
+ } else {
+ return clientPackageNameMaybe;
+ }
+}
+
+template<class CALLBACK, class CLIENT>
+Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const std::string& cameraId,
+ int api1CameraId, const std::string& clientPackageName, bool systemNativeClient,
+ const std::optional<std::string>& clientFeatureId, int clientUid, int clientPid,
+ apiLevel effectiveApiLevel, bool shimUpdateOnly, int oomScoreOffset, int targetSdkVersion,
+ int rotationOverride, bool forceSlowJpegMode,
+ const std::string& originalCameraId, bool isNonSystemNdk, /*out*/sp<CLIENT>& device) {
+ binder::Status ret = binder::Status::ok();
+
+ int packageUid = (clientUid == USE_CALLING_UID) ?
+ getCallingUid() : clientUid;
+ int packagePid = (clientPid == USE_CALLING_PID) ?
+ getCallingPid() : clientPid;
nsecs_t openTimeNs = systemTime();
@@ -2470,7 +2482,7 @@
// Enforce client permissions and do basic validity checks
if (!(ret = validateConnectLocked(cameraId, clientPackageName,
- /*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
+ /*inout*/clientUid, /*inout*/clientPid)).isOk()) {
return ret;
}
@@ -2487,7 +2499,7 @@
sp<BasicClient> clientTmp = nullptr;
std::shared_ptr<resource_policy::ClientDescriptor<std::string, sp<BasicClient>>> partial;
- if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
+ if ((err = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel,
IInterface::asBinder(cameraCb), clientPackageName, oomScoreOffset,
systemNativeClient, /*out*/&clientTmp, /*out*/&partial)) != NO_ERROR) {
switch (err) {
@@ -2533,9 +2545,11 @@
bool overrideForPerfClass = SessionConfigurationUtils::targetPerfClassPrimaryCamera(
mPerfClassPrimaryCameraIds, cameraId, targetSdkVersion);
+ // Only use passed in clientPid to check permission. Use calling PID as the client PID
+ // that's connected to camera service directly.
if(!(ret = makeClient(this, cameraCb, clientPackageName, systemNativeClient,
clientFeatureId, cameraId, api1CameraId, facing,
- orientation, clientPid, clientUid, getpid(),
+ orientation, getCallingPid(), clientUid, getpid(),
deviceVersionAndTransport, effectiveApiLevel, overrideForPerfClass,
rotationOverride, forceSlowJpegMode, originalCameraId,
/*out*/&tmp)).isOk()) {
@@ -5179,7 +5193,7 @@
}
if (hasAny) ret << "\n";
ret << "]\n";
- return std::move(ret.str());
+ return ret.str();
}
CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 3866c12..d5c57cb 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -173,17 +173,15 @@
hardware::camera2::params::VendorTagDescriptorCache* cache);
virtual binder::Status connect(const sp<hardware::ICameraClient>& cameraClient,
- int32_t cameraId, const std::string& clientPackageName,
- int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
+ int32_t cameraId, int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
const AttributionSourceState& clientAttribution,
int32_t devicePolicy, /*out*/ sp<hardware::ICamera>* device) override;
virtual binder::Status connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
- const std::string& cameraId,
- const std::string& clientPackageName, const std::optional<std::string>& clientFeatureId,
- int scoreOffset, int targetSdkVersion, int rotationOverride,
- const AttributionSourceState& clientAttribution, int32_t devicePolicy,
+ const std::string& cameraId, int scoreOffset, int targetSdkVersion,
+ int rotationOverride, const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy,
/*out*/
sp<hardware::camera2::ICameraDeviceUser>* device);
@@ -949,17 +947,17 @@
void removeStates(const std::string& id);
// Check if we can connect, before we acquire the service lock.
- // The returned originalClientPid is the PID of the original process that wants to connect to
- // camera.
- // The returned clientPid is the PID of the client that directly connects to camera.
- // originalClientPid and clientPid are usually the same except when the application uses
- // mediaserver to connect to camera (using MediaRecorder to connect to camera). In that case,
- // clientPid is the PID of mediaserver and originalClientPid is the PID of the application.
+ // If clientPid/clientUid are USE_CALLING_PID/USE_CALLING_UID, they will be overwritten with
+ // the calling pid/uid.
binder::Status validateConnectLocked(const std::string& cameraId, const std::string& clientName,
- /*inout*/int& clientUid, /*inout*/int& clientPid, /*out*/int& originalClientPid) const;
+ /*inout*/int& clientUid, /*inout*/int& clientPid) const;
binder::Status validateClientPermissionsLocked(const std::string& cameraId,
- const std::string& clientName, /*inout*/int& clientUid, /*inout*/int& clientPid,
- /*out*/int& originalClientPid) const;
+ const std::string& clientName, /*inout*/int& clientUid, /*inout*/int& clientPid) const;
+
+ // If clientPackageNameMaybe is empty, attempts to resolve the package name.
+ std::string resolvePackageName(int clientUid, const std::string& clientPackageNameMaybe) const;
+ void logConnectionAttempt(int clientPid, const std::string& clientPackageName,
+ const std::string& cameraId, apiLevel effectiveApiLevel) const;
bool isCameraPrivacyEnabled(const String16& packageName,const std::string& cameraId,
int clientPid, int ClientUid);
@@ -1004,16 +1002,16 @@
// as for legacy apps we will toggle the app op for all packages in the UID.
// The caveat is that the operation may be attributed to the wrong package and
// stats based on app ops may be slightly off.
- std::string getPackageNameFromUid(int clientUid);
+ std::string getPackageNameFromUid(int clientUid) const;
// Single implementation shared between the various connect calls
template<class CALLBACK, class CLIENT>
binder::Status connectHelper(const sp<CALLBACK>& cameraCb, const std::string& cameraId,
- int api1CameraId, const std::string& clientPackageNameMaybe, bool systemNativeClient,
+ int api1CameraId, const std::string& clientPackageName, bool systemNativeClient,
const std::optional<std::string>& clientFeatureId, int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool shimUpdateOnly, int scoreOffset, int targetSdkVersion,
int rotationOverride, bool forceSlowJpegMode,
- const std::string& originalCameraId,
+ const std::string& originalCameraId, bool isNonSystemNdk,
/*out*/sp<CLIENT>& device);
// Lock guarding camera service state
diff --git a/services/camera/libcameraservice/aidl/AidlCameraService.cpp b/services/camera/libcameraservice/aidl/AidlCameraService.cpp
index 8923655..7f674bd 100644
--- a/services/camera/libcameraservice/aidl/AidlCameraService.cpp
+++ b/services/camera/libcameraservice/aidl/AidlCameraService.cpp
@@ -154,11 +154,11 @@
hardware::ICameraService::USE_CALLING_PID,
hardware::ICameraService::USE_CALLING_UID,
kDefaultDeviceId);
+ clientAttribution.packageName = "";
+ clientAttribution.attributionTag = std::nullopt;
binder::Status serviceRet = mCameraService->connectDevice(
callbacks,
in_cameraId,
- std::string(),
- /* clientFeatureId= */{},
/* scoreOffset= */ 0,
/* targetSdkVersion= */ __ANDROID_API_FUTURE__,
ROTATION_OVERRIDE_NONE,
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 61577e4..861414f 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -24,11 +24,12 @@
#include <utils/Log.h>
#include <utils/Trace.h>
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
#include <camera/CameraUtils.h>
#include <camera/StringUtils.h>
+#include <com_android_internal_camera_flags.h>
#include <cutils/properties.h>
#include <gui/Surface.h>
-#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
#include "api1/Camera2Client.h"
@@ -50,6 +51,8 @@
namespace android {
using namespace camera2;
+namespace flags = com::android::internal::camera::flags;
+
// Interface used by CameraService
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
@@ -420,7 +423,7 @@
result << " none\n";
}
- std::string resultStr = std::move(result.str());
+ std::string resultStr = result.str();
write(fd, resultStr.c_str(), resultStr.size());
@@ -501,7 +504,16 @@
bool hasDeviceError = mDevice->hasDeviceError();
mDevice->disconnect();
- CameraService::Client::disconnect();
+ if (flags::api1_release_binderlock_before_cameraservice_disconnect()) {
+ // CameraService::Client::disconnect calls CameraService which attempts to lock
+ // CameraService's mServiceLock. This might lead to a deadlock if the cameraservice is
+ // currently waiting to lock mSerializationLock on another thread.
+ mBinderSerializationLock.unlock();
+ CameraService::Client::disconnect();
+ mBinderSerializationLock.lock();
+ } else {
+ CameraService::Client::disconnect();
+ }
int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
mCameraServiceProxyWrapper->logClose(mCameraIdStr, closeLatencyMs, hasDeviceError);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 2990099..fcba9bc 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -295,7 +295,7 @@
}
List<const CameraDeviceBase::PhysicalCameraSettingsList> metadataRequestList;
- std::list<const SurfaceMap> surfaceMapList;
+ std::list<SurfaceMap> surfaceMapList;
submitInfo->mRequestId = mRequestIdCounter;
uint32_t loopCounter = 0;
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 1b7fc6e..fa569ce 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -23,7 +23,7 @@
#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
#include <camera/CameraMetadata.h>
#include <camera/camera2/OutputConfiguration.h>
-#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include "common/CameraDeviceBase.h"
#include "device3/Camera3StreamInterface.h"
@@ -96,9 +96,12 @@
const CameraMetadata& settings) override;
protected:
- struct ProducerListener : public BnProducerListener {
- // ProducerListener impementation
+ struct StreamSurfaceListener : public SurfaceListener {
+ // StreamSurfaceListener implementation
void onBufferReleased() override { /*No impl. for now*/ };
+ bool needsReleaseNotify() override { return true; };
+ void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {};
+ void onBufferDetached(int /*slot*/) override {};
};
status_t registerCompositeStreamListener(int32_t streamId);
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 8b41d00..81eb7d1 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -48,7 +48,7 @@
mBlobHeight(0),
mDepthBufferAcquired(false),
mBlobBufferAcquired(false),
- mProducerListener(new ProducerListener()),
+ mStreamSurfaceListener(new StreamSurfaceListener()),
mMaxJpegBufferSize(-1),
mUHRMaxJpegBufferSize(-1),
mIsLogicalCamera(false) {
@@ -690,7 +690,7 @@
return NO_INIT;
}
- auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener);
+ auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mStreamSurfaceListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mBlobStreamId);
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 3f8f6a2..75deef7 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -130,12 +130,12 @@
static const auto kDepthMapDataSpace = HAL_DATASPACE_DEPTH;
static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF;
- int mBlobStreamId, mBlobSurfaceId, mDepthStreamId, mDepthSurfaceId;
- size_t mBlobWidth, mBlobHeight;
- sp<CpuConsumer> mBlobConsumer, mDepthConsumer;
- bool mDepthBufferAcquired, mBlobBufferAcquired;
- sp<Surface> mDepthSurface, mBlobSurface, mOutputSurface;
- sp<ProducerListener> mProducerListener;
+ int mBlobStreamId, mBlobSurfaceId, mDepthStreamId, mDepthSurfaceId;
+ size_t mBlobWidth, mBlobHeight;
+ sp<CpuConsumer> mBlobConsumer, mDepthConsumer;
+ bool mDepthBufferAcquired, mBlobBufferAcquired;
+ sp<Surface> mDepthSurface, mBlobSurface, mOutputSurface;
+ sp<StreamSurfaceListener> mStreamSurfaceListener;
ssize_t mMaxJpegBufferSize;
ssize_t mUHRMaxJpegBufferSize;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index cf6ff84..2f05d4d 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -68,7 +68,7 @@
mMainImageStreamId(-1),
mMainImageSurfaceId(-1),
mYuvBufferAcquired(false),
- mProducerListener(new ProducerListener()),
+ mStreamSurfaceListener(new StreamSurfaceListener()),
mDequeuedOutputBufferCnt(0),
mCodecOutputCounter(0),
mQuality(-1),
@@ -513,7 +513,7 @@
return NO_INIT;
}
- auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener);
+ auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mStreamSurfaceListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mMainImageStreamId);
@@ -1589,7 +1589,7 @@
// The chrome plane could be either Cb first, or Cr first. Take the
// smaller address.
uint8_t *src = std::min(yuvBuffer.dataCb, yuvBuffer.dataCr);
- MediaImage2::PlaneIndex dstPlane = codecUvOffsetDiff > 0 ? MediaImage2::U : MediaImage2::V;
+ MediaImage2::PlaneIndex dstPlane = codecUPlaneFirst ? MediaImage2::U : MediaImage2::V;
for (auto row = top/2; row < (top+height)/2; row++) {
uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[dstPlane].mOffset +
imageInfo->mPlane[dstPlane].mRowInc * (row - top/2);
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index 9cfd78b..ba10e05 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -19,7 +19,6 @@
#include <queue>
-#include <gui/IProducerListener.h>
#include <gui/CpuConsumer.h>
#include <media/hardware/VideoAPI.h>
@@ -234,10 +233,10 @@
bool mYuvBufferAcquired; // Only applicable to HEVC codec
std::queue<int64_t> mMainImageFrameNumbers;
- static const int32_t kMaxOutputSurfaceProducerCount = 1;
- sp<Surface> mOutputSurface;
- sp<ProducerListener> mProducerListener;
- int32_t mDequeuedOutputBufferCnt;
+ static const int32_t kMaxOutputSurfaceProducerCount = 1;
+ sp<Surface> mOutputSurface;
+ sp<StreamSurfaceListener> mStreamSurfaceListener;
+ int32_t mDequeuedOutputBufferCnt;
// Map from frame number to JPEG setting of orientation+quality
struct HeicSettings {
diff --git a/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp b/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
index dadefcc..1646d5e 100644
--- a/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
@@ -54,7 +54,7 @@
mOutputColorSpace(ANDROID_REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED),
mOutputStreamUseCase(0),
mFirstRequestLatency(-1),
- mProducerListener(new ProducerListener()),
+ mStreamSurfaceListener(new StreamSurfaceListener()),
mMaxJpegBufferSize(-1),
mUHRMaxJpegBufferSize(-1),
mStaticInfo(device->info()) {
@@ -653,7 +653,7 @@
return NO_INIT;
}
- auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener);
+ auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mStreamSurfaceListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mP010StreamId);
diff --git a/services/camera/libcameraservice/api2/JpegRCompositeStream.h b/services/camera/libcameraservice/api2/JpegRCompositeStream.h
index 6669739..d3ab19c 100644
--- a/services/camera/libcameraservice/api2/JpegRCompositeStream.h
+++ b/services/camera/libcameraservice/api2/JpegRCompositeStream.h
@@ -128,7 +128,8 @@
int32_t mOutputColorSpace;
int64_t mOutputStreamUseCase;
nsecs_t mFirstRequestLatency;
- sp<ProducerListener> mProducerListener;
+
+ sp<StreamSurfaceListener> mStreamSurfaceListener;
ssize_t mMaxJpegBufferSize;
ssize_t mUHRMaxJpegBufferSize;
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index cfc41c3..aceb5c0 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -136,7 +136,7 @@
* Output lastFrameNumber is the expected last frame number of the list of requests.
*/
virtual status_t captureList(const List<const PhysicalCameraSettingsList> &requests,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber = NULL) = 0;
/**
@@ -152,7 +152,7 @@
* Output lastFrameNumber is the last frame number of the previous streaming request.
*/
virtual status_t setStreamingRequestList(const List<const PhysicalCameraSettingsList> &requests,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber = NULL) = 0;
/**
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
index c42e51a..65fee7d 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -485,7 +485,7 @@
streamId, bufferCount);
}
}
- std::string linesStr = std::move(lines.str());
+ std::string linesStr = lines.str();
write(fd, linesStr.c_str(), linesStr.size());
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 97cfdac..11891e9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -612,10 +612,35 @@
{
lines = " Last request sent:\n";
+ LatestRequestInfo lastRequestInfo = getLatestRequestInfoLocked();
+ // Print out output and input stream ids
+ if (flags::dumpsys_request_stream_ids()) {
+ if (lastRequestInfo.outputStreamIds.size() != 0) {
+ lines += " Output Stream Ids:\n";
+ for (const auto &streamId: lastRequestInfo.outputStreamIds) {
+ lines += " " + std::to_string(streamId) + "\n";
+ }
+ }
+ if (lastRequestInfo.inputStreamId != -1) {
+ lines += " Input Stream Id: " + std::to_string(lastRequestInfo.inputStreamId)
+ + "\n";
+ }
+ }
+ // Keeping this write() outside the flagged if makes it easier while
+ // removing the flag.
+ write(fd, lines.c_str(), lines.size());
+ lines = " Logical request settings:\n";
+ CameraMetadata lastRequestSettings = lastRequestInfo.requestSettings;
write(fd, lines.c_str(), lines.size());
- CameraMetadata lastRequest = getLatestRequestLocked();
- lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
+ lastRequestSettings.dump(fd, /*verbosity=all info*/2, /*indentation*/6);
+ if (flags::dumpsys_request_stream_ids()) {
+ for (const auto& pair: lastRequestInfo.physicalRequestSettings) {
+ lines = " Physical request settings for camera id " + pair.first + ":\n";
+ write(fd, lines.c_str(), lines.size());
+ pair.second.dump(fd, /*verbosity=all info*/2, /*indentation*/8);
+ }
+ }
}
if (dumpTemplates) {
@@ -720,7 +745,7 @@
status_t Camera3Device::convertMetadataListToRequestListLocked(
const List<const PhysicalCameraSettingsList> &metadataList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
bool repeating, nsecs_t requestTimeNs,
RequestList *requestList) {
if (requestList == NULL) {
@@ -730,7 +755,7 @@
int32_t burstId = 0;
List<const PhysicalCameraSettingsList>::const_iterator metadataIt = metadataList.begin();
- std::list<const SurfaceMap>::const_iterator surfaceMapIt = surfaceMaps.begin();
+ std::list<SurfaceMap>::const_iterator surfaceMapIt = surfaceMaps.begin();
for (; metadataIt != metadataList.end() && surfaceMapIt != surfaceMaps.end();
++metadataIt, ++surfaceMapIt) {
sp<CaptureRequest> newRequest = setUpRequestLocked(*metadataIt, *surfaceMapIt);
@@ -778,14 +803,14 @@
ATRACE_CALL();
List<const PhysicalCameraSettingsList> requestsList;
- std::list<const SurfaceMap> surfaceMaps;
+ std::list<SurfaceMap> surfaceMaps;
convertToRequestList(requestsList, surfaceMaps, request);
return captureList(requestsList, surfaceMaps, lastFrameNumber);
}
void Camera3Device::convertToRequestList(List<const PhysicalCameraSettingsList>& requestsList,
- std::list<const SurfaceMap>& surfaceMaps,
+ std::list<SurfaceMap>& surfaceMaps,
const CameraMetadata& request) {
PhysicalCameraSettingsList requestList;
requestList.push_back({getId(), request});
@@ -803,7 +828,7 @@
status_t Camera3Device::submitRequestsHelper(
const List<const PhysicalCameraSettingsList> &requests,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
bool repeating,
/*out*/
int64_t *lastFrameNumber) {
@@ -851,7 +876,7 @@
}
status_t Camera3Device::captureList(const List<const PhysicalCameraSettingsList> &requestsList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber) {
ATRACE_CALL();
@@ -863,7 +888,7 @@
ATRACE_CALL();
List<const PhysicalCameraSettingsList> requestsList;
- std::list<const SurfaceMap> surfaceMaps;
+ std::list<SurfaceMap> surfaceMaps;
convertToRequestList(requestsList, surfaceMaps, request);
return setStreamingRequestList(requestsList, /*surfaceMap*/surfaceMaps,
@@ -872,7 +897,7 @@
status_t Camera3Device::setStreamingRequestList(
const List<const PhysicalCameraSettingsList> &requestsList,
- const std::list<const SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) {
+ const std::list<SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) {
ATRACE_CALL();
return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/true, lastFrameNumber);
@@ -2911,13 +2936,13 @@
camera3::flushInflightRequests(states);
}
-CameraMetadata Camera3Device::getLatestRequestLocked() {
+Camera3Device::LatestRequestInfo Camera3Device::getLatestRequestInfoLocked() {
ALOGV("%s", __FUNCTION__);
- CameraMetadata retVal;
+ LatestRequestInfo retVal;
if (mRequestThread != NULL) {
- retVal = mRequestThread->getLatestRequest();
+ retVal = mRequestThread->getLatestRequestInfo();
}
return retVal;
@@ -3487,30 +3512,40 @@
if (halRequest.settings != nullptr) { // Don't update if they were unchanged
Mutex::Autolock al(mLatestRequestMutex);
- camera_metadata_t* cloned = clone_camera_metadata(halRequest.settings);
- mLatestRequest.acquire(cloned);
+ // Fill in latest request and physical request
+ camera_metadata_t *cloned = clone_camera_metadata(halRequest.settings);
+ mLatestRequestInfo.requestSettings.acquire(cloned);
- mLatestPhysicalRequest.clear();
+ mLatestRequestInfo.physicalRequestSettings.clear();
+ mLatestRequestInfo.outputStreamIds.clear();
for (uint32_t i = 0; i < halRequest.num_physcam_settings; i++) {
cloned = clone_camera_metadata(halRequest.physcam_settings[i]);
- mLatestPhysicalRequest.emplace(halRequest.physcam_id[i],
- CameraMetadata(cloned));
+ mLatestRequestInfo.physicalRequestSettings.emplace(halRequest.physcam_id[i],
+ CameraMetadata(cloned));
}
if (parent != nullptr) {
int32_t inputStreamId = -1;
if (halRequest.input_buffer != nullptr) {
inputStreamId = Camera3Stream::cast(halRequest.input_buffer->stream)->getId();
+ mLatestRequestInfo.inputStreamId = inputStreamId;
}
+ for (size_t i = 0; i < halRequest.num_output_buffers; i++) {
+ int32_t outputStreamId =
+ Camera3Stream::cast(halRequest.output_buffers[i].stream)->getId();
+ mLatestRequestInfo.outputStreamIds.emplace(outputStreamId);
+ }
+
parent->monitorMetadata(TagMonitor::REQUEST,
halRequest.frame_number,
- 0, mLatestRequest, mLatestPhysicalRequest, halRequest.output_buffers,
+ 0, mLatestRequestInfo.requestSettings,
+ mLatestRequestInfo.physicalRequestSettings, halRequest.output_buffers,
halRequest.num_output_buffers, inputStreamId);
}
}
if (parent != nullptr) {
- parent->collectRequestStats(halRequest.frame_number, mLatestRequest);
+ parent->collectRequestStats(halRequest.frame_number, mLatestRequestInfo.requestSettings);
}
if (halRequest.settings != nullptr) {
@@ -4170,13 +4205,13 @@
return OK;
}
-CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
+Camera3Device::LatestRequestInfo Camera3Device::RequestThread::getLatestRequestInfo() const {
ATRACE_CALL();
Mutex::Autolock al(mLatestRequestMutex);
ALOGV("RequestThread::%s", __FUNCTION__);
- return mLatestRequest;
+ return mLatestRequestInfo;
}
bool Camera3Device::RequestThread::isStreamPending(
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 9f414e8..6f87ca3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -132,12 +132,12 @@
// idle state
status_t capture(CameraMetadata &request, int64_t *lastFrameNumber = NULL) override;
status_t captureList(const List<const PhysicalCameraSettingsList> &requestsList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber = NULL) override;
status_t setStreamingRequest(const CameraMetadata &request,
int64_t *lastFrameNumber = NULL) override;
status_t setStreamingRequestList(const List<const PhysicalCameraSettingsList> &requestsList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
int64_t *lastFrameNumber = NULL) override;
status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL) override;
@@ -703,17 +703,17 @@
status_t convertMetadataListToRequestListLocked(
const List<const PhysicalCameraSettingsList> &metadataList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
bool repeating, nsecs_t requestTimeNs,
/*out*/
RequestList *requestList);
void convertToRequestList(List<const PhysicalCameraSettingsList>& requestsList,
- std::list<const SurfaceMap>& surfaceMaps,
+ std::list<SurfaceMap>& surfaceMaps,
const CameraMetadata& request);
status_t submitRequestsHelper(const List<const PhysicalCameraSettingsList> &requestsList,
- const std::list<const SurfaceMap> &surfaceMaps,
+ const std::list<SurfaceMap> &surfaceMaps,
bool repeating,
int64_t *lastFrameNumber = NULL);
@@ -736,12 +736,19 @@
virtual void applyMaxBatchSizeLocked(
RequestList* requestList, const sp<camera3::Camera3OutputStreamInterface>& stream) = 0;
+ struct LatestRequestInfo {
+ CameraMetadata requestSettings;
+ std::unordered_map<std::string, CameraMetadata> physicalRequestSettings;
+ int32_t inputStreamId = -1;
+ std::set<int32_t> outputStreamIds;
+ };
+
/**
* Get the last request submitted to the hal by the request thread.
*
* Must be called with mLock held.
*/
- virtual CameraMetadata getLatestRequestLocked();
+ virtual LatestRequestInfo getLatestRequestInfoLocked();
virtual status_t injectionCameraInitialize(const std::string &injectCamId,
sp<CameraProviderManager> manager) = 0;
@@ -992,7 +999,7 @@
* Get the latest request that was sent to the HAL
* with process_capture_request.
*/
- CameraMetadata getLatestRequest() const;
+ LatestRequestInfo getLatestRequestInfo() const;
/**
* Returns true if the stream is a target of any queued or repeating
@@ -1192,8 +1199,7 @@
// android.request.id for latest process_capture_request
int32_t mLatestRequestId;
int32_t mLatestFailedRequestId;
- CameraMetadata mLatestRequest;
- std::unordered_map<std::string, CameraMetadata> mLatestPhysicalRequest;
+ LatestRequestInfo mLatestRequestInfo;
typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
Mutex mTriggerMutex;
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 22d2716..61c5a3b 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -104,7 +104,7 @@
lines << fmt::sprintf(" Total buffers: %zu, currently dequeued: %zu, "
"currently cached: %zu\n", mTotalBufferCount, mHandoutTotalBufferCount,
mCachedOutputBufferCount);
- std::string linesStr = std::move(lines.str());
+ std::string linesStr = lines.str();
write(fd, linesStr.c_str(), linesStr.size());
Camera3Stream::dump(fd, args);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 3cd4543..54d55a1 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -555,8 +555,8 @@
// Configure consumer-side ANativeWindow interface. The listener may be used
// to notify buffer manager (if it is used) of the returned buffers.
res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA,
- /*reportBufferRemoval*/true,
- /*listener*/mBufferProducerListener);
+ /*listener*/mBufferProducerListener,
+ /*reportBufferRemoval*/true);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 8a93ed8..f8b78c1 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -193,6 +193,7 @@
virtual void onBufferReleased();
virtual bool needsReleaseNotify() { return mNeedsReleaseNotify; }
virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers);
+ virtual void onBufferDetached(int /*slot*/) override {};
private:
wp<Camera3OutputStream> mParent;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 1feb4a0..43f12fb 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -22,7 +22,7 @@
#include <camera/CameraMetadata.h>
#include <gui/IConsumerListener.h>
-#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include <gui/BufferItemConsumer.h>
#include <utils/Condition.h>
@@ -159,7 +159,7 @@
// the IProducerListener::onBufferReleased callback is associated with. We
// create one of these per output BufferQueue, and then pass the producer
// into onBufferReleasedByOutput above.
- class OutputListener : public BnProducerListener,
+ class OutputListener : public SurfaceListener,
public IBinder::DeathRecipient {
public:
OutputListener(wp<Camera3StreamSplitter> splitter,
@@ -168,6 +168,9 @@
// From IProducerListener
void onBufferReleased() override;
+ bool needsReleaseNotify() override { return true; };
+ void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {};
+ void onBufferDetached(int /*slot*/) override {}
// From IBinder::DeathRecipient
void binderDied(const wp<IBinder>& who) override;
diff --git a/services/camera/libcameraservice/device3/PreviewFrameSpacer.cpp b/services/camera/libcameraservice/device3/PreviewFrameSpacer.cpp
index 83caa00..a04406e 100644
--- a/services/camera/libcameraservice/device3/PreviewFrameSpacer.cpp
+++ b/services/camera/libcameraservice/device3/PreviewFrameSpacer.cpp
@@ -18,10 +18,16 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include <com_android_internal_camera_flags.h>
+
#include <utils/Log.h>
#include "PreviewFrameSpacer.h"
#include "Camera3OutputStream.h"
+#include "utils/SchedulingPolicyUtils.h"
+#include "utils/Utils.h"
+
+namespace flags = com::android::internal::camera::flags;
namespace android {
@@ -129,6 +135,24 @@
mLastCameraReadoutTime = bufferHolder.readoutTimestamp;
}
+status_t PreviewFrameSpacer::run(const char* name, int32_t priority, size_t stack) {
+ auto ret = Thread::run(name, priority, stack);
+ if (flags::bump_preview_frame_space_priority()) {
+ // Boost priority of the preview frame spacer thread to SCHED_FIFO.
+ pid_t previewFrameSpacerTid = getTid();
+ auto res = SchedulingPolicyUtils::requestPriorityDirect(getpid(), previewFrameSpacerTid,
+ RunThreadWithRealtimePriority::kRequestThreadPriority);
+ if (res != OK) {
+ ALOGW("Can't set realtime priority for preview frame spacer thread: %s (%d)",
+ strerror(-res), res);
+ } else {
+ ALOGV("Set real time priority for preview frame spacer thread (tid %d)",
+ previewFrameSpacerTid);
+ }
+ }
+ return ret;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/PreviewFrameSpacer.h b/services/camera/libcameraservice/device3/PreviewFrameSpacer.h
index f46de3d..ab85189 100644
--- a/services/camera/libcameraservice/device3/PreviewFrameSpacer.h
+++ b/services/camera/libcameraservice/device3/PreviewFrameSpacer.h
@@ -58,6 +58,7 @@
bool threadLoop() override;
void requestExit() override;
+ status_t run(const char* name, int32_t priority = PRIORITY_DEFAULT, size_t stack = 0) override;
private:
// structure holding cached preview buffer info
diff --git a/services/camera/libcameraservice/fuzzer/Android.bp b/services/camera/libcameraservice/fuzzer/Android.bp
index 7760f6a..667ba02 100644
--- a/services/camera/libcameraservice/fuzzer/Android.bp
+++ b/services/camera/libcameraservice/fuzzer/Android.bp
@@ -26,7 +26,18 @@
cc_defaults {
name: "libcameraservice_fuzz_defaults",
fuzz_config: {
- componentid: 41727
+ cc: [
+ "android-camera-fwk-eng@google.com",
+ ],
+ componentid: 41727,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libcameraservice",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
@@ -37,9 +48,9 @@
"DistortionMapperFuzzer.cpp",
],
shared_libs: [
- "libcameraservice",
+ "camera_platform_flags_c_lib",
"libcamera_client",
- "camera_platform_flags_c_lib"
+ "libcameraservice",
],
}
@@ -50,8 +61,8 @@
"DepthProcessorFuzzer.cpp",
],
shared_libs: [
+ "camera_platform_flags_c_lib",
"libcameraservice",
- "camera_platform_flags_c_lib"
],
corpus: ["corpus/*.jpg"],
}
diff --git a/services/camera/libcameraservice/fuzzer/DepthProcessorFuzzer.cpp b/services/camera/libcameraservice/fuzzer/DepthProcessorFuzzer.cpp
index 650ca91..5c5e177 100644
--- a/services/camera/libcameraservice/fuzzer/DepthProcessorFuzzer.cpp
+++ b/services/camera/libcameraservice/fuzzer/DepthProcessorFuzzer.cpp
@@ -14,49 +14,92 @@
* limitations under the License.
*/
-#include <array>
-#include <vector>
+#include "common/DepthPhotoProcessor.h"
+
+#include <random>
#include <fuzzer/FuzzedDataProvider.h>
-#include "common/DepthPhotoProcessor.h"
-
using namespace android;
using namespace android::camera3;
-static const size_t kTestBufferWidth = 640;
-static const size_t kTestBufferHeight = 480;
-static const size_t kTestBufferDepthSize (kTestBufferWidth * kTestBufferHeight);
+static const float kMinRatio = 0.1f;
+static const float kMaxRatio = 0.9f;
-void generateDepth16Buffer(const uint8_t* data, size_t size, std::array<uint16_t, kTestBufferDepthSize> *depth16Buffer /*out*/) {
- FuzzedDataProvider dataProvider(data, size);
- for (size_t i = 0; i < depth16Buffer->size(); i++) {
- (*depth16Buffer)[i] = dataProvider.ConsumeIntegral<uint16_t>();
+static const uint8_t kTotalDepthJpegBufferCount = 3;
+static const uint8_t kIntrinsicCalibrationSize = 5;
+static const uint8_t kLensDistortionSize = 5;
+
+static const DepthPhotoOrientation kDepthPhotoOrientations[] = {
+ DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES,
+ DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES,
+ DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES,
+ DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES};
+
+void generateDepth16Buffer(std::vector<uint16_t>* depth16Buffer /*out*/, size_t length,
+ FuzzedDataProvider& fdp) {
+ std::default_random_engine gen(fdp.ConsumeIntegral<uint8_t>());
+ std::uniform_int_distribution uniDist(0, UINT16_MAX - 1);
+ for (size_t i = 0; i < length; ++i) {
+ (*depth16Buffer)[i] = uniDist(gen);
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
DepthPhotoInputFrame inputFrame;
+
+ /**
+ * Consuming 80% of the data to set mMainJpegBuffer. This ensures that we
+ * don't completely exhaust data and use the rest 20% for fuzzing of APIs.
+ */
+ std::vector<uint8_t> buffer = fdp.ConsumeBytes<uint8_t>((size * 80) / 100);
+ inputFrame.mMainJpegBuffer = reinterpret_cast<const char*>(buffer.data());
+
+ /**
+ * Calculate height and width based on buffer size and a ratio within [0.1, 0.9].
+ * The ratio adjusts the dimensions while maintaining a relationship to the total buffer size.
+ */
+ const float ratio = fdp.ConsumeFloatingPointInRange<float>(kMinRatio, kMaxRatio);
+ const size_t height = std::sqrt(buffer.size()) * ratio;
+ const size_t width = std::sqrt(buffer.size()) / ratio;
+
+ inputFrame.mMainJpegHeight = height;
+ inputFrame.mMainJpegWidth = width;
+ inputFrame.mMainJpegSize = buffer.size();
// Worst case both depth and confidence maps have the same size as the main color image.
- inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3;
+ inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * kTotalDepthJpegBufferCount;
+
+ std::vector<uint16_t> depth16Buffer(height * width);
+ generateDepth16Buffer(&depth16Buffer, height * width, fdp);
+ inputFrame.mDepthMapBuffer = depth16Buffer.data();
+ inputFrame.mDepthMapHeight = height;
+ inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = width;
+
+ inputFrame.mIsLogical = fdp.ConsumeBool();
+
+ inputFrame.mOrientation = fdp.PickValueInArray<DepthPhotoOrientation>(kDepthPhotoOrientations);
+
+ if (fdp.ConsumeBool()) {
+ for (uint8_t i = 0; i < kIntrinsicCalibrationSize; ++i) {
+ inputFrame.mIntrinsicCalibration[i] = fdp.ConsumeFloatingPoint<float>();
+ }
+ inputFrame.mIsIntrinsicCalibrationValid = 1;
+ }
+
+ if (fdp.ConsumeBool()) {
+ for (uint8_t i = 0; i < kLensDistortionSize; ++i) {
+ inputFrame.mLensDistortion[i] = fdp.ConsumeFloatingPoint<float>();
+ }
+ inputFrame.mIsLensDistortionValid = 1;
+ }
std::vector<uint8_t> depthPhotoBuffer(inputFrame.mMaxJpegSize);
size_t actualDepthPhotoSize = 0;
- std::array<uint16_t, kTestBufferDepthSize> depth16Buffer;
- generateDepth16Buffer(data, size, &depth16Buffer);
+ processDepthPhotoFrame(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(),
+ &actualDepthPhotoSize);
- inputFrame.mMainJpegBuffer = reinterpret_cast<const char*> (data);
- inputFrame.mMainJpegSize = size;
- inputFrame.mDepthMapBuffer = depth16Buffer.data();
- inputFrame.mDepthMapStride = kTestBufferWidth;
- inputFrame.mDepthMapWidth = kTestBufferWidth;
- inputFrame.mDepthMapHeight = kTestBufferHeight;
- processDepthPhotoFrame(
- inputFrame,
- depthPhotoBuffer.size(),
- depthPhotoBuffer.data(),
- &actualDepthPhotoSize);
-
- return 0;
+ return 0;
}
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
index 463aefb..59e892f 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraService.cpp
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
@@ -130,8 +130,10 @@
hardware::ICameraService::USE_CALLING_PID,
hardware::ICameraService::USE_CALLING_UID,
kDefaultDeviceId);
+ clientAttribution.packageName = "";
+ clientAttribution.attributionTag = std::nullopt;
binder::Status serviceRet = mAidlICameraService->connectDevice(
- callbacks, cameraId, std::string(), {}, 0/*oomScoreOffset*/,
+ callbacks, cameraId, 0/*oomScoreOffset*/,
/*targetSdkVersion*/__ANDROID_API_FUTURE__, ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, /*out*/&deviceRemote);
HStatus status = HStatus::NO_ERROR;
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
index 515a2a7..ac2fd64 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
@@ -361,7 +361,7 @@
clientAttribution.deviceId = kDefaultDeviceId;
clientAttribution.uid = android::CameraService::USE_CALLING_UID;
clientAttribution.pid = android::CameraService::USE_CALLING_PID;
- rc = mCameraService->connect(this, cameraId, std::string(),
+ rc = mCameraService->connect(this, cameraId,
/*targetSdkVersion*/ __ANDROID_API_FUTURE__,
ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT,
/*forceSlowJpegMode*/false,
@@ -604,7 +604,8 @@
AttributionSourceState clientAttribution;
clientAttribution.deviceId = kDefaultDeviceId;
clientAttribution.uid = android::CameraService::USE_CALLING_UID;
- mCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ clientAttribution.pid = android::CameraService::USE_CALLING_PID;
+ mCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT,
clientAttribution, /*devicePolicy*/0, &device);
diff --git a/services/camera/libcameraservice/tests/Android.bp b/services/camera/libcameraservice/tests/Android.bp
index 8bb5ffa..bbc10dc 100644
--- a/services/camera/libcameraservice/tests/Android.bp
+++ b/services/camera/libcameraservice/tests/Android.bp
@@ -84,6 +84,7 @@
"libcutils",
"libhidlbase",
"libcamera_client",
+ "libgui",
"libui",
"android.companion.virtualdevice.flags-aconfig-cc",
"android.hardware.camera.common@1.0",
@@ -108,6 +109,7 @@
// Only include sources that can't be run host-side here
srcs: [
+ "Camera3StreamSplitterTest.cpp",
"CameraPermissionsTest.cpp",
"CameraProviderManagerTest.cpp",
],
diff --git a/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp b/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp
new file mode 100644
index 0000000..acb82d2
--- /dev/null
+++ b/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3StreamSplitterTest"
+// #define LOG_NDEBUG 0
+
+#include "../device3/Camera3StreamSplitter.h"
+
+#include <android/hardware_buffer.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/PixelFormat.h>
+
+#include <system/window.h>
+#include <vndk/window.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+uint64_t kConsumerUsage = AHARDWAREBUFFER_USAGE_CAMERA_READ;
+uint64_t kProducerUsage = AHARDWAREBUFFER_USAGE_CAMERA_READ;
+size_t kHalMaxBuffers = 3;
+uint32_t kWidth = 640;
+uint32_t kHeight = 480;
+PixelFormat kFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
+int64_t kDynamicRangeProfile = 0;
+
+std::tuple<sp<BufferItemConsumer>, sp<Surface>> createConsumerAndSurface() {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ return {sp<BufferItemConsumer>::make(consumer, kConsumerUsage), sp<Surface>::make(producer)};
+}
+
+class Camera3StreamSplitterTest : public testing::Test {
+ public:
+ void SetUp() override { mSplitter = sp<Camera3StreamSplitter>::make(); }
+
+ protected:
+ sp<Camera3StreamSplitter> mSplitter;
+};
+
+class TestSurfaceListener : public SurfaceListener {
+ public:
+ virtual void onBufferReleased() override { mNumBuffersReleased++; }
+ virtual bool needsReleaseNotify() { return true; }
+ virtual void onBufferDetached(int) override {}
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>&) override {};
+
+ uint32_t mNumBuffersReleased = 0;
+};
+
+class TestConsumerListener : public BufferItemConsumer::FrameAvailableListener {
+ public:
+ TestConsumerListener(const wp<BufferItemConsumer>& consumer) : mConsumer(consumer) {}
+
+ virtual void onFrameAvailable(const BufferItem&) {
+ sp<BufferItemConsumer> consumer = mConsumer.promote();
+ EXPECT_NE(nullptr, consumer);
+
+ BufferItem item;
+ EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0));
+ mNumBuffersAcquired++;
+ EXPECT_EQ(OK, consumer->releaseBuffer(item, Fence::NO_FENCE));
+ }
+ virtual void onFrameReplaced(const BufferItem&) {}
+ virtual void onFrameDequeued(const uint64_t) {}
+ virtual void onFrameCancelled(const uint64_t) {}
+ virtual void onFrameDetached(const uint64_t) {}
+
+ wp<BufferItemConsumer> mConsumer;
+ uint32_t mNumBuffersAcquired = 0;
+};
+
+} // namespace
+
+TEST_F(Camera3StreamSplitterTest, TestWithoutSurfaces_NoBuffersConsumed) {
+ sp<Surface> consumer;
+ EXPECT_EQ(OK, mSplitter->connect({}, kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth,
+ kHeight, kFormat, &consumer, kDynamicRangeProfile));
+
+ sp<TestSurfaceListener> surfaceListener = sp<TestSurfaceListener>::make();
+ EXPECT_EQ(OK, consumer->connect(NATIVE_WINDOW_API_CAMERA, surfaceListener, false));
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(kWidth, kHeight, kFormat, kProducerUsage);
+ EXPECT_EQ(OK, consumer->attachBuffer(buffer->getNativeBuffer()));
+ // TODO: Do this with the surface itself once the API is available.
+ EXPECT_EQ(OK,
+ ANativeWindow_queueBuffer(consumer.get(), buffer->getNativeBuffer(), /*fenceFd*/ -1));
+
+ EXPECT_EQ(0u, surfaceListener->mNumBuffersReleased);
+}
+
+TEST_F(Camera3StreamSplitterTest, TestProcessSingleBuffer) {
+ //
+ // Set up output consumers:
+ //
+ constexpr auto kSurfaceId1 = 1;
+ auto [bufferItemConsumer1, surface1] = createConsumerAndSurface();
+ sp<TestConsumerListener> consumerListener1 =
+ sp<TestConsumerListener>::make(bufferItemConsumer1);
+ bufferItemConsumer1->setFrameAvailableListener(consumerListener1);
+
+ constexpr auto kSurfaceId2 = 2;
+ auto [bufferItemConsumer2, surface2] = createConsumerAndSurface();
+ sp<TestConsumerListener> consumerListener2 =
+ sp<TestConsumerListener>::make(bufferItemConsumer2);
+ bufferItemConsumer2->setFrameAvailableListener(consumerListener2);
+
+ //
+ // Connect it to the splitter, get the input surface, and set it up:
+ //
+ sp<Surface> inputSurface;
+ EXPECT_EQ(OK, mSplitter->connect({{kSurfaceId1, surface1}, {kSurfaceId2, surface2}},
+ kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth,
+ kHeight, kFormat, &inputSurface, kDynamicRangeProfile));
+ sp<TestSurfaceListener> surfaceListener = sp<TestSurfaceListener>::make();
+ EXPECT_EQ(OK, inputSurface->connect(NATIVE_WINDOW_API_CAMERA, surfaceListener, false));
+ // TODO: Do this with the surface itself once the API is available.
+ EXPECT_EQ(OK, inputSurface->getIGraphicBufferProducer()->allowAllocation(false));
+
+ //
+ // Create a buffer to use:
+ //
+ sp<GraphicBuffer> singleBuffer = new GraphicBuffer(kWidth, kHeight, kFormat, kProducerUsage);
+ EXPECT_NE(nullptr, singleBuffer);
+ mSplitter->attachBufferToOutputs(singleBuffer->getNativeBuffer(), {kSurfaceId1, kSurfaceId2});
+
+ //
+ // Verify that when we attach the buffer, it's processed appropriately:
+ //
+ EXPECT_EQ(OK, inputSurface->attachBuffer(singleBuffer->getNativeBuffer()));
+ EXPECT_EQ(OK, mSplitter->getOnFrameAvailableResult());
+ // TODO: Do this with the surface itself once the API is available.
+ EXPECT_EQ(OK, ANativeWindow_queueBuffer(inputSurface.get(), singleBuffer->getNativeBuffer(),
+ /*fenceFd*/ -1));
+
+ EXPECT_EQ(1u, consumerListener1->mNumBuffersAcquired);
+ EXPECT_EQ(1u, consumerListener2->mNumBuffersAcquired);
+ EXPECT_EQ(1u, surfaceListener->mNumBuffersReleased);
+}
diff --git a/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
index cd07a69..50aeaca 100644
--- a/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
@@ -227,6 +227,7 @@
AttributionSourceState clientAttribution;
clientAttribution.deviceId = kDefaultDeviceId;
clientAttribution.uid = android::CameraService::USE_CALLING_UID;
+ clientAttribution.pid = android::CameraService::USE_CALLING_PID;
std::vector<hardware::CameraStatus> statuses;
sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
@@ -238,7 +239,7 @@
sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
sp<hardware::camera2::ICameraDeviceUser> device;
binder::Status status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &device);
@@ -253,7 +254,7 @@
sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
sp<hardware::camera2::ICameraDeviceUser> device;
binder::Status status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &device);
@@ -277,7 +278,7 @@
sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
binder::Status status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &deviceA);
@@ -285,7 +286,7 @@
ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
" service specific error code " << status.serviceSpecificErrorCode();
status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &deviceB);
@@ -311,7 +312,7 @@
sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
binder::Status status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
0/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &deviceA);
@@ -319,7 +320,7 @@
ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
" service specific error code " << status.serviceSpecificErrorCode();
status =
- sCameraService->connectDevice(callbacks, s.cameraId, std::string(), {},
+ sCameraService->connectDevice(callbacks, s.cameraId,
1/*oomScoreDiff*/, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
hardware::ICameraService::ROTATION_OVERRIDE_NONE,
clientAttribution, /*devicePolicy*/0, &deviceB);
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index d5e3790..85bca6f 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -268,6 +268,12 @@
const auto& gbps = config.getGraphicBufferProducers();
int32_t width = 0, height = 0;
if (gbps.size() > 0) {
+ if (gbps[0] == nullptr) {
+ ALOGE("%s: Failed to query size due to abandoned surface.",
+ __FUNCTION__);
+ return CameraFeatureCombinationStats::CAMERA_FEATURE_UNKNOWN;
+ }
+
sp<Surface> surface = new Surface(gbps[0], /*useAsync*/false);
ANativeWindow *anw = surface.get();
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index 38de93a..5258c0e 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -384,7 +384,7 @@
}
returnStr << "]\n";
}
- return std::move(returnStr.str());
+ return returnStr.str();
}
template<typename T>
diff --git a/services/camera/virtualcamera/Android.bp b/services/camera/virtualcamera/Android.bp
index fc186fb..7ece0cb 100644
--- a/services/camera/virtualcamera/Android.bp
+++ b/services/camera/virtualcamera/Android.bp
@@ -65,14 +65,7 @@
cc_library_static {
name: "libvirtualcamera",
srcs: [
- "VirtualCameraProvider.cc",
- "VirtualCameraDevice.cc",
- "VirtualCameraSession.cc",
- "VirtualCameraStream.cc",
- "VirtualCameraService.cc",
- "VirtualCameraSessionContext.cc",
- "VirtualCameraTestInstance.cc",
- "VirtualCameraRenderThread.cc",
+ "*.cc",
],
defaults: [
"libvirtualcamera_defaults",
diff --git a/services/camera/virtualcamera/README.md b/services/camera/virtualcamera/README.md
new file mode 100644
index 0000000..04b4811
--- /dev/null
+++ b/services/camera/virtualcamera/README.md
@@ -0,0 +1,164 @@
+# Virtual Camera
+
+The virtual camera feature allows 3rd party application to expose a remote or
+virtual camera to the standard Android camera frameworks (Camera2/CameraX, NDK,
+camera1).
+
+The stack is composed into 4 different parts:
+
+1. The **Virtual Camera Service** (this directory), implementing the Camera HAL
+ and acts as an interface between the Android Camera Server and the *Virtual
+ Camera Owner* (via the VirtualDeviceManager APIs).
+
+2. The **VirtualDeviceManager** running in the system process and handling the
+ communication between the Virtual Camera service and the Virtual Camera
+ owner
+
+3. The **Virtual Camera Owner**, the client application declaring the Virtual
+ Camera and handling the production of image data. We will also refer to this
+ part as the **producer**
+
+4. The **Consumer Application**, the client application consuming camera data,
+ which can be any application using the camera APIs
+
+This document describes the functionalities of the *Virtual Camera Service*
+
+## Before reading
+
+The service implements the Camera HAL. It's best to have a bit of an
+understanding of how it works by reading the
+[HAL documentation first](https://source.android.com/docs/core/camera)
+
+
+
+The HAL implementations are declared in: -
+[VirtualCameraDevice](./VirtualCameraDevice.h) -
+[VirtualCameraProvider](./VirtualCameraProvider.h) -
+[VirtualCameraSession](./VirtualCameraSession.h)
+
+## Current supported features
+
+Virtual Cameras report `EXTERNAL`
+[hardware level](https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL)
+but some
+[functionalities of `EXTERNAL`](https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL)
+hardware level are not fully supported.
+
+Here is a list of supported features - Single input multiple output stream and
+capture:
+
+- Support for YUV and JPEG
+
+Notable missing features:
+
+- Support for auto 3A (AWB, AE, AF): virtual camera will announce convergence
+ of 3A algorithm even though it can't receive any information about this from
+ the owner.
+
+- No flash/torch support
+
+## Overview
+
+Graphic data are exchanged using the Surface infrastructure. Like any other
+Camera HAL, the Surfaces to write data into are received from the client.
+Virtual Camera exposes a **different** Surface onto which the owner can write
+data. In the middle, we use an EGL Texture which adapts (if needed) the producer
+data to the required consumer format (scaling only for now, but we might also
+add support for rotation and cropping in the future).
+
+When the client application requires multiple resolutions, the closest one among
+supported resolutions is used for the input data and the image data is down
+scaled for the lower resolutions.
+
+Depending on the type of output, the rendering pipelines change. Here is an
+overview of the YUV and JPEG pipelines.
+
+**YUV Rendering:**
+
+```
+Virtual Device Owner Surface[1] (Producer) --{binds to}--> EGL
+Texture[1] --{renders into}--> Client Surface[1-n] (Consumer)
+```
+
+**JPEG Rendering:**
+
+```
+Virtual Device Owner Surface[1] (Producer) --{binds to}--> EGL
+Texture[1] --{compress data into}--> temporary buffer --{renders into}-->
+Client Surface[1-n] (Consumer)
+```
+
+## Life of a capture request
+
+> Before reading the following, you must understand the concepts of
+> [CaptureRequest](https://developer.android.com/reference/android/hardware/camera2/CaptureRequest)
+> and
+> [OutputConfiguration](https://developer.android.com/reference/android/hardware/camera2/OutputConfiguration).
+
+1. The consumer creates a session with one or more `Surfaces`
+
+2. The VirtualCamera owner will receive a call to
+ `VirtualCameraCallback#onStreamConfigured` with a reference to another
+ `Suface` where it can write into.
+
+3. The consumer will then start sending `CaptureRequests`. The owner will
+ receive a call to `VirtualCameraCallback#onProcessCaptureRequest`, at which
+ points it should write the required data into the surface it previously
+ received. At the same time, a new task will be enqueued in the render thread
+
+4. The [VirtualCameraRenderThread](./VirtualCameraRenderThread.cc) will consume
+ the enqueued tasks as they come. It will wait for the producer to write into
+ the input Surface (using `Surface::waitForNextFrame`).
+
+ > **Note:** Since the Surface API allows us to wait for the next frame,
+ > there is no need for the producer to notify when the frame is ready by
+ > calling a `processCaptureResult()` equivalent.
+
+5. The EGL Texture is updated with the content of the Surface.
+
+6. The EGL Texture renders into the output Surfaces.
+
+7. The Camera client is notified of the "shutter" event and the `CaptureResult`
+ is sent to the consumer.
+
+## EGL Rendering
+
+### The render thread
+
+The [VirtualCameraRenderThread](./VirtualCameraRenderThread.h) module takes care
+of rendering the input from the owner to the output via the EGL Texture. The
+rendering is done either to a JPEG buffer, which is the BLOB rendering for
+creating a JPEG or to a YUV buffer used mainly for preview Surfaces or video.
+Two EGLPrograms (shaders) defined in [EglProgram](./util/EglProgram.cc) handle
+the rendering of the data.
+
+### Initialization
+
+[EGlDisplayContext](./util/EglDisplayContext.h) initializes the whole EGL
+environment (Display, Surface, Context, and Config).
+
+The EGL Rendering is backed by a
+[ANativeWindow](https://developer.android.com/ndk/reference/group/a-native-window)
+which is just the native counterpart of the
+[Surface](https://developer.android.com/reference/android/view/Surface), which
+itself is the producer side of buffer queue, the consumer being either the
+display (Camera preview) or some encoder (to save the data or send it across the
+network).
+
+### More about OpenGL
+
+To better understand how the EGL rendering works the following resources can be
+used:
+
+Introduction to OpenGL: https://learnopengl.com/
+
+The official documentation of EGL API can be queried at:
+https://www.khronos.org/registry/egl/sdk/docs/man/xhtml/
+
+And using Google search with the following query:
+
+```
+[function name] site:https://registry.khronos.org/EGL/sdk/docs/man/html/
+
+// example: eglSwapBuffers site:https://registry.khronos.org/EGL/sdk/docs/man/html/
+```
diff --git a/services/camera/virtualcamera/VirtualCameraCaptureRequest.h b/services/camera/virtualcamera/VirtualCameraCaptureRequest.h
new file mode 100644
index 0000000..cf5402e
--- /dev/null
+++ b/services/camera/virtualcamera/VirtualCameraCaptureRequest.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTUREREQUEST_H
+#define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTUREREQUEST_H
+
+#include "VirtualCameraDevice.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+
+// Struct used to pass request settings in the different part of
+// the virtual camera system.
+struct RequestSettings {
+ // JPEG_QUALITY metadata
+ int jpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
+
+ // JPEG_ORIENTATION metadata
+ int jpegOrientation = VirtualCameraDevice::kDefaultJpegOrientation;
+
+ // JPEG_THUMBNAIL_SIZE metadata
+ Resolution thumbnailResolution = Resolution(0, 0);
+
+ // JPEG_THUMBNAIL_QUALITY metadata
+ int thumbnailJpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
+
+ // ANDROID_CONTROL_AE_TARGET_FPS_RANGE metadata
+ std::optional<FpsRange> fpsRange;
+
+ // CONTROL_CAPTURE_INTENT metadata
+ camera_metadata_enum_android_control_capture_intent_t captureIntent =
+ VirtualCameraDevice::kDefaultCaptureIntent;
+
+ // JPEG_GPS_LOCATION metadata
+ std::optional<GpsCoordinates> gpsCoordinates;
+
+ // CONTROL_AE_PRECAPTURE_TRIGGER metadata
+ std::optional<camera_metadata_enum_android_control_ae_precapture_trigger>
+ aePrecaptureTrigger;
+};
+
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android
+
+#endif // ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTUREREQUEST_H
\ No newline at end of file
diff --git a/services/camera/virtualcamera/VirtualCameraCaptureResult.cc b/services/camera/virtualcamera/VirtualCameraCaptureResult.cc
new file mode 100644
index 0000000..a61f553
--- /dev/null
+++ b/services/camera/virtualcamera/VirtualCameraCaptureResult.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "VirtualCameraCaptureResult.h"
+
+#include <cstdint>
+
+#include "VirtualCameraCaptureRequest.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
+#include "util/MetadataUtil.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+namespace {
+// See REQUEST_PIPELINE_DEPTH in CaptureResult.java.
+// This roughly corresponds to frame latency, we set to
+// documented minimum of 2.
+static constexpr uint8_t kPipelineDepth = 2;
+
+} // namespace
+
+CameraMetadata createCaptureResultMetadata(
+ const std::chrono::nanoseconds timestamp,
+ const RequestSettings& requestSettings,
+ const Resolution reportedSensorSize) {
+ // All of the keys used in the response needs to be referenced in
+ // availableResultKeys in CameraCharacteristics (see initCameraCharacteristics
+ // in VirtualCameraDevice.cc).
+ MetadataBuilder builder =
+ MetadataBuilder()
+ .setAberrationCorrectionMode(
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF)
+ .setControlAeAvailableAntibandingModes(
+ {ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF})
+ .setControlAeAntibandingMode(ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF)
+ .setControlAeExposureCompensation(0)
+ .setControlAeLockAvailable(false)
+ .setControlAeLock(ANDROID_CONTROL_AE_LOCK_OFF)
+ .setControlAeMode(ANDROID_CONTROL_AE_MODE_ON)
+ .setControlAePrecaptureTrigger(
+ // Limited devices are expected to have precapture ae enabled and
+ // respond to cancellation request. Since we don't actuall support
+ // AE at all, let's just respect the cancellation expectation in
+ // case it's requested
+ requestSettings.aePrecaptureTrigger ==
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
+ ? ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
+ : ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
+ .setControlAeState(ANDROID_CONTROL_AE_STATE_INACTIVE)
+ .setControlAfMode(ANDROID_CONTROL_AF_MODE_OFF)
+ .setControlAfTrigger(ANDROID_CONTROL_AF_TRIGGER_IDLE)
+ .setControlAfState(ANDROID_CONTROL_AF_STATE_INACTIVE)
+ .setControlAwbMode(ANDROID_CONTROL_AWB_MODE_AUTO)
+ .setControlAwbLock(ANDROID_CONTROL_AWB_LOCK_OFF)
+ .setControlAwbState(ANDROID_CONTROL_AWB_STATE_INACTIVE)
+ .setControlCaptureIntent(requestSettings.captureIntent)
+ .setControlEffectMode(ANDROID_CONTROL_EFFECT_MODE_OFF)
+ .setControlMode(ANDROID_CONTROL_MODE_AUTO)
+ .setControlSceneMode(ANDROID_CONTROL_SCENE_MODE_DISABLED)
+ .setControlVideoStabilizationMode(
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF)
+ .setCropRegion(0, 0, reportedSensorSize.width,
+ reportedSensorSize.height)
+ .setFaceDetectMode(ANDROID_STATISTICS_FACE_DETECT_MODE_OFF)
+ .setFlashState(ANDROID_FLASH_STATE_UNAVAILABLE)
+ .setFlashMode(ANDROID_FLASH_MODE_OFF)
+ .setFocalLength(VirtualCameraDevice::kFocalLength)
+ .setJpegQuality(requestSettings.jpegQuality)
+ .setJpegOrientation(requestSettings.jpegOrientation)
+ .setJpegThumbnailSize(requestSettings.thumbnailResolution.width,
+ requestSettings.thumbnailResolution.height)
+ .setJpegThumbnailQuality(requestSettings.thumbnailJpegQuality)
+ .setLensOpticalStabilizationMode(
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF)
+ .setNoiseReductionMode(ANDROID_NOISE_REDUCTION_MODE_OFF)
+ .setPipelineDepth(kPipelineDepth)
+ .setSensorTimestamp(timestamp)
+ .setStatisticsHotPixelMapMode(
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF)
+ .setStatisticsLensShadingMapMode(
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF)
+ .setStatisticsSceneFlicker(ANDROID_STATISTICS_SCENE_FLICKER_NONE);
+
+ if (requestSettings.fpsRange.has_value()) {
+ builder.setControlAeTargetFpsRange(requestSettings.fpsRange.value());
+ }
+
+ if (requestSettings.gpsCoordinates.has_value()) {
+ const GpsCoordinates& coordinates = requestSettings.gpsCoordinates.value();
+ builder.setJpegGpsCoordinates(coordinates);
+ }
+
+ std::unique_ptr<CameraMetadata> metadata = builder.build();
+
+ if (metadata == nullptr) {
+ ALOGE("%s: Failed to build capture result metadata", __func__);
+ return CameraMetadata();
+ }
+ return std::move(*metadata);
+}
+
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android
\ No newline at end of file
diff --git a/services/camera/virtualcamera/VirtualCameraCaptureResult.h b/services/camera/virtualcamera/VirtualCameraCaptureResult.h
new file mode 100644
index 0000000..9e5b4d7
--- /dev/null
+++ b/services/camera/virtualcamera/VirtualCameraCaptureResult.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTURERESULT_H
+#define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTURERESULT_H
+
+#include <chrono>
+#include <cstdint>
+#include <cstring>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "Exif.h"
+#include "GLES/gl.h"
+#include "VirtualCameraCaptureRequest.h"
+#include "VirtualCameraDevice.h"
+#include "VirtualCameraRenderThread.h"
+#include "VirtualCameraSessionContext.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+
+// Construct the Metadata for the Capture result based on the request
+// settings, timestamp and reported sensore size
+::aidl::android::hardware::camera::device::CameraMetadata
+createCaptureResultMetadata(std::chrono::nanoseconds timestamp,
+ const RequestSettings& requestSettings,
+ Resolution reportedSensorSize);
+
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android
+
+#endif // ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERACAPTURERESULT_H
\ No newline at end of file
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index cd17517..40a96e4 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -29,6 +29,7 @@
#include "Exif.h"
#include "GLES/gl.h"
+#include "VirtualCameraCaptureResult.h"
#include "VirtualCameraDevice.h"
#include "VirtualCameraSessionContext.h"
#include "aidl/android/hardware/camera/common/Status.h"
@@ -92,95 +93,10 @@
static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
-// See REQUEST_PIPELINE_DEPTH in CaptureResult.java.
-// This roughly corresponds to frame latency, we set to
-// documented minimum of 2.
-static constexpr uint8_t kPipelineDepth = 2;
-
static constexpr size_t kJpegThumbnailBufferSize = 32 * 1024; // 32 KiB
static constexpr UpdateTextureTask kUpdateTextureTask;
-CameraMetadata createCaptureResultMetadata(
- const std::chrono::nanoseconds timestamp,
- const RequestSettings& requestSettings,
- const Resolution reportedSensorSize) {
- // All of the keys used in the response needs to be referenced in
- // availableResultKeys in CameraCharacteristics (see initCameraCharacteristics
- // in VirtualCameraDevice.cc).
- MetadataBuilder builder =
- MetadataBuilder()
- .setAberrationCorrectionMode(
- ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF)
- .setControlAeAvailableAntibandingModes(
- {ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF})
- .setControlAeAntibandingMode(ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF)
- .setControlAeExposureCompensation(0)
- .setControlAeLockAvailable(false)
- .setControlAeLock(ANDROID_CONTROL_AE_LOCK_OFF)
- .setControlAeMode(ANDROID_CONTROL_AE_MODE_ON)
- .setControlAePrecaptureTrigger(
- // Limited devices are expected to have precapture ae enabled and
- // respond to cancellation request. Since we don't actuall support
- // AE at all, let's just respect the cancellation expectation in
- // case it's requested
- requestSettings.aePrecaptureTrigger ==
- ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
- ? ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
- : ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
- .setControlAeState(ANDROID_CONTROL_AE_STATE_INACTIVE)
- .setControlAfMode(ANDROID_CONTROL_AF_MODE_OFF)
- .setControlAfTrigger(ANDROID_CONTROL_AF_TRIGGER_IDLE)
- .setControlAfState(ANDROID_CONTROL_AF_STATE_INACTIVE)
- .setControlAwbMode(ANDROID_CONTROL_AWB_MODE_AUTO)
- .setControlAwbLock(ANDROID_CONTROL_AWB_LOCK_OFF)
- .setControlAwbState(ANDROID_CONTROL_AWB_STATE_INACTIVE)
- .setControlCaptureIntent(requestSettings.captureIntent)
- .setControlEffectMode(ANDROID_CONTROL_EFFECT_MODE_OFF)
- .setControlMode(ANDROID_CONTROL_MODE_AUTO)
- .setControlSceneMode(ANDROID_CONTROL_SCENE_MODE_DISABLED)
- .setControlVideoStabilizationMode(
- ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF)
- .setCropRegion(0, 0, reportedSensorSize.width,
- reportedSensorSize.height)
- .setFaceDetectMode(ANDROID_STATISTICS_FACE_DETECT_MODE_OFF)
- .setFlashState(ANDROID_FLASH_STATE_UNAVAILABLE)
- .setFlashMode(ANDROID_FLASH_MODE_OFF)
- .setFocalLength(VirtualCameraDevice::kFocalLength)
- .setJpegQuality(requestSettings.jpegQuality)
- .setJpegOrientation(requestSettings.jpegOrientation)
- .setJpegThumbnailSize(requestSettings.thumbnailResolution.width,
- requestSettings.thumbnailResolution.height)
- .setJpegThumbnailQuality(requestSettings.thumbnailJpegQuality)
- .setLensOpticalStabilizationMode(
- ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF)
- .setNoiseReductionMode(ANDROID_NOISE_REDUCTION_MODE_OFF)
- .setPipelineDepth(kPipelineDepth)
- .setSensorTimestamp(timestamp)
- .setStatisticsHotPixelMapMode(
- ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF)
- .setStatisticsLensShadingMapMode(
- ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF)
- .setStatisticsSceneFlicker(ANDROID_STATISTICS_SCENE_FLICKER_NONE);
-
- if (requestSettings.fpsRange.has_value()) {
- builder.setControlAeTargetFpsRange(requestSettings.fpsRange.value());
- }
-
- if (requestSettings.gpsCoordinates.has_value()) {
- const GpsCoordinates& coordinates = requestSettings.gpsCoordinates.value();
- builder.setJpegGpsCoordinates(coordinates);
- }
-
- std::unique_ptr<CameraMetadata> metadata = builder.build();
-
- if (metadata == nullptr) {
- ALOGE("%s: Failed to build capture result metadata", __func__);
- return CameraMetadata();
- }
- return std::move(*metadata);
-}
-
NotifyMsg createShutterNotifyMsg(int frameNumber,
std::chrono::nanoseconds timestamp) {
NotifyMsg msg;
@@ -679,7 +595,7 @@
return {};
}
- // TODO(b/324383963) Add support for letterboxing if the thumbnail size
+ // TODO(b/324383963) Add support for letterboxing if the thumbnail sizese
// doesn't correspond
// to input texture aspect ratio.
if (!renderIntoEglFramebuffer(*framebuffer, /*fence=*/nullptr,
@@ -753,6 +669,7 @@
PlanesLockGuard planesLock(hwBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
fence);
if (planesLock.getStatus() != OK) {
+ ALOGE("Failed to lock hwBuffer planes");
return cameraStatus(Status::INTERNAL_ERROR);
}
@@ -760,23 +677,35 @@
createExif(Resolution(stream->width, stream->height), resultMetadata,
createThumbnail(requestSettings.thumbnailResolution,
requestSettings.thumbnailJpegQuality));
+
+ unsigned long outBufferSize = stream->bufferSize - sizeof(CameraBlob);
+ void* outBuffer = (*planesLock).planes[0].data;
std::optional<size_t> compressedSize = compressJpeg(
stream->width, stream->height, requestSettings.jpegQuality,
- framebuffer->getHardwareBuffer(), app1ExifData,
- stream->bufferSize - sizeof(CameraBlob), (*planesLock).planes[0].data);
+ framebuffer->getHardwareBuffer(), app1ExifData, outBufferSize, outBuffer);
if (!compressedSize.has_value()) {
ALOGE("%s: Failed to compress JPEG image", __func__);
return cameraStatus(Status::INTERNAL_ERROR);
}
+ // Add the transport header at the end of the JPEG output buffer.
+ //
+ // jpegBlobId must start at byte[buffer_size - sizeof(CameraBlob)],
+ // where the buffer_size is the size of gralloc buffer.
+ //
+ // See
+ // hardware/interfaces/camera/device/aidl/android/hardware/camera/device/CameraBlobId.aidl
+ // for the full explanation of the following code.
CameraBlob cameraBlob{
.blobId = CameraBlobId::JPEG,
.blobSizeBytes = static_cast<int32_t>(compressedSize.value())};
- memcpy(reinterpret_cast<uint8_t*>((*planesLock).planes[0].data) +
- (stream->bufferSize - sizeof(cameraBlob)),
- &cameraBlob, sizeof(cameraBlob));
+ // Copy the cameraBlob to the end of the JPEG buffer.
+ uint8_t* jpegStreamEndAddress =
+ reinterpret_cast<uint8_t*>((*planesLock).planes[0].data) +
+ (stream->bufferSize - sizeof(cameraBlob));
+ memcpy(jpegStreamEndAddress, &cameraBlob, sizeof(cameraBlob));
ALOGV("%s: Successfully compressed JPEG image, resulting size %zu B",
__func__, compressedSize.value());
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index 5a5966b..aafed44 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -26,6 +26,7 @@
#include <variant>
#include <vector>
+#include "VirtualCameraCaptureRequest.h"
#include "VirtualCameraDevice.h"
#include "VirtualCameraSessionContext.h"
#include "aidl/android/hardware/camera/device/CameraMetadata.h"
@@ -56,19 +57,6 @@
const sp<Fence> mFence;
};
-struct RequestSettings {
- int jpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
- int jpegOrientation = VirtualCameraDevice::kDefaultJpegOrientation;
- Resolution thumbnailResolution = Resolution(0, 0);
- int thumbnailJpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
- std::optional<FpsRange> fpsRange;
- camera_metadata_enum_android_control_capture_intent_t captureIntent =
- VirtualCameraDevice::kDefaultCaptureIntent;
- std::optional<GpsCoordinates> gpsCoordinates;
- std::optional<camera_metadata_enum_android_control_ae_precapture_trigger>
- aePrecaptureTrigger;
-};
-
// Represents single capture request to fill set of buffers.
class ProcessCaptureRequestTask {
public:
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index e1815c7..88929cc 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -72,7 +72,6 @@
namespace companion {
namespace virtualcamera {
-using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
using ::aidl::android::hardware::camera::common::Status;
@@ -87,7 +86,6 @@
using ::aidl::android::hardware::camera::device::Stream;
using ::aidl::android::hardware::camera::device::StreamBuffer;
using ::aidl::android::hardware::camera::device::StreamConfiguration;
-using ::aidl::android::hardware::camera::device::StreamRotation;
using ::aidl::android::hardware::common::fmq::MQDescriptor;
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using ::aidl::android::hardware::graphics::common::BufferUsage;
diff --git a/services/camera/virtualcamera/util/EglDisplayContext.cc b/services/camera/virtualcamera/util/EglDisplayContext.cc
index 70da25b..ccd0d71 100644
--- a/services/camera/virtualcamera/util/EglDisplayContext.cc
+++ b/services/camera/virtualcamera/util/EglDisplayContext.cc
@@ -54,7 +54,9 @@
EGLint numConfigs = 0;
EGLint configAttribs[] = {
EGL_SURFACE_TYPE,
- nativeWindow == nullptr ? EGL_PBUFFER_BIT : EGL_WINDOW_BIT,
+ nativeWindow == nullptr
+ ? EGL_PBUFFER_BIT // Render into individual AHardwareBuffer
+ : EGL_WINDOW_BIT, // Render into Surface (ANativeWindow)
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE,
8, EGL_BLUE_SIZE, 8,
// no alpha
@@ -83,6 +85,9 @@
}
}
+ // EGL is a big state machine. Now that we have a configuration ready, we set
+ // this state machine to that configuration (we make it the "current"
+ // configuration).
if (!makeCurrent()) {
ALOGE(
"Failed to set newly initialized EGLContext and EGLDisplay connection "
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index 28f04cf..eda4169 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -96,6 +96,7 @@
fragColor = texture(uTexture, vTextureCoord);
})";
+// Shader to render a RGBA texture into a YUV buffer.
constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
#extension GL_EXT_YUV_target : require
diff --git a/services/camera/virtualcamera/util/EglSurfaceTexture.cc b/services/camera/virtualcamera/util/EglSurfaceTexture.cc
index c81d36d..465531b 100644
--- a/services/camera/virtualcamera/util/EglSurfaceTexture.cc
+++ b/services/camera/virtualcamera/util/EglSurfaceTexture.cc
@@ -31,6 +31,12 @@
namespace android {
namespace companion {
namespace virtualcamera {
+namespace {
+
+// Maximal number of buffers producer can dequeue without blocking.
+constexpr int kBufferProducerMaxDequeueBufferCount = 64;
+
+} // namespace
EglSurfaceTexture::EglSurfaceTexture(const uint32_t width, const uint32_t height)
: mWidth(width), mHeight(height) {
@@ -40,6 +46,10 @@
return;
}
BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer);
+ // Set max dequeue buffer count for producer to maximal value to prevent
+ // blocking when dequeuing input buffers.
+ mBufferProducer->setMaxDequeuedBufferCount(
+ kBufferProducerMaxDequeueBufferCount);
mGlConsumer = sp<GLConsumer>::make(
mBufferConsumer, mTextureId, GLConsumer::TEXTURE_EXTERNAL, false, false);
mGlConsumer->setName(String8("VirtualCameraEglSurfaceTexture"));
@@ -75,7 +85,26 @@
}
GLuint EglSurfaceTexture::updateTexture() {
- mGlConsumer->updateTexImage();
+ int previousFrameId;
+ int framesAdvance = 0;
+ // Consume buffers one at the time.
+ // Contrary to the code comments in GLConsumer, the GLConsumer acquires
+ // next queued buffer (not the most recently queued buffer).
+ while (true) {
+ previousFrameId = mGlConsumer->getFrameNumber();
+ mGlConsumer->updateTexImage();
+ int currentFrameId = mGlConsumer->getFrameNumber();
+ if (previousFrameId == currentFrameId) {
+ // Frame number didn't change after updating the texture,
+ // this means we're at the end of the queue and current attached
+ // buffer is the most recent buffer.
+ break;
+ }
+
+ framesAdvance++;
+ previousFrameId = currentFrameId;
+ }
+ ALOGV("%s: Advanced %d frames", __func__, framesAdvance);
return mTextureId;
}
diff --git a/services/camera/virtualcamera/util/JpegUtil.h b/services/camera/virtualcamera/util/JpegUtil.h
index 184dd56..0a8df90 100644
--- a/services/camera/virtualcamera/util/JpegUtil.h
+++ b/services/camera/virtualcamera/util/JpegUtil.h
@@ -18,6 +18,7 @@
#define ANDROID_COMPANION_VIRTUALCAMERA_JPEGUTIL_H
#include <optional>
+#include <vector>
#include "android/hardware_buffer.h"
#include "util/Util.h"
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
index 41efce0..92f0745 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
@@ -85,5 +85,6 @@
getegid32: 1
getgroups32: 1
sysinfo: 1
+setsockopt: 1
@include /apex/com.android.media.swcodec/etc/seccomp_policy/code_coverage.arm.policy
diff --git a/services/medialog/Android.bp b/services/medialog/Android.bp
index fdb56e5..7a4c3ad 100644
--- a/services/medialog/Android.bp
+++ b/services/medialog/Android.bp
@@ -29,6 +29,8 @@
"packagemanager_aidl-cpp",
],
+ export_include_dirs: ["."],
+
cflags: [
"-Wall",
"-Werror",
diff --git a/services/mediametrics/include/mediametricsservice/TimedAction.h b/services/mediametrics/include/mediametricsservice/TimedAction.h
index 8b53ded..8901ced 100644
--- a/services/mediametrics/include/mediametricsservice/TimedAction.h
+++ b/services/mediametrics/include/mediametricsservice/TimedAction.h
@@ -81,9 +81,8 @@
void threadLoop() NO_THREAD_SAFETY_ANALYSIS { // thread safety doesn't cover unique_lock
std::unique_lock l(mLock);
while (!mQuit) {
- auto sleepUntilTime = std::chrono::time_point<TimerClock>::max();
if (!mMap.empty()) {
- sleepUntilTime = mMap.begin()->first;
+ auto sleepUntilTime = mMap.begin()->first;
const auto now = TimerClock::now();
if (sleepUntilTime <= now) {
auto node = mMap.extract(mMap.begin()); // removes from mMap.
@@ -96,8 +95,17 @@
// of REALTIME specification, use kWakeupInterval to ensure minimum
// granularity if suspended.
sleepUntilTime = std::min(sleepUntilTime, now + kWakeupInterval);
+ mCondition.wait_until(l, sleepUntilTime);
+ } else {
+ // As TimerClock is system_clock (which is not monotonic), libcxx's
+ // implementation of condition_variable::wait_until(l, std::chrono::time_point)
+ // recalculates the 'until' time into the wait duration and then goes back to the
+ // absolute timestamp when calling pthread_cond_timedwait(); this back-and-forth
+ // calculation sometimes loses the 'max' value because enough time passes in
+ // between, and instead passes incorrect timestamp into the syscall, causing a
+ // crash. Mitigating it by explicitly calling the non-timed wait here.
+ mCondition.wait(l);
}
- mCondition.wait_until(l, sleepUntilTime);
}
}
diff --git a/services/mediaresourcemanager/ResourceManagerMetrics.cpp b/services/mediaresourcemanager/ResourceManagerMetrics.cpp
index a8a1de1..8b3711c 100644
--- a/services/mediaresourcemanager/ResourceManagerMetrics.cpp
+++ b/services/mediaresourcemanager/ResourceManagerMetrics.cpp
@@ -821,7 +821,7 @@
metricsLog += getAppsPixelCount(mProcessPixelsMap);
metricsLog += getAppsCodecUsageMetrics(mProcessConcurrentCodecsMap);
- return std::move(metricsLog);
+ return metricsLog;
}
} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
index 679ab13..49f68e9 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
@@ -94,7 +94,7 @@
str.append("\n");
}
- return std::move(str);
+ return str;
}
bool ResourceList::operator==(const ResourceList& rhs) const {
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index e3601a1..67b319f 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -84,6 +84,7 @@
shared_libs: [
"aaudio-aidl-cpp",
"com.android.media.aaudio-aconfig-cc",
+ "com.android.media.aaudio-aconfig-cc",
"framework-permission-aidl-cpp",
"libaaudio_internal",
"libaudioclient",
@@ -157,6 +158,8 @@
"frameworks/av/media/libnbaio/include_mono",
],
+ export_include_dirs: ["."],
+
tidy: true,
tidy_checks: tidy_errors,
tidy_checks_as_errors: tidy_errors,