Merge "Merge 24Q4 into AOSP main" into main
diff --git a/aidl/com/android/media/permission/PermissionEnum.aidl b/aidl/com/android/media/permission/PermissionEnum.aidl
index 834a275..b08db44 100644
--- a/aidl/com/android/media/permission/PermissionEnum.aidl
+++ b/aidl/com/android/media/permission/PermissionEnum.aidl
@@ -21,11 +21,21 @@
* {@hide}
*/
enum PermissionEnum {
- MODIFY_AUDIO_ROUTING = 0,
- MODIFY_PHONE_STATE = 1,
- CALL_AUDIO_INTERCEPTION = 2,
// This is a runtime + WIU permission, which means data delivery should be protected by AppOps
// We query the controller only for early fails/hard errors
- RECORD_AUDIO = 3,
- ENUM_SIZE = 4, // Not for actual usage
+ RECORD_AUDIO = 0,
+ MODIFY_AUDIO_ROUTING = 1,
+ MODIFY_AUDIO_SETTINGS = 2,
+ MODIFY_PHONE_STATE = 3,
+ MODIFY_DEFAULT_AUDIO_EFFECTS = 4,
+ WRITE_SECURE_SETTINGS = 5,
+ CALL_AUDIO_INTERCEPTION = 6,
+ ACCESS_ULTRASOUND = 7,
+ CAPTURE_AUDIO_OUTPUT = 8,
+ CAPTURE_MEDIA_OUTPUT = 9,
+ CAPTURE_AUDIO_HOTWORD = 10,
+ CAPTURE_TUNER_AUDIO_INPUT = 11,
+ CAPTURE_VOICE_COMMUNICATION_OUTPUT = 12,
+ BLUETOOTH_CONNECT = 13,
+ ENUM_SIZE = 14, // Not for actual usage, used by Java
}
diff --git a/camera/Android.bp b/camera/Android.bp
index b6241f4..25b5e2c 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -81,6 +81,7 @@
local_include_dirs: ["aidl"],
include_dirs: [
"frameworks/native/aidl/gui",
+ "frameworks/native/libs/permission/aidl",
],
},
@@ -112,6 +113,7 @@
shared_libs: [
"camera_platform_flags_c_lib",
+ "framework-permission-aidl-cpp",
"lib-platform-compat-native-api",
"libbase",
"libbinder",
@@ -120,6 +122,7 @@
"libgui",
"liblog",
"libnativewindow",
+ "libpermission",
"libutils",
],
@@ -132,6 +135,7 @@
"include/camera",
],
export_shared_lib_headers: [
+ "framework-permission-aidl-cpp",
"libcamera_metadata",
"libgui",
"libnativewindow",
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 8018390..d90f7c9 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -69,13 +69,12 @@
// deadlock if we call any method of ICamera here.
}
-sp<Camera> Camera::connect(int cameraId, const std::string& clientPackageName,
- int clientUid, int clientPid, int targetSdkVersion, int rotationOverride,
- bool forceSlowJpegMode, int32_t deviceId, int32_t devicePolicy)
+sp<Camera> Camera::connect(int cameraId, int targetSdkVersion, int rotationOverride,
+ bool forceSlowJpegMode, const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy)
{
- return CameraBaseT::connect(cameraId, clientPackageName, clientUid,
- clientPid, targetSdkVersion, rotationOverride, forceSlowJpegMode, deviceId,
- devicePolicy);
+ return CameraBaseT::connect(cameraId, targetSdkVersion, rotationOverride,
+ forceSlowJpegMode, clientAttribution, devicePolicy);
}
status_t Camera::reconnect()
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index d7415a3..774db25 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -161,10 +161,10 @@
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
- const std::string& clientPackageName,
- int clientUid, int clientPid, int targetSdkVersion,
- int rotationOverride, bool forceSlowJpegMode,
- int32_t deviceId, int32_t devicePolicy)
+ int targetSdkVersion, int rotationOverride,
+ bool forceSlowJpegMode,
+ const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy)
{
ALOGV("%s: connect", __FUNCTION__);
sp<TCam> c = new TCam(cameraId);
@@ -176,9 +176,9 @@
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
ALOGI("Connect camera (legacy API) - rotationOverride %d, forceSlowJpegMode %d",
rotationOverride, forceSlowJpegMode);
- ret = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
- clientPid, targetSdkVersion, rotationOverride, forceSlowJpegMode, deviceId,
- devicePolicy, /*out*/ &c->mCamera);
+ ret = (cs.get()->*fnConnectService)(cl, cameraId, targetSdkVersion,
+ rotationOverride, forceSlowJpegMode, clientAttribution, devicePolicy,
+ /*out*/ &c->mCamera);
}
if (ret.isOk() && c->mCamera != nullptr) {
IInterface::asBinder(c->mCamera)->linkToDeath(c);
@@ -257,7 +257,8 @@
}
template <typename TCam, typename TCamTraits>
-int CameraBase<TCam, TCamTraits>::getNumberOfCameras(int32_t deviceId, int32_t devicePolicy) {
+int CameraBase<TCam, TCamTraits>::getNumberOfCameras(
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy) {
const sp<::android::hardware::ICameraService> cs = getCameraService();
if (!cs.get()) {
@@ -266,7 +267,7 @@
}
int32_t count;
binder::Status res = cs->getNumberOfCameras(
- ::android::hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE, deviceId,
+ ::android::hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE, clientAttribution,
devicePolicy, &count);
if (!res.isOk()) {
ALOGE("Error reading number of cameras: %s",
@@ -279,12 +280,12 @@
// this can be in BaseCamera but it should be an instance method
template <typename TCam, typename TCamTraits>
status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId,
- int rotationOverride, int32_t deviceId, int32_t devicePolicy,
+ int rotationOverride, const AttributionSourceState& clientAttribution, int32_t devicePolicy,
struct hardware::CameraInfo* cameraInfo) {
const sp<::android::hardware::ICameraService> cs = getCameraService();
if (cs == 0) return UNKNOWN_ERROR;
- binder::Status res = cs->getCameraInfo(cameraId, rotationOverride, deviceId, devicePolicy,
- cameraInfo);
+ binder::Status res = cs->getCameraInfo(cameraId, rotationOverride, clientAttribution,
+ devicePolicy, cameraInfo);
return res.isOk() ? OK : res.serviceSpecificErrorCode();
}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index d9a0934..ce6c2d3 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -16,6 +16,7 @@
package android.hardware;
+import android.content.AttributionSourceState;
import android.hardware.ICamera;
import android.hardware.ICameraClient;
import android.hardware.camera2.ICameraDeviceUser;
@@ -66,13 +67,13 @@
*
* @param type The type of the camera, can be either CAMERA_TYPE_BACKWARD_COMPATIBLE
* or CAMERA_TYPE_ALL.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
* policy.
*/
- int getNumberOfCameras(int type, int deviceId, int devicePolicy);
+ int getNumberOfCameras(int type, in AttributionSourceState clientAttribution, int devicePolicy);
/**
* If changed, reflect in
@@ -97,19 +98,20 @@
* will override the sensor orientation and rotate and crop, while {@link
* ICameraService#ROTATION_OVERRIDE_ROTATION_ONLY} will rotate and crop the camera feed
* without changing the sensor orientation.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
* policy.
* @return CameraInfo for the camera.
*/
- CameraInfo getCameraInfo(int cameraId, int rotationOverride, int deviceId,
- int devicePolicy);
+ CameraInfo getCameraInfo(int cameraId, int rotationOverride,
+ in AttributionSourceState clientAttribution, int devicePolicy);
/**
- * Default UID/PID values for non-privileged callers of
- * connect() and connectDevice()
+ * Default UID/PID values for non-privileged callers of connect() and connectDevice(). Can be
+ * used to set the pid/uid fields of AttributionSourceState to indicate the calling uid/pid
+ * should be used.
*/
const int USE_CALLING_UID = -1;
const int USE_CALLING_PID = -1;
@@ -118,9 +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 clientUid UID for the calling client.
- * @param clientPid PID for the calling client.
* @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}
@@ -128,7 +127,7 @@
* ICameraService#ROTATION_OVERRIDE_ROTATION_ONLY} will rotate and crop the camera feed
* without changing the sensor orientation.
* @param forceSlowJpegMode Whether to force slow jpeg mode.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -136,12 +135,10 @@
*/
ICamera connect(ICameraClient client,
int cameraId,
- @utf8InCpp String opPackageName,
- int clientUid, int clientPid,
int targetSdkVersion,
int rotationOverride,
boolean forceSlowJpegMode,
- int deviceId,
+ in AttributionSourceState clientAttribution,
int devicePolicy);
/**
@@ -149,15 +146,13 @@
* 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 clientUid UID for the calling client.
* @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}
* will override the sensor orientation and rotate and crop, while {@link
* ICameraService#ROTATION_OVERRIDE_ROTATION_ONLY} will rotate and crop the camera feed
* without changing the sensor orientation.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -165,12 +160,10 @@
*/
ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
@utf8InCpp String cameraId,
- @utf8InCpp String opPackageName,
- @nullable @utf8InCpp String featureId,
- int clientUid, int oomScoreOffset,
+ int oomScoreOffset,
int targetSdkVersion,
int rotationOverride,
- int deviceId,
+ in AttributionSourceState clientAttribution,
int devicePolicy);
/**
@@ -194,7 +187,7 @@
*
* @param sessions the set of camera id and session configuration pairs to be queried.
* @param targetSdkVersion the target sdk level of the application calling this function.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -206,7 +199,7 @@
*/
boolean isConcurrentSessionConfigurationSupported(
in CameraIdAndSessionConfiguration[] sessions,
- int targetSdkVersion, int deviceId, int devicePolicy);
+ int targetSdkVersion, in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Inject Session Params into an existing camera session.
@@ -236,7 +229,7 @@
* will override the sensor orientation and rotate and crop, while {@link
* ICameraService#ROTATION_OVERRIDE_ROTATION_ONLY} will rotate and crop the camera feed
* without changing the sensor orientation.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -244,7 +237,7 @@
* @return Characteristics for the given camera.
*/
CameraMetadataNative getCameraCharacteristics(@utf8InCpp String cameraId, int targetSdkVersion,
- int rotationOverride, int deviceId, int devicePolicy);
+ int rotationOverride, in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Read in the vendor tag descriptors from the camera module HAL.
@@ -284,14 +277,14 @@
* Set the torch mode for a camera device.
*
* @param cameraId The ID of the camera to set torch mode for.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
* policy.
*/
void setTorchMode(@utf8InCpp String cameraId, boolean enabled, IBinder clientBinder,
- int deviceId, int devicePolicy);
+ in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Change the brightness level of the flash unit associated with cameraId to strengthLevel.
@@ -299,27 +292,28 @@
*
* @param cameraId The ID of the camera.
* @param strengthLevel The torch strength level to set for the camera.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
* policy.
*/
void turnOnTorchWithStrengthLevel(@utf8InCpp String cameraId, int strengthLevel,
- IBinder clientBinder, int deviceId, int devicePolicy);
+ IBinder clientBinder, in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Get the brightness level of the flash unit associated with cameraId.
*
* @param cameraId The ID of the camera.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
* policy.
* @return Torch strength level for the camera.
*/
- int getTorchStrengthLevel(@utf8InCpp String cameraId, int deviceId, int devicePolicy);
+ int getTorchStrengthLevel(@utf8InCpp String cameraId,
+ in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Notify the camera service of a system event. Should only be called from system_server.
@@ -385,7 +379,7 @@
*
* @param cameraId The camera id to create the CaptureRequest for.
* @param templateId The template id create the CaptureRequest for.
- * @param deviceId the device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -393,7 +387,7 @@
* @return Metadata representing the CaptureRequest.
*/
CameraMetadataNative createDefaultRequest(@utf8InCpp String cameraId, int templateId,
- int deviceId, int devicePolicy);
+ in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Check whether a particular session configuration with optional session parameters
@@ -402,7 +396,7 @@
* @param cameraId The camera id to query session configuration for
* @param targetSdkVersion the target sdk level of the application calling this function.
* @param sessionConfiguration Specific session configuration to be verified.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -412,7 +406,7 @@
*/
boolean isSessionConfigurationWithParametersSupported(@utf8InCpp String cameraId,
int targetSdkVersion, in SessionConfiguration sessionConfiguration,
- int deviceId, int devicePolicy);
+ in AttributionSourceState clientAttribution, int devicePolicy);
/**
* Get the camera characteristics for a particular session configuration for
@@ -427,7 +421,7 @@
* without changing the sensor orientation.
* @param sessionConfiguration Session configuration for which the characteristics
* must be fetched.
- * @param deviceId The device id of the context associated with the caller.
+ * @param clientAttribution The AttributionSource of the client.
* @param devicePolicy The camera policy of the device of the associated context (default
* policy for default device context). Only virtual cameras would be exposed
* only for custom policy and only real cameras would be exposed for default
@@ -438,6 +432,6 @@
int targetSdkVersion,
int rotationOverride,
in SessionConfiguration sessionConfiguration,
- int deviceId,
+ in AttributionSourceState clientAttribution,
int devicePolicy);
}
diff --git a/camera/camera2/SessionConfiguration.cpp b/camera/camera2/SessionConfiguration.cpp
index 2f1f22d..065d283 100644
--- a/camera/camera2/SessionConfiguration.cpp
+++ b/camera/camera2/SessionConfiguration.cpp
@@ -72,17 +72,15 @@
bool hasSessionParameters = false;
CameraMetadata settings;
- if (flags::feature_combination_query()) {
- if ((err = parcel->readBool(&hasSessionParameters)) != OK) {
- ALOGE("%s: Failed to read hasSessionParameters flag from parcel", __FUNCTION__);
- return err;
- }
+ if ((err = parcel->readBool(&hasSessionParameters)) != OK) {
+ ALOGE("%s: Failed to read hasSessionParameters flag from parcel", __FUNCTION__);
+ return err;
+ }
- if (hasSessionParameters) {
- if ((err = settings.readFromParcel(parcel)) != OK) {
- ALOGE("%s: Failed to read metadata flag from parcel", __FUNCTION__);
- return err;
- }
+ if (hasSessionParameters) {
+ if ((err = settings.readFromParcel(parcel)) != OK) {
+ ALOGE("%s: Failed to read metadata flag from parcel", __FUNCTION__);
+ return err;
}
}
@@ -94,10 +92,8 @@
for (auto& stream : outputStreams) {
mOutputStreams.push_back(stream);
}
- if (flags::feature_combination_query()) {
- mHasSessionParameters = hasSessionParameters;
- mSessionParameters = std::move(settings);
- }
+ mHasSessionParameters = hasSessionParameters;
+ mSessionParameters = std::move(settings);
return err;
}
@@ -125,14 +121,12 @@
err = parcel->writeParcelableVector(mOutputStreams);
if (err != OK) return err;
- if (flags::feature_combination_query()) {
- err = parcel->writeBool(mHasSessionParameters);
- if (err != OK) return err;
+ err = parcel->writeBool(mHasSessionParameters);
+ if (err != OK) return err;
- if (mHasSessionParameters) {
- err = mSessionParameters.writeToParcel(parcel);
- if (err != OK) return err;
- }
+ if (mHasSessionParameters) {
+ err = mSessionParameters.writeToParcel(parcel);
+ if (err != OK) return err;
}
return OK;
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index fe10e12..bf1520b 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -27,20 +27,6 @@
flag {
namespace: "camera_platform"
- name: "watch_foreground_changes"
- description: "Request AppOps to notify changes in the foreground status of the client"
- bug: "290086710"
-}
-
-flag {
- namespace: "camera_platform"
- name: "log_ultrawide_usage"
- description: "Enable measuring how much usage there is for ultrawide-angle cameras"
- bug: "300515796"
-}
-
-flag {
- namespace: "camera_platform"
name: "camera_manual_flash_strength_control"
is_exported: true
description: "Flash brightness level control in manual flash mode"
@@ -49,20 +35,6 @@
flag {
namespace: "camera_platform"
- name: "lazy_aidl_wait_for_service"
- description: "Use waitForService instead of getService with lazy AIDL HALs"
- bug: "285546208"
-}
-
-flag {
- namespace: "camera_platform"
- name: "log_zoom_override_usage"
- description: "Enable measuring how much usage there is for zoom settings overrde"
- bug: "307409002"
-}
-
-flag {
- namespace: "camera_platform"
name: "session_hal_buf_manager"
description: "Enable or disable HAL buffer manager as requested by the camera HAL"
bug: "311263114"
@@ -137,16 +109,6 @@
flag {
namespace: "camera_platform"
- name: "surface_ipc"
- description: "Optimize Surface binder IPC"
- bug: "323292530"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "camera_platform"
name: "extension_10_bit"
is_exported: true
description: "Enables 10-bit support in the camera extensions."
@@ -155,9 +117,9 @@
flag {
namespace: "camera_platform"
- name: "single_thread_executor"
- description: "Ensure device logic is run within one thread."
- bug: "305857746"
+ name: "single_thread_executor_naming"
+ description: "Set the device executor thread name."
+ bug: "359709863"
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -165,24 +127,6 @@
flag {
namespace: "camera_platform"
- name: "surface_leak_fix"
- description: "Address Surface release leaks in CaptureRequest"
- bug: "324071855"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "camera_platform"
- name: "concert_mode_api"
- description: "Covers the eyes free videography public facing API"
- bug: "297083874"
-}
-
-
-flag {
- namespace: "camera_platform"
name: "cache_permission_services"
description: "Cache IPermissionController and IPermissionChecker in CameraService to reduce query latency."
bug: "326139956"
@@ -220,16 +164,6 @@
flag {
namespace: "camera_platform"
- name: "realtime_priority_bump"
- description: "Bump the scheduling priority of performance critical code paths"
- bug: "336628522"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "camera_platform"
name: "use_system_api_for_vndk_version"
description: "ro.board.api_level isn't reliable. Use system api to replace ro.vndk.version"
bug: "312315580"
@@ -247,3 +181,47 @@
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
+ }
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "enable_hal_abort_from_cameraservicewatchdog"
+ description: "Enable CameraServiceWatchdog to abort camera HAL to generate HAL tombstones"
+ bug: "349652177"
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "enable_stream_reconfiguration_for_unchanged_streams"
+ description: "Enable stream reconfiguration for unchanged streams"
+ bug: "341740105"
+}
diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h
index 3ecd10d..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, int, int, bool, int32_t, int32_t,
+ int, int, int, bool, const AttributionSourceState&, int32_t,
/*out*/
sp<::android::hardware::ICamera>*);
static TCamConnectService fnConnectService;
@@ -81,10 +81,9 @@
// 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 clientUid, int clientPid, int targetSdkVersion,
- int rotationOverride, bool forceSlowJpegMode,
- int32_t deviceId = kDefaultDeviceId, int32_t devicePolicy = 0);
+ int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
+ const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy = 0);
virtual ~Camera();
diff --git a/camera/include/camera/CameraBase.h b/camera/include/camera/CameraBase.h
index 3370b3d..d98abe4 100644
--- a/camera/include/camera/CameraBase.h
+++ b/camera/include/camera/CameraBase.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HARDWARE_CAMERA_BASE_H
#define ANDROID_HARDWARE_CAMERA_BASE_H
+#include <android/content/AttributionSourceState.h>
#include <android/hardware/ICameraServiceListener.h>
#include <utils/Mutex.h>
@@ -107,6 +108,7 @@
} // namespace hardware
+using content::AttributionSourceState;
using hardware::CameraInfo;
template <typename TCam>
@@ -123,19 +125,19 @@
typedef typename TCamTraits::TCamConnectService TCamConnectService;
static sp<TCam> connect(int cameraId,
- const std::string& clientPackageName,
- int clientUid, int clientPid, int targetSdkVersion,
- int rotationOverride, bool forceSlowJpegMode,
- int32_t deviceId, int32_t devicePolicy);
+ int targetSdkVersion, int rotationOverride, bool forceSlowJpegMode,
+ const AttributionSourceState &clientAttribution,
+ int32_t devicePolicy);
virtual void disconnect();
void setListener(const sp<TCamListener>& listener);
- static int getNumberOfCameras(int32_t deviceId, int32_t devicePolicy);
+ static int getNumberOfCameras(const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy);
static status_t getCameraInfo(int cameraId,
int rotationOverride,
- int32_t deviceId,
+ const AttributionSourceState& clientAttribution,
int32_t devicePolicy,
/*out*/
struct hardware::CameraInfo* cameraInfo);
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index a603659..379c0b5 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -79,6 +79,7 @@
shared_libs: [
"android.companion.virtual.virtualdevice_aidl-cpp",
"android.companion.virtualdevice.flags-aconfig-cc",
+ "framework-permission-aidl-cpp",
"libandroid_runtime",
"libbinder",
"libcamera_client",
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 0c80e7e..6d29ef5 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -807,9 +807,15 @@
CameraMetadata rawMetadata;
int targetSdkVersion = android_get_application_target_sdk_version();
+
+ AttributionSourceState clientAttribution;
+ clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
+ clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
+ clientAttribution.deviceId = mDeviceContext.deviceId;
+
binder::Status serviceRet = cs->getCameraCharacteristics(cameraIdStr,
targetSdkVersion, /*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_NONE,
- mDeviceContext.deviceId, static_cast<int32_t>(mDeviceContext.policy),
+ clientAttribution, static_cast<int32_t>(mDeviceContext.policy),
&rawMetadata);
if (!serviceRet.isOk()) {
switch(serviceRet.serviceSpecificErrorCode()) {
@@ -857,13 +863,20 @@
sp<hardware::camera2::ICameraDeviceCallbacks> callbacks = device->getServiceCallback();
sp<hardware::camera2::ICameraDeviceUser> deviceRemote;
int targetSdkVersion = android_get_application_target_sdk_version();
+
+ AttributionSourceState clientAttribution;
+ 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, "", {},
- hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/0,
+ callbacks, cameraId, /*oomScoreOffset*/0,
targetSdkVersion, /*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_NONE,
- mDeviceContext.deviceId, static_cast<int32_t>(mDeviceContext.policy),
+ clientAttribution, static_cast<int32_t>(mDeviceContext.policy),
/*out*/&deviceRemote);
if (!serviceRet.isOk()) {
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/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 7d234bb..1817490 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -91,7 +91,6 @@
ACAMERA_AUTOMOTIVE_LENS,
ACAMERA_EXTENSION,
ACAMERA_JPEGR,
- ACAMERA_EFV,
ACAMERA_SECTION_COUNT,
ACAMERA_VENDOR = 0x8000
@@ -139,7 +138,6 @@
ACAMERA_AUTOMOTIVE_LENS_START = ACAMERA_AUTOMOTIVE_LENS << 16,
ACAMERA_EXTENSION_START = ACAMERA_EXTENSION << 16,
ACAMERA_JPEGR_START = ACAMERA_JPEGR << 16,
- ACAMERA_EFV_START = ACAMERA_EFV << 16,
ACAMERA_VENDOR_START = ACAMERA_VENDOR << 16
} acamera_metadata_section_start_t;
@@ -646,9 +644,14 @@
* be made, and for firing pre-capture flash pulses to estimate
* scene brightness and required final capture flash power, when
* the flash is enabled.</p>
- * <p>Normally, this entry should be set to START for only a
- * single request, and the application should wait until the
- * sequence completes before starting a new one.</p>
+ * <p>Flash is enabled during precapture sequence when:</p>
+ * <ul>
+ * <li>AE mode is ON_ALWAYS_FLASH</li>
+ * <li>AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or</li>
+ * <li>AE mode is ON and flash mode is TORCH or SINGLE</li>
+ * </ul>
+ * <p>Normally, this entry should be set to START for only single request, and the
+ * application should wait until the sequence completes before starting a new one.</p>
* <p>When a precapture metering sequence is finished, the camera device
* may lock the auto-exposure routine internally to be able to accurately expose the
* subsequent still capture image (<code>ACAMERA_CONTROL_CAPTURE_INTENT == STILL_CAPTURE</code>).
@@ -2289,8 +2292,6 @@
* boost when the light level threshold is exceeded.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
- * <p>This key will be absent from the CaptureResult if AE mode is not set to
- * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
* <p>The default value will always be 'INACTIVE'.</p>
*/
ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE = // byte (acamera_metadata_enum_android_control_low_light_boost_state_t)
@@ -2448,6 +2449,13 @@
* in ACAMERA_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL.
* If ACAMERA_CONTROL_AE_MODE is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
* <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
+ * <p>When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+ * the AE mode, flash mode, and flash strength level remain the same between precapture
+ * trigger request and final capture request. The flash strength level being set during
+ * precapture sequence is used by the camera device as a reference. The actual strength
+ * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+ * exposure time and sensitivities between precapture and final capture for the specified
+ * strength level.</p>
*
* @see ACAMERA_CONTROL_AE_MODE
* @see ACAMERA_FLASH_MODE
@@ -11563,7 +11571,6 @@
-
__END_DECLS
#endif /* _NDK_CAMERA_METADATA_TAGS_H */
diff --git a/camera/tests/Android.bp b/camera/tests/Android.bp
index 9aaac6a..484335a 100644
--- a/camera/tests/Android.bp
+++ b/camera/tests/Android.bp
@@ -29,6 +29,7 @@
"CameraCharacteristicsPermission.cpp",
],
shared_libs: [
+ "framework-permission-aidl-cpp",
"liblog",
"libutils",
"libcutils",
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index e5f99be..5135b5d 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -33,6 +33,7 @@
#include <hardware/gralloc.h>
#include <camera/CameraMetadata.h>
+#include <android/content/AttributionSourceState.h>
#include <android/hardware/ICameraService.h>
#include <android/hardware/ICameraServiceListener.h>
#include <android/hardware/BnCameraServiceListener.h>
@@ -46,6 +47,7 @@
#include <camera/CameraUtils.h>
#include <camera/StringUtils.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
@@ -347,7 +349,11 @@
binder::Status res;
int32_t numCameras = 0;
- res = service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_ALL, kDefaultDeviceId,
+ 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;
EXPECT_LE(0, numCameras);
@@ -360,7 +366,7 @@
EXPECT_EQ(numCameras, static_cast<const int>(statuses.size()));
for (const auto &it : statuses) {
- listener->onStatusChanged(it.status, it.cameraId, kDefaultDeviceId);
+ listener->onStatusChanged(it.status, it.cameraId, clientAttribution.deviceId);
}
for (int32_t i = 0; i < numCameras; i++) {
@@ -379,7 +385,7 @@
CameraMetadata metadata;
res = service->getCameraCharacteristics(cameraId,
/*targetSdkVersion*/__ANDROID_API_FUTURE__, /*overrideToPortrait*/false,
- kDefaultDeviceId, /*devicePolicy*/0, &metadata);
+ clientAttribution, /*devicePolicy*/0, &metadata);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_FALSE(metadata.isEmpty());
@@ -393,10 +399,10 @@
// Check connect binder calls
sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
sp<hardware::camera2::ICameraDeviceUser> device;
- res = service->connectDevice(callbacks, cameraId, "meeeeeeeee!",
- {}, hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/ 0,
+ res = service->connectDevice(callbacks, cameraId,
+ /*oomScoreOffset*/ 0,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
- /*overrideToPortrait*/false, kDefaultDeviceId, /*devicePolicy*/0, /*out*/&device);
+ /*overrideToPortrait*/false, clientAttribution, /*devicePolicy*/0, /*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
ASSERT_NE(nullptr, device.get());
device->disconnect();
@@ -406,12 +412,12 @@
if (torchStatus == hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF) {
// Check torch calls
res = service->setTorchMode(cameraId,
- /*enabled*/true, callbacks, kDefaultDeviceId, /*devicePolicy*/0);
+ /*enabled*/true, callbacks, clientAttribution, /*devicePolicy*/0);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_TRUE(listener->waitForTorchState(
hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON, i));
res = service->setTorchMode(cameraId,
- /*enabled*/false, callbacks, kDefaultDeviceId, /*devicePolicy*/0);
+ /*enabled*/false, callbacks, clientAttribution, /*devicePolicy*/0);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_TRUE(listener->waitForTorchState(
hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF, i));
@@ -437,10 +443,14 @@
sp<hardware::camera2::ICameraDeviceUser> device;
{
SCOPED_TRACE("openNewDevice");
- binder::Status res = service->connectDevice(callbacks, deviceId, "meeeeeeeee!",
- {}, hardware::ICameraService::USE_CALLING_UID, /*oomScoreOffset*/ 0,
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
+ clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
+ clientAttribution.packageName = "meeeeeeeee!";
+ binder::Status res = service->connectDevice(callbacks, deviceId,
+ /*oomScoreOffset*/ 0,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
- /*overrideToPortrait*/false, kDefaultDeviceId, /*devicePolicy*/0,
+ /*overrideToPortrait*/false, clientAttribution, /*devicePolicy*/0,
/*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
}
@@ -473,11 +483,13 @@
serviceListener = new TestCameraServiceListener();
std::vector<hardware::CameraStatus> statuses;
service->addListener(serviceListener, &statuses);
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
for (const auto &it : statuses) {
- serviceListener->onStatusChanged(it.status, it.cameraId, kDefaultDeviceId);
+ serviceListener->onStatusChanged(it.status, it.cameraId, clientAttribution.deviceId);
}
service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE,
- kDefaultDeviceId, /*devicePolicy*/0, &numCameras);
+ clientAttribution, /*devicePolicy*/0, &numCameras);
}
virtual void TearDown() {
@@ -507,6 +519,23 @@
// Setup a buffer queue; I'm just using the vendor opaque format here as that is
// guaranteed to be present
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> opaqueConsumer = new BufferItemConsumer(
+ GRALLOC_USAGE_SW_READ_NEVER, /*maxImages*/ 2, /*controlledByApp*/ true);
+ EXPECT_TRUE(opaqueConsumer.get() != nullptr);
+ opaqueConsumer->setName(String8("nom nom nom"));
+
+ // Set to VGA dimens for default, as that is guaranteed to be present
+ EXPECT_EQ(OK, opaqueConsumer->setDefaultBufferSize(640, 480));
+ EXPECT_EQ(OK,
+ opaqueConsumer->setDefaultBufferFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED));
+
+ sp<Surface> surface = opaqueConsumer->getSurface();
+
+ sp<IGraphicBufferProducer> producer = surface->getIGraphicBufferProducer();
+ std::string noPhysicalId;
+ OutputConfiguration output(producer, /*rotation*/ 0, noPhysicalId);
+#else
sp<IGraphicBufferProducer> gbProducer;
sp<IGraphicBufferConsumer> gbConsumer;
BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
@@ -523,6 +552,7 @@
std::string noPhysicalId;
OutputConfiguration output(gbProducer, /*rotation*/0, noPhysicalId);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Can we configure?
res = device->beginConfigure();
diff --git a/camera/tests/CameraCharacteristicsPermission.cpp b/camera/tests/CameraCharacteristicsPermission.cpp
index 10f7f22..9204eb1 100644
--- a/camera/tests/CameraCharacteristicsPermission.cpp
+++ b/camera/tests/CameraCharacteristicsPermission.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
+#include <android/content/AttributionSourceState.h>
#include <binder/ProcessState.h>
#include <utils/Errors.h>
#include <utils/Log.h>
@@ -47,8 +48,10 @@
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.camera"));
mCameraService = interface_cast<ICameraService>(binder);
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
rc = mCameraService->getNumberOfCameras(
- hardware::ICameraService::CAMERA_TYPE_ALL, kDefaultDeviceId, /*devicePolicy*/0,
+ hardware::ICameraService::CAMERA_TYPE_ALL, clientAttribution, /*devicePolicy*/0,
&numCameras);
EXPECT_TRUE(rc.isOk());
}
@@ -73,9 +76,11 @@
CameraMetadata metadata;
std::vector<int32_t> tagsNeedingPermission;
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
rc = mCameraService->getCameraCharacteristics(cameraIdStr,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
- /*overrideToPortrait*/false, kDefaultDeviceId, /*devicePolicy*/0, &metadata);
+ /*overrideToPortrait*/false, clientAttribution, /*devicePolicy*/0, &metadata);
ASSERT_TRUE(rc.isOk());
EXPECT_FALSE(metadata.isEmpty());
EXPECT_EQ(metadata.removePermissionEntries(CAMERA_METADATA_INVALID_VENDOR_ID,
diff --git a/camera/tests/CameraZSLTests.cpp b/camera/tests/CameraZSLTests.cpp
index 56fcfa4..2740d09 100644
--- a/camera/tests/CameraZSLTests.cpp
+++ b/camera/tests/CameraZSLTests.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
+#include <android/content/AttributionSourceState.h>
#include <binder/ProcessState.h>
#include <utils/Errors.h>
#include <utils/Log.h>
@@ -84,8 +85,10 @@
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.camera"));
mCameraService = interface_cast<ICameraService>(binder);
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
rc = mCameraService->getNumberOfCameras(
- hardware::ICameraService::CAMERA_TYPE_ALL, kDefaultDeviceId, /*devicePolicy*/0,
+ hardware::ICameraService::CAMERA_TYPE_ALL, clientAttribution, /*devicePolicy*/0,
&numCameras);
EXPECT_TRUE(rc.isOk());
@@ -183,9 +186,11 @@
}
CameraMetadata metadata;
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
rc = mCameraService->getCameraCharacteristics(cameraIdStr,
/*targetSdkVersion*/__ANDROID_API_FUTURE__, /*overrideToPortrait*/false,
- kDefaultDeviceId, /*devicePolicy*/0, &metadata);
+ clientAttribution, /*devicePolicy*/0, &metadata);
if (!rc.isOk()) {
// The test is relevant only for cameras with Hal 3.x
// support.
@@ -209,11 +214,12 @@
continue;
}
+ clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
+ clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
+ clientAttribution.packageName = "ZSLTest";
rc = mCameraService->connect(this, cameraId,
- "ZSLTest", hardware::ICameraService::USE_CALLING_UID,
- hardware::ICameraService::USE_CALLING_PID,
/*targetSdkVersion*/__ANDROID_API_FUTURE__,
- /*overrideToPortrait*/false, /*forceSlowJpegMode*/false, kDefaultDeviceId,
+ /*overrideToPortrait*/false, /*forceSlowJpegMode*/false, clientAttribution,
/*devicePolicy*/0, &cameraDevice);
EXPECT_TRUE(rc.isOk());
diff --git a/camera/tests/fuzzer/Android.bp b/camera/tests/fuzzer/Android.bp
index bd97c39..3b6413c 100644
--- a/camera/tests/fuzzer/Android.bp
+++ b/camera/tests/fuzzer/Android.bp
@@ -31,6 +31,7 @@
],
shared_libs: [
"camera_platform_flags_c_lib",
+ "framework-permission-aidl-cpp",
"libbase",
"libcutils",
"libutils",
diff --git a/camera/tests/fuzzer/camera_fuzzer.cpp b/camera/tests/fuzzer/camera_fuzzer.cpp
index b0f59f1..f46d246 100644
--- a/camera/tests/fuzzer/camera_fuzzer.cpp
+++ b/camera/tests/fuzzer/camera_fuzzer.cpp
@@ -17,6 +17,7 @@
#include <Camera.h>
#include <CameraParameters.h>
#include <CameraUtils.h>
+#include <android/content/AttributionSourceState.h>
#include <binder/MemoryDealer.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <gui/Surface.h>
@@ -123,21 +124,24 @@
sp<ICameraService> cameraService = nullptr;
cameraService = interface_cast<ICameraService>(binder);
sp<ICamera> cameraDevice = nullptr;
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
if (mFDP->ConsumeBool()) {
- cameraService->connect(this, mFDP->ConsumeIntegral<int32_t>() /* cameraId */, "CAMERAFUZZ",
- hardware::ICameraService::USE_CALLING_UID,
- hardware::ICameraService::USE_CALLING_PID,
+ clientAttribution.uid = hardware::ICameraService::USE_CALLING_UID;
+ clientAttribution.pid = hardware::ICameraService::USE_CALLING_PID;
+ clientAttribution.packageName = "CAMERAFUZZ";
+ cameraService->connect(this, mFDP->ConsumeIntegral<int32_t>() /* cameraId */,
/*targetSdkVersion*/ __ANDROID_API_FUTURE__,
/*overrideToPortrait*/ false, /*forceSlowJpegMode*/ false,
- kDefaultDeviceId, /*devicePolicy*/0, &cameraDevice);
+ 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(),
- mFDP->ConsumeIntegral<int8_t>() /* clientUid */,
- mFDP->ConsumeIntegral<int8_t>() /* clientPid */,
/*targetSdkVersion*/ mFDP->ConsumeIntegral<int32_t>(),
/*overrideToPortrait*/ mFDP->ConsumeBool(),
- /*forceSlowJpegMode*/ mFDP->ConsumeBool(), kDefaultDeviceId,
+ /*forceSlowJpegMode*/ mFDP->ConsumeBool(), clientAttribution,
/*devicePolicy*/0, &cameraDevice);
}
@@ -165,13 +169,15 @@
}
int32_t cameraId = mFDP->ConsumeIntegral<int32_t>();
- Camera::getNumberOfCameras(kDefaultDeviceId, /*devicePolicy*/0);
+ AttributionSourceState clientAttribution;
+ clientAttribution.deviceId = kDefaultDeviceId;
+ Camera::getNumberOfCameras(clientAttribution, /*devicePolicy*/0);
CameraInfo cameraInfo;
cameraInfo.facing = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidFacing)
: mFDP->ConsumeIntegral<int32_t>();
cameraInfo.orientation = mFDP->ConsumeBool() ? mFDP->PickValueInArray(kValidOrientation)
: mFDP->ConsumeIntegral<int32_t>();
- Camera::getCameraInfo(cameraId, /*overrideToPortrait*/false, kDefaultDeviceId,
+ Camera::getCameraInfo(cameraId, /*overrideToPortrait*/false, clientAttribution,
/*devicePolicy*/0, &cameraInfo);
mCamera->reconnect();
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
index ee7ace6..6388518 100644
--- a/cmds/screenrecord/FrameOutput.cpp
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -15,11 +15,14 @@
*/
#define LOG_TAG "ScreenRecord"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
+//#define LOG_NDEBUG 0
+
+#include <com_android_graphics_libgui_flags.h>
+#include <gui/Surface.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <utils/Log.h>
#include "FrameOutput.h"
@@ -67,11 +70,17 @@
return UNKNOWN_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mGlConsumer = new GLConsumer(mExtTextureName, GL_TEXTURE_EXTERNAL_OES, /*useFenceSync=*/true,
+ /*isControlledByApp=*/false);
+ auto producer = mGlConsumer->getSurface()->getIGraphicBufferProducer();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
mGlConsumer = new GLConsumer(consumer, mExtTextureName,
GL_TEXTURE_EXTERNAL_OES, true, false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mGlConsumer->setName(String8("virtual display"));
mGlConsumer->setDefaultBufferSize(width, height);
producer->setMaxDequeuedBufferCount(4);
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index a19ef8e..727f16a 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -172,10 +172,16 @@
return UNKNOWN_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mGlConsumer = new GLConsumer(mExtTextureName, GL_TEXTURE_EXTERNAL_OES, /*useFenceSync=*/true,
+ /*isControlledByApp=*/false);
+ mProducer = mGlConsumer->getSurface()->getIGraphicBufferProducer();
+#else
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
mGlConsumer = new GLConsumer(consumer, mExtTextureName,
GL_TEXTURE_EXTERNAL_OES, true, false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mGlConsumer->setName(String8("virtual display"));
mGlConsumer->setDefaultBufferSize(width, height);
mProducer->setMaxDequeuedBufferCount(4);
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 03c765a..de925b8 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -362,13 +362,26 @@
const ui::DisplayState& displayState,
const sp<IGraphicBufferProducer>& bufferProducer,
sp<IBinder>* pDisplayHandle, sp<SurfaceControl>* mirrorRoot) {
- static const std::string kDisplayName("ScreenRecorder");
+ std::string displayName = gPhysicalDisplayId
+ ? "ScreenRecorder " + to_string(*gPhysicalDisplayId)
+ : "ScreenRecorder";
+ static const std::string kDisplayName(displayName);
+
sp<IBinder> dpy = SurfaceComposerClient::createVirtualDisplay(kDisplayName, gSecureDisplay);
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(dpy, bufferProducer);
setDisplayProjection(t, dpy, displayState);
+
+ // ensures that random layer stack assigned to virtual display changes
+ // between calls - if a list of displays with their layer stacks becomes
+ // available, we should use it to ensure a new layer stack is used here
+ std::srand(
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch()
+ ).count());
ui::LayerStack layerStack = ui::LayerStack::fromValue(std::rand());
t.setDisplayLayerStack(dpy, layerStack);
+
PhysicalDisplayId displayId;
status_t err = getPhysicalDisplayId(displayId);
if (err != NO_ERROR) {
@@ -1224,6 +1237,8 @@
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"--verbose\n"
" Display interesting information on stdout.\n"
+ "--version\n"
+ " Show Android screenrecord version.\n"
"--help\n"
" Show this message.\n"
"\n"
@@ -1255,6 +1270,7 @@
{ "bframes", required_argument, NULL, 'B' },
{ "display-id", required_argument, NULL, 'd' },
{ "capture-secure", no_argument, NULL, 'S' },
+ { "version", no_argument, NULL, 'x' },
{ NULL, 0, NULL, 0 }
};
@@ -1377,6 +1393,9 @@
case 'S':
gSecureDisplay = true;
break;
+ case 'x':
+ fprintf(stderr, "%d.%d\n", kVersionMajor, kVersionMinor);
+ return 0;
default:
if (ic != '?') {
fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
diff --git a/cmds/screenrecord/screenrecord.h b/cmds/screenrecord/screenrecord.h
index cec7c13..57826b0 100644
--- a/cmds/screenrecord/screenrecord.h
+++ b/cmds/screenrecord/screenrecord.h
@@ -18,6 +18,6 @@
#define SCREENRECORD_SCREENRECORD_H
#define kVersionMajor 1
-#define kVersionMinor 3
+#define kVersionMinor 4
#endif /*SCREENRECORD_SCREENRECORD_H*/
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index f26e3a8..1a6e5e8 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -60,6 +60,7 @@
#include <private/media/VideoFrame.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -1133,7 +1134,12 @@
CHECK(gSurface != NULL);
} else {
CHECK(useSurfaceTexAlloc);
-
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<GLConsumer> texture =
+ new GLConsumer(0 /* tex */, GLConsumer::TEXTURE_EXTERNAL,
+ true /* useFenceSync */, false /* isControlledByApp */);
+ gSurface = texture->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -1141,6 +1147,7 @@
GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
false /* isControlledByApp */);
gSurface = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
}
diff --git a/drm/libmediadrmrkp/Android.bp b/drm/libmediadrmrkp/Android.bp
index b1a01e4..88f0571 100644
--- a/drm/libmediadrmrkp/Android.bp
+++ b/drm/libmediadrmrkp/Android.bp
@@ -14,6 +14,7 @@
],
static_libs: [
"android.hardware.common-V2-ndk",
+ "android.hardware.drm.common-V1-ndk",
"android.hardware.drm-V1-ndk",
"android.hardware.security.rkp-V3-ndk",
"libbase",
@@ -39,6 +40,7 @@
],
static_libs: [
"android.hardware.common-V2-ndk",
+ "android.hardware.drm.common-V1-ndk",
"android.hardware.drm-V1-ndk",
"android.hardware.security.rkp-V3-ndk",
"libbase",
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
index f46409f..4132ba2 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -40,6 +40,7 @@
static_libs: [
"android.hardware.common-V2-ndk",
+ "android.hardware.drm.common-V1-ndk",
"android.hardware.drm-V1-ndk",
"libbase",
"libclearkeybase",
diff --git a/media/audio/aconfig/audio.aconfig b/media/audio/aconfig/audio.aconfig
index c3ae59c..c732708 100644
--- a/media/audio/aconfig/audio.aconfig
+++ b/media/audio/aconfig/audio.aconfig
@@ -60,6 +60,15 @@
}
flag {
+ name: "equal_sco_lea_vc_index_range"
+ namespace: "media_audio"
+ description:
+ "Introduce the same index range for voice calls over SCO and "
+ "LE audio"
+ bug: "364364777"
+}
+
+flag {
name: "music_fx_edge_to_edge"
namespace: "media_audio"
description: "Enable Edge-to-edge feature for MusicFx and handle insets"
@@ -83,6 +92,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/audio_framework.aconfig b/media/audio/aconfig/audio_framework.aconfig
index b155bac..587bdbb 100644
--- a/media/audio/aconfig/audio_framework.aconfig
+++ b/media/audio/aconfig/audio_framework.aconfig
@@ -22,6 +22,13 @@
bug: "302323921"
}
+flag{
+ name: "enable_ringtone_haptics_customization"
+ namespace: "media_audio"
+ description: "Enables haptic customization for playing ringtone."
+ bug: "351974934"
+}
+
flag {
name: "deprecate_stream_bt_sco"
namespace: "media_audio"
@@ -102,6 +109,13 @@
}
flag {
+ name: "muted_by_port_volume_api"
+ namespace: "media_audio"
+ description: "Playback monitoring flag used when player muted by port volume"
+ bug: "319515324"
+}
+
+flag {
name: "sco_managed_by_audio"
is_exported: true
namespace: "media_audio"
diff --git a/media/audio/aconfig/audioserver.aconfig b/media/audio/aconfig/audioserver.aconfig
index 6f76408..1ce4d00 100644
--- a/media/audio/aconfig/audioserver.aconfig
+++ b/media/audio/aconfig/audioserver.aconfig
@@ -22,6 +22,13 @@
}
flag {
+ name: "enable_audio_input_device_routing"
+ namespace: "media_audio"
+ description: "Allow audio input devices routing control."
+ bug: "364923030"
+}
+
+flag {
name: "fdtostring_timeout_fix"
namespace: "media_audio"
description: "Improve fdtostring implementation to properly handle timing out."
@@ -29,6 +36,14 @@
}
flag {
+ name: "fix_call_audio_patch"
+ namespace: "media_audio"
+ description:
+ "optimize creation and release of audio patches for call routing"
+ bug: "292492229"
+}
+
+flag {
name: "fix_concurrent_playback_behavior_with_bit_perfect_client"
namespace: "media_audio"
description:
@@ -57,6 +72,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/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/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 4a956f5..90d1874 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
@@ -31,6 +31,7 @@
#include <C2Debug.h>
#include <codec2/common/HalSelection.h>
#include <codec2/hidl/client.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
@@ -423,7 +424,6 @@
surfaceMode_t surfMode) {
using namespace android;
sp<IGraphicBufferProducer> producer = nullptr;
- sp<IGraphicBufferConsumer> consumer = nullptr;
sp<GLConsumer> texture = nullptr;
sp<ANativeWindow> surface = nullptr;
static std::atomic_uint32_t surfaceGeneration{0};
@@ -442,6 +442,16 @@
}
if (surfMode == SURFACE) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ texture = new GLConsumer(0 /* tex */, GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
+ false /* isControlledByApp */);
+ sp<Surface> s = texture->getSurface();
+ surface = s;
+ ASSERT_NE(surface, nullptr) << "failed to create Surface object";
+
+ producer = s->getIGraphicBufferProducer();
+#else
+ sp<IGraphicBufferConsumer> consumer = nullptr;
BufferQueue::createBufferQueue(&producer, &consumer);
ASSERT_NE(producer, nullptr) << "createBufferQueue returned invalid producer";
ASSERT_NE(consumer, nullptr) << "createBufferQueue returned invalid consumer";
@@ -452,6 +462,7 @@
surface = new Surface(producer);
ASSERT_NE(surface, nullptr) << "failed to create Surface object";
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
producer->setGenerationNumber(generation);
}
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index 67c6715..7998524 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -46,9 +46,9 @@
],
static_libs: [
"aaudio-aidl-cpp",
+ "audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
- "audio-permission-aidl-cpp",
"audiopolicy-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"av-types-aidl-cpp",
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 1e27a81..1e8ac8d 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -602,6 +602,7 @@
}
AAUDIO_API aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy) {
+ ALOGD("%s(%d)", __func__, policy);
return AudioGlobal_setMMapPolicy(policy);
}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index c9d8b35..01f0038 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -109,10 +109,12 @@
std::vector<AudioMMapPolicyInfo> policyInfos;
aaudio_policy_t mmapPolicy = AudioGlobal_getMMapPolicy();
- if (android::AudioSystem::getMmapPolicyInfo(
- AudioMMapPolicyType::DEFAULT, &policyInfos) == NO_ERROR) {
+ ALOGD("%s, global mmap policy is %d", __func__, mmapPolicy);
+ if (status_t status = android::AudioSystem::getMmapPolicyInfo(
+ AudioMMapPolicyType::DEFAULT, &policyInfos); status == NO_ERROR) {
aaudio_policy_t systemMmapPolicy = AAudio_getAAudioPolicy(
policyInfos, AAUDIO_MMAP_POLICY_DEFAULT_AIDL);
+ ALOGD("%s, system mmap policy is %d", __func__, systemMmapPolicy);
if (mmapPolicy == AAUDIO_POLICY_ALWAYS && systemMmapPolicy == AAUDIO_POLICY_NEVER) {
// No need to try as AAudioService is not created and the client only wants MMAP path.
return AAUDIO_ERROR_NO_SERVICE;
@@ -125,6 +127,7 @@
mmapPolicy = systemMmapPolicy;
}
} else {
+ ALOGD("%s, failed to query system mmap policy, error=%d", __func__, status);
// If it fails querying mmap policy info, it is highly possible that the AAudioService is
// not created. In this case, we don't try mmap path.
if (mmapPolicy == AAUDIO_POLICY_ALWAYS) {
@@ -136,17 +139,22 @@
if (mmapPolicy == AAUDIO_UNSPECIFIED) {
mmapPolicy = AAUDIO_MMAP_POLICY_DEFAULT;
}
+ ALOGD("%s, final mmap policy is %d", __func__, mmapPolicy);
policyInfos.clear();
aaudio_policy_t mmapExclusivePolicy = AAUDIO_UNSPECIFIED;
- if (android::AudioSystem::getMmapPolicyInfo(
- AudioMMapPolicyType::EXCLUSIVE, &policyInfos) == NO_ERROR) {
+ if (status_t status = android::AudioSystem::getMmapPolicyInfo(
+ AudioMMapPolicyType::EXCLUSIVE, &policyInfos); status == NO_ERROR) {
mmapExclusivePolicy = AAudio_getAAudioPolicy(
policyInfos, AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT_AIDL);
+ ALOGD("%s, system mmap exclusive policy is %d", __func__, mmapExclusivePolicy);
+ } else {
+ ALOGD("%s, failed to query mmap exclusive policy, error=%d", __func__, status);
}
if (mmapExclusivePolicy == AAUDIO_UNSPECIFIED) {
mmapExclusivePolicy = AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT;
}
+ ALOGD("%s, final mmap exclusive policy is %d", __func__, mmapExclusivePolicy);
aaudio_sharing_mode_t sharingMode = getSharingMode();
if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE)
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 8595308..255bd0f 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -261,6 +261,11 @@
void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
audio_port_handle_t deviceId) {
+ // Check for an invalid deviceId. Why change to UNSPECIFIED?
+ if (deviceId == AAUDIO_UNSPECIFIED) {
+ ALOGE("%s(, deviceId = AAUDIO_UNSPECIFIED)! Why?", __func__);
+ return;
+ }
// Device routing is a common source of errors and DISCONNECTS.
// Please leave this log in place. If there is a bug then this might
// get called after the stream has been deleted so log before we
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 5fe43dd..1ddff84 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -282,6 +282,13 @@
mTracker.reset(new RecordingActivityTracker());
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("audio"));
+ if (binder != nullptr) {
+ // Barrier to ensure runtime permission update propagates to audioflinger
+ // Must be client-side
+ interface_cast<IAudioManager>(binder)->permissionUpdateBarrier();
+ }
+
mSelectedDeviceId = selectedDeviceId;
mSelectedMicDirection = selectedMicDirection;
mSelectedMicFieldDimension = microphoneFieldDimension;
@@ -708,6 +715,17 @@
AutoMutex lock(mLock);
ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d",
__func__, mPortId, deviceId, mSelectedDeviceId);
+ const int64_t beginNs = systemTime();
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPREFERREDDEVICE)
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)deviceId)
+ .record(); });
if (mSelectedDeviceId != deviceId) {
mSelectedDeviceId = deviceId;
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 3f4fcfd..55f74e1 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -322,20 +322,15 @@
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;
+status_t AudioSystem::setPortsVolume(
+ const std::vector<audio_port_handle_t>& portIds, float volume, audio_io_handle_t output) {
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);
+ std::vector<int32_t> portIdsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<int32_t>>(
+ portIds, legacy2aidl_audio_port_handle_t_int32_t));
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ af->setPortsVolume(portIdsAidl, volume, outputAidl);
return NO_ERROR;
}
@@ -1098,7 +1093,8 @@
audio_port_handle_t* portId,
std::vector<audio_io_handle_t>* secondaryOutputs,
bool *isSpatialized,
- bool *isBitPerfect) {
+ bool *isBitPerfect,
+ float *volume) {
if (attr == nullptr) {
ALOGE("%s NULL audio attributes", __func__);
return BAD_VALUE;
@@ -1164,6 +1160,7 @@
*isBitPerfect = responseAidl.isBitPerfect;
*attr = VALUE_OR_RETURN_STATUS(
aidl2legacy_AudioAttributes_audio_attributes_t(responseAidl.attr));
+ *volume = responseAidl.volume;
return OK;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index fd55bf3..5a6285e 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1707,6 +1707,18 @@
AutoMutex lock(mLock);
ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d",
__func__, mPortId, deviceId, mSelectedDeviceId);
+ const int64_t beginNs = systemTime();
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPREFERREDDEVICE)
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)deviceId)
+ .record(); });
+
if (mSelectedDeviceId != deviceId) {
mSelectedDeviceId = deviceId;
if (mStatus == NO_ERROR) {
@@ -2423,12 +2435,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
@@ -2598,7 +2612,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;
}
}
@@ -2686,7 +2700,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;
}
@@ -2772,7 +2786,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..9241973 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -350,32 +350,13 @@
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::setPortsVolume(
+ const std::vector<audio_port_handle_t>& portIds, float volume, audio_io_handle_t output) {
+ std::vector<int32_t> portIdsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<int32_t>>(
+ portIds, legacy2aidl_audio_port_handle_t_int32_t));
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ return statusTFromBinderStatus(mDelegate->setPortsVolume(portIdsAidl, volume, outputAidl));
}
status_t AudioFlingerClientAdapter::setMode(audio_mode_t mode) {
@@ -1040,21 +1021,14 @@
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));
+Status AudioFlingerServerAdapter::setPortsVolume(
+ const std::vector<int32_t>& portIds, float volume, int32_t output) {
+ std::vector<audio_port_handle_t> portIdsLegacy = VALUE_OR_RETURN_BINDER(
+ convertContainer<std::vector<audio_port_handle_t>>(
+ portIds, aidl2legacy_int32_t_audio_port_handle_t));
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();
+ return Status::fromStatusT(mDelegate->setPortsVolume(portIdsLegacy, volume, outputLegacy));
}
Status AudioFlingerServerAdapter::setMode(AudioMode mode) {
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 79fcea8..d325d0a 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -1033,17 +1033,11 @@
mState = TONE_IDLE;
- if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) {
- ALOGE("Unable to marshal AudioFlinger");
- return;
- }
mThreadCanCallJava = threadCanCallJava;
mStreamType = streamType;
mVolume = volume;
mpToneDesc = NULL;
mpNewToneDesc = NULL;
- // Generate tone by chunks of 20 ms to keep cadencing precision
- mProcessSize = (mSamplingRate * 20) / 1000;
char value[PROPERTY_VALUE_MAX];
if (property_get("gsm.operator.iso-country", value, "") == 0) {
@@ -1321,21 +1315,21 @@
mpAudioTrack = new AudioTrack(attributionSource);
ALOGV("AudioTrack(%p) created", mpAudioTrack.get());
+
audio_attributes_t attr;
audio_stream_type_t streamType = mStreamType;
- if (mStreamType == AUDIO_STREAM_VOICE_CALL) {
+ if (mStreamType == AUDIO_STREAM_VOICE_CALL || mStreamType == AUDIO_STREAM_BLUETOOTH_SCO) {
streamType = AUDIO_STREAM_DTMF;
}
attr = AudioSystem::streamTypeToAttributes(streamType);
attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_LOW_LATENCY);
- const size_t frameCount = mProcessSize;
status_t status = mpAudioTrack->set(
AUDIO_STREAM_DEFAULT,
0, // sampleRate
AUDIO_FORMAT_PCM_16_BIT,
AUDIO_CHANNEL_OUT_MONO,
- frameCount,
+ 0, // frameCount
AUDIO_OUTPUT_FLAG_NONE,
wp<AudioTrack::IAudioTrackCallback>::fromExisting(this),
0, // notificationFrames
@@ -1355,6 +1349,10 @@
return false;
}
+ mSamplingRate = mpAudioTrack->getSampleRate();
+ // Generate tone by chunks of 20 ms to keep cadencing precision
+ mProcessSize = (mSamplingRate * 20) / 1000;
+
mpAudioTrack->setVolume(mVolume);
mState = TONE_INIT;
return true;
diff --git a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
index b814b85..4b26d5b 100644
--- a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
@@ -39,4 +39,6 @@
boolean isBitPerfect;
/** The corrected audio attributes. **/
AudioAttributes attr;
+ /** initial port volume for the new audio track */
+ float volume;
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index 4f00f83..1c825bc 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -94,13 +94,18 @@
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 AudioTrack port ids volume attribute. This is the new way of controlling volume from
+ * AudioPolicyManager to AudioFlinger.
+ */
+ void setPortsVolume(in int[] /* audio_port_handle_t[] */ portIds, float volume,
+ int /* audio_io_handle_t */ output);
// set audio mode.
void setMode(AudioMode mode);
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index ac42ea9..f7c3b89 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -116,9 +116,9 @@
void releaseInput(int /* audio_port_handle_t */ portId);
- oneway void setDeviceAbsoluteVolumeEnabled(in AudioDevice device,
- boolean enabled,
- AudioStreamType streamToDriveAbs);
+ void setDeviceAbsoluteVolumeEnabled(in AudioDevice device,
+ boolean enabled,
+ AudioStreamType streamToDriveAbs);
void initStreamVolume(AudioStreamType stream,
int indexMin,
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index dfdb4cf..710a656 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -26,6 +26,7 @@
#include <android/content/AttributionSourceState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
+#include <com_android_media_audioserver.h>
#include <media/AidlConversion.h>
#include <media/AudioEffect.h>
#include <media/AudioRecord.h>
@@ -41,6 +42,8 @@
constexpr int32_t kMaxSampleRateHz = 192000;
constexpr int32_t kSampleRateUnspecified = 0;
+namespace audioserver_flags = com::android::media::audioserver;
+
using namespace std;
using namespace android;
@@ -501,13 +504,19 @@
AudioSystem::getMasterMute(&state);
AudioSystem::isMicrophoneMuted(&state);
- audio_stream_type_t stream = getValue(&mFdp, kStreamtypes);
- AudioSystem::setStreamMute(getValue(&mFdp, kStreamtypes), mFdp.ConsumeBool());
+ audio_stream_type_t stream ;
+ if (!audioserver_flags::portid_volume_management()) {
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::setStreamMute(getValue(&mFdp, kStreamtypes), mFdp.ConsumeBool());
- stream = getValue(&mFdp, kStreamtypes);
- AudioSystem::setStreamVolume(stream, mFdp.ConsumeFloatingPoint<float>(),
- mFdp.ConsumeIntegral<int32_t>());
-
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::setStreamVolume(stream, mFdp.ConsumeFloatingPoint<float>(),
+ mFdp.ConsumeIntegral<int32_t>());
+ } else {
+ std::vector <audio_port_handle_t> portsForVolumeChange{};
+ AudioSystem::setPortsVolume(portsForVolumeChange, mFdp.ConsumeFloatingPoint<float>(),
+ mFdp.ConsumeIntegral<int32_t>());
+ }
audio_mode_t mode = getValue(&mFdp, kModes);
AudioSystem::setMode(mode);
@@ -519,12 +528,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..40e5673 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -124,15 +124,22 @@
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 volume for given AudioTrack port ids on specified output
+ * @param portIds to consider
+ * @param volume to set
+ * @param output to consider
+ * @return NO_ERROR if successful
+ */
+ static status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds,
+ float volume, audio_io_handle_t output);
// set audio mode in audio hardware
static status_t setMode(audio_mode_t mode);
@@ -337,7 +344,8 @@
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs,
bool *isSpatialized,
- bool *isBitPerfect);
+ bool *isBitPerfect,
+ float *volume);
static status_t startOutput(audio_port_handle_t portId);
static status_t stopOutput(audio_port_handle_t portId);
static void releaseOutput(audio_port_handle_t portId);
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 211fffa..a5f3217 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -229,9 +229,15 @@
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 volume for given AudioTrack port ids on specified output
+ * @param portIds to consider
+ * @param volume to set
+ * @param output to consider
+ * @return NO_ERROR if successful
+ */
+ virtual status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume,
+ audio_io_handle_t output) = 0;
// set audio mode
virtual status_t setMode(audio_mode_t mode) = 0;
@@ -424,9 +430,8 @@
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 setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume,
+ audio_io_handle_t output) override;
status_t setMode(audio_mode_t mode) override;
status_t setMicMute(bool state) override;
bool getMicMute() const override;
@@ -549,8 +554,7 @@
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_PORTS_VOLUME = media::BnAudioFlingerService::TRANSACTION_setPortsVolume,
SET_MODE = media::BnAudioFlingerService::TRANSACTION_setMode,
SET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_setMicMute,
GET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_getMicMute,
@@ -673,9 +677,8 @@
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 setPortsVolume(const std::vector<int32_t>& portIds, float volume, int32_t output)
+ 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/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/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/impl/effectsAidlConversion/AidlConversionHapticGenerator.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionHapticGenerator.cpp
index bdee7b65..e87993a 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionHapticGenerator.cpp
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionHapticGenerator.cpp
@@ -52,12 +52,17 @@
switch (type) {
case HG_PARAM_HAPTIC_INTENSITY: {
int32_t id = 0, scale;
- if (OK != param.readFromValue(&id) || OK != param.readFromValue(&scale)) {
+ float scaleFactor, adaptiveScaleFactor;
+ if (OK != param.readFromValue(&id) || OK != param.readFromValue(&scale) ||
+ OK != param.readFromValue(&scaleFactor) ||
+ OK != param.readFromValue(&adaptiveScaleFactor)) {
ALOGE("%s invalid intensity %s", __func__, param.toString().c_str());
return BAD_VALUE;
}
- HapticGenerator::HapticScale hpScale(
- {.id = id, .scale = (HapticGenerator::VibratorScale)(scale)});
+ HapticGenerator::HapticScale hpScale({.id = id,
+ .scale = (HapticGenerator::VibratorScale)(scale),
+ .scaleFactor = scaleFactor,
+ .adaptiveScaleFactor = adaptiveScaleFactor});
aidlParam = MAKE_SPECIFIC_PARAMETER(HapticGenerator, hapticGenerator, hapticScales,
{hpScale});
break;
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/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index f60d616..258dca2 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,28 +310,40 @@
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 id, scaleLevel;
+ float scaleFactor, adaptiveScaleFactor;
+ if (reader.readFromValue(&id) != OK || reader.readFromValue(&scaleLevel) != OK ||
+ reader.readFromValue(&scaleFactor) != OK ||
+ reader.readFromValue(&adaptiveScaleFactor) != OK) {
+ ALOGE("%s error reading haptic intensity %s", __func__, reader.toString().c_str());
+ return -EINVAL;
+ }
+ os::HapticScale hapticScale(static_cast<os::HapticLevel>(scaleLevel), scaleFactor,
+ adaptiveScaleFactor);
+ ALOGD("Updating haptic scale, %s", hapticScale.toString().c_str());
if (hapticScale.isScaleMute()) {
context->param.id2HapticScale.erase(id);
} else {
context->param.id2HapticScale.emplace(id, hapticScale);
}
context->param.maxHapticScale = hapticScale;
- for (const auto&[id, scale] : context->param.id2HapticScale) {
+ for (const auto&[_, scale] : context->param.id2HapticScale) {
+ // TODO(b/360314386): update to use new scale factors
if (scale.getLevel() > context->param.maxHapticScale.getLevel()) {
context->param.maxHapticScale = scale;
}
@@ -336,12 +351,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 +389,7 @@
HapticGenerator_Reset(context);
} break;
default:
- ALOGW("Unknown param: %d", param);
+ ALOGW("Unknown param: %d", paramType);
return -EINVAL;
}
@@ -573,8 +593,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 55e07fb..dd29e86 100644
--- a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
@@ -55,6 +55,17 @@
namespace aidl::android::hardware::audio::effect {
+const std::vector<Range::HapticGeneratorRange> kHapticRange = {
+ MAKE_RANGE(HapticGenerator, vibratorInfo,
+ HapticGenerator::VibratorInformation(
+ {.resonantFrequencyHz = 1, .qFactor = 1, .maxAmplitude = -1}),
+ HapticGenerator::VibratorInformation(
+ {.resonantFrequencyHz = std::numeric_limits<float>::max(),
+ .qFactor = std::numeric_limits<float>::max(),
+ .maxAmplitude = 1}))};
+
+static const Capability kHapticCap = {.range = kHapticRange};
+
const std::string HapticGeneratorImpl::kEffectName = "Haptic Generator";
const Descriptor HapticGeneratorImpl::kDescriptor = {
.common = {.id = {.type = getEffectTypeUuidHapticGenerator(),
@@ -62,7 +73,8 @@
.proxy = std::nullopt},
.flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST},
.name = HapticGeneratorImpl::kEffectName,
- .implementor = "The Android Open Source Project"}};
+ .implementor = "The Android Open Source Project"},
+ .capability = kHapticCap};
ndk::ScopedAStatus HapticGeneratorImpl::getDescriptor(Descriptor* _aidl_return) {
RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
@@ -76,6 +88,8 @@
RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
auto& hgParam = specific.get<Parameter::Specific::hapticGenerator>();
+ RETURN_IF(!inRange(hgParam, kHapticRange), EX_ILLEGAL_ARGUMENT, "outOfRange");
+
auto tag = hgParam.getTag();
switch (tag) {
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
index e040fec..d8f9093 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
@@ -36,7 +36,7 @@
: EffectContext(statusDepth, common) {
mState = HAPTIC_GENERATOR_STATE_UNINITIALIZED;
- mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
+ mParams.mMaxHapticScale = {.scale = HapticGenerator::VibratorScale::MUTE};
mParams.mVibratorInfo.resonantFrequencyHz = DEFAULT_RESONANT_FREQUENCY;
mParams.mVibratorInfo.qFactor = DEFAULT_BSF_ZERO_Q;
mParams.mVibratorInfo.maxAmplitude = 0.f;
@@ -87,13 +87,17 @@
RetCode HapticGeneratorContext::setHgHapticScales(
const std::vector<HapticGenerator::HapticScale>& hapticScales) {
for (auto hapticScale : hapticScales) {
- mParams.mHapticScales.insert_or_assign(hapticScale.id, hapticScale.scale);
+ mParams.mHapticScales.insert_or_assign(hapticScale.id, hapticScale);
}
- mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
+ mParams.mMaxHapticScale = {.scale = HapticGenerator::VibratorScale::MUTE};
for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
- mParams.mMaxVibratorScale = std::max(mParams.mMaxVibratorScale, vibratorScale);
+ // TODO(b/360314386): update to use new scale factors
+ if (vibratorScale.scale > mParams.mMaxHapticScale.scale) {
+ mParams.mMaxHapticScale = vibratorScale;
+ }
}
- LOG(INFO) << " HapticGenerator VibratorScale set to " << toString(mParams.mMaxVibratorScale);
+ LOG(INFO) << " HapticGenerator VibratorScale set to "
+ << toString(mParams.mMaxHapticScale.scale);
return RetCode::SUCCESS;
}
@@ -103,8 +107,8 @@
std::vector<HapticGenerator::HapticScale> HapticGeneratorContext::getHgHapticScales() const {
std::vector<HapticGenerator::HapticScale> result;
- for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
- result.push_back({id, vibratorScale});
+ for (const auto& [_, hapticScale] : mParams.mHapticScales) {
+ result.push_back(hapticScale);
}
return result;
}
@@ -150,7 +154,7 @@
return status;
}
- if (mParams.mMaxVibratorScale == HapticGenerator::VibratorScale::MUTE) {
+ if (mParams.mMaxHapticScale.scale == HapticGenerator::VibratorScale::MUTE) {
// Haptic channels are muted, not need to generate haptic data.
return {STATUS_OK, samples, samples};
}
@@ -177,7 +181,10 @@
runProcessingChain(mInputBuffer.data(), mOutputBuffer.data(), mFrameCount);
::android::os::scaleHapticData(
hapticOutBuffer, hapticSampleCount,
- {static_cast<::android::os::HapticLevel>(mParams.mMaxVibratorScale)} /* scale */,
+ ::android::os::HapticScale(
+ static_cast<::android::os::HapticLevel>(mParams.mMaxHapticScale.scale),
+ mParams.mMaxHapticScale.scaleFactor,
+ mParams.mMaxHapticScale.adaptiveScaleFactor),
mParams.mVibratorInfo.maxAmplitude /* limit */);
// For haptic data, the haptic playback thread will copy the data from effect input
@@ -346,16 +353,17 @@
std::string HapticGeneratorContext::paramToString(const struct HapticGeneratorParam& param) const {
std::stringstream ss;
- ss << "\t\ttHapticGenerator Parameters:\n";
+ ss << "\t\tHapticGenerator Parameters:\n";
ss << "\t\t- mHapticChannelCount: " << param.mHapticChannelCount << '\n';
ss << "\t\t- mAudioChannelCount: " << param.mAudioChannelCount << '\n';
ss << "\t\t- mHapticChannelSource: " << param.mHapticChannelSource[0] << ", "
<< param.mHapticChannelSource[1] << '\n';
- ss << "\t\t- mMaxVibratorScale: " << ::android::internal::ToString(param.mMaxVibratorScale)
- << '\n';
+ ss << "\t\t- mMaxHapticScale: " << ::android::internal::ToString(param.mMaxHapticScale.scale)
+ << ", scaleFactor=" << param.mMaxHapticScale.scaleFactor
+ << ", adaptiveScaleFactor=" << param.mMaxHapticScale.adaptiveScaleFactor << '\n';
ss << "\t\t- mVibratorInfo: " << param.mVibratorInfo.toString() << '\n';
for (const auto& it : param.mHapticScales)
- ss << "\t\t\t" << it.first << ": " << toString(it.second) << '\n';
+ ss << "\t\t\t" << it.first << ": " << toString(it.second.scale) << '\n';
return ss.str();
}
@@ -370,7 +378,7 @@
ss << "\t\t- distortion input gain: " << DEFAULT_DISTORTION_INPUT_GAIN << '\n';
ss << "\t\t- distortion cube threshold: " << DEFAULT_DISTORTION_CUBE_THRESHOLD << '\n';
ss << "\t\t- distortion output gain: " << getDistortionOutputGain() << '\n';
- ss << "\t\tHapticGenerator Parameters:\n" << paramToString(mParams) << "\n";
+ ss << paramToString(mParams) << "\n";
return ss.str();
}
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
index cf38e47..37532f6 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
@@ -41,9 +41,9 @@
int mHapticChannelCount;
int mAudioChannelCount;
- std::map<int, HapticGenerator::VibratorScale> mHapticScales;
+ std::map<int, HapticGenerator::HapticScale> mHapticScales;
// max intensity will be used to scale haptic data.
- HapticGenerator::VibratorScale mMaxVibratorScale;
+ HapticGenerator::HapticScale mMaxHapticScale;
HapticGenerator::VibratorInformation mVibratorInfo;
};
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index f367a3e..98c3382 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -267,6 +267,7 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETLOGSESSIONID "setLogSessionId" // AudioTrack, Record
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID "setPlayerIId" // AudioTrack
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPREFERREDDEVICE "setPreferredDevice" // AudioTrack, Record
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETSAMPLERATE "setSampleRate" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETSTARTTHRESHOLD "setStartThreshold" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index f4143da..3987a67 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -43,6 +43,12 @@
#include <mpeg2ts/ATSParser.h>
#include <gui/Surface.h>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+
+#include <android-base/stringprintf.h>
+using ::android::base::StringPrintf;
+
namespace android {
static float kDisplayRefreshingRate = 60.f; // TODO: get this from the display
@@ -157,7 +163,10 @@
int32_t index;
CHECK(msg->findInt32("index", &index));
+ ATRACE_BEGIN(StringPrintf("Nuplayer::handleAnInputBuffer [%s]",
+ mIsAudio ? "audio" : "video").c_str());
handleAnInputBuffer(index);
+ ATRACE_END();
break;
}
@@ -175,7 +184,10 @@
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", &flags));
+ ATRACE_BEGIN(StringPrintf("Nuplayer::handleAnOutputBuffer [%s]",
+ mIsAudio ? "audio" : "video").c_str());
handleAnOutputBuffer(index, offset, size, timeUs, flags);
+ ATRACE_END();
break;
}
@@ -184,7 +196,10 @@
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
+ ATRACE_BEGIN(StringPrintf("Nuplayer::handleOutputFormatChange [%s]",
+ mIsAudio ? "audio" : "video").c_str());
handleOutputFormatChange(format);
+ ATRACE_END();
break;
}
@@ -205,15 +220,16 @@
break;
}
}
-
break;
}
case kWhatRenderBuffer:
{
+ ATRACE_BEGIN("Nuplayer::onRenderBuffer");
if (!isStaleReply(msg)) {
onRenderBuffer(msg);
}
+ ATRACE_END();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 3e96d27..0cb5062 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -27,6 +27,12 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+
+#include <android-base/stringprintf.h>
+using ::android::base::StringPrintf;
+
namespace android {
NuPlayer::DecoderBase::DecoderBase(const sp<AMessage> ¬ify)
@@ -129,9 +135,11 @@
switch (msg->what()) {
case kWhatConfigure:
{
+ ATRACE_BEGIN("NuPlayer::DecoderBase::onConfigure");
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
onConfigure(format);
+ ATRACE_END();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index c6595ba..851d252 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -36,6 +36,11 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/FoundationUtils.h>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+#include <android-base/stringprintf.h>
+using ::android::base::StringPrintf;
+
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
@@ -146,9 +151,11 @@
const char *url,
const KeyedVector<String8, String8> *headers) {
ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
+ ATRACE_BEGIN(StringPrintf("setDataSource(%p)", this).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
+ ATRACE_END();
return INVALID_OPERATION;
}
@@ -159,15 +166,18 @@
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
+ ATRACE_END();
return mAsyncResult;
}
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%p) file(%d)", this, fd);
+ ATRACE_BEGIN(StringPrintf("setDataSource(%p) file(%d)", this, fd).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
+ ATRACE_END();
return INVALID_OPERATION;
}
@@ -178,15 +188,18 @@
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
+ ATRACE_END();
return mAsyncResult;
}
status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
ALOGV("setDataSource(%p) stream source", this);
+ ATRACE_BEGIN(StringPrintf("setDataSource(%p) stream source", this).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
+ ATRACE_END();
return INVALID_OPERATION;
}
@@ -197,15 +210,18 @@
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
+ ATRACE_END();
return mAsyncResult;
}
status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) {
ALOGV("setDataSource(%p) callback source", this);
+ ATRACE_BEGIN(StringPrintf("setDataSource(%p) callback source", this).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
+ ATRACE_END();
return INVALID_OPERATION;
}
@@ -216,15 +232,18 @@
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
+ ATRACE_END();
return mAsyncResult;
}
status_t NuPlayerDriver::setDataSource(const String8& rtpParams) {
ALOGV("setDataSource(%p) rtp source", this);
+ ATRACE_BEGIN(StringPrintf("setDataSource(%p) rtp source", this).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
+ ATRACE_END();
return INVALID_OPERATION;
}
@@ -235,6 +254,7 @@
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
+ ATRACE_END();
return mAsyncResult;
}
@@ -295,8 +315,11 @@
status_t NuPlayerDriver::prepare() {
ALOGV("prepare(%p)", this);
+ ATRACE_BEGIN(StringPrintf("prepare(%p)", this).c_str());
Mutex::Autolock autoLock(mLock);
- return prepare_l();
+ status_t ret = prepare_l();
+ ATRACE_END();
+ return ret;
}
status_t NuPlayerDriver::prepare_l() {
@@ -354,8 +377,11 @@
status_t NuPlayerDriver::start() {
ALOGV("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
+ ATRACE_BEGIN(StringPrintf("start(%p), state is %d, eos is %d", this, mState, mAtEOS).c_str());
Mutex::Autolock autoLock(mLock);
- return start_l();
+ status_t ret = start_l();
+ ATRACE_END();
+ return ret;
}
status_t NuPlayerDriver::start_l() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 225cfdd..4ded33a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -15,6 +15,7 @@
*/
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
#define LOG_TAG "NuPlayerRenderer"
#include <utils/Log.h>
@@ -37,6 +38,9 @@
#include <inttypes.h>
+#include <android-base/stringprintf.h>
+using ::android::base::StringPrintf;
+
namespace android {
/*
@@ -1985,6 +1989,8 @@
bool isStreaming) {
ALOGV("openAudioSink: offloadOnly(%d) offloadingAudio(%d)",
offloadOnly, offloadingAudio());
+ ATRACE_BEGIN(StringPrintf("NuPlayer::Renderer::onOpenAudioSink: offloadOnly(%d) "
+ "offloadingAudio(%d)", offloadOnly, offloadingAudio()).c_str());
bool audioSinkChanged = false;
int32_t numChannels;
@@ -2056,6 +2062,7 @@
if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) {
ALOGV("openAudioSink: no change in offload mode");
// no change from previous configuration, everything ok.
+ ATRACE_END();
return OK;
}
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
@@ -2125,6 +2132,7 @@
if (memcmp(&mCurrentPcmInfo, &info, sizeof(info)) == 0) {
ALOGV("openAudioSink: no change in pcm mode");
// no change from previous configuration, everything ok.
+ ATRACE_END();
return OK;
}
@@ -2169,6 +2177,7 @@
ALOGW("openAudioSink: non offloaded open failed status: %d", err);
mAudioSink->close();
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
+ ATRACE_END();
return err;
}
mCurrentPcmInfo = info;
@@ -2180,13 +2189,16 @@
onAudioSinkChanged();
}
mAudioTornDown = false;
+ ATRACE_END();
return OK;
}
void NuPlayer::Renderer::onCloseAudioSink() {
+ ATRACE_BEGIN("NuPlyer::Renderer::onCloseAudioSink");
mAudioSink->close();
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
+ ATRACE_END();
}
void NuPlayer::Renderer::onChangeAudioFormat(
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index e229844..e26f189 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -33,6 +33,7 @@
#include <camera/Camera.h>
#include <camera/CameraParameters.h>
#include <camera/StringUtils.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/Surface.h>
#include <utils/String8.h>
#include <cutils/properties.h>
@@ -150,10 +151,15 @@
int32_t cameraId, const std::string& clientName, uid_t clientUid, pid_t clientPid) {
if (camera == 0) {
- mCamera = Camera::connect(cameraId, clientName, clientUid, clientPid,
- /*targetSdkVersion*/__ANDROID_API_FUTURE__,
+ AttributionSourceState clientAttribution;
+ clientAttribution.pid = clientPid;
+ clientAttribution.uid = clientUid;
+ clientAttribution.deviceId = kDefaultDeviceId;
+ clientAttribution.packageName = clientName;
+
+ mCamera = Camera::connect(cameraId, /*targetSdkVersion*/__ANDROID_API_FUTURE__,
/*rotationOverride*/hardware::ICameraService::ROTATION_OVERRIDE_NONE,
- /*forceSlowJpegMode*/false);
+ /*forceSlowJpegMode*/false, clientAttribution);
if (mCamera == 0) return -EBUSY;
mCameraFlags &= ~FLAGS_HOT_CAMERA;
} else {
@@ -466,11 +472,13 @@
ALOGE("%s: Buffer queue already exists", __FUNCTION__);
return ALREADY_EXISTS;
}
-
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Create a buffer queue.
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
+#endif // !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN;
if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
@@ -479,9 +487,15 @@
bufferCount += kConsumerBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mVideoBufferConsumer = new BufferItemConsumer(usage, bufferCount);
+ mVideoBufferConsumer->setName(String8::format("StageFright-CameraSource"));
+ mVideoBufferProducer = mVideoBufferConsumer->getSurface()->getIGraphicBufferProducer();
+#else
mVideoBufferConsumer = new BufferItemConsumer(consumer, usage, bufferCount);
mVideoBufferConsumer->setName(String8::format("StageFright-CameraSource"));
mVideoBufferProducer = producer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
status_t res = mVideoBufferConsumer->setDefaultBufferSize(width, height);
if (res != OK) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ff626d4..467cec0 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -847,7 +847,7 @@
const sp<AMessage> mNotify;
};
-class OnBufferReleasedListener : public ::android::BnProducerListener{
+class OnBufferReleasedListener : public ::android::SurfaceListener{
private:
uint32_t mGeneration;
std::weak_ptr<BufferChannelBase> mBufferChannel;
@@ -878,6 +878,9 @@
notifyBufferReleased();
}
+ void onBuffersDiscarded([[maybe_unused]] const std::vector<sp<GraphicBuffer>>& buffers)
+ override { }
+
void onBufferDetached([[maybe_unused]] int slot) override {
notifyBufferReleased();
}
@@ -6978,7 +6981,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/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/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index 5d68890..979edab 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -297,9 +297,10 @@
}
void MtpFfsHandle::close() {
- auto timeout = std::chrono::seconds(2);
- std::unique_lock lk(m);
- cv.wait_for(lk, timeout ,[this]{return child_threads==0;});
+ // Join all child threads before destruction
+ for (auto& thread : mChildThreads) {
+ thread.join();
+ }
io_destroy(mCtx);
closeEndpoints();
@@ -677,12 +678,10 @@
memcpy(temp, me.data, me.length);
me.data = temp;
- std::unique_lock lk(m);
- child_threads++;
- lk.unlock();
-
std::thread t([this, me]() { return this->doSendEvent(me); });
- t.detach();
+
+ // Store the thread object for later joining
+ mChildThreads.emplace_back(std::move(t));
return 0;
}
@@ -692,11 +691,6 @@
if (static_cast<unsigned>(ret) != length)
PLOG(ERROR) << "Mtp error sending event thread!";
delete[] reinterpret_cast<char*>(me.data);
-
- std::unique_lock lk(m);
- child_threads--;
- lk.unlock();
- cv.notify_one();
}
} // namespace android
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index 51cdef0..8f4b769 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -60,9 +60,7 @@
bool mCanceled;
bool mBatchCancel;
- std::mutex m;
- std::condition_variable cv;
- std::atomic<int> child_threads{0};
+ std::vector<std::thread> mChildThreads;
android::base::unique_fd mControl;
// "in" from the host's perspective => sink for mtp server
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 7797841..889ddad 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -22,13 +22,15 @@
#include "NdkImagePriv.h"
#include "NdkImageReaderPriv.h"
-#include <cutils/atomic.h>
-#include <utils/Log.h>
#include <android_media_Utils.h>
-#include <ui/PublicFormat.h>
-#include <private/android/AHardwareBufferHelpers.h>
+#include <com_android_graphics_libgui_flags.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <ui/PublicFormat.h>
+#include <utils/Log.h>
+
+#include <cutils/atomic.h>
using namespace android;
@@ -291,22 +293,30 @@
AImageReader::init() {
mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<IGraphicBufferProducer> gbProducer;
sp<IGraphicBufferConsumer> gbConsumer;
BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+#endif // !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
createProcessUniqueId());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = new BufferItemConsumer(mHalUsage, mMaxImages, /*controlledByApp*/ true);
+#else
mBufferItemConsumer =
new BufferItemConsumer(gbConsumer, mHalUsage, mMaxImages, /*controlledByApp*/ true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
if (mBufferItemConsumer == nullptr) {
ALOGE("Failed to allocate BufferItemConsumer");
return AMEDIA_ERROR_UNKNOWN;
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mProducer = gbProducer;
+#endif // !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mBufferItemConsumer->setName(consumerName);
mBufferItemConsumer->setFrameAvailableListener(mFrameListener);
mBufferItemConsumer->setBufferFreedListener(mBufferRemovedListener);
@@ -328,10 +338,18 @@
return AMEDIA_ERROR_UNKNOWN;
}
if (mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer->setConsumerIsProtected(true);
+#else
gbConsumer->setConsumerIsProtected(true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mSurface = mBufferItemConsumer->getSurface();
+#else
mSurface = new Surface(mProducer, /*controlledByApp*/true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
if (mSurface == nullptr) {
ALOGE("Failed to create surface");
return AMEDIA_ERROR_UNKNOWN;
@@ -578,8 +596,13 @@
*handle = mWindowHandle;
return AMEDIA_OK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<HGraphicBufferProducer> hgbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(
+ mSurface->getIGraphicBufferProducer());
+#else
sp<HGraphicBufferProducer> hgbp =
new TWGraphicBufferProducer<HGraphicBufferProducer>(mProducer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
HalToken halToken;
if (!createHalToken(hgbp, &halToken)) {
return AMEDIA_ERROR_UNKNOWN;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 1c50d83..89a33f8 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -26,6 +26,7 @@
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/Surface.h>
@@ -163,7 +164,9 @@
uint64_t mHalUsage;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<IGraphicBufferProducer> mProducer;
+#endif // !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<Surface> mSurface;
sp<BufferItemConsumer> mBufferItemConsumer;
sp<ANativeWindow> mWindow;
diff --git a/media/psh_utils/Android.bp b/media/psh_utils/Android.bp
new file mode 100644
index 0000000..dafa63b
--- /dev/null
+++ b/media/psh_utils/Android.bp
@@ -0,0 +1,52 @@
+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: [
+ "AudioPowerManager.cpp",
+ "AudioToken.cpp",
+ "HealthStats.cpp",
+ "HealthStatsProvider.cpp",
+ "PowerClientStats.cpp",
+ "PowerStats.cpp",
+ "PowerStatsCollector.cpp",
+ "PowerStatsProvider.cpp",
+ ],
+ shared_libs: [
+ "com.android.media.audio-aconfig-cc",
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+ shared: {
+ shared_libs: ndk_libs,
+ },
+ static: {
+ whole_static_libs: ndk_libs,
+ },
+}
diff --git a/media/psh_utils/AudioPowerManager.cpp b/media/psh_utils/AudioPowerManager.cpp
new file mode 100644
index 0000000..3ae681a
--- /dev/null
+++ b/media/psh_utils/AudioPowerManager.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "AudioToken.h"
+#define LOG_TAG "AudioPowerManager"
+#include <com_android_media_audioserver.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <psh_utils/AudioPowerManager.h>
+
+namespace android::media::psh_utils {
+
+/* static */
+AudioPowerManager& AudioPowerManager::getAudioPowerManager() {
+ [[clang::no_destroy]] static AudioPowerManager apm;
+ return apm;
+}
+
+std::unique_ptr<Token> AudioPowerManager::startClient(pid_t pid, uid_t uid,
+ const std::string& additional) {
+ std::shared_ptr<PowerClientStats> powerClientStats;
+ std::lock_guard l(mMutex);
+ if (mPowerClientStats.count(uid) == 0) {
+ const auto it = mHistoricalClients.find(uid);
+ if (it == mHistoricalClients.end()) {
+ powerClientStats = std::make_shared<PowerClientStats>(uid, additional);
+ } else {
+ powerClientStats = it->second;
+ mHistoricalClients.erase(it);
+ }
+ mPowerClientStats[uid] = powerClientStats;
+ } else {
+ powerClientStats = mPowerClientStats[uid];
+ }
+ powerClientStats->addPid(pid);
+ mPidToUid[pid] = uid;
+ std::unique_ptr<Token> token =
+ std::make_unique<AudioClientToken>(powerClientStats, pid, uid, additional);
+ mOutstandingTokens.emplace(token.get());
+ return token;
+}
+
+std::unique_ptr<Token> AudioPowerManager::startTrack(uid_t uid, const std::string& additional) {
+ std::lock_guard l(mMutex);
+ if (mPowerClientStats.count(uid) == 0) {
+ ALOGW("%s: Cannot find uid: %d", __func__, uid);
+ return {};
+ }
+ auto powerClientStats = mPowerClientStats[uid];
+ std::unique_ptr<Token> token =
+ std::make_unique<AudioTrackToken>(powerClientStats, additional);
+ mOutstandingTokens.emplace(token.get());
+ return token;
+}
+
+std::unique_ptr<Token> AudioPowerManager::startThread(
+ pid_t pid, const std::string& wakeLockName,
+ WakeFlag wakeFlag, const std::string& additional) {
+ std::lock_guard l(mMutex);
+ std::unique_ptr<Token> token =
+ std::make_unique<AudioThreadToken>(pid, wakeLockName, wakeFlag, additional);
+ mOutstandingTokens.emplace(token.get());
+ return token;
+}
+
+std::string AudioPowerManager::toString() const {
+ const std::string prefix(" ");
+ std::string result;
+ std::lock_guard l(mMutex);
+ result.append("Power Tokens:\n");
+ std::vector<std::string> tokenInfo;
+ for (const auto& token: mOutstandingTokens) {
+ tokenInfo.emplace_back(token->toString());
+ }
+ std::sort(tokenInfo.begin(), tokenInfo.end());
+ for (const auto& info: tokenInfo) {
+ result.append(prefix).append(info).append("\n");
+ }
+ result.append("Power Clients:\n");
+ for (const auto& [uid, powerClientStats]: mPowerClientStats) {
+ result.append(powerClientStats->toString(true, prefix));
+ }
+ result.append("Power Client History:\n");
+ for (const auto& [power, powerClientStats]: mHistoricalClients) {
+ result.append(powerClientStats->toString(true, prefix));
+ }
+ return result;
+}
+
+void AudioPowerManager::stopClient(pid_t pid) {
+ std::lock_guard l(mMutex);
+ const auto pidit = mPidToUid.find(pid);
+ if (pidit == mPidToUid.end()) return;
+ const uid_t uid = pidit->second;
+ const auto it = mPowerClientStats.find(uid);
+ if (it == mPowerClientStats.end()) return;
+
+ auto powerClientStats = it->second;
+ size_t count = powerClientStats->removePid(pid);
+ if (count == 0) {
+ mHistoricalClients[uid] = powerClientStats;
+ mPowerClientStats.erase(it);
+ if (mHistoricalClients.size() > kHistory) {
+ mHistoricalClients.erase(mHistoricalClients.begin()); // remove oldest.
+ }
+ }
+ mPidToUid.erase(pid);
+}
+
+void AudioPowerManager::clear_token_ptr(Token* token) {
+ if (token != nullptr) {
+ std::lock_guard l(mMutex);
+ (void)mOutstandingTokens.erase(token);
+ }
+}
+
+/* static */
+bool AudioPowerManager::enabled() {
+ static const bool enabled = com::android::media::audioserver::power_stats()
+ && property_get_bool("persist.audio.power_stats.enabled", false);
+ return enabled;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/AudioToken.cpp b/media/psh_utils/AudioToken.cpp
new file mode 100644
index 0000000..f7bf382
--- /dev/null
+++ b/media/psh_utils/AudioToken.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.
+ */
+
+#define LOG_TAG "AudioToken"
+#include <android-base/logging.h>
+#include <utils/Log.h>
+#include "AudioToken.h"
+#include <psh_utils/AudioPowerManager.h>
+
+namespace android::media::psh_utils {
+
+/* static */
+constinit std::atomic<size_t> AudioClientToken::sIdCounter{};
+
+AudioClientToken::AudioClientToken(
+ std::shared_ptr<PowerClientStats> powerClientStats, pid_t pid, uid_t uid,
+ const std::string& additional)
+ : mPowerClientStats(std::move(powerClientStats))
+ , mPid(pid)
+ , mAdditional(additional)
+ , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
+ (void)uid;
+}
+
+AudioClientToken::~AudioClientToken() {
+ auto& apm = AudioPowerManager::getAudioPowerManager();
+
+ // APM has a back pointer to AudioToken, which is accessible on toString().
+ // We first remove ourselves to prevent use after free.
+ apm.clear_token_ptr(this);
+
+ // The client token is released when it is no longer registered with AudioFlinger.
+ // However, it is possible that AudioTrackTokens are still active when the client is released
+ // after crashing and some of its tracks are draining. Those track tokens also
+ // maintain a pointer to the PowerClientStats keeping that consistent.
+
+ // Stopping the client moves its PowerClientStats from active to historical
+ // if it is the last pid associated with the client uid.
+ apm.stopClient(mPid);
+}
+
+std::string AudioClientToken::toString() const {
+ std::string result("Client-");
+ result.append(std::to_string(mId)).append(": ")
+ .append(" pid: ").append(std::to_string(mPid));
+ if (!mAdditional.empty()) {
+ result.append(" ").append(mAdditional);
+ }
+ return result;
+}
+
+std::unique_ptr<Token> createAudioClientToken(pid_t pid, uid_t uid,
+ const std::string& additional) {
+ return AudioPowerManager::getAudioPowerManager().startClient(pid, uid, additional);
+}
+
+/* static */
+constinit std::atomic<size_t> AudioThreadToken::sIdCounter{};
+
+AudioThreadToken::AudioThreadToken(
+ pid_t tid, const std::string& wakeLockName,
+ WakeFlag wakeFlag, const std::string& additional)
+ : mTid(tid)
+ , mWakeLockName(wakeLockName)
+ , mWakeFlag(wakeFlag)
+ , mAdditional(additional)
+ , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
+}
+
+AudioThreadToken::~AudioThreadToken() {
+ auto& apm = AudioPowerManager::getAudioPowerManager();
+
+ // APM has a back pointer to AudioToken, which is accessible on toString().
+ // We first remove ourselves to prevent use after free.
+ apm.clear_token_ptr(this);
+}
+
+std::string AudioThreadToken::toString() const {
+ std::string result("Thread-");
+ result.append(std::to_string(mId)).append(": ")
+ .append(" ThreadBase-tid: ").append(std::to_string(mTid))
+ .append(" wakeLockName: ").append(mWakeLockName)
+ .append(" wakeFlag: ").append(::android::media::psh_utils::toString(mWakeFlag));
+ if (!mAdditional.empty()) {
+ result.append(" ").append(mAdditional);
+ }
+ return result;
+}
+
+std::unique_ptr<Token> createAudioThreadToken(
+ pid_t pid, const std::string& wakeLockName,
+ WakeFlag wakeFlag, const std::string& additional) {
+ return AudioPowerManager::getAudioPowerManager().startThread(
+ pid, wakeLockName, wakeFlag, additional);
+}
+
+/* static */
+constinit std::atomic<size_t> AudioTrackToken::sIdCounter{};
+
+AudioTrackToken::AudioTrackToken(
+ std::shared_ptr<PowerClientStats> powerClientStats, const std::string& additional)
+ : mPowerClientStats(std::move(powerClientStats))
+ , mAdditional(additional)
+ , mId(sIdCounter.fetch_add(1, std::memory_order_relaxed)) {
+ if (mPowerClientStats){
+ mPowerClientStats->getCommandThread().add(
+ "start",
+ [pas = mPowerClientStats, actualNs = systemTime(SYSTEM_TIME_BOOTTIME)]() {
+ pas->start(actualNs);
+ });
+ }
+}
+
+AudioTrackToken::~AudioTrackToken() {
+ // APM has a back pointer to AudioToken, which is accessible on toString().
+ // We first remove ourselves to prevent use after free.
+ AudioPowerManager::getAudioPowerManager().clear_token_ptr(this);
+ if (mPowerClientStats) {
+ mPowerClientStats->getCommandThread().add(
+ "stop",
+ [pas = mPowerClientStats, actualNs = systemTime(SYSTEM_TIME_BOOTTIME)]() {
+ pas->stop(actualNs);
+ });
+ }
+}
+
+std::string AudioTrackToken::toString() const {
+ std::string result("Track-");
+ result.append(std::to_string(mId)).append(": ")
+ .append(mPowerClientStats ? mPowerClientStats->toString() : std::string("null"));
+ if (!mAdditional.empty()) {
+ result.append(" ").append(mAdditional);
+ }
+ return result;
+}
+
+std::unique_ptr<Token> createAudioTrackToken(uid_t uid, const std::string& additional) {
+ return AudioPowerManager::getAudioPowerManager().startTrack(uid, additional);
+}
+
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/AudioToken.h b/media/psh_utils/AudioToken.h
new file mode 100644
index 0000000..aa25b04
--- /dev/null
+++ b/media/psh_utils/AudioToken.h
@@ -0,0 +1,83 @@
+/*
+ * 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/PowerClientStats.h>
+#include <psh_utils/Token.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+
+namespace android::media::psh_utils {
+
+class AudioClientToken : public Token {
+public:
+ AudioClientToken(std::shared_ptr<PowerClientStats> powerClientStats, pid_t pid, uid_t uid,
+ const std::string& additional);
+ ~AudioClientToken() override;
+
+ // AudioPowerManager may call toString() while AudioToken is in its dtor.
+ // It is safe so long as toString is final.
+ std::string toString() const final;
+
+private:
+ const std::shared_ptr<PowerClientStats> mPowerClientStats;
+ const pid_t mPid;
+ const std::string mAdditional;
+ const size_t mId;
+ static constinit std::atomic<size_t> sIdCounter;
+};
+
+class AudioThreadToken : public Token {
+public:
+ AudioThreadToken(
+ pid_t tid, const std::string& wakeLockName,
+ WakeFlag wakeFlag, const std::string& additional);
+ ~AudioThreadToken() override;
+
+ // AudioPowerManager may call toString() while AudioToken is in its dtor.
+ // It is safe so long as toString is final.
+ std::string toString() const final;
+
+private:
+ const pid_t mTid;
+ const std::string mWakeLockName;
+ const WakeFlag mWakeFlag;
+ const std::string mAdditional;
+ const size_t mId;
+ static constinit std::atomic<size_t> sIdCounter;
+};
+
+class AudioTrackToken : public Token {
+public:
+ AudioTrackToken(
+ std::shared_ptr<PowerClientStats> powerClientStats, const std::string& additional);
+ ~AudioTrackToken() override;
+
+ // AudioPowerManager may call toString() while AudioToken is in its dtor.
+ // It is safe so long as toString is final.
+ std::string toString() const final;
+
+private:
+ const std::shared_ptr<PowerClientStats> mPowerClientStats;
+ const std::string mAdditional;
+ const size_t mId;
+ static constinit std::atomic<size_t> sIdCounter;
+};
+
+} // namespace android::media::psh_utils
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..de72463
--- /dev/null
+++ b/media/psh_utils/HealthStatsProvider.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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>
+#include <psh_utils/ServiceSingleton.h>
+
+using ::aidl::android::hardware::health::HealthInfo;
+using ::aidl::android::hardware::health::IHealth;
+
+namespace android::media::psh_utils {
+
+static auto getHealthService() {
+ return getServiceSingleton<IHealth>();
+}
+
+status_t HealthStatsDataProvider::fill(PowerStats* stat) const {
+ if (stat == nullptr) return BAD_VALUE;
+ HealthStats& stats = stat->health_stats;
+ auto healthService = getHealthService();
+ if (healthService == nullptr) {
+ 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/PowerClientStats.cpp b/media/psh_utils/PowerClientStats.cpp
new file mode 100644
index 0000000..65f65a44
--- /dev/null
+++ b/media/psh_utils/PowerClientStats.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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/PowerClientStats.h>
+#include <mediautils/ServiceUtilities.h>
+
+namespace android::media::psh_utils {
+
+/* static */
+audio_utils::CommandThread& PowerClientStats::getCommandThread() {
+ [[clang::no_destroy]] static audio_utils::CommandThread ct;
+ return ct;
+}
+
+PowerClientStats::PowerClientStats(uid_t uid, const std::string& additional)
+ : mUid(uid), mAdditional(additional) {}
+
+void PowerClientStats::start(int64_t actualNs) {
+ std::lock_guard l(mMutex);
+ ++mTokenCount;
+ if (mStartNs == 0) mStartNs = actualNs;
+ if (mStartStats) return;
+ mStartStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
+}
+
+void PowerClientStats::stop(int64_t actualNs) {
+ std::lock_guard l(mMutex);
+ if (--mTokenCount > 0) return;
+ if (mStartNs != 0) mCumulativeNs += actualNs - mStartNs;
+ mStartNs = 0;
+ if (!mStartStats) return;
+ const auto stopStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
+ if (stopStats && stopStats != mStartStats) {
+ *mCumulativeStats += *stopStats - *mStartStats;
+ }
+ mStartStats.reset();
+}
+
+void PowerClientStats::addPid(pid_t pid) {
+ std::lock_guard l(mMutex);
+ mPids.emplace(pid);
+}
+
+size_t PowerClientStats::removePid(pid_t pid) {
+ std::lock_guard l(mMutex);
+ mPids.erase(pid);
+ return mPids.size();
+}
+
+std::string PowerClientStats::toString(bool stats, const std::string& prefix) const {
+ std::lock_guard l(mMutex);
+
+ // Adjust delta time and stats if currently running.
+ auto cumulativeStats = mCumulativeStats;
+ auto cumulativeNs = mCumulativeNs;
+ if (mStartNs) cumulativeNs += systemTime(SYSTEM_TIME_BOOTTIME) - mStartNs;
+ if (mStartStats) {
+ const auto stopStats = PowerStatsCollector::getCollector().getStats(kStatTimeToleranceNs);
+ if (stopStats && stopStats != mStartStats) {
+ auto newStats = std::make_shared<PowerStats>(*cumulativeStats);
+ *newStats += *stopStats - *mStartStats;
+ cumulativeStats = newStats;
+ }
+ }
+
+ std::string result(prefix);
+ result.append("uid: ")
+ .append(std::to_string(mUid))
+ .append(" ").append(mediautils::UidInfo::getInfo(mUid)->package)
+ .append(" streams: ").append(std::to_string(mTokenCount))
+ .append(" seconds: ").append(std::to_string(cumulativeNs * 1e-9));
+ result.append(" {");
+ for (auto pid : mPids) {
+ result.append(" ").append(std::to_string(pid));
+ }
+ result.append(" }");
+ if (!mAdditional.empty()) {
+ result.append("\n").append(prefix).append(mAdditional);
+ }
+ if (stats) {
+ std::string prefix2(prefix);
+ prefix2.append(" ");
+ result.append("\n").append(cumulativeStats->normalizedEnergy(prefix2));
+ }
+ return result;
+}
+
+} // 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..e5bf2aa
--- /dev/null
+++ b/media/psh_utils/PowerStatsCollector.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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) {
+ (void) provider->fill(stats); // on error, we continue to proceed.
+ }
+
+ // 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..112c323
--- /dev/null
+++ b/media/psh_utils/PowerStatsProvider.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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/power/stats/IPowerStats.h>
+#include <android-base/logging.h>
+#include <psh_utils/ServiceSingleton.h>
+#include <unordered_map>
+
+using ::aidl::android::hardware::power::stats::IPowerStats;
+
+namespace android::media::psh_utils {
+
+static auto getPowerStatsService() {
+ return getServiceSingleton<IPowerStats>();
+}
+
+status_t RailEnergyDataProvider::fill(PowerStats *stat) const {
+ if (stat == nullptr) return BAD_VALUE;
+ auto powerStatsService = getPowerStatsService();
+ if (powerStatsService == nullptr) {
+ 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) {
+ 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..c3888ac
--- /dev/null
+++ b/media/psh_utils/PowerStatsProvider.h
@@ -0,0 +1,38 @@
+/*
+ * 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>
+
+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..2382c69
--- /dev/null
+++ b/media/psh_utils/benchmarks/Android.bp
@@ -0,0 +1,75 @@
+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: [
+ "libpshutils",
+ ],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+}
+
+cc_benchmark {
+ name: "audio_powerstatscollector_benchmark",
+
+ srcs: ["audio_powerstatscollector_benchmark.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpshutils",
+ ],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+}
+
+cc_benchmark {
+ name: "audio_token_benchmark",
+
+ srcs: ["audio_token_benchmark.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpshutils",
+ ],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libmediautils",
+ "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..4d8b224
--- /dev/null
+++ b/media/psh_utils/benchmarks/audio_powerstats_benchmark.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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>
+
+/*
+Pixel 9 XL Pro
+---------------------------------------------------------------------------------------------------------------
+Benchmark Time CPU Iteration
+---------------------------------------------------------------------------------------------------------------
+audio_powerstats_benchmark:
+ #MemoryFixture/CacheAccess/64/0/0/1 5.195761589711465 ns 5.183635029038574 ns 135160912
+ #MemoryFixture/CacheAccess/128/0/0/1 10.37270431027728 ns 10.341754343667125 ns 67574354
+ #MemoryFixture/CacheAccess/256/0/0/1 20.767353363364098 ns 20.708496782017836 ns 33809541
+ #MemoryFixture/CacheAccess/512/0/0/1 41.53473855852046 ns 41.45724926375999 ns 16900399
+ #MemoryFixture/CacheAccess/1024/0/0/1 82.89673650172568 ns 82.68064919937272 ns 8462177
+ #MemoryFixture/CacheAccess/2048/0/0/1 165.77648929323732 ns 165.45127650324827 ns 4227878
+ #MemoryFixture/CacheAccess/4096/0/0/1 331.9272979248067 ns 331.0722959129879 ns 2114919
+ #MemoryFixture/CacheAccess/8192/0/0/1 663.8090302013887 ns 662.2813002594532 ns 1054528
+ #MemoryFixture/CacheAccess/16384/0/0/1 1327.4224893455748 ns 1324.0114138292752 ns 529095
+ #MemoryFixture/CacheAccess/32768/0/0/1 2657.1037276954685 ns 2651.8974883509522 ns 263970
+ #MemoryFixture/CacheAccess/65536/0/0/1 5314.170125835522 ns 5305.20127734871 ns 131679
+ #MemoryFixture/CacheAccess/131072/0/0/1 10624.517848490625 ns 10602.467739493763 ns 66056
+ #MemoryFixture/CacheAccess/262144/0/0/1 21271.09560700047 ns 21224.851075464823 ns 32916
+ #MemoryFixture/CacheAccess/524288/0/0/1 42556.76641626909 ns 42444.65628786041 ns 16508
+ #MemoryFixture/CacheAccess/1048576/0/0/1 85440.6313100312 ns 85076.15703685701 ns 8221
+ #MemoryFixture/CacheAccess/2097152/0/0/1 170908.37391089948 ns 169402.58059051324 ns 4132
+ #MemoryFixture/CacheAccess/4194304/0/0/1 373635.4350000207 ns 372955.40777777723 ns 1800
+ #MemoryFixture/CacheAccess/8388608/0/0/1 685101.2127660838 ns 681594.2330754338 ns 1034
+ #MemoryFixture/CacheAccess/16777216/0/0/1 1588009.158696047 ns 1581510.0217391246 ns 460
+ #MemoryFixture/CacheAccess/33554432/0/0/1 2721626.5387591715 ns 2708149.166666674 ns 258
+ #MemoryFixture/CacheAccess/67108864/0/0/1 5433705.728680161 ns 5413515.914728661 ns 129
+ #MemoryFixture/CacheAccess/64/1/0/1 5.201213751848433 ns 5.180967321755995 ns 135673202
+ #MemoryFixture/CacheAccess/128/1/0/1 10.386209252693448 ns 10.351378045484042 ns 67486234
+ #MemoryFixture/CacheAccess/256/1/0/1 20.742666405371747 ns 20.686210353062208 ns 33848169
+ #MemoryFixture/CacheAccess/512/1/0/1 41.52781367071582 ns 41.4438085044977 ns 16870391
+ #MemoryFixture/CacheAccess/1024/1/0/1 83.03849985460687 ns 82.6732197351271 ns 8442915
+ #MemoryFixture/CacheAccess/2048/1/0/1 166.02706801365886 ns 165.60695561393828 ns 4226169
+ #MemoryFixture/CacheAccess/4096/1/0/1 332.05696890075365 ns 331.3395040136246 ns 2112679
+ #MemoryFixture/CacheAccess/8192/1/0/1 664.3009073119205 ns 662.5811781316487 ns 1055315
+ #MemoryFixture/CacheAccess/16384/1/0/1 1329.0792867154223 ns 1325.0185471435798 ns 527251
+ #MemoryFixture/CacheAccess/32768/1/0/1 2652.9089904482526 ns 2645.5388137876826 ns 264236
+ #MemoryFixture/CacheAccess/65536/1/0/1 5312.635002724743 ns 5300.412875575496 ns 132064
+ #MemoryFixture/CacheAccess/131072/1/0/1 10625.299202810635 ns 10594.376178351697 ns 65982
+ #MemoryFixture/CacheAccess/262144/1/0/1 21270.763359464152 ns 21206.192921372138 ns 33029
+ #MemoryFixture/CacheAccess/524288/1/0/1 42496.14168177758 ns 42381.692498487435 ns 16530
+ #MemoryFixture/CacheAccess/1048576/1/0/1 85425.34302253627 ns 85063.54206182965 ns 8119
+ #MemoryFixture/CacheAccess/2097152/1/0/1 170961.8011639407 ns 169732.18840931158 ns 4124
+ #MemoryFixture/CacheAccess/4194304/1/0/1 440086.77439029363 ns 439010.07195122127 ns 1640
+ #MemoryFixture/CacheAccess/8388608/1/0/1 677684.5246376056 ns 675888.058937202 ns 1035
+ #MemoryFixture/CacheAccess/16777216/1/0/1 1571417.6297115851 ns 1566423.4922394755 ns 451
+ #MemoryFixture/CacheAccess/33554432/1/0/1 2723418.325581582 ns 2708535.8062015465 ns 258
+ #MemoryFixture/CacheAccess/67108864/1/0/1 5435209.372094963 ns 5413991.821705426 ns 129
+ #MemoryFixture/CacheAccess/64/2/0/1 5.204700890931938 ns 5.183036658664568 ns 135406460
+ #MemoryFixture/CacheAccess/128/2/0/1 10.376355078178406 ns 10.348754235460566 ns 67518337
+ #MemoryFixture/CacheAccess/256/2/0/1 20.726238269323797 ns 20.670377070062745 ns 33834057
+ #MemoryFixture/CacheAccess/512/2/0/1 41.49336362336435 ns 41.38084547087122 ns 16890919
+ #MemoryFixture/CacheAccess/1024/2/0/1 82.96761236822086 ns 82.7676652782051 ns 8440753
+ #MemoryFixture/CacheAccess/2048/2/0/1 165.90277344671065 ns 165.63781837950896 ns 4223301
+ #MemoryFixture/CacheAccess/4096/2/0/1 332.08415463794364 ns 331.07455763248197 ns 2110971
+ #MemoryFixture/CacheAccess/8192/2/0/1 662.569068830069 ns 661.1656746088121 ns 1055382
+ #MemoryFixture/CacheAccess/16384/2/0/1 1327.7767031437843 ns 1324.268331214332 ns 528552
+ #MemoryFixture/CacheAccess/32768/2/0/1 2654.714983405558 ns 2647.9097623853727 ns 264546
+ #MemoryFixture/CacheAccess/65536/2/0/1 5304.774145979664 ns 5290.380748589911 ns 131554
+ #MemoryFixture/CacheAccess/131072/2/0/1 10631.0978039924 ns 10602.125724165107 ns 65938
+ #MemoryFixture/CacheAccess/262144/2/0/1 21258.936606489668 ns 21202.585867458216 ns 33016
+ #MemoryFixture/CacheAccess/524288/2/0/1 42460.85577331506 ns 42355.304775195626 ns 16481
+ #MemoryFixture/CacheAccess/1048576/2/0/1 85428.60153206348 ns 85160.29036964968 ns 8224
+ #MemoryFixture/CacheAccess/2097152/2/0/1 170233.91140170072 ns 169409.06511740564 ns 4131
+ #MemoryFixture/CacheAccess/4194304/2/0/1 402022.9022378908 ns 401018.78327444167 ns 1698
+ #MemoryFixture/CacheAccess/8388608/2/0/1 677677.2908216701 ns 675843.1642512042 ns 1035
+ #MemoryFixture/CacheAccess/16777216/2/0/1 1554294.1339100641 ns 1549490.831533465 ns 463
+ #MemoryFixture/CacheAccess/33554432/2/0/1 2722937.453488912 ns 2709007.093023249 ns 258
+ #MemoryFixture/CacheAccess/67108864/2/0/1 5435791.6511618495 ns 5415184.511627913 ns 129
+ #MemoryFixture/CacheAccess/64/0/2/1 5.198916380394579 ns 5.178607363536682 ns 135270162
+ #MemoryFixture/CacheAccess/128/0/2/1 10.39285189976819 ns 10.361873548918089 ns 67571996
+ #MemoryFixture/CacheAccess/256/0/2/1 20.75920269645178 ns 20.69937217558235 ns 33765810
+ #MemoryFixture/CacheAccess/512/0/2/1 41.51556112567147 ns 41.372342254073665 ns 16947585
+ #MemoryFixture/CacheAccess/1024/0/2/1 82.96516513710067 ns 82.78508396196703 ns 8459485
+ #MemoryFixture/CacheAccess/2048/0/2/1 166.19624228249646 ns 165.74873814180103 ns 4221552
+ #MemoryFixture/CacheAccess/4096/0/2/1 331.7269469760912 ns 330.91601941885546 ns 2111762
+ #MemoryFixture/CacheAccess/8192/0/2/1 663.8953077112178 ns 662.4540856828183 ns 1059419
+ #MemoryFixture/CacheAccess/16384/0/2/1 1326.843343898467 ns 1323.254749209487 ns 527193
+ #MemoryFixture/CacheAccess/32768/0/2/1 2656.6743123958327 ns 2651.2139933123217 ns 264069
+ #MemoryFixture/CacheAccess/65536/0/2/1 5316.515245822549 ns 5306.343742646622 ns 131741
+ #MemoryFixture/CacheAccess/131072/0/2/1 10623.00981164003 ns 10584.927048634307 ns 66044
+ #MemoryFixture/CacheAccess/262144/0/2/1 21210.760294289023 ns 21148.895028460767 ns 33028
+ #MemoryFixture/CacheAccess/524288/0/2/1 42522.49017237178 ns 42412.58560628969 ns 16535
+ #MemoryFixture/CacheAccess/1048576/0/2/1 86800.15632693251 ns 86501.64057114806 ns 8124
+ #MemoryFixture/CacheAccess/2097152/0/2/1 177705.58147680553 ns 177010.52665832458 ns 3995
+ #MemoryFixture/CacheAccess/4194304/0/2/1 449051.5944408481 ns 448237.065698044 ns 1583
+ #MemoryFixture/CacheAccess/8388608/0/2/1 1389931.2189924652 ns 1386035.8565891527 ns 516
+ #MemoryFixture/CacheAccess/16777216/0/2/1 4438020.074999826 ns 4420751.918750021 ns 160
+ #MemoryFixture/CacheAccess/33554432/0/2/1 1.730178560976084E7 ns 1.7182623121951293E7 ns 41
+ #MemoryFixture/CacheAccess/67108864/0/2/1 5.283456416664952E7 ns 5.258297450000053E7 ns 12
+ #MemoryFixture/CacheAccess/64/1/2/1 5.204121573005314 ns 5.179569988739665 ns 135600170
+ #MemoryFixture/CacheAccess/128/1/2/1 10.387460007442089 ns 10.354541036512236 ns 67512560
+ #MemoryFixture/CacheAccess/256/1/2/1 20.77893771735786 ns 20.727591539055314 ns 33750321
+ #MemoryFixture/CacheAccess/512/1/2/1 41.4739992379063 ns 41.315239742240664 ns 16908639
+ #MemoryFixture/CacheAccess/1024/1/2/1 82.95454097741914 ns 82.7976946763163 ns 8446970
+ #MemoryFixture/CacheAccess/2048/1/2/1 165.86154320354674 ns 165.43862697234525 ns 4233855
+ #MemoryFixture/CacheAccess/4096/1/2/1 331.8942415618145 ns 331.28362462222265 ns 2109704
+ #MemoryFixture/CacheAccess/8192/1/2/1 663.6968508366361 ns 662.640989545053 ns 1057011
+ #MemoryFixture/CacheAccess/16384/1/2/1 1328.002697434852 ns 1325.3893625606 ns 527909
+ #MemoryFixture/CacheAccess/32768/1/2/1 2656.826225607798 ns 2651.831997636693 ns 264032
+ #MemoryFixture/CacheAccess/65536/1/2/1 5313.403312198365 ns 5296.6562514184625 ns 132178
+ #MemoryFixture/CacheAccess/131072/1/2/1 10603.411688232678 ns 10580.430523642488 ns 66152
+ #MemoryFixture/CacheAccess/262144/1/2/1 21213.68814698657 ns 21160.64858647625 ns 33038
+ #MemoryFixture/CacheAccess/524288/1/2/1 42446.96972817497 ns 42358.49900102921 ns 16517
+ #MemoryFixture/CacheAccess/1048576/1/2/1 85427.89922199522 ns 85099.99477267203 ns 8226
+ #MemoryFixture/CacheAccess/2097152/1/2/1 179576.28781830988 ns 178747.97179230847 ns 4006
+ #MemoryFixture/CacheAccess/4194304/1/2/1 453971.4271099782 ns 453200.38874680526 ns 1564
+ #MemoryFixture/CacheAccess/8388608/1/2/1 1413810.749999729 ns 1409767.6830708506 ns 508
+ #MemoryFixture/CacheAccess/16777216/1/2/1 4481396.176099637 ns 4463691.635220161 ns 159
+ #MemoryFixture/CacheAccess/33554432/1/2/1 1.7363190725006916E7 ns 1.7271956449999947E7 ns 40
+ #MemoryFixture/CacheAccess/67108864/1/2/1 5.310257300000861E7 ns 5.283166808333325E7 ns 12
+ #MemoryFixture/CacheAccess/64/2/2/1 5.194585073441566 ns 5.169936491706671 ns 135797225
+ #MemoryFixture/CacheAccess/128/2/2/1 10.375776978615239 ns 10.351150907177248 ns 67504271
+ #MemoryFixture/CacheAccess/256/2/2/1 20.73537619800892 ns 20.682392672592464 ns 33819437
+ #MemoryFixture/CacheAccess/512/2/2/1 41.46680809632523 ns 41.35825233901641 ns 16913944
+ #MemoryFixture/CacheAccess/1024/2/2/1 82.84606240770246 ns 82.6134204462559 ns 8446202
+ #MemoryFixture/CacheAccess/2048/2/2/1 165.87278324509214 ns 165.32429617950757 ns 4232933
+ #MemoryFixture/CacheAccess/4096/2/2/1 331.2406082982817 ns 330.5629607515063 ns 2116922
+ #MemoryFixture/CacheAccess/8192/2/2/1 663.159315900038 ns 661.6959690484747 ns 1056103
+ #MemoryFixture/CacheAccess/16384/2/2/1 1327.5666883524236 ns 1324.3426747207684 ns 528758
+ #MemoryFixture/CacheAccess/32768/2/2/1 2654.9809699966722 ns 2648.132907418347 ns 264372
+ #MemoryFixture/CacheAccess/65536/2/2/1 5314.47981229803 ns 5303.806613499892 ns 131912
+ #MemoryFixture/CacheAccess/131072/2/2/1 10606.19082560071 ns 10585.181869071148 ns 66097
+ #MemoryFixture/CacheAccess/262144/2/2/1 21187.819721722273 ns 21154.040502117125 ns 33060
+ #MemoryFixture/CacheAccess/524288/2/2/1 42442.62465239758 ns 42311.67198645875 ns 16542
+ #MemoryFixture/CacheAccess/1048576/2/2/1 85539.82432104382 ns 85200.15385547924 ns 8248
+ #MemoryFixture/CacheAccess/2097152/2/2/1 180928.070870083 ns 180122.69382093282 ns 3965
+ #MemoryFixture/CacheAccess/4194304/2/2/1 456790.68648981495 ns 455950.1693600544 ns 1547
+ #MemoryFixture/CacheAccess/8388608/2/2/1 1427351.7287124782 ns 1423232.2613861358 ns 505
+ #MemoryFixture/CacheAccess/16777216/2/2/1 4513772.829113805 ns 4495839.088607608 ns 158
+ #MemoryFixture/CacheAccess/33554432/2/2/1 1.7454686475002743E7 ns 1.7364546800000016E7 ns 40
+ #MemoryFixture/CacheAccess/67108864/2/2/1 5.2963768833327174E7 ns 5.266439433333403E7 ns 12
+
+ */
+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..021eb5a
--- /dev/null
+++ b/media/psh_utils/benchmarks/audio_powerstatscollector_benchmark.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_TAG "audio_token_benchmark"
+#include <utils/Log.h>
+
+#include <psh_utils/PowerStatsCollector.h>
+
+#include <benchmark/benchmark.h>
+
+/*
+ Pixel 9 Pro XL
+ (tolerance is the amount of time a cached value is valid).
+------------------------------------------------------------------------------------------
+ Benchmark Time CPU Iteration
+------------------------------------------------------------------------------------------
+audio_powerstatscollector_benchmark:
+ #BM_StatsToleranceMs/0 6.346578290999787E7 ns 2069264.56 ns 100
+ #BM_StatsToleranceMs/50 454.12461256065177 ns 203.1644161064639 ns 2615571
+ #BM_StatsToleranceMs/100 167.74983887731364 ns 101.99598388920647 ns 5436852
+ #BM_StatsToleranceMs/200 102.57950838168422 ns 79.40969988086803 ns 7600815
+ #BM_StatsToleranceMs/500 86.87348495571898 ns 75.24841434306252 ns 9789318
+*/
+
+// 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/benchmarks/audio_token_benchmark.cpp b/media/psh_utils/benchmarks/audio_token_benchmark.cpp
new file mode 100644
index 0000000..47003c0
--- /dev/null
+++ b/media/psh_utils/benchmarks/audio_token_benchmark.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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/Token.h>
+
+#include <benchmark/benchmark.h>
+
+/*
+ Pixel 9 Pro XL
+------------------------------------------------------------------------------------------
+ Benchmark Time CPU Iteration
+------------------------------------------------------------------------------------------
+audio_token_benchmark:
+ #BM_ClientToken 494.6548907301575 ns 492.4932166101717 ns 1376819
+ #BM_ThreadToken 140.34316175293938 ns 139.91778452790845 ns 5000397
+ #BM_TrackToken 944.0571625384163 ns 893.7912613357879 ns 643096
+*/
+
+static void BM_ClientToken(benchmark::State& state) {
+ constexpr pid_t kPid = 10;
+ constexpr uid_t kUid = 100;
+ while (state.KeepRunning()) {
+ auto token = android::media::psh_utils::createAudioClientToken(
+ kPid, kUid);
+ benchmark::ClobberMemory();
+ }
+}
+
+BENCHMARK(BM_ClientToken);
+
+static void BM_ThreadToken(benchmark::State& state) {
+ constexpr pid_t kTid = 20;
+ constexpr const char* kWakeLockTag = "thread";
+ while (state.KeepRunning()) {
+ auto token = android::media::psh_utils::createAudioThreadToken(
+ kTid, kWakeLockTag);
+ benchmark::ClobberMemory();
+ }
+}
+
+BENCHMARK(BM_ThreadToken);
+
+static void BM_TrackToken(benchmark::State& state) {
+ constexpr pid_t kPid = 10;
+ constexpr uid_t kUid = 100;
+ auto clientToken = android::media::psh_utils::createAudioClientToken(
+ kPid, kUid);
+ while (state.KeepRunning()) {
+ auto token = android::media::psh_utils::createAudioTrackToken(kUid);
+ benchmark::ClobberMemory();
+ }
+}
+
+BENCHMARK(BM_TrackToken);
+
+BENCHMARK_MAIN();
diff --git a/media/psh_utils/include/psh_utils/AudioPowerManager.h b/media/psh_utils/include/psh_utils/AudioPowerManager.h
new file mode 100644
index 0000000..47dfdb2
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/AudioPowerManager.h
@@ -0,0 +1,84 @@
+/*
+ * 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 "PowerClientStats.h"
+#include "PowerStatsCollector.h"
+#include "Token.h"
+
+#include <android-base/thread_annotations.h>
+#include <audio_utils/linked_hash_map.h>
+#include <list>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android::media::psh_utils {
+
+/**
+ * AudioPowerManager is a singleton class that
+ * serializes the power, wakelock, and performance
+ * messages
+ */
+class AudioPowerManager {
+ friend class AudioClientToken;
+ friend class AudioThreadToken;
+ friend class AudioTrackToken;
+
+public:
+ static AudioPowerManager& getAudioPowerManager();
+
+ /**
+ * Returns a token indicating that a client is started.
+ * This is associated with an application.
+ */
+ std::unique_ptr<Token> startClient(pid_t pid, uid_t uid,
+ const std::string& additional);
+
+ /**
+ * Returns a token that represents a start instance for uid.
+ * This is typically associated with an AudioTrack / AudioRecord start.
+ */
+ std::unique_ptr<Token> startTrack(uid_t uid, const std::string& additional);
+
+ /**
+ * Returns a token that represents a wakelock for a Thread start.
+ */
+ std::unique_ptr<Token> startThread(
+ pid_t pid, const std::string& wakeLockName,
+ WakeFlag wakeFlag, const std::string& additional);
+
+ std::string toString() const;
+
+ static bool enabled();
+
+private:
+ // For AudioToken dtor only.
+ void clear_token_ptr(Token* token);
+ void stopClient(pid_t pid);
+
+ static constexpr size_t kHistory = 6;
+
+ mutable std::mutex mMutex;
+ std::unordered_set<Token *> mOutstandingTokens GUARDED_BY(mMutex);
+ std::unordered_map<pid_t, uid_t> mPidToUid GUARDED_BY(mMutex);
+ std::map<uid_t, std::shared_ptr<PowerClientStats>> mPowerClientStats GUARDED_BY(mMutex);
+ audio_utils::linked_hash_map<uid_t, std::shared_ptr<PowerClientStats>>
+ mHistoricalClients GUARDED_BY(mMutex);
+};
+
+} // namespace android::media::psh_utils
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/PowerClientStats.h b/media/psh_utils/include/psh_utils/PowerClientStats.h
new file mode 100644
index 0000000..6e27e41
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/PowerClientStats.h
@@ -0,0 +1,98 @@
+/*
+ * 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 "PowerStatsCollector.h"
+
+#include <android-base/thread_annotations.h>
+#include <audio_utils/CommandThread.h>
+#include <memory>
+#include <set>
+
+namespace android::media::psh_utils {
+
+/**
+ * PowerClientStats accumulates power measurements based on start and stop events.
+ *
+ * The start and stop events must eventually be matched, but several start events
+ * in a row only results in the power counted once.
+ */
+class PowerClientStats {
+public:
+ // A command thread is used for tokens to dispatch start and stop sequentially
+ // with less overhead to the caller.
+ static audio_utils::CommandThread& getCommandThread();
+
+ /**
+ * Creates an UID based power stat tracker.
+ *
+ * @param uid uid of app
+ * @param additional string to be printed out.
+ */
+ PowerClientStats(uid_t uid, const std::string& additional);
+
+ /**
+ * Starts power tracking.
+ */
+ void start(int64_t actualNs) EXCLUDES(mMutex);
+
+ /**
+ * Stops power tracking (saves the difference) - must be paired with start().
+ */
+ void stop(int64_t actualNs) EXCLUDES(mMutex);
+
+ /**
+ * Adds a pid to the App for string printing.
+ */
+ void addPid(pid_t pid) EXCLUDES(mMutex);
+
+ /**
+ * Removes the pid from the App for string printing.
+ */
+ size_t removePid(pid_t pid) EXCLUDES(mMutex);
+
+ /**
+ * Returns the string info.
+ * @param stats if true returns the stats.
+ * @return stat string.
+ */
+ std::string toString(bool stats = false, const std::string& prefix = {})
+ const EXCLUDES(mMutex);
+
+private:
+ // Snapshots are taken no more often than 500ms.
+ static constexpr int64_t kStatTimeToleranceNs = 500'000'000;
+
+ mutable std::mutex mMutex;
+ const uid_t mUid;
+ const std::string mName;
+ const std::string mAdditional;
+ std::set<pid_t> mPids GUARDED_BY(mMutex); // pids sharing same uid
+ int64_t mTokenCount GUARDED_BY(mMutex) = 0;
+ int64_t mStartNs GUARDED_BY(mMutex) = 0;
+ std::shared_ptr<const PowerStats> mStartStats GUARDED_BY(mMutex);
+
+ // Cumulative time while active: sum of deltas of (stop - start).
+ int64_t mCumulativeNs GUARDED_BY(mMutex) = 0;
+ // Cumulative stats while active: sum of deltas of (stop - start),
+ // where snapshots are quantized to ~500ms accuracy.
+ std::shared_ptr<PowerStats> mCumulativeStats GUARDED_BY(mMutex) =
+ std::make_shared<PowerStats>();
+};
+
+} // 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/include/psh_utils/ServiceSingleton.h b/media/psh_utils/include/psh_utils/ServiceSingleton.h
new file mode 100644
index 0000000..d0cd6d2
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/ServiceSingleton.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android-base/thread_annotations.h>
+#include <mutex>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+namespace android::media::psh_utils {
+
+struct DefaultServiceTraits {
+ static constexpr int64_t kThresholdRetryNs = 1'000'000'000;
+ static constexpr int64_t kMaxRetries = 5;
+ static constexpr const char* kServiceVersion = "/default";
+ static constexpr bool kShowLog = true;
+};
+
+template<typename Service, typename ServiceTraits = DefaultServiceTraits>
+std::shared_ptr<Service> getServiceSingleton() {
+ [[clang::no_destroy]] static constinit std::mutex m;
+ [[clang::no_destroy]] static constinit std::shared_ptr<Service> service GUARDED_BY(m);
+ static int64_t nextTryNs GUARDED_BY(m) = 0;
+ static int64_t tries GUARDED_BY(m) = 0;
+
+ std::lock_guard l(m);
+ if (service
+ || tries > ServiceTraits::kMaxRetries // try too many times
+ || systemTime(SYSTEM_TIME_BOOTTIME) < nextTryNs) { // try too frequently.
+ return service;
+ }
+
+ const auto serviceName = std::string(Service::descriptor)
+ .append(ServiceTraits::kServiceVersion);
+ service = Service::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
+
+ if (!service) {
+ // If failed, set a time limit before retry.
+ // No need to log an error, it is already done.
+ nextTryNs = systemTime(SYSTEM_TIME_BOOTTIME) + ServiceTraits::kThresholdRetryNs;
+ ALOGV_IF(ServiceTraits::kShowLog, "service:%s retries:%lld of %lld nextTryNs:%lld",
+ Service::descriptor, (long long)tries,
+ (long long)kMaxRetries, (long long)nextTryNs);
+ ++tries;
+ }
+
+ return service;
+}
+
+} // namespace android::media::psh_utils
diff --git a/media/psh_utils/include/psh_utils/Token.h b/media/psh_utils/include/psh_utils/Token.h
new file mode 100644
index 0000000..2b52d11
--- /dev/null
+++ b/media/psh_utils/include/psh_utils/Token.h
@@ -0,0 +1,62 @@
+
+/*
+ * 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 {
+
+class Token {
+public:
+ virtual ~Token() = default;
+ virtual std::string toString() const = 0;
+};
+
+// Client tokens (one per Audio Client PID)
+std::unique_ptr<Token> createAudioClientToken(pid_t pid, uid_t uid,
+ const std::string& additional = {});
+
+enum class WakeFlag {
+ kNone = 0,
+ kLowLatency = 1,
+ kLowPower = 2,
+};
+
+inline std::string toString(WakeFlag wakeFlag) {
+ std::string result;
+ for (const auto& [flag, name] : std::initializer_list<std::pair<WakeFlag, std::string>> {
+ {WakeFlag::kLowLatency, "kLowLatency"},
+ {WakeFlag::kLowPower, "kLowPower"},
+ }) {
+ if (static_cast<int>(flag) & static_cast<int>(wakeFlag)) {
+ if (!result.empty()) result.append("|");
+ result.append(name);
+ }
+ }
+ return result;
+}
+
+// Thread tokens (one per ThreadBase PID started).
+std::unique_ptr<Token> createAudioThreadToken(
+ pid_t pid, const std::string& wakeLockName,
+ WakeFlag wakeFlag = WakeFlag::kNone, const std::string& additional = {});
+
+// AudioTrack/AudioRecord tokens.
+std::unique_ptr<Token> createAudioTrackToken(uid_t uid, const std::string& additional = {});
+
+} // 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/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 1049d5e..8f9ee86 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -69,6 +69,6 @@
java_defaults {
name: "MediaBenchmark-defaults",
- min_sdk_version: "29",
- target_sdk_version: "30",
+ min_sdk_version: "35",
+ target_sdk_version: "35",
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
index 28c2654..bc0c16f 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
@@ -26,7 +26,7 @@
tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon"
tools:remove="android:appComponentFactory">
</application>
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
+ <uses-sdk android:minSdkVersion="35" android:targetSdkVersion="35"/>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.media.benchmark"
android:label="Benchmark Media Test"/>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index a2af701..87fc24c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -27,11 +27,11 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 30
+ compileSdkVersion 35
defaultConfig {
applicationId "com.android.media.benchmark"
minSdkVersion 29
- targetSdkVersion 30
+ targetSdkVersion 35
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index afd70a3..c68a990 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -118,7 +119,7 @@
}
@Test(timeout = PER_TEST_TIMEOUT_MS)
- public void testDecoder() throws IOException {
+ public void testDecoder() throws IOException, InterruptedException {
File inputFile = new File(mInputFilePath + mInputFile);
assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
inputFile.exists());
@@ -133,7 +134,7 @@
extractor.selectExtractorTrack(currentTrack);
MediaFormat format = extractor.getFormat(currentTrack);
String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ List<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
@@ -205,7 +206,7 @@
extractor.selectExtractorTrack(currentTrack);
MediaFormat format = extractor.getFormat(currentTrack);
String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ List<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
for (String codecName : mediaCodecs) {
Log.i("Test: %s\n", mInputFile);
Native nativeDecoder = new Native();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 4202732..4ce5214 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -50,6 +50,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -150,7 +151,7 @@
}
@BeforeClass
- public static void prepareInput() throws IOException {
+ public static void prepareInput() throws IOException, InterruptedException {
mDecodedFileFullHd = new File(mFileDirPath + DECODE_FULLHD_UNPACKED);
int status = decodeFile(mInputFilePath + DECODE_FULLHD_INPUT, mDecodedFileFullHd);
@@ -165,7 +166,8 @@
assertEquals("Decoder returned error " + status, 0, status);
}
- private static int decodeFile(String inputFileName, File outputDecodeFile) throws IOException {
+ private static int decodeFile(String inputFileName, File outputDecodeFile)
+ throws IOException, InterruptedException {
int status = -1;
File inputFile = new File(inputFileName);
assertTrue("Cannot open input file " + inputFileName, inputFile.exists());
@@ -220,7 +222,7 @@
int status;
int frameSize;
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
+ List<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
assertTrue("No suitable codecs found for mimetype: " + mMime, (mediaCodecs.size() > 0));
Boolean[] encodeMode = {true, false};
// Encoding the decoded input file
@@ -297,7 +299,7 @@
@Test(timeout = PER_TEST_TIMEOUT_MS)
public void testNativeEncoder() {
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
+ List<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
assertTrue("No suitable codecs found for mimetype: " + mMime, (mediaCodecs.size() > 0));
for (String codecName : mediaCodecs) {
Native nativeEncoder = new Native();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index 1e10b37..f223242 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -5,6 +5,7 @@
import android.media.MediaFormat;
import android.os.Build;
import java.util.ArrayList;
+import java.util.List;
public class CodecUtils {
private CodecUtils() {}
@@ -15,7 +16,7 @@
* @param isEncoder Specifies encoder or decoder
* @return ArrayList of codec names
*/
- public static ArrayList<String> selectCodecs(String mimeType, boolean isEncoder) {
+ public static List<String> selectCodecs(String mimeType, boolean isEncoder) {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
ArrayList<String> supportedCodecs = new ArrayList<>();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index e947ef6..e9b337d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -28,7 +28,10 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import com.android.media.benchmark.library.IBufferXfer;
@@ -37,28 +40,28 @@
private static final boolean DEBUG = false;
private static final int kQueueDequeueTimeoutUs = 1000;
- private final Object mLock = new Object();
- private MediaCodec mCodec;
- private Surface mSurface = null;
- private boolean mRender = false;
- private ArrayList<BufferInfo> mInputBufferInfo;
- private Stats mStats;
- private String mMime;
+ protected final Object mLock = new Object();
+ protected MediaCodec mCodec;
+ protected Surface mSurface = null;
+ protected boolean mRender = false;
+ protected ArrayList<BufferInfo> mInputBufferInfo;
+ protected Stats mStats;
+ protected String mMime;
- private boolean mSawInputEOS;
- private boolean mSawOutputEOS;
- private boolean mSignalledError;
+ protected boolean mSawInputEOS;
+ protected boolean mSawOutputEOS;
+ protected boolean mSignalledError;
- private int mNumInFramesProvided;
- private int mNumInFramesRequired;
+ protected int mNumInFramesProvided;
+ protected int mNumInFramesRequired;
- private int mNumOutputFrame;
- private int mIndex;
+ protected int mNumOutputFrame;
+ protected int mIndex;
- private ArrayList<ByteBuffer> mInputBuffer;
- private FileOutputStream mOutputStream;
- private FrameReleaseQueue mFrameReleaseQueue = null;
- private IBufferXfer.ISendBuffer mIBufferSend = null;
+ protected ArrayList<ByteBuffer> mInputBuffer;
+ protected FileOutputStream mOutputStream;
+ protected FrameReleaseQueue mFrameReleaseQueue = null;
+ protected IBufferXfer.ISendBuffer mIBufferSend = null;
/* success for decoder */
public static final int DECODE_SUCCESS = 0;
@@ -71,7 +74,9 @@
@Override
public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
MediaCodec codec = (MediaCodec)info.obj;
- codec.releaseOutputBuffer(info.idx, mRender);
+ if (info.isComplete) {
+ codec.releaseOutputBuffer(info.idx, mRender);
+ }
return true;
}
@Override
@@ -133,6 +138,49 @@
}
}
+ protected void setCallback(MediaCodec codec) {
+ codec.setCallback(new MediaCodec.Callback() {
+ @Override
+ public void onInputBufferAvailable(
+ @NonNull MediaCodec mediaCodec, int inputBufferId) {
+ try {
+ mStats.addInputTime();
+ onInputAvailable(inputBufferId, mediaCodec);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ @Override
+ public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+ int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+ mStats.addOutputTime();
+ onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+ if (mSawOutputEOS) {
+ synchronized (mLock) { mLock.notify(); }
+ }
+ }
+
+ @Override
+ public void onOutputFormatChanged(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+ Log.i(TAG, "Output format changed. Format: " + format.toString());
+ }
+
+ @Override
+ public void onError(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+ mSignalledError = true;
+ Log.e(TAG, "Codec Error: " + e.toString());
+ e.printStackTrace();
+ synchronized (mLock) { mLock.notify(); }
+ }
+ });
+
+
+ }
+
/**
* Decodes the given input buffer,
* provided valid list of buffer info and format are passed as inputs.
@@ -146,9 +194,10 @@
* DECODE_CREATE_ERROR for decoder not created
* @throws IOException if the codec cannot be created.
*/
- public int decode(@NonNull ArrayList<ByteBuffer> inputBuffer,
- @NonNull ArrayList<BufferInfo> inputBufferInfo, final boolean asyncMode,
- @NonNull MediaFormat format, String codecName) throws IOException {
+ public int decode(@NonNull List<ByteBuffer> inputBuffer,
+ @NonNull List<BufferInfo> inputBufferInfo, final boolean asyncMode,
+ @NonNull MediaFormat format, String codecName)
+ throws IOException, InterruptedException {
mInputBuffer = new ArrayList<>(inputBuffer.size());
mInputBuffer.addAll(inputBuffer);
mInputBufferInfo = new ArrayList<>(inputBufferInfo.size());
@@ -170,64 +219,34 @@
mFrameReleaseQueue.setMediaCodec(mCodec);
mFrameReleaseQueue.setMime(mMime);
}
+
if (asyncMode) {
- mCodec.setCallback(new MediaCodec.Callback() {
- @Override
- public void onInputBufferAvailable(
- @NonNull MediaCodec mediaCodec, int inputBufferId) {
- try {
- mStats.addInputTime();
- onInputAvailable(inputBufferId, mediaCodec);
- } catch (Exception e) {
- e.printStackTrace();
- Log.e(TAG, e.toString());
- }
- }
-
- @Override
- public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
- int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
- mStats.addOutputTime();
- onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
- if (mSawOutputEOS) {
- synchronized (mLock) { mLock.notify(); }
- }
- }
-
- @Override
- public void onOutputFormatChanged(
- @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
- Log.i(TAG, "Output format changed. Format: " + format.toString());
- }
-
- @Override
- public void onError(
- @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
- mSignalledError = true;
- Log.e(TAG, "Codec Error: " + e.toString());
- e.printStackTrace();
- synchronized (mLock) { mLock.notify(); }
- }
- });
+ setCallback(mCodec);
}
int isEncoder = 0;
if (DEBUG) {
Log.d(TAG, "Media Format : " + format.toString());
}
mCodec.configure(format, mSurface, null, isEncoder);
+
mCodec.start();
- Log.i(TAG, "Codec started ");
+ Log.i(TAG, "Codec started async mode ? " + asyncMode);
long eTime = mStats.getCurTime();
mStats.setInitTime(mStats.getTimeDiff(sTime, eTime));
mStats.setStartTime();
if (asyncMode) {
try {
- synchronized (mLock) { mLock.wait(); }
- if (mSignalledError) {
- return DECODE_DECODER_ERROR;
+ synchronized (mLock) {
+ while (!mSawOutputEOS && !mSignalledError) {
+ mLock.wait();
+ }
+ if (mSignalledError) {
+ return DECODE_DECODER_ERROR;
+ }
}
} catch (InterruptedException e) {
- e.printStackTrace();
+ Log.e(TAG, "Error in waiting");
+ throw e;
}
} else {
while (!mSawOutputEOS && !mSignalledError) {
@@ -319,7 +338,7 @@
return mCodec.getOutputFormat();
}
- private void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
+ protected void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
if (inputBufferId >= 0) {
ByteBuffer inputCodecBuffer = mediaCodec.getInputBuffer(inputBufferId);
BufferInfo bufInfo;
@@ -351,7 +370,7 @@
}
}
- private void onOutputAvailable(
+ protected void onOutputAvailable(
MediaCodec mediaCodec, int outputBufferId, BufferInfo outputBufferInfo) {
if (mSawOutputEOS || outputBufferId < 0) {
return;
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 63d17ee..3aa38d1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -200,7 +200,8 @@
* @throws IOException If the codec cannot be created.
*/
public int encode(String codecName, MediaFormat encodeFormat, String mime, int frameRate,
- int sampleRate, int frameSize, boolean asyncMode) throws IOException {
+ int sampleRate, int frameSize, boolean asyncMode)
+ throws IOException, InterruptedException {
mInputBufferSize = (mInputStream != null) ? mInputStream.getChannel().size() : 0;
mOffset = 0;
mFrameRate = frameRate;
@@ -275,12 +276,16 @@
mStats.setStartTime();
if (asyncMode) {
try {
- synchronized (mLock) { mLock.wait(); }
- if (mSignalledError) {
- return ENCODE_ENCODER_ERROR;
+ synchronized (mLock) {
+ while (!mSawOutputEOS && !mSignalledError) {
+ mLock.wait();
+ }
+ if (mSignalledError) {
+ return ENCODE_ENCODER_ERROR;
+ }
}
} catch (InterruptedException e) {
- e.printStackTrace();
+ throw e;
}
} else {
while (!mSawOutputEOS && !mSignalledError) {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
index f3024e7..1c0f810 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
@@ -76,6 +76,21 @@
public MediaCodec.BufferInfo getBufferInfo() { return this.mBufferInfo; }
/**
+ * Returns the maximum sample size for the selected track
+ * @return max sample size in the given track
+ */
+ public int getMaxSampleSize() {
+ int size = 0;
+ int maxSampleSize = 0;
+ while ((size = (int) mExtractor.getSampleSize()) != -1) {
+ maxSampleSize = Math.max(maxSampleSize, size);
+ mExtractor.advance();
+ }
+ mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ return maxSampleSize;
+ }
+
+ /**
* Returns the duration of the sample
*/
public long getClipDuration() { return this.mDurationUs; }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
index 90731ed..20a2573 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -185,7 +185,7 @@
try {
mCodec.releaseOutputBuffer(curFrameInfo.bufferId, actualRender);
} catch (IllegalStateException e) {
- e.printStackTrace();
+ throw(e);
}
});
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
index a75962c..c97a35c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -28,6 +28,7 @@
public Object obj;
int flag;
int bytesRead;
+ boolean isComplete = true;
long presentationTimeUs;
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
index ab55df5..3e6cee1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -16,9 +16,9 @@
package com.android.media.benchmark.library;
-/**
+/*
* Class that manages the buffer senders
-*/
+ */
import com.android.media.benchmark.library.IBufferXfer;
import java.util.ArrayDeque;
import android.util.Log;
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
new file mode 100644
index 0000000..cb92f06
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+package com.android.media.benchmark.library;
+
+import android.view.Surface;
+
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.media.benchmark.library.IBufferXfer;
+import com.android.media.benchmark.library.Decoder;
+
+public class MultiAccessUnitDecoder extends Decoder {
+ private static final String TAG = "MultiAccessUnitDecoder";
+ private static final boolean DEBUG = false;
+ private final ArrayDeque<BufferInfo> mInputInfos = new ArrayDeque<>();
+
+ @Override
+ public void setCallback(MediaCodec codec) {
+ mCodec.setCallback(new MediaCodec.Callback() {
+ boolean isUsingLargeFrameMode = false;
+
+ @Override
+ public void onInputBufferAvailable(
+ @NonNull MediaCodec mediaCodec, int inputBufferId) {
+ try {
+ mStats.addInputTime();
+ if (isUsingLargeFrameMode) {
+ onInputsAvailable(inputBufferId, mediaCodec);
+ } else {
+ onInputAvailable(inputBufferId, mediaCodec);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ @Override
+ public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+ int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+ mStats.addOutputTime();
+ onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+ if (mSawOutputEOS) {
+ synchronized (mLock) { mLock.notify(); }
+ }
+ }
+
+ @Override
+ public void onOutputBuffersAvailable(
+ @NonNull MediaCodec mediaCodec,
+ int outputBufferId, @NonNull ArrayDeque<BufferInfo> infos) {
+ int i = 0;
+ while(i++ < infos.size()) {
+ mStats.addOutputTime();
+ }
+ onOutputsAvailable(mediaCodec, outputBufferId, infos);
+ if (mSawOutputEOS) {
+ synchronized (mLock) { mLock.notify(); }
+ }
+ }
+
+ @Override
+ public void onOutputFormatChanged(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+ Log.i(TAG, "Output format changed. Format: " + format.toString());
+ final int maxOutputSize = format.getNumber(
+ MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+ isUsingLargeFrameMode = (maxOutputSize > 0);
+ }
+
+ @Override
+ public void onError(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+ mSignalledError = true;
+ Log.e(TAG, "Codec Error: " + e.toString());
+ e.printStackTrace();
+ synchronized (mLock) { mLock.notify(); }
+ }
+ });
+
+ }
+ /**
+ * Decodes the given input buffer,
+ * provided valid list of buffer info and format are passed as inputs.
+ *
+ * @param inputBuffer Decode the provided list of ByteBuffers
+ * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+ * @param asyncMode Will run on async implementation if true
+ * @param format For creating the decoder if codec name is empty and configuring it
+ * @param codecName Will create the decoder with codecName
+ * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+ * DECODE_CREATE_ERROR for decoder not created
+ * @throws IOException if the codec cannot be created.
+ */
+ @Override
+ public int decode(@NonNull List<ByteBuffer> inputBuffer,
+ @NonNull List<BufferInfo> inputBufferInfo, final boolean asyncMode,
+ @NonNull MediaFormat format, String codecName)
+ throws IOException, InterruptedException {
+ return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+ }
+
+ private void onInputsAvailable(int inputBufferId, MediaCodec mediaCodec) {
+ if (inputBufferId >= 0) {
+ ByteBuffer inputCodecBuffer = mediaCodec.getInputBuffer(inputBufferId);
+ BufferInfo bufInfo;
+ mInputInfos.clear();
+ int offset = 0;
+ while (mNumInFramesProvided < mNumInFramesRequired) {
+ bufInfo = mInputBufferInfo.get(mIndex);
+ mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (inputCodecBuffer.remaining() < bufInfo.size) {
+ if (mInputInfos.size() == 0) {
+ Log.d(TAG, "SampleSize " + inputCodecBuffer.remaining()
+ + "greater than MediaCodec Buffer size " + bufInfo.size);
+ }
+ break;
+ }
+ inputCodecBuffer.put(mInputBuffer.get(mIndex).array());
+ bufInfo.offset = offset; offset += bufInfo.size;
+ mInputInfos.add(bufInfo);
+ mNumInFramesProvided++;
+ mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+ }
+ if (mNumInFramesProvided >= mNumInFramesRequired) {
+ mIndex = mInputBufferInfo.size() - 1;
+ bufInfo = mInputBufferInfo.get(mIndex);
+ if (inputCodecBuffer.remaining() > bufInfo.size) {
+ if ((bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0) {
+ Log.e(TAG, "Error in EOS flag for Decoder");
+ }
+ mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ inputCodecBuffer.put(mInputBuffer.get(mIndex).array());
+ bufInfo.offset = offset; offset += bufInfo.size;
+ mInputInfos.add(bufInfo);
+ mNumInFramesProvided++;
+ }
+ }
+ if (mInputInfos.size() == 0) {
+ Log.d(TAG, " No inputs to queue");
+ } else {
+ mStats.addFrameSize(offset);
+ mediaCodec.queueInputBuffers(inputBufferId, mInputInfos);
+ }
+ }
+ }
+
+ private void onOutputsAvailable(MediaCodec mc, int outputBufferId,
+ ArrayDeque<BufferInfo> infos) {
+ if (mSawOutputEOS || outputBufferId < 0) {
+ return;
+ }
+ Iterator<BufferInfo> iter = infos.iterator();
+ while (iter.hasNext()) {
+ BufferInfo bufferInfo = iter.next();
+ mNumOutputFrame++;
+ if (DEBUG) {
+ Log.d(TAG,
+ "In OutputBufferAvailable ,"
+ + " output frame number = " + mNumOutputFrame
+ + " timestamp = " + bufferInfo.presentationTimeUs
+ + " size = " + bufferInfo.size);
+ }
+ if (mIBufferSend != null) {
+ IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+ info.buf = mc.getOutputBuffer(outputBufferId);
+ info.idx = outputBufferId;
+ info.obj = mc;
+ info.bytesRead = bufferInfo.size;
+ info.presentationTimeUs = bufferInfo.presentationTimeUs;
+ info.flag = bufferInfo.flags;
+ info.isComplete = iter.hasNext() ? false : true;
+ mIBufferSend.sendBuffer(this, info);
+ }
+ mSawOutputEOS |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ }
+ if (mOutputStream != null) {
+ try {
+ ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
+ byte[] bytesOutput = new byte[outputBuffer.remaining()];
+ outputBuffer.get(bytesOutput);
+ mOutputStream.write(bytesOutput);
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+ }
+ }
+ if (mIBufferSend == null) {
+ mc.releaseOutputBuffer(outputBufferId, mRender);
+ }
+ if (mSawOutputEOS) {
+ Log.i(TAG, "Large frame - saw output EOS");
+ }
+ // we don't support frame release queue for large audio frame
+ }
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
index 340b539..786290d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.List;
public class Muxer {
private Stats mStats;
@@ -61,8 +62,8 @@
* @param inputBufferInfo Buffer information related to these samples
* @return Returns Status as 0 if write operation is successful, -1 otherwise
*/
- public int mux(int trackIndex, ArrayList<ByteBuffer> inputExtractedBuffer,
- ArrayList<MediaCodec.BufferInfo> inputBufferInfo) {
+ public int mux(int trackIndex, List<ByteBuffer> inputExtractedBuffer,
+ List<MediaCodec.BufferInfo> inputBufferInfo) {
mStats.setStartTime();
for (int sampleCount = 0; sampleCount < inputExtractedBuffer.size(); sampleCount++) {
try {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
index 0ebf798..17de1e7 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
@@ -23,6 +23,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
/**
* Measures Performance.
@@ -88,9 +89,9 @@
public long getStartTime() { return mStartTimeNs; }
- public ArrayList<Long> getOutputTimers() { return mOutputTimer; }
+ public List<Long> getOutputTimers() { return mOutputTimer; }
- public ArrayList<Long> getInputTimers() { return mInputTimer; }
+ public List<Long> getInputTimers() { return mInputTimer; }
public long getTimeDiff(long sTime, long eTime) { return (eTime - sTime); }
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 7bec8cf..e13f8f7 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -41,6 +41,10 @@
namespace android {
+namespace {
+constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED;
+}
+
using content::AttributionSourceState;
static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
@@ -115,7 +119,7 @@
return std::optional<AttributionSourceState>{myAttributionSource};
}
- static bool checkRecordingInternal(const AttributionSourceState &attributionSource,
+ static int checkRecordingInternal(const AttributionSourceState &attributionSource,
const uint32_t virtualDeviceId,
const String16 &msg, bool start, audio_source_t source) {
// Okay to not track in app ops as audio server or media server is us and if
@@ -138,15 +142,15 @@
const int32_t attributedOpCode = getOpForSource(source);
permission::PermissionChecker permissionChecker;
- bool permitted = false;
+ int permitted;
if (start) {
- permitted = (permissionChecker.checkPermissionForStartDataDeliveryFromDatasource(
+ permitted = permissionChecker.checkPermissionForStartDataDeliveryFromDatasource(
sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
- attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED);
+ attributedOpCode);
} else {
- permitted = (permissionChecker.checkPermissionForPreflightFromDatasource(
+ permitted = permissionChecker.checkPermissionForPreflightFromDatasource(
sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
- attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED);
+ attributedOpCode);
}
return permitted;
@@ -156,17 +160,17 @@
bool recordingAllowed(const AttributionSourceState &attributionSource, audio_source_t source) {
return checkRecordingInternal(attributionSource, DEVICE_ID_DEFAULT, String16(), /*start*/ false,
- source);
+ source) != PERMISSION_HARD_DENIED;
}
bool recordingAllowed(const AttributionSourceState &attributionSource,
const uint32_t virtualDeviceId,
audio_source_t source) {
return checkRecordingInternal(attributionSource, virtualDeviceId,
- String16(), /*start*/ false, source);
+ String16(), /*start*/ false, source) != PERMISSION_HARD_DENIED;
}
-bool startRecording(const AttributionSourceState& attributionSource,
+int startRecording(const AttributionSourceState& attributionSource,
const uint32_t virtualDeviceId,
const String16& msg,
audio_source_t source) {
@@ -286,7 +290,7 @@
bool modifyAudioRoutingAllowed(const AttributionSourceState& attributionSource) {
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
- if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
+ if (isAudioServerUid(uid)) return true;
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkPermission(sModifyAudioRouting, pid, uid);
if (!ok) ALOGE("%s(): android.permission.MODIFY_AUDIO_ROUTING denied for uid %d",
@@ -301,7 +305,7 @@
bool modifyDefaultAudioEffectsAllowed(const AttributionSourceState& attributionSource) {
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
- if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
+ if (isAudioServerUid(uid)) return true;
static const String16 sModifyDefaultAudioEffectsAllowed(
"android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
@@ -473,35 +477,38 @@
}
}
+namespace mediautils {
+
// How long we hold info before we re-fetch it (24 hours) if we found it previously.
static constexpr nsecs_t INFO_EXPIRATION_NS = 24 * 60 * 60 * NANOS_PER_SECOND;
// Maximum info records we retain before clearing everything.
static constexpr size_t INFO_CACHE_MAX = 1000;
// The original code is from MediaMetricsService.cpp.
-mediautils::UidInfo::Info mediautils::UidInfo::getInfo(uid_t uid)
+std::shared_ptr<const UidInfo::Info> UidInfo::getCachedInfo(uid_t uid)
{
+ std::shared_ptr<const UidInfo::Info> info;
+
const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- struct mediautils::UidInfo::Info info;
{
std::lock_guard _l(mLock);
auto it = mInfoMap.find(uid);
if (it != mInfoMap.end()) {
info = it->second;
ALOGV("%s: uid %d expiration %lld now %lld",
- __func__, uid, (long long)info.expirationNs, (long long)now);
- if (info.expirationNs <= now) {
+ __func__, uid, (long long)info->expirationNs, (long long)now);
+ if (info->expirationNs <= now) {
// purge the stale entry and fall into re-fetching
ALOGV("%s: entry for uid %d expired, now %lld",
__func__, uid, (long long)now);
mInfoMap.erase(it);
- info.uid = (uid_t)-1; // this is always fully overwritten
+ info.reset(); // force refetch
}
}
}
// if we did not find it in our map, look it up
- if (info.uid == (uid_t)(-1)) {
+ if (!info) {
sp<IServiceManager> sm = defaultServiceManager();
sp<content::pm::IPackageManagerNative> package_mgr;
if (sm.get() == nullptr) {
@@ -586,17 +593,30 @@
// first clear if we have too many cached elements. This would be rare.
if (mInfoMap.size() >= INFO_CACHE_MAX) mInfoMap.clear();
- // always overwrite
- info.uid = uid;
- info.package = std::move(pkg);
- info.installer = std::move(installer);
- info.versionCode = versionCode;
- info.expirationNs = now + (notFound ? 0 : INFO_EXPIRATION_NS);
+ info = std::make_shared<const UidInfo::Info>(
+ uid,
+ std::move(pkg),
+ std::move(installer),
+ versionCode,
+ now + (notFound ? 0 : INFO_EXPIRATION_NS));
ALOGV("%s: adding uid %d package '%s' expirationNs: %lld",
- __func__, uid, info.package.c_str(), (long long)info.expirationNs);
+ __func__, uid, info->package.c_str(), (long long)info->expirationNs);
mInfoMap[uid] = info;
}
return info;
}
+/* static */
+UidInfo& UidInfo::getUidInfo() {
+ [[clang::no_destroy]] static UidInfo uidInfo;
+ return uidInfo;
+}
+
+/* static */
+std::shared_ptr<const UidInfo::Info> UidInfo::getInfo(uid_t uid) {
+ return UidInfo::getUidInfo().getCachedInfo(uid);
+}
+
+} // namespace mediautils
+
} // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e0fabfd..2631469 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <android/content/pm/IPackageManagerNative.h>
+#include <android-base/thread_annotations.h>
#include <binder/IMemory.h>
#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
@@ -91,7 +92,7 @@
bool recordingAllowed(const AttributionSourceState &attributionSource,
uint32_t virtualDeviceId,
audio_source_t source);
-bool startRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
+int startRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
const String16& msg, audio_source_t source);
void finishRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
audio_source_t source);
@@ -167,12 +168,18 @@
*
* \param uid is the uid of the app or service.
*/
- Info getInfo(uid_t uid);
+ std::shared_ptr<const Info> getCachedInfo(uid_t uid);
+
+ /* return a singleton */
+ static UidInfo& getUidInfo();
+
+ /* returns a non-null pointer to a const Info struct */
+ static std::shared_ptr<const Info> getInfo(uid_t uid);
private:
std::mutex mLock;
// TODO: use concurrent hashmap with striped lock.
- std::unordered_map<uid_t, Info> mInfoMap; // GUARDED_BY(mLock)
+ std::unordered_map<uid_t, std::shared_ptr<const Info>> mInfoMap GUARDED_BY(mLock);
};
} // namespace mediautils
diff --git a/media/utils/include/mediautils/jthread.h b/media/utils/include/mediautils/jthread.h
new file mode 100644
index 0000000..17532a4
--- /dev/null
+++ b/media/utils/include/mediautils/jthread.h
@@ -0,0 +1,92 @@
+/*
+ * 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 <atomic>
+#include <thread>
+#include <utility>
+
+namespace android::mediautils {
+
+namespace impl {
+class stop_source;
+/**
+ * Const view on stop source, which the running thread uses and an interface
+ * for cancellation.
+ */
+class stop_token {
+ public:
+ stop_token(const stop_source& source) : stop_source_(source) {}
+ bool stop_requested() const;
+
+ private:
+ const stop_source& stop_source_;
+};
+
+class stop_source {
+ public:
+ stop_token get_token() { return stop_token{*this}; }
+ bool stop_requested() const { return cancellation_signal_.load(); }
+ bool request_stop() {
+ auto f = false;
+ return cancellation_signal_.compare_exchange_strong(f, true);
+ }
+
+ private:
+ std::atomic_bool cancellation_signal_ = false;
+};
+
+inline bool stop_token::stop_requested() const {
+ return stop_source_.stop_requested();
+}
+} // namespace impl
+
+using stop_token = impl::stop_token;
+/**
+ * Just a jthread, since std::jthread is still experimental in our toolchain.
+ * Implements a subset of essential functionality (co-op cancellation and join on dtor).
+ * If jthread gets picked up, usage can be cut over.
+ */
+class jthread {
+ public:
+ /**
+ * Construct/launch and thread with a callable which consumes a stop_token.
+ * The callable must be cooperatively cancellable via stop_token::stop_requested(), and will be
+ * automatically stopped then joined on destruction.
+ * Example:
+ * jthread([](stop_token stok) {
+ * while(!stok.stop_requested) {
+ * // do work
+ * }
+ * }
+ */
+ template <typename F>
+ jthread(F&& f) : stop_source_{}, thread_{std::forward<F>(f), stop_source_.get_token()} {}
+
+ ~jthread() {
+ stop_source_.request_stop();
+ thread_.join();
+ }
+
+ bool request_stop() { return stop_source_.request_stop(); }
+
+ private:
+ // order matters
+ impl::stop_source stop_source_;
+ std::thread thread_;
+};
+} // namespace android::mediautils
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index a68569a..ff11b42 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -237,3 +237,11 @@
"shared_memory_allocator_tests.cpp",
],
}
+
+cc_test {
+ name: "jthread_tests",
+ defaults: ["libmediautils_tests_defaults"],
+ srcs: [
+ "jthread_tests.cpp",
+ ],
+}
diff --git a/media/utils/tests/jthread_tests.cpp b/media/utils/tests/jthread_tests.cpp
new file mode 100644
index 0000000..ed77c27
--- /dev/null
+++ b/media/utils/tests/jthread_tests.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 "jthread_tests"
+
+#include <mediautils/jthread.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+
+using namespace android::mediautils;
+
+namespace {
+TEST(jthread_tests, dtor) {
+ std::atomic_int x = 0;
+ std::atomic_bool is_stopped = false;
+ {
+ auto jt = jthread([&](stop_token stok) {
+ while (!stok.stop_requested()) {
+ if (x.load() < std::numeric_limits<int>::max())
+ x++;
+ }
+ is_stopped = true;
+ });
+ while (x.load() < 1000)
+ ;
+ }
+ // Check we triggered a stop on dtor
+ ASSERT_TRUE(is_stopped.load());
+ // Check we actually ran
+ ASSERT_GE(x.load(), 1000);
+}
+TEST(jthread_tests, request_stop) {
+ std::atomic_int x = 0;
+ std::atomic_bool is_stopped = false;
+ auto jt = jthread([&](stop_token stok) {
+ while (!stok.stop_requested()) {
+ if (x.load() < std::numeric_limits<int>::max())
+ x++;
+ }
+ is_stopped = true;
+ });
+ while (x.load() < 1000)
+ ;
+ // request stop manually
+ ASSERT_TRUE(jt.request_stop());
+ // busy loop till thread acks
+ while (!is_stopped.load())
+ ;
+ // Check we triggered a stop on dtor
+ ASSERT_TRUE(is_stopped.load());
+ // Check we actually ran
+ ASSERT_GE(x.load(), 1000);
+}
+
+} // namespace
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index bf2915a..01bde42 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -152,6 +152,7 @@
"audiopermissioncontroller",
"av-types-aidl-cpp",
"com.android.media.audio-aconfig-cc",
+ "com.android.media.audioserver-aconfig-cc",
"effect-aidl-cpp",
"libactivitymanager_aidl",
"libaudioclient",
@@ -233,6 +234,22 @@
"libpermission",
],
+ export_static_lib_headers: [
+ "libpshutils",
+ ],
+
+ shared: {
+ static_libs: [
+ "libpshutils",
+ ],
+ },
+
+ static: {
+ whole_static_libs: [
+ "libpshutils",
+ ],
+ },
+
cflags: [
"-Wall",
"-Werror",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 282f3fa..4e993c6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
// Define AUDIO_ARRAYS_STATIC_CHECK to check all audio arrays are correct
#define AUDIO_ARRAYS_STATIC_CHECK 1
@@ -39,13 +41,17 @@
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
+#include <com_android_media_audio.h>
#include <com_android_media_audioserver.h>
#include <media/AidlConversion.h>
#include <media/AudioParameter.h>
#include <media/AudioValidator.h>
#include <media/IMediaLogService.h>
+#include <media/IPermissionProvider.h>
#include <media/MediaMetricsItem.h>
+#include <media/NativePermissionController.h>
#include <media/TypeConverter.h>
+#include <media/ValidatedAttributionSourceState.h>
#include <mediautils/BatteryNotifier.h>
#include <mediautils/MemoryLeakTrackUtil.h>
#include <mediautils/MethodStatistics.h>
@@ -81,12 +87,17 @@
namespace android {
using ::android::base::StringPrintf;
+using aidl_utils::statusTFromBinderStatus;
using media::IEffectClient;
using media::audio::common::AudioMMapPolicyInfo;
using media::audio::common::AudioMMapPolicyType;
using media::audio::common::AudioMode;
using android::content::AttributionSourceState;
using android::detail::AudioHalVersionInfo;
+using com::android::media::permission::INativePermissionController;
+using com::android::media::permission::IPermissionProvider;
+using com::android::media::permission::NativePermissionController;
+using com::android::media::permission::ValidatedAttributionSourceState;
static const AudioHalVersionInfo kMaxAAudioPropertyDeviceHalVersion =
AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 7, 1);
@@ -114,6 +125,52 @@
}
}
+static error::BinderResult<ValidatedAttributionSourceState>
+validateAttributionFromContextOrTrustedCaller(AttributionSourceState attr,
+ const IPermissionProvider& provider) {
+ const auto callingUid = IPCThreadState::self()->getCallingUid();
+ // We trust the following UIDs to appropriate validated identities above us
+ if (isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
+ // Legacy paths may not properly populate package name, so we attempt to handle.
+ if (!attr.packageName.has_value() || attr.packageName.value() == "") {
+ ALOGW("Trusted client %d provided attr with missing package name" , callingUid);
+ attr.packageName = VALUE_OR_RETURN(provider.getPackagesForUid(callingUid))[0];
+ }
+ // Behavior change: In the case of delegation, if pid is invalid,
+ // filling it in with the callingPid will cause a mismatch between the
+ // pid and the uid in the attribution, which is error-prone.
+ // Instead, assert that the pid from a trusted source is valid
+ if (attr.pid == -1) {
+ if (callingUid != static_cast<uid_t>(attr.uid)) {
+ return error::unexpectedExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
+ "validateAttribution: Invalid pid from delegating trusted source");
+ } else {
+ // Legacy handling for trusted clients which may not fill pid correctly
+ attr.pid = IPCThreadState::self()->getCallingPid();
+ }
+ }
+ return ValidatedAttributionSourceState::createFromTrustedSource(std::move(attr));
+ } else {
+ // Behavior change: Populate pid with callingPid unconditionally. Previously, we
+ // allowed caller provided pid, if uid matched calling context, but this is error-prone
+ // since it allows mismatching uid/pid
+ return ValidatedAttributionSourceState::createFromBinderContext(std::move(attr), provider);
+ }
+}
+
+#define VALUE_OR_RETURN_CONVERTED(exp) \
+ ({ \
+ auto _tmp = (exp); \
+ if (!_tmp.ok()) { \
+ ALOGE("Function: %s Line: %d Failed result (%s)", __FUNCTION__, __LINE__, \
+ errorToString(_tmp.error()).c_str()); \
+ return statusTFromBinderStatus(_tmp.error()); \
+ } \
+ std::move(_tmp.value()); \
+ })
+
+
+
// Creates association between Binder code to name for IAudioFlinger.
#define IAUDIOFLINGER_BINDER_METHOD_MACRO_LIST \
BINDER_METHOD_ENTRY(createTrack) \
@@ -128,8 +185,7 @@
BINDER_METHOD_ENTRY(masterMute) \
BINDER_METHOD_ENTRY(setStreamVolume) \
BINDER_METHOD_ENTRY(setStreamMute) \
-BINDER_METHOD_ENTRY(streamVolume) \
-BINDER_METHOD_ENTRY(streamMute) \
+BINDER_METHOD_ENTRY(setPortsVolume) \
BINDER_METHOD_ENTRY(setMode) \
BINDER_METHOD_ENTRY(setMicMute) \
BINDER_METHOD_ENTRY(getMicMute) \
@@ -515,30 +571,42 @@
audio_attributes_t localAttr = *attr;
// TODO b/182392553: refactor or make clearer
- pid_t clientPid =
- VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(client.attributionSource.pid));
- bool updatePid = (clientPid == (pid_t)-1);
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ AttributionSourceState adjAttributionSource;
+ if (!com::android::media::audio::audioserver_permissions()) {
+ pid_t clientPid =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(client.attributionSource.pid));
+ bool updatePid = (clientPid == (pid_t)-1);
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- AttributionSourceState adjAttributionSource = client.attributionSource;
- if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
- uid_t clientUid =
- VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(client.attributionSource.uid));
- ALOGW_IF(clientUid != callingUid,
- "%s uid %d tried to pass itself off as %d",
- __FUNCTION__, callingUid, clientUid);
- adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
- updatePid = true;
- }
- if (updatePid) {
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
- "%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, clientPid);
- adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
- }
- adjAttributionSource = afutils::checkAttributionSourcePackage(
+ adjAttributionSource = client.attributionSource;
+ if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
+ uid_t clientUid =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(client.attributionSource.uid));
+ ALOGW_IF(clientUid != callingUid,
+ "%s uid %d tried to pass itself off as %d",
+ __FUNCTION__, callingUid, clientUid);
+ adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_uid_t_int32_t(callingUid));
+ updatePid = true;
+ }
+ if (updatePid) {
+ const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
+ "%s uid %d pid %d tried to pass itself off as pid %d",
+ __func__, callingUid, callingPid, clientPid);
+ adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_pid_t_int32_t(callingPid));
+ }
+ adjAttributionSource = afutils::checkAttributionSourcePackage(
adjAttributionSource);
+ } else {
+ auto validatedAttrSource = VALUE_OR_RETURN_CONVERTED(
+ validateAttributionFromContextOrTrustedCaller(client.attributionSource,
+ getPermissionProvider()
+ ));
+ // TODO pass wrapped object around
+ adjAttributionSource = std::move(validatedAttrSource).unwrapInto();
+ }
if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER;
@@ -548,6 +616,7 @@
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized;
bool isBitPerfect;
+ float volume;
ret = AudioSystem::getOutputForAttr(&localAttr, &io,
actualSessionId,
&streamType, adjAttributionSource,
@@ -555,7 +624,8 @@
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
deviceId, &portId, &secondaryOutputs, &isSpatialized,
- &isBitPerfect);
+ &isBitPerfect,
+ &volume);
if (ret != NO_ERROR) {
config->sample_rate = fullConfig.sample_rate;
config->channel_mask = fullConfig.channel_mask;
@@ -679,20 +749,22 @@
result.append("Notification Clients:\n");
result.append(" pid uid name\n");
- for (size_t i = 0; i < mNotificationClients.size(); ++i) {
- const pid_t pid = mNotificationClients[i]->getPid();
- const uid_t uid = mNotificationClients[i]->getUid();
- const mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
- result.appendFormat("%6d %6u %s\n", pid, uid, info.package.c_str());
+ for (const auto& [ _, client ] : mNotificationClients) {
+ const uid_t uid = client->getUid();
+ const std::shared_ptr<const mediautils::UidInfo::Info> info =
+ mediautils::UidInfo::getInfo(uid);
+ result.appendFormat("%6d %6u %s\n",
+ client->getPid(), uid, info->package.c_str());
}
result.append("Global session refs:\n");
result.append(" session cnt pid uid name\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
AudioSessionRef *r = mAudioSessionRefs[i];
- const mediautils::UidInfo::Info info = mUidInfo.getInfo(r->mUid);
+ const std::shared_ptr<const mediautils::UidInfo::Info> info =
+ mediautils::UidInfo::getInfo(r->mUid);
result.appendFormat(" %7d %4d %7d %6u %s\n", r->mSessionid, r->mCnt, r->mPid,
- r->mUid, info.package.c_str());
+ r->mUid, info->package.c_str());
}
write(fd, result.c_str(), result.size());
}
@@ -825,6 +897,17 @@
BUFLOG_RESET;
+ if (media::psh_utils::AudioPowerManager::enabled()) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.build.display.id", value, "Unknown build");
+ std::string build(value);
+ build.append("\n");
+ write(fd, build.c_str(), build.size());
+ const std::string powerLog =
+ media::psh_utils::AudioPowerManager::getAudioPowerManager().toString();
+ write(fd, powerLog.c_str(), powerLog.size());
+ }
+
if (locked) {
mutex().unlock();
}
@@ -980,6 +1063,7 @@
status_t AudioFlinger::createTrack(const media::CreateTrackRequest& _input,
media::CreateTrackResponse& _output)
{
+ ATRACE_CALL();
// Local version of VALUE_OR_RETURN, specific to this method's calling conventions.
CreateTrackInput input = VALUE_OR_RETURN_STATUS(CreateTrackInput::fromAidl(_input));
CreateTrackOutput output;
@@ -992,37 +1076,52 @@
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized = false;
bool isBitPerfect = false;
+ float volume;
- // TODO b/182392553: refactor or make clearer
- pid_t clientPid =
- VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(input.clientInfo.attributionSource.pid));
- bool updatePid = (clientPid == (pid_t)-1);
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- uid_t clientUid =
- VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(input.clientInfo.attributionSource.uid));
audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE;
std::vector<int> effectIds;
audio_attributes_t localAttr = input.attr;
- AttributionSourceState adjAttributionSource = input.clientInfo.attributionSource;
- if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
- ALOGW_IF(clientUid != callingUid,
- "%s uid %d tried to pass itself off as %d",
- __FUNCTION__, callingUid, clientUid);
- adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
- clientUid = callingUid;
- updatePid = true;
+ AttributionSourceState adjAttributionSource;
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ if (!com::android::media::audio::audioserver_permissions()) {
+ adjAttributionSource = input.clientInfo.attributionSource;
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ uid_t clientUid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(
+ input.clientInfo.attributionSource.uid));
+ pid_t clientPid =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(
+ input.clientInfo.attributionSource.pid));
+ bool updatePid = (clientPid == (pid_t)-1);
+
+ if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
+ ALOGW_IF(clientUid != callingUid,
+ "%s uid %d tried to pass itself off as %d",
+ __FUNCTION__, callingUid, clientUid);
+ adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_uid_t_int32_t(callingUid));
+ clientUid = callingUid;
+ updatePid = true;
+ }
+ if (updatePid) {
+ ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
+ "%s uid %d pid %d tried to pass itself off as pid %d",
+ __func__, callingUid, callingPid, clientPid);
+ clientPid = callingPid;
+ adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_pid_t_int32_t(callingPid));
+ }
+ adjAttributionSource = afutils::checkAttributionSourcePackage(
+ adjAttributionSource);
+
+ } else {
+ auto validatedAttrSource = VALUE_OR_RETURN_CONVERTED(
+ validateAttributionFromContextOrTrustedCaller(input.clientInfo.attributionSource,
+ getPermissionProvider()
+ ));
+ // TODO pass wrapped object around
+ adjAttributionSource = std::move(validatedAttrSource).unwrapInto();
}
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- if (updatePid) {
- ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
- "%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, clientPid);
- clientPid = callingPid;
- adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
- }
- adjAttributionSource = afutils::checkAttributionSourcePackage(
- adjAttributionSource);
audio_session_t sessionId = input.sessionId;
if (sessionId == AUDIO_SESSION_ALLOCATE) {
@@ -1038,7 +1137,7 @@
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
adjAttributionSource, &input.config, input.flags,
&output.selectedDeviceId, &portId, &secondaryOutputs,
- &isSpatialized, &isBitPerfect);
+ &isSpatialized, &isBitPerfect, &volume);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -1075,7 +1174,7 @@
goto Exit;
}
- client = registerPid(clientPid);
+ client = registerPid(adjAttributionSource.pid);
IAfPlaybackThread* effectThread = nullptr;
sp<IAfEffectChain> effectChain = nullptr;
@@ -1095,7 +1194,7 @@
if (effectThread == nullptr) {
effectChain = getOrphanEffectChain_l(sessionId);
}
- ALOGV("createTrack() sessionId: %d", sessionId);
+ ALOGV("createTrack() sessionId: %d volume: %f", sessionId, volume);
output.sampleRate = input.config.sample_rate;
output.frameCount = input.frameCount;
@@ -1110,7 +1209,7 @@
input.sharedBuffer, sessionId, &output.flags,
callingPid, adjAttributionSource, input.clientInfo.clientTid,
&lStatus, portId, input.audioTrackCallback, isSpatialized,
- isBitPerfect, &output.afTrackFlags);
+ isBitPerfect, &output.afTrackFlags, volume);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
@@ -1561,6 +1660,33 @@
return NO_ERROR;
}
+status_t AudioFlinger::setPortsVolume(
+ const std::vector<audio_port_handle_t>& ports, float volume, audio_io_handle_t output)
+{
+ for (const auto& port : ports) {
+ if (port == AUDIO_PORT_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ }
+ if (isnan(volume) || volume > 1.0f || volume < 0.0f) {
+ return BAD_VALUE;
+ }
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ audio_utils::lock_guard lock(mutex());
+ IAfPlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread != nullptr) {
+ return thread->setPortsVolume(ports, volume);
+ }
+ const sp<IAfMmapThread> mmapThread = checkMmapThread_l(output);
+ if (mmapThread != nullptr && mmapThread->isOutput()) {
+ IAfMmapPlaybackThread *mmapPlaybackThread = mmapThread->asIAfMmapPlaybackThread().get();
+ return mmapPlaybackThread->setPortsVolume(ports, volume);
+ }
+ return BAD_VALUE;
+}
+
status_t AudioFlinger::setRequestedLatencyMode(
audio_io_handle_t output, audio_latency_mode_t mode) {
if (output == AUDIO_IO_HANDLE_NONE) {
@@ -1663,37 +1789,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++) {
@@ -2079,24 +2174,22 @@
void AudioFlinger::registerClient(const sp<media::IAudioFlingerClient>& client)
{
- audio_utils::lock_guard _l(mutex());
if (client == 0) {
return;
}
- pid_t pid = IPCThreadState::self()->getCallingPid();
+ const pid_t pid = IPCThreadState::self()->getCallingPid();
const uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ audio_utils::lock_guard _l(mutex());
{
audio_utils::lock_guard _cl(clientMutex());
- if (mNotificationClients.indexOfKey(pid) < 0) {
- sp<NotificationClient> notificationClient = new NotificationClient(this,
- client,
- pid,
- uid);
+ if (mNotificationClients.count(pid) == 0) {
+ auto notificationClient = sp<NotificationClient>::make(
+ this, client, pid, uid);
ALOGV("registerClient() client %p, pid %d, uid %u",
notificationClient.get(), pid, uid);
- mNotificationClients.add(pid, notificationClient);
-
+ mNotificationClients[pid] = notificationClient;
sp<IBinder> binder = IInterface::asBinder(client);
binder->linkToDeath(notificationClient);
}
@@ -2123,7 +2216,7 @@
audio_utils::lock_guard _l(mutex());
{
audio_utils::lock_guard _cl(clientMutex());
- mNotificationClients.removeItem(pid);
+ mNotificationClients.erase(pid);
}
ALOGV("%d died, releasing its sessions", pid);
@@ -2164,11 +2257,13 @@
legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(ioDesc));
audio_utils::lock_guard _l(clientMutex());
- size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(eventAidl,
- descAidl);
+ if (pid != 0) {
+ if (auto it = mNotificationClients.find(pid); it != mNotificationClients.end()) {
+ it->second->audioFlingerClient()->ioConfigChanged(eventAidl, descAidl);
+ }
+ } else {
+ for (const auto& [ client_pid, client] : mNotificationClients) {
+ client->audioFlingerClient()->ioConfigChanged(eventAidl, descAidl);
}
}
}
@@ -2182,9 +2277,8 @@
audio_utils::lock_guard _l(clientMutex());
size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->audioFlingerClient()
- ->onSupportedLatencyModesChanged(outputAidl, modesAidl);
+ for (const auto& [_, client] : mNotificationClients) {
+ client->audioFlingerClient()->onSupportedLatencyModesChanged(outputAidl, modesAidl);
}
}
@@ -2195,6 +2289,12 @@
}
}
+const IPermissionProvider& AudioFlinger::getPermissionProvider() {
+ // This is inited as part of service construction, prior to binder registration,
+ // so it should always be non-null.
+ return mAudioPolicyServiceLocal.load()->getPermissionProvider();
+}
+
// removeClient_l() must be called with AudioFlinger::clientMutex() held
void AudioFlinger::removeClient_l(pid_t pid)
{
@@ -2237,6 +2337,9 @@
pid_t pid,
uid_t uid)
: mAudioFlinger(audioFlinger), mPid(pid), mUid(uid), mAudioFlingerClient(client)
+ , mClientToken(media::psh_utils::AudioPowerManager::enabled()
+ ? media::psh_utils::createAudioClientToken(pid, uid)
+ : nullptr)
{
}
@@ -2246,7 +2349,7 @@
void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who __unused)
{
- sp<NotificationClient> keep(this);
+ const auto keep = sp<NotificationClient>::fromExisting(this);
mAudioFlinger->removeNotificationClient(mPid);
}
@@ -2304,30 +2407,43 @@
output.buffers.clear();
output.inputId = AUDIO_IO_HANDLE_NONE;
- // TODO b/182392553: refactor or clean up
- AttributionSourceState adjAttributionSource = input.clientInfo.attributionSource;
- bool updatePid = (adjAttributionSource.pid == -1);
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- const uid_t currentUid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(
- adjAttributionSource.uid));
- if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
- ALOGW_IF(currentUid != callingUid,
- "%s uid %d tried to pass itself off as %d",
- __FUNCTION__, callingUid, currentUid);
- adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
- updatePid = true;
+ AttributionSourceState adjAttributionSource;
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ if (!com::android::media::audio::audioserver_permissions()) {
+ adjAttributionSource = input.clientInfo.attributionSource;
+ bool updatePid = (adjAttributionSource.pid == -1);
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ const uid_t currentUid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(
+ adjAttributionSource.uid));
+ if (!isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
+ ALOGW_IF(currentUid != callingUid,
+ "%s uid %d tried to pass itself off as %d",
+ __FUNCTION__, callingUid, currentUid);
+ adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_uid_t_int32_t(callingUid));
+ updatePid = true;
+ }
+ const pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(
+ adjAttributionSource.pid));
+ if (updatePid) {
+ ALOGW_IF(currentPid != (pid_t)-1 && currentPid != callingPid,
+ "%s uid %d pid %d tried to pass itself off as pid %d",
+ __func__, callingUid, callingPid, currentPid);
+ adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_pid_t_int32_t(callingPid));
+ }
+ adjAttributionSource = afutils::checkAttributionSourcePackage(
+ adjAttributionSource);
+ } else {
+ auto validatedAttrSource = VALUE_OR_RETURN_CONVERTED(
+ validateAttributionFromContextOrTrustedCaller(
+ input.clientInfo.attributionSource,
+ getPermissionProvider()
+ ));
+ // TODO pass wrapped object around
+ adjAttributionSource = std::move(validatedAttrSource).unwrapInto();
}
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- const pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(
- adjAttributionSource.pid));
- if (updatePid) {
- ALOGW_IF(currentPid != (pid_t)-1 && currentPid != callingPid,
- "%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, currentPid);
- adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
- }
- adjAttributionSource = afutils::checkAttributionSourcePackage(
- adjAttributionSource);
+
// further format checks are performed by createRecordTrack_l()
if (!audio_is_valid_format(input.config.format)) {
ALOGE("createRecord() invalid format %#x", input.config.format);
@@ -3500,7 +3616,7 @@
// is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be
// called from a different pid leaving a stale session reference. Also we don't know how
// to clear this reference if the client process dies.
- if (mNotificationClients.indexOfKey(caller) < 0) {
+ if (mNotificationClients.count(caller) == 0) {
ALOGW("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
return;
}
@@ -3761,8 +3877,7 @@
// checkPlaybackThread_l() must be called with AudioFlinger::mutex() held
-sp<VolumeInterface> AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const
-{
+sp<VolumeInterface> AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const {
sp<VolumeInterface> volumeInterface = mPlaybackThreads.valueFor(output).get();
if (volumeInterface == nullptr) {
IAfMmapThread* const mmapThread = mMmapThreads.valueFor(output).get();
@@ -3961,7 +4076,8 @@
outputFlags,
0ns /* timeout */,
frameCountToBeReady,
- track->getSpeed());
+ track->getSpeed(),
+ 1.f /* volume */);
status = patchTrack->initCheck();
if (status != NO_ERROR) {
ALOGE("Secondary output patchTrack init failed: %d", status);
@@ -4129,20 +4245,31 @@
int idOut = -1;
status_t lStatus = NO_ERROR;
-
- // TODO b/182392553: refactor or make clearer
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
- pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjAttributionSource.pid));
- if (currentPid == -1 || !isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- ALOGW_IF(currentPid != -1 && currentPid != callingPid,
- "%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, currentPid);
- adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
- currentPid = callingPid;
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t currentPid;
+ if (!com::android::media::audio::audioserver_permissions()) {
+ adjAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
+ currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjAttributionSource.pid));
+ if (currentPid == -1 || !isAudioServerOrMediaServerOrSystemServerOrRootUid(callingUid)) {
+ const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW_IF(currentPid != -1 && currentPid != callingPid,
+ "%s uid %d pid %d tried to pass itself off as pid %d",
+ __func__, callingUid, callingPid, currentPid);
+ adjAttributionSource.pid = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_pid_t_int32_t(callingPid));
+ currentPid = callingPid;
+ }
+ adjAttributionSource = afutils::checkAttributionSourcePackage(adjAttributionSource);
+ } else {
+ auto validatedAttrSource = VALUE_OR_RETURN_CONVERTED(
+ validateAttributionFromContextOrTrustedCaller(request.attributionSource,
+ getPermissionProvider()
+ ));
+ // TODO pass wrapped object around
+ adjAttributionSource = std::move(validatedAttrSource).unwrapInto();
+ currentPid = adjAttributionSource.pid;
}
- adjAttributionSource = afutils::checkAttributionSourcePackage(adjAttributionSource);
+
ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d, factory %p",
adjAttributionSource.pid, effectClient.get(), priority, sessionId, io,
@@ -5042,6 +5169,7 @@
case TransactionCode::GET_AUDIO_MIX_PORT:
case TransactionCode::SET_TRACKS_INTERNAL_MUTE:
case TransactionCode::RESET_REFERENCES_FOR_TEST:
+ case TransactionCode::SET_PORTS_VOLUME:
ALOGW("%s: transaction %d received from PID %d",
__func__, static_cast<int>(code), IPCThreadState::self()->getCallingPid());
// return status only for non void methods
@@ -5129,7 +5257,7 @@
}
}, mediautils::TimeCheck::getDefaultTimeoutDuration(),
mediautils::TimeCheck::getDefaultSecondChanceDuration(),
- true /* crashOnTimeout */);
+ !property_get_bool("audio.timecheck.disabled", false) /* crashOnTimeout */);
return delegate();
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c229e83..10a7ab0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -38,6 +38,7 @@
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <mediautils/ServiceUtilities.h>
#include <mediautils/Synchronization.h>
+#include <psh_utils/AudioPowerManager.h>
// not needed with the includes above, added to prevent transitive include dependency.
#include <utils/KeyedVector.h>
@@ -96,9 +97,8 @@
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 setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume,
+ audio_io_handle_t output) final EXCLUDES_AudioFlinger_Mutex;
status_t setMode(audio_mode_t mode) final EXCLUDES_AudioFlinger_Mutex;
@@ -410,6 +410,8 @@
void onHardError(std::set<audio_port_handle_t>& trackPortIds) final
EXCLUDES_AudioFlinger_ClientMutex;
+ const ::com::android::media::permission::IPermissionProvider& getPermissionProvider() final;
+
// ---- end of IAfThreadCallback interface
/* List available audio ports and their attributes */
@@ -498,6 +500,7 @@
const pid_t mPid;
const uid_t mUid;
const sp<media::IAudioFlingerClient> mAudioFlingerClient;
+ const std::unique_ptr<media::psh_utils::Token> mClientToken;
};
// --- MediaLogNotifier ---
@@ -554,6 +557,7 @@
IAfPlaybackThread* checkMixerThread_l(audio_io_handle_t output) const REQUIRES(mutex());
sp<VolumeInterface> getVolumeInterface_l(audio_io_handle_t output) const REQUIRES(mutex());
+
std::vector<sp<VolumeInterface>> getAllVolumeInterfaces_l() const REQUIRES(mutex());
@@ -696,8 +700,7 @@
DefaultKeyedVector<audio_io_handle_t, sp<IAfRecordThread>> mRecordThreads GUARDED_BY(mutex());
- DefaultKeyedVector<pid_t, sp<NotificationClient>> mNotificationClients
- GUARDED_BY(clientMutex());
+ std::map<pid_t, sp<NotificationClient>> mNotificationClients GUARDED_BY(clientMutex());
// updated by atomic_fetch_add_explicit
volatile atomic_uint_fast32_t mNextUniqueIds[AUDIO_UNIQUE_ID_USE_MAX]; // ctor init
@@ -772,8 +775,6 @@
bool mSystemReady GUARDED_BY(mutex()) = false;
std::atomic<bool> mAudioPolicyReady = false;
- mediautils::UidInfo mUidInfo GUARDED_BY(mutex());
-
// no mutex needed.
SimpleLog mRejectedSetParameterLog;
SimpleLog mAppSetParameterLog;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index cfd0a5e..84505d3 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;
@@ -1585,16 +1587,27 @@
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();
+ // Scale param fields
+ int32_t intensityParam = static_cast<int32_t>(HG_PARAM_HAPTIC_INTENSITY);
+ int32_t scaleLevel = static_cast<int32_t>(hapticScale.getLevel());
+ float scaleFactor = hapticScale.getScaleFactor();
+ float adaptiveScaleFactor = hapticScale.getAdaptiveScaleFactor();
+
+ size_t psize = sizeof(int32_t); // HG_PARAM_HAPTIC_INTENSITY
+ size_t vsize = 2 * sizeof(int32_t) + 2 * sizeof(float); // id + scale fields
+ 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;
+
+ EffectParamWriter writer(*effectParam);
+ writer.writeToParameter(&intensityParam);
+ writer.writeToValue(&id);
+ writer.writeToValue(&scaleLevel);
+ writer.writeToValue(&scaleFactor);
+ writer.writeToValue(&adaptiveScaleFactor);
+ writer.finishValueWrite();
+
std::vector<uint8_t> response;
status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
if (status == NO_ERROR) {
@@ -1613,17 +1626,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) {
@@ -1773,7 +1790,7 @@
int32_t priority, bool notifyFramesProcessed,
bool isInternal,
audio_utils::MutexOrder mutexOrder)
- : BnEffect(), mMutex(audio_utils::mutex{mutexOrder}),
+ : BnEffect(), mMutex(mutexOrder),
mEffect(effect), mEffectClient(media::EffectClientAsyncProxy::makeIfNeeded(effectClient)),
mClient(client), mCblk(nullptr),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false),
@@ -3249,7 +3266,9 @@
}
if (mThreadType == IAfThreadBase::SPATIALIZER) {
- if (c->sessionId() == AUDIO_SESSION_OUTPUT_STAGE) {
+ if (c->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ return t->mixerChannelMask();
+ } else if (c->sessionId() == AUDIO_SESSION_OUTPUT_STAGE) {
if (c->isFirstEffect_l(id)) {
return t->mixerChannelMask();
} else {
@@ -3296,7 +3315,8 @@
return t->channelMask();
}
} else {
- return t->channelMask();
+ return (c->sessionId() == AUDIO_SESSION_OUTPUT_MIX) ? t->mixerChannelMask()
+ : t->channelMask();
}
} else {
return t->channelMask();
@@ -3557,7 +3577,7 @@
mHalEffect->setDevices({mDevice});
}
}
- *handle = new InternalEffectHandle(mHalEffect, mNotifyFramesProcessed);
+ *handle = sp<InternalEffectHandle>::make(mHalEffect, mNotifyFramesProcessed);
status = (*handle)->initCheck();
if (status == OK) {
status = mHalEffect->addHandle((*handle).get());
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index a7da658..8596acb 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -26,6 +26,7 @@
#include <datapath/AudioStreamIn.h>
#include <datapath/AudioStreamOut.h>
#include <datapath/VolumeInterface.h>
+#include <datapath/VolumePortInterface.h>
#include <fastpath/FastMixerDumpState.h>
#include <media/DeviceDescriptorBase.h>
#include <media/MmapStreamInterface.h>
@@ -37,6 +38,10 @@
#include <optional>
+namespace com::android::media::permission {
+ class IPermissionProvider;
+}
+
namespace android {
class IAfDirectOutputThread;
@@ -122,6 +127,9 @@
EXCLUDES_AudioFlinger_ClientMutex = 0;
virtual void onHardError(std::set<audio_port_handle_t>& trackPortIds) = 0;
+
+ virtual const ::com::android::media::permission::IPermissionProvider&
+ getPermissionProvider() = 0;
};
class IAfThreadBase : public virtual RefBase {
@@ -472,7 +480,8 @@
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
bool isBitPerfect,
- audio_output_flags_t* afTrackFlags)
+ audio_output_flags_t* afTrackFlags,
+ float volume)
REQUIRES(audio_utils::AudioFlinger_Mutex) = 0;
virtual status_t addTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex()) = 0;
@@ -548,6 +557,9 @@
virtual void setTracksInternalMute(std::map<audio_port_handle_t, bool>* tracksInternalMute)
EXCLUDES_ThreadBase_Mutex = 0;
+
+ virtual status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume)
+ EXCLUDES_ThreadBase_Mutex = 0;
};
class IAfDirectOutputThread : public virtual IAfPlaybackThread {
@@ -687,6 +699,9 @@
AudioHwDevice* hwDev, AudioStreamOut* output, bool systemReady);
virtual AudioStreamOut* clearOutput() EXCLUDES_ThreadBase_Mutex = 0;
+
+ virtual status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume)
+ EXCLUDES_ThreadBase_Mutex = 0;
};
class IAfMmapCaptureThread : public virtual IAfMmapThread {
diff --git a/services/audioflinger/IAfTrack.h b/services/audioflinger/IAfTrack.h
index 091453b..4615596 100644
--- a/services/audioflinger/IAfTrack.h
+++ b/services/audioflinger/IAfTrack.h
@@ -21,6 +21,7 @@
#include <audio_utils/mutex.h>
#include <audiomanager/IAudioManager.h>
#include <binder/IMemory.h>
+#include <datapath/VolumePortInterface.h>
#include <fastpath/FastMixerDumpState.h>
#include <media/AudioSystem.h>
#include <media/VolumeShaper.h>
@@ -254,7 +255,7 @@
};
// Common interface for Playback tracks.
-class IAfTrack : public virtual IAfTrackBase {
+class IAfTrack : public virtual IAfTrackBase, public virtual VolumePortInterface {
public:
// FillingStatus is used for suppressing volume ramp at begin of playing
enum FillingStatus { FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE };
@@ -289,7 +290,8 @@
size_t frameCountToBeReady = SIZE_MAX,
float speed = 1.0f,
bool isSpatialized = false,
- bool isBitPerfect = false);
+ bool isBitPerfect = false,
+ float volume = 0.0f);
virtual void pause() = 0;
virtual void flush() = 0;
@@ -452,7 +454,7 @@
virtual ExtendedTimestamp getClientProxyTimestamp() const = 0;
};
-class IAfMmapTrack : public virtual IAfTrackBase {
+class IAfMmapTrack : public virtual IAfTrackBase, public virtual VolumePortInterface {
public:
static sp<IAfMmapTrack> create(IAfThreadBase* thread,
const audio_attributes_t& attr,
@@ -463,7 +465,8 @@
bool isOut,
const android::content::AttributionSourceState& attributionSource,
pid_t creatorPid,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
+ float volume = 0.0f);
// protected by MMapThread::mLock
virtual void setSilenced_l(bool silenced) = 0;
@@ -584,7 +587,8 @@
* as soon as possible to have
* the lowest possible latency
* even if it might glitch. */
- float speed = 1.0f);
+ float speed = 1.0f,
+ float volume = 1.0f);
};
class IAfPatchRecord : public virtual IAfRecordTrack, public virtual IAfPatchTrackBase {
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index 85ce142..8758bd0 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -35,7 +35,8 @@
bool isOut,
const android::content::AttributionSourceState& attributionSource,
pid_t creatorPid,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
+ float volume = 0.0f);
~MmapTrack() override;
status_t initCheck() const final;
@@ -65,6 +66,13 @@
void processMuteEvent_l(const sp<IAudioManager>& audioManager,
mute_state_t muteState)
/* REQUIRES(MmapPlaybackThread::mLock) */ final;
+
+ // VolumePortInterface implementation
+ void setPortVolume(float volume) override {
+ mVolume = volume;
+ }
+ float getPortVolume() const override { return mVolume; }
+
private:
DISALLOW_COPY_AND_ASSIGN(MmapTrack);
@@ -87,6 +95,8 @@
/* GUARDED_BY(MmapPlaybackThread::mLock) */;
mute_state_t mMuteState
/* GUARDED_BY(MmapPlaybackThread::mLock) */;
+
+ float mVolume = 0.0f;
}; // end of Track
} // namespace android
\ No newline at end of file
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 994dd47..d0b96de 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -649,7 +649,8 @@
outputFlags,
{} /*timeout*/,
frameCountToBeReady,
- 1.0f);
+ 1.0f /*speed*/,
+ 1.0f /*volume*/);
status = mPlayback.checkTrack(tempPatchTrack.get());
if (status != NO_ERROR) {
return status;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 2cc6236..84758a4 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -96,7 +96,8 @@
size_t frameCountToBeReady = SIZE_MAX,
float speed = 1.0f,
bool isSpatialized = false,
- bool isBitPerfect = false);
+ bool isBitPerfect = false,
+ float volume = 0.0f);
~Track() override;
status_t initCheck() const final;
void appendDumpHeader(String8& result) const final;
@@ -222,6 +223,11 @@
bool getInternalMute() const final { return mInternalMute; }
void setInternalMute(bool muted) final { mInternalMute = muted; }
+
+ // VolumePortInterface implementation
+ void setPortVolume(float volume) override;
+ float getPortVolume() const override { return mVolume; }
+
protected:
DISALLOW_COPY_AND_ASSIGN(Track);
@@ -362,6 +368,8 @@
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
};
+ void populateUsageAndContentTypeFromStreamType();
+
size_t mPresentationCompleteFrames = 0; // (Used for Mixed tracks)
// The number of frames written to the
// audio HAL when this track is considered fully rendered.
@@ -403,8 +411,8 @@
// access these two variables only when holding player thread lock.
std::unique_ptr<os::PersistableBundle> mMuteEventExtras;
mute_state_t mMuteState;
-
bool mInternalMute = false;
+ std::atomic<float> mVolume = 0.0f;
}; // end of Track
@@ -501,7 +509,8 @@
* as soon as possible to have
* the lowest possible latency
* even if it might glitch. */
- float speed = 1.0f);
+ float speed = 1.0f,
+ float volume = 1.0f);
~PatchTrack() override;
size_t framesReady() const final;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ad48517..cf6ae02 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -49,6 +49,7 @@
#include <binder/IServiceManager.h>
#include <binder/PersistableBundle.h>
#include <com_android_media_audio.h>
+#include <com_android_media_audioserver.h>
#include <cutils/bitops.h>
#include <cutils/properties.h>
#include <fastpath/AutoPark.h>
@@ -72,6 +73,7 @@
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
#include <media/nbaio/SourceAudioBufferProvider.h>
+#include <media/ValidatedAttributionSourceState.h>
#include <mediautils/BatteryNotifier.h>
#include <mediautils/Process.h>
#include <mediautils/SchedulingPolicyService.h>
@@ -79,6 +81,7 @@
#include <powermanager/PowerManager.h>
#include <private/android_filesystem_config.h>
#include <private/media/AudioTrackShared.h>
+#include <psh_utils/AudioPowerManager.h>
#include <system/audio_effects/effect_aec.h>
#include <system/audio_effects/effect_downmix.h>
#include <system/audio_effects/effect_ns.h>
@@ -120,6 +123,9 @@
return a < b ? a : b;
}
+using com::android::media::permission::ValidatedAttributionSourceState;
+namespace audioserver_flags = com::android::media::audioserver;
+
namespace android {
using audioflinger::SyncEvent;
@@ -162,6 +168,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;
@@ -728,9 +737,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;
@@ -1186,6 +1197,8 @@
return String16("MmapCapture");
case SPATIALIZER:
return String16("AudioSpatial");
+ case BIT_PERFECT:
+ return String16("AudioBitPerfect");
default:
ALOG_ASSERT(false);
return String16("AudioUnknown");
@@ -1206,6 +1219,10 @@
{} /* historyTag */);
if (status.isOk()) {
mWakeLockToken = binder;
+ if (media::psh_utils::AudioPowerManager::enabled()) {
+ mThreadToken = media::psh_utils::createAudioThreadToken(
+ getTid(), String8(getWakeLockTag()).c_str());
+ }
}
ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status.exceptionCode());
}
@@ -1231,6 +1248,7 @@
}
mWakeLockToken.clear();
}
+ mThreadToken.reset();
}
void ThreadBase::getPowerManager_l() {
@@ -1570,14 +1588,13 @@
}
break;
case SPATIALIZER:
- // Global effects (AUDIO_SESSION_OUTPUT_MIX) are not supported on spatializer mixer
- // as there is no common accumulation buffer for sptialized and non sptialized tracks.
+ // Global effects (AUDIO_SESSION_OUTPUT_MIX) are supported on spatializer mixer, but only
+ // the spatialized track have global effects applied for now.
// Post processing effects (AUDIO_SESSION_OUTPUT_STAGE or AUDIO_SESSION_DEVICE)
// are supported and added after the spatializer.
if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- ALOGW("%s: global effect %s not supported on spatializer thread %s",
- __func__, desc->name, mThreadName);
- return BAD_VALUE;
+ ALOGD("%s: global effect %s on spatializer thread %s", __func__, desc->name,
+ mThreadName);
} else if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
// only post processing , downmixer or spatializer effects on output stage session
if (IAfEffectModule::isSpatializer(&desc->type)
@@ -2209,17 +2226,18 @@
(int64_t)(mIsMsdDevice ? AUDIO_DEVICE_OUT_BUS // turn on by default for MSD
: AUDIO_DEVICE_NONE));
}
-
- for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_FOR_POLICY_CNT; ++i) {
- const audio_stream_type_t stream{static_cast<audio_stream_type_t>(i)};
- mStreamTypes[stream].volume = 0.0f;
- mStreamTypes[stream].mute = mAfThreadCallback->streamMute_l(stream);
+ if (!audioserver_flags::portid_volume_management()) {
+ for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_FOR_POLICY_CNT; ++i) {
+ const audio_stream_type_t stream{static_cast<audio_stream_type_t>(i)};
+ mStreamTypes[stream].volume = 0.0f;
+ mStreamTypes[stream].mute = mAfThreadCallback->streamMute_l(stream);
+ }
+ // Audio patch and call assistant volume are always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
+ mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].mute = false;
}
- // Audio patch and call assistant volume are always max
- mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
- mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
- mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].volume = 1.0f;
- mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].mute = false;
}
PlaybackThread::~PlaybackThread()
@@ -2270,16 +2288,17 @@
void PlaybackThread::dumpTracks_l(int fd, const Vector<String16>& /* args */)
{
String8 result;
-
- result.appendFormat(" Stream volumes in dB: ");
- for (int i = 0; i < AUDIO_STREAM_CNT; ++i) {
- const stream_type_t *st = &mStreamTypes[i];
- if (i > 0) {
- result.appendFormat(", ");
- }
- result.appendFormat("%d:%.2g", i, 20.0 * log10(st->volume));
- if (st->mute) {
- result.append("M");
+ if (!audioserver_flags::portid_volume_management()) {
+ result.appendFormat(" Stream volumes in dB: ");
+ for (int i = 0; i < AUDIO_STREAM_CNT; ++i) {
+ const stream_type_t *st = &mStreamTypes[i];
+ if (i > 0) {
+ result.appendFormat(", ");
+ }
+ result.appendFormat("%d:%.2g", i, 20.0 * log10(st->volume));
+ if (st->mute) {
+ result.append("M");
+ }
}
}
result.append("\n");
@@ -2387,7 +2406,8 @@
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
bool isBitPerfect,
- audio_output_flags_t *afTrackFlags)
+ audio_output_flags_t *afTrackFlags,
+ float volume)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2716,7 +2736,7 @@
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, creatorPid, attributionSource, trackFlags,
IAfTrackBase::TYPE_DEFAULT, portId, SIZE_MAX /*frameCountToBeReady*/,
- speed, isSpatialized, isBitPerfect);
+ speed, isSpatialized, isBitPerfect, volume);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
@@ -2844,6 +2864,22 @@
return mStreamTypes[stream].volume;
}
+status_t PlaybackThread::setPortsVolume(
+ const std::vector<audio_port_handle_t>& portIds, float volume) {
+ audio_utils::lock_guard _l(mutex());
+ for (const auto& portId : portIds) {
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<IAfTrack> track = mTracks[i].get();
+ if (portId == track->portId()) {
+ track->setPortVolume(volume);
+ break;
+ }
+ }
+ }
+ broadcast_l();
+ return NO_ERROR;
+}
+
void PlaybackThread::setVolumeForOutput_l(float left, float right) const
{
mOutput->stream->setVolume(left, right);
@@ -3777,19 +3813,31 @@
ALOGV("addEffectChain_l() creating new input buffer %p session %d",
buffer, session);
} else {
- // A global session on a SPATIALIZER thread is either OUTPUT_STAGE or DEVICE
- // - OUTPUT_STAGE session uses the mEffectBuffer as input buffer and
- // mPostSpatializerBuffer as output buffer
- // - DEVICE session uses the mPostSpatializerBuffer as input and output buffer.
- status_t result = mAfThreadCallback->getEffectsFactoryHal()->mirrorBuffer(
- mEffectBuffer, mEffectBufferSize, &halInBuffer);
- if (result != OK) return result;
- result = mAfThreadCallback->getEffectsFactoryHal()->mirrorBuffer(
- mPostSpatializerBuffer, mPostSpatializerBufferSize, &halOutBuffer);
- if (result != OK) return result;
+ status_t result = INVALID_OPERATION;
+ // Buffer configuration for global sessions on a SPATIALIZER thread:
+ // - AUDIO_SESSION_OUTPUT_MIX session uses the mEffectBuffer as input and output buffer
+ // - AUDIO_SESSION_OUTPUT_STAGE session uses the mEffectBuffer as input buffer and
+ // mPostSpatializerBuffer as output buffer
+ // - AUDIO_SESSION_DEVICE session uses the mPostSpatializerBuffer as input and output
+ // buffer
+ if (session == AUDIO_SESSION_OUTPUT_MIX || session == AUDIO_SESSION_OUTPUT_STAGE) {
+ result = mAfThreadCallback->getEffectsFactoryHal()->mirrorBuffer(
+ mEffectBuffer, mEffectBufferSize, &halInBuffer);
+ if (result != OK) return result;
- if (session == AUDIO_SESSION_DEVICE) {
- halInBuffer = halOutBuffer;
+ if (session == AUDIO_SESSION_OUTPUT_MIX) {
+ halOutBuffer = halInBuffer;
+ }
+ }
+
+ if (session == AUDIO_SESSION_OUTPUT_STAGE || session == AUDIO_SESSION_DEVICE) {
+ result = mAfThreadCallback->getEffectsFactoryHal()->mirrorBuffer(
+ mPostSpatializerBuffer, mPostSpatializerBufferSize, &halOutBuffer);
+ if (result != OK) return result;
+
+ if (session == AUDIO_SESSION_DEVICE) {
+ halInBuffer = halOutBuffer;
+ }
}
}
} else {
@@ -5196,7 +5244,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++;
@@ -5770,12 +5818,19 @@
}
sp<AudioTrackServerProxy> proxy = track->audioTrackServerProxy();
float volume;
- if (track->isPlaybackRestricted() || mStreamTypes[track->streamType()].mute) {
- volume = 0.f;
+ if (!audioserver_flags::portid_volume_management()) {
+ if (track->isPlaybackRestricted() || mStreamTypes[track->streamType()].mute) {
+ volume = 0.f;
+ } else {
+ volume = masterVolume * mStreamTypes[track->streamType()].volume;
+ }
} else {
- volume = masterVolume * mStreamTypes[track->streamType()].volume;
+ if (track->isPlaybackRestricted()) {
+ volume = 0.f;
+ } else {
+ volume = masterVolume * track->getPortVolume();
+ }
}
-
handleVoipVolume_l(&volume);
// cache the combined master volume and stream type volume for fast mixer; this
@@ -5787,15 +5842,23 @@
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
-
- track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
- /*muteState=*/{masterVolume == 0.f,
- mStreamTypes[track->streamType()].volume == 0.f,
- mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted(),
- vlf == 0.f && vrf == 0.f,
- vh == 0.f});
-
+ if (!audioserver_flags::portid_volume_management()) {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+ } else {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ track->getPortVolume() == 0.f,
+ /* muteFromStreamMuted= */ false,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+ }
vlf *= volume;
vrf *= volume;
@@ -5946,16 +6009,22 @@
uint32_t vl, vr; // in U8.24 integer format
float vlf, vrf, vaf; // in [0.0, 1.0] float format
// read original volumes with volume control
- float v = masterVolume * mStreamTypes[track->streamType()].volume;
// Always fetch volumeshaper volume to ensure state is updated.
const sp<AudioTrackServerProxy> proxy = track->audioTrackServerProxy();
const float vh = track->getVolumeHandler()->getVolume(
track->audioTrackServerProxy()->framesReleased()).first;
-
- if (mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
- v = 0;
+ float v;
+ if (!audioserver_flags::portid_volume_management()) {
+ v = masterVolume * mStreamTypes[track->streamType()].volume;
+ if (mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
+ v = 0;
+ }
+ } else {
+ v = masterVolume * track->getPortVolume();
+ if (track->isPlaybackRestricted()) {
+ v = 0;
+ }
}
-
handleVoipVolume_l(&v);
if (track->isPausing()) {
@@ -5975,15 +6044,23 @@
ALOGV("Track right volume out of range: %.3g", vrf);
vrf = GAIN_FLOAT_UNITY;
}
-
- track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
- /*muteState=*/{masterVolume == 0.f,
- mStreamTypes[track->streamType()].volume == 0.f,
- mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted(),
- vlf == 0.f && vrf == 0.f,
- vh == 0.f});
-
+ if (!audioserver_flags::portid_volume_management()) {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+ } else {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ track->getPortVolume() == 0.f,
+ /* muteFromStreamMuted= */ false,
+ track->isPlaybackRestricted(),
+ vlf == 0.f && vrf == 0.f,
+ vh == 0.f});
+ }
// now apply the master volume and stream type volume and shaper volume
vlf *= v * vh;
vrf *= v * vh;
@@ -6709,34 +6786,64 @@
const bool clientVolumeMute = (left == 0.f && right == 0.f);
- if (mMasterMute || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
- left = right = 0;
- } else {
- float typeVolume = mStreamTypes[track->streamType()].volume;
- const float v = mMasterVolume * typeVolume * shaperVolume;
+ if (!audioserver_flags::portid_volume_management()) {
+ if (mMasterMute || mStreamTypes[track->streamType()].mute ||
+ track->isPlaybackRestricted()) {
+ left = right = 0;
+ } else {
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ const float v = mMasterVolume * typeVolume * shaperVolume;
- if (left > GAIN_FLOAT_UNITY) {
- left = GAIN_FLOAT_UNITY;
- }
- if (right > GAIN_FLOAT_UNITY) {
- right = GAIN_FLOAT_UNITY;
- }
- left *= v;
- right *= v;
- if (mAfThreadCallback->getMode() != AUDIO_MODE_IN_COMMUNICATION
+ if (left > GAIN_FLOAT_UNITY) {
+ left = GAIN_FLOAT_UNITY;
+ }
+ if (right > GAIN_FLOAT_UNITY) {
+ right = GAIN_FLOAT_UNITY;
+ }
+ left *= v;
+ right *= v;
+ if (mAfThreadCallback->getMode() != AUDIO_MODE_IN_COMMUNICATION
|| audio_channel_count_from_out_mask(mChannelMask) > 1) {
- left *= mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
- right *= mMasterBalanceRight;
+ left *= mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
+ right *= mMasterBalanceRight;
+ }
}
- }
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted(),
+ clientVolumeMute,
+ shaperVolume == 0.f});
+ } else {
+ if (mMasterMute || track->isPlaybackRestricted()) {
+ left = right = 0;
+ } else {
+ float typeVolume = track->getPortVolume();
+ const float v = mMasterVolume * typeVolume * shaperVolume;
- track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
- /*muteState=*/{mMasterMute,
- mStreamTypes[track->streamType()].volume == 0.f,
- mStreamTypes[track->streamType()].mute,
- track->isPlaybackRestricted(),
- clientVolumeMute,
- shaperVolume == 0.f});
+ if (left > GAIN_FLOAT_UNITY) {
+ left = GAIN_FLOAT_UNITY;
+ }
+ if (right > GAIN_FLOAT_UNITY) {
+ right = GAIN_FLOAT_UNITY;
+ }
+ left *= v;
+ right *= v;
+ if (mAfThreadCallback->getMode() != AUDIO_MODE_IN_COMMUNICATION
+ || audio_channel_count_from_out_mask(mChannelMask) > 1) {
+ left *= mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
+ right *= mMasterBalanceRight;
+ }
+ }
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ track->getPortVolume() == 0.f,
+ /* muteFromStreamMuted= */ false,
+ track->isPlaybackRestricted(),
+ clientVolumeMute,
+ shaperVolume == 0.f});
+ }
if (lastTrack) {
track->setFinalVolume(left, right);
@@ -7138,7 +7245,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;
@@ -7149,7 +7256,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;
@@ -7160,7 +7267,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;
@@ -7177,7 +7284,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);
@@ -7835,7 +7942,9 @@
ALOGE("addOutputTrack() initCheck failed %d", status);
return;
}
- thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
+ if (!audioserver_flags::portid_volume_management()) {
+ thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
+ }
mOutputTracks.add(outputTrack);
ALOGV("addOutputTrack() track %p, on thread %p", outputTrack.get(), thread);
updateWaitTime_l();
@@ -10306,11 +10415,27 @@
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io = mId;
- const AttributionSourceState adjAttributionSource = afutils::checkAttributionSourcePackage(
- client.attributionSource);
+ AttributionSourceState adjAttributionSource;
+ if (!com::android::media::audio::audioserver_permissions()) {
+ adjAttributionSource = afutils::checkAttributionSourcePackage(
+ client.attributionSource);
+ } else {
+ // TODO(b/342475009) validate in oboeservice, and plumb downwards
+ auto validatedRes = ValidatedAttributionSourceState::createFromTrustedUidNoPackage(
+ client.attributionSource,
+ mAfThreadCallback->getPermissionProvider()
+ );
+ if (!validatedRes.has_value()) {
+ ALOGE("MMAP client package validation fail: %s",
+ validatedRes.error().toString8().c_str());
+ return aidl_utils::statusTFromBinderStatus(validatedRes.error());
+ }
+ adjAttributionSource = std::move(validatedRes.value()).unwrapInto();
+ }
const auto localSessionId = mSessionId;
auto localAttr = mAttr;
+ float volume = 0.0f;
if (isOutput()) {
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = mSampleRate;
@@ -10334,7 +10459,8 @@
&portId,
&secondaryOutputs,
&isSpatialized,
- &isBitPerfect);
+ &isBitPerfect,
+ &volume);
mutex().lock();
mAttr = localAttr;
ALOGD_IF(!secondaryOutputs.empty(),
@@ -10403,7 +10529,8 @@
this, attr == nullptr ? mAttr : *attr, mSampleRate, mFormat,
mChannelMask, mSessionId, isOutput(),
client.attributionSource,
- IPCThreadState::self()->getCallingPid(), portId);
+ IPCThreadState::self()->getCallingPid(), portId,
+ volume);
if (!isOutput()) {
track->setSilenced_l(isClientSilenced_l(portId));
}
@@ -10988,18 +11115,18 @@
mChannelCount = audio_channel_count_from_out_mask(mChannelMask);
mMasterVolume = afThreadCallback->masterVolume_l();
mMasterMute = afThreadCallback->masterMute_l();
-
- for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_FOR_POLICY_CNT; ++i) {
- const audio_stream_type_t stream{static_cast<audio_stream_type_t>(i)};
- mStreamTypes[stream].volume = 0.0f;
- mStreamTypes[stream].mute = mAfThreadCallback->streamMute_l(stream);
+ if (!audioserver_flags::portid_volume_management()) {
+ for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_FOR_POLICY_CNT; ++i) {
+ const audio_stream_type_t stream{static_cast<audio_stream_type_t>(i)};
+ mStreamTypes[stream].volume = 0.0f;
+ mStreamTypes[stream].mute = mAfThreadCallback->streamMute_l(stream);
+ }
+ // Audio patch and call assistant volume are always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
+ mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].mute = false;
}
- // Audio patch and call assistant volume are always max
- mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
- mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
- mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].volume = 1.0f;
- mStreamTypes[AUDIO_STREAM_CALL_ASSISTANT].mute = false;
-
if (mAudioHwDev) {
if (mAudioHwDev->canSetMasterVolume()) {
mMasterVolume = 1.0;
@@ -11078,6 +11205,21 @@
}
}
+status_t MmapPlaybackThread::setPortsVolume(
+ const std::vector<audio_port_handle_t>& portIds, float volume) {
+ audio_utils::lock_guard _l(mutex());
+ for (const auto& portId : portIds) {
+ for (const sp<IAfMmapTrack>& track : mActiveTracks) {
+ if (portId == track->portId()) {
+ track->setPortVolume(volume);
+ break;
+ }
+ }
+ }
+ broadcast_l();
+ return NO_ERROR;
+}
+
void MmapPlaybackThread::invalidateTracks(audio_stream_type_t streamType)
{
audio_utils::lock_guard _l(mutex());
@@ -11111,14 +11253,26 @@
void MmapPlaybackThread::processVolume_l()
NO_THREAD_SAFETY_ANALYSIS // access of track->processMuteEvent_l
{
- float volume;
-
- if (mMasterMute || streamMuted_l()) {
- volume = 0;
+ float volume = 0;
+ if (!audioserver_flags::portid_volume_management()) {
+ if (mMasterMute || streamMuted_l()) {
+ volume = 0;
+ } else {
+ volume = mMasterVolume * streamVolume_l();
+ }
} else {
- volume = mMasterVolume * streamVolume_l();
+ if (mMasterMute) {
+ volume = 0;
+ } else {
+ // All mmap tracks are declared with the same audio attributes to the audio policy
+ // manager. Hence, they follow the same routing / volume group. Any change of volume
+ // will be broadcasted to all tracks. Thus, take arbitrarily first track volume.
+ size_t numtracks = mActiveTracks.size();
+ if (numtracks) {
+ volume = mMasterVolume * mActiveTracks[0]->getPortVolume();
+ }
+ }
}
-
if (volume != mHalVolFloat) {
// Convert volumes from float to 8.24
uint32_t vol = (uint32_t)(volume * (1 << 24));
@@ -11151,14 +11305,25 @@
}
for (const sp<IAfMmapTrack>& track : mActiveTracks) {
track->setMetadataHasChanged();
- track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
- /*muteState=*/{mMasterMute,
- streamVolume_l() == 0.f,
- streamMuted_l(),
- // TODO(b/241533526): adjust logic to include mute from AppOps
- false /*muteFromPlaybackRestricted*/,
- false /*muteFromClientVolume*/,
- false /*muteFromVolumeShaper*/});
+ if (!audioserver_flags::portid_volume_management()) {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ streamVolume_l() == 0.f,
+ streamMuted_l(),
+ // TODO(b/241533526): adjust logic to include mute from AppOps
+ false /*muteFromPlaybackRestricted*/,
+ false /*muteFromClientVolume*/,
+ false /*muteFromVolumeShaper*/});
+ } else {
+ track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ track->getPortVolume() == 0.f,
+ /* muteFromStreamMuted= */ false,
+ // TODO(b/241533526): adjust logic to include mute from AppOps
+ false /*muteFromPlaybackRestricted*/,
+ false /*muteFromClientVolume*/,
+ false /*muteFromVolumeShaper*/});
+ }
}
}
}
@@ -11255,9 +11420,13 @@
void MmapPlaybackThread::dumpInternals_l(int fd, const Vector<String16>& args)
{
MmapThread::dumpInternals_l(fd, args);
-
- dprintf(fd, " Stream type: %d Stream volume: %f HAL volume: %f Stream mute %d\n",
- mStreamType, streamVolume_l(), mHalVolFloat, streamMuted_l());
+ if (!audioserver_flags::portid_volume_management()) {
+ dprintf(fd, " Stream type: %d Stream volume: %f HAL volume: %f Stream mute %d",
+ mStreamType, streamVolume_l(), mHalVolFloat, streamMuted_l());
+ } else {
+ dprintf(fd, " HAL volume: %f", mHalVolFloat);
+ }
+ dprintf(fd, "\n");
dprintf(fd, " Master volume: %f Master mute %d\n", mMasterVolume, mMasterMute);
}
@@ -11431,6 +11600,7 @@
void BitPerfectThread::setTracksInternalMute(
std::map<audio_port_handle_t, bool>* tracksInternalMute) {
+ audio_utils::lock_guard _l(mutex());
for (auto& track : mTracks) {
if (auto it = tracksInternalMute->find(track->portId()); it != tracksInternalMute->end()) {
track->setInternalMute(it->second);
@@ -11447,6 +11617,11 @@
// Return the bit perfect track if all other tracks are muted
for (const auto& track : mActiveTracks) {
if (track->isBitPerfect()) {
+ if (track->getInternalMute()) {
+ // There can only be one bit-perfect client active. If it is mute internally,
+ // there is no need to stream bit-perfectly.
+ break;
+ }
bitPerfectTrack = track;
} else if (track->getFinalVolume() != 0.f) {
allOtherTracksMuted = false;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 10a77ef..4c4939b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -33,6 +33,7 @@
#include <fastpath/FastMixer.h>
#include <mediautils/Synchronization.h>
#include <mediautils/ThreadSnapshot.h>
+#include <psh_utils/Token.h>
#include <timing/MonotonicFrameCounter.h>
#include <utils/Log.h>
@@ -725,6 +726,7 @@
char mThreadName[kThreadNameLength]; // guaranteed NUL-terminated
sp<os::IPowerManager> mPowerManager GUARDED_BY(mutex());
sp<IBinder> mWakeLockToken GUARDED_BY(mutex());
+ std::unique_ptr<media::psh_utils::Token> mThreadToken GUARDED_BY(mutex());
const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first (outer) vector is
// keyed by session ID, the second (inner) by type UUID timeLow field
@@ -836,6 +838,12 @@
typename SortedVector<sp<T>>::iterator end() {
return mActiveTracks.end();
}
+ typename SortedVector<const sp<T>>::iterator begin() const {
+ return mActiveTracks.begin();
+ }
+ typename SortedVector<const sp<T>>::iterator end() const {
+ return mActiveTracks.end();
+ }
// Due to Binder recursion optimization, clear() and updatePowerState()
// cannot be called from a Binder thread because they may call back into
@@ -1011,6 +1019,9 @@
void setStreamVolume(audio_stream_type_t stream, float value) final EXCLUDES_ThreadBase_Mutex;
void setStreamMute(audio_stream_type_t stream, bool muted) final EXCLUDES_ThreadBase_Mutex;
float streamVolume(audio_stream_type_t stream) const final EXCLUDES_ThreadBase_Mutex;
+ status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume)
+ final EXCLUDES_ThreadBase_Mutex;
+
void setVolumeForOutput_l(float left, float right) const final;
sp<IAfTrack> createTrack_l(
@@ -1035,7 +1046,8 @@
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
bool isBitPerfect,
- audio_output_flags_t* afTrackFlags) final
+ audio_output_flags_t* afTrackFlags,
+ float volume) final
REQUIRES(audio_utils::AudioFlinger_Mutex);
bool isTrackActive(const sp<IAfTrack>& track) const final {
@@ -2385,6 +2397,8 @@
void setStreamVolume(audio_stream_type_t stream, float value) final EXCLUDES_ThreadBase_Mutex;
void setStreamMute(audio_stream_type_t stream, bool muted) final EXCLUDES_ThreadBase_Mutex;
float streamVolume(audio_stream_type_t stream) const final EXCLUDES_ThreadBase_Mutex;
+ status_t setPortsVolume(const std::vector<audio_port_handle_t>& portIds, float volume)
+ final EXCLUDES_ThreadBase_Mutex;
void setMasterMute_l(bool muted) REQUIRES(mutex()) { mMasterMute = muted; }
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index a0b85f7..cde7fc2 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -24,6 +24,7 @@
#include <android-base/macros.h> // DISALLOW_COPY_AND_ASSIGN
#include <datapath/TrackMetrics.h>
#include <mediautils/BatteryNotifier.h>
+#include <psh_utils/AudioPowerManager.h>
#include <atomic> // avoid transitive dependency
#include <list> // avoid transitive dependency
@@ -240,17 +241,13 @@
* Called when a track moves to active state to record its contribution to battery usage.
* Track state transitions should eventually be handled within the track class.
*/
- void beginBatteryAttribution() final {
- mBatteryStatsHolder.emplace(uid());
- }
+ void beginBatteryAttribution() final;
/**
* Called when a track moves out of the active state to record its contribution
* to battery usage.
*/
- void endBatteryAttribution() final {
- mBatteryStatsHolder.reset();
- }
+ void endBatteryAttribution() final;
protected:
DISALLOW_COPY_AND_ASSIGN(TrackBase);
@@ -347,7 +344,7 @@
size_t mBufferSize; // size of mBuffer in bytes
// we don't really need a lock for these
MirroredVariable<track_state> mState;
- const audio_attributes_t mAttr;
+ audio_attributes_t mAttr;
const uint32_t mSampleRate; // initial sample rate only; for tracks which
// support dynamic rates, the current value is in control block
const audio_format_t mFormat;
@@ -400,6 +397,7 @@
std::atomic_flag mChangeNotified = ATOMIC_FLAG_INIT;
// RAII object for battery stats book-keeping
std::optional<mediautils::BatteryStatsAudioHandle> mBatteryStatsHolder;
+ std::unique_ptr<media::psh_utils::Token> mTrackToken;
};
class PatchTrackBase : public PatchProxyBufferProvider, public virtual IAfPatchTrackBase
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 86c56c8..f88149c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -318,13 +318,25 @@
{
const auto thread = mThread.promote();
if (thread == nullptr) return;
- thread->getThreadloopExecutor().defer(
- [track = wp<TrackBase>::fromExisting(this)] {
- const auto actual = track.promote();
+ auto weakTrack = wp<TrackBase>::fromExisting(this);
+ thread->getThreadloopExecutor().defer([weakTrack] {
+ const auto actual = weakTrack.promote();
if (actual) actual->restartIfDisabled();
});
}
+void TrackBase::beginBatteryAttribution() {
+ mBatteryStatsHolder.emplace(uid());
+ if (media::psh_utils::AudioPowerManager::enabled()) {
+ mTrackToken = media::psh_utils::createAudioTrackToken(uid());
+ }
+}
+
+void TrackBase::endBatteryAttribution() {
+ mBatteryStatsHolder.reset();
+ mTrackToken.reset();
+}
+
PatchTrackBase::PatchTrackBase(const sp<ClientProxy>& proxy,
IAfThreadBase* thread, const Timeout& timeout)
: mProxy(proxy)
@@ -715,7 +727,8 @@
size_t frameCountToBeReady,
float speed,
bool isSpatialized,
- bool isBitPerfect) {
+ bool isBitPerfect,
+ float volume) {
return sp<Track>::make(thread,
client,
streamType,
@@ -736,7 +749,8 @@
frameCountToBeReady,
speed,
isSpatialized,
- isBitPerfect);
+ isBitPerfect,
+ volume);
}
// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
@@ -761,7 +775,8 @@
size_t frameCountToBeReady,
float speed,
bool isSpatialized,
- bool isBitPerfect)
+ bool isBitPerfect,
+ float volume)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -797,7 +812,8 @@
mFlags(flags),
mSpeed(speed),
mIsSpatialized(isSpatialized),
- mIsBitPerfect(isBitPerfect)
+ mIsBitPerfect(isBitPerfect),
+ mVolume(volume)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -843,6 +859,14 @@
thread->fastTrackAvailMask_l() &= ~(1 << i);
}
+ populateUsageAndContentTypeFromStreamType();
+
+ // Audio patch and call assistant volume are always max
+ if (mAttr.usage == AUDIO_USAGE_CALL_ASSISTANT
+ || mAttr.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+ mVolume = 1.0f;
+ }
+
mServerLatencySupported = checkServerLatencySupported(format, flags);
#ifdef TEE_SINK
mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
@@ -865,6 +889,62 @@
mTrackMetrics.logConstructor(creatorPid, uid, id(), traits, streamType);
}
+// When attributes are undefined, derive default values from stream type.
+// See AudioAttributes.java, usageForStreamType() and Builder.setInternalLegacyStreamType()
+void Track::populateUsageAndContentTypeFromStreamType() {
+ if (mAttr.usage == AUDIO_USAGE_UNKNOWN) {
+ switch (mStreamType) {
+ case AUDIO_STREAM_VOICE_CALL:
+ mAttr.usage = AUDIO_USAGE_VOICE_COMMUNICATION;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+ break;
+ case AUDIO_STREAM_SYSTEM:
+ mAttr.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ break;
+ case AUDIO_STREAM_RING:
+ mAttr.usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ break;
+ case AUDIO_STREAM_MUSIC:
+ mAttr.usage = AUDIO_USAGE_MEDIA;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ break;
+ case AUDIO_STREAM_ALARM:
+ mAttr.usage = AUDIO_USAGE_ALARM;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ break;
+ case AUDIO_STREAM_NOTIFICATION:
+ mAttr.usage = AUDIO_USAGE_NOTIFICATION;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ break;
+ case AUDIO_STREAM_DTMF:
+ mAttr.usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ break;
+ case AUDIO_STREAM_ACCESSIBILITY:
+ mAttr.usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+ break;
+ case AUDIO_STREAM_ASSISTANT:
+ mAttr.usage = AUDIO_USAGE_ASSISTANT;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+ break;
+ case AUDIO_STREAM_REROUTING:
+ case AUDIO_STREAM_PATCH:
+ mAttr.usage = AUDIO_USAGE_VIRTUAL_SOURCE;
+ // unknown content type
+ break;
+ case AUDIO_STREAM_CALL_ASSISTANT:
+ mAttr.usage = AUDIO_USAGE_CALL_ASSISTANT;
+ mAttr.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
Track::~Track()
{
ALOGV("%s(%d)", __func__, mId);
@@ -923,7 +1003,7 @@
result.appendFormat("Type Id Active Client Session Port Id S Flags "
" Format Chn mask SRate "
"ST Usg CT "
- " G db L dB R dB VS dB "
+ " G db L dB R dB VS dB PortVol dB "
" Server FrmCnt FrmRdy F Underruns Flushed BitPerfect InternalMute"
"%s\n",
isServerLatencySupported() ? " Latency" : "");
@@ -1009,7 +1089,7 @@
result.appendFormat("%7s %6u %7u %7u %2s 0x%03X "
"%08X %08X %6u "
"%2u %3x %2x "
- "%5.2g %5.2g %5.2g %5.2g%c "
+ "%5.2g %5.2g %5.2g %5.2g%c %11.2g "
"%08X %6zu%c %6zu %c %9u%c %7u %10s %12s",
active ? "yes" : "no",
(mClient == 0) ? getpid() : mClient->pid(),
@@ -1031,6 +1111,7 @@
20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
20.0 * log10(vsVolume.first), // VolumeShaper(s) total volume
vsVolume.second ? 'A' : ' ', // if any VolumeShapers active
+ 20.0 * log10(mVolume),
mCblk->mServer,
bufferSizeInFrames,
@@ -1532,6 +1613,16 @@
return INVALID_OPERATION;
}
+void Track::setPortVolume(float volume) {
+ mVolume = volume;
+ if (mType != TYPE_PATCH) {
+ // Do not recursively propagate a PatchTrack setPortVolume to
+ // downstream PatchTracks.
+ forEachTeePatchTrack_l([volume](const auto& patchTrack) {
+ patchTrack->setPortVolume(volume); });
+ }
+}
+
VolumeShaper::Status Track::applyVolumeShaper(
const sp<VolumeShaper::Configuration>& configuration,
const sp<VolumeShaper::Operation>& operation)
@@ -1587,59 +1678,6 @@
.gain = mFinalVolume,
};
- // When attributes are undefined, derive default values from stream type.
- // See AudioAttributes.java, usageForStreamType() and Builder.setInternalLegacyStreamType()
- if (mAttr.usage == AUDIO_USAGE_UNKNOWN) {
- switch (mStreamType) {
- case AUDIO_STREAM_VOICE_CALL:
- metadata.base.usage = AUDIO_USAGE_VOICE_COMMUNICATION;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SPEECH;
- break;
- case AUDIO_STREAM_SYSTEM:
- metadata.base.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
- break;
- case AUDIO_STREAM_RING:
- metadata.base.usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
- break;
- case AUDIO_STREAM_MUSIC:
- metadata.base.usage = AUDIO_USAGE_MEDIA;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_MUSIC;
- break;
- case AUDIO_STREAM_ALARM:
- metadata.base.usage = AUDIO_USAGE_ALARM;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
- break;
- case AUDIO_STREAM_NOTIFICATION:
- metadata.base.usage = AUDIO_USAGE_NOTIFICATION;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
- break;
- case AUDIO_STREAM_DTMF:
- metadata.base.usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
- break;
- case AUDIO_STREAM_ACCESSIBILITY:
- metadata.base.usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SPEECH;
- break;
- case AUDIO_STREAM_ASSISTANT:
- metadata.base.usage = AUDIO_USAGE_ASSISTANT;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SPEECH;
- break;
- case AUDIO_STREAM_REROUTING:
- metadata.base.usage = AUDIO_USAGE_VIRTUAL_SOURCE;
- // unknown content type
- break;
- case AUDIO_STREAM_CALL_ASSISTANT:
- metadata.base.usage = AUDIO_USAGE_CALL_ASSISTANT;
- metadata.base.content_type = AUDIO_CONTENT_TYPE_SPEECH;
- break;
- default:
- break;
- }
- }
-
metadata.channel_mask = mChannelMask;
strncpy(metadata.tags, mAttr.tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
*backInserter++ = metadata;
@@ -2191,14 +2229,13 @@
size_t frameCount,
const AttributionSourceState& attributionSource)
: Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
- audio_attributes_t{} /* currently unused for output track */,
+ AUDIO_ATTRIBUTES_INITIALIZER ,
sampleRate, format, channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, nullptr /* sharedBuffer */,
AUDIO_SESSION_NONE, getpid(), attributionSource, AUDIO_OUTPUT_FLAG_NONE,
TYPE_OUTPUT),
mActive(false), mSourceThread(sourceThread)
{
-
if (mCblk != NULL) {
mOutBuffer.frameCount = 0;
playbackThread->addOutputTrack_l(this);
@@ -2464,7 +2501,8 @@
* as soon as possible to have
* the lowest possible latency
* even if it might glitch. */
- float speed)
+ float speed,
+ float volume)
{
return sp<PatchTrack>::make(
playbackThread,
@@ -2478,7 +2516,8 @@
flags,
timeout,
frameCountToBeReady,
- speed);
+ speed,
+ volume);
}
PatchTrack::PatchTrack(IAfPlaybackThread* playbackThread,
@@ -2492,13 +2531,15 @@
audio_output_flags_t flags,
const Timeout& timeout,
size_t frameCountToBeReady,
- float speed)
+ float speed,
+ float volume)
: Track(playbackThread, NULL, streamType,
- audio_attributes_t{} /* currently unused for patch track */,
+ AUDIO_ATTRIBUTES_INITIALIZER,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
AUDIO_SESSION_NONE, getpid(), audioServerAttributionSource(getpid()), flags,
- TYPE_PATCH, AUDIO_PORT_HANDLE_NONE, frameCountToBeReady, speed),
+ TYPE_PATCH, AUDIO_PORT_HANDLE_NONE, frameCountToBeReady, speed,
+ false /*isSpatialized*/, false /*isBitPerfect*/, volume),
PatchTrackBase(mCblk ? new AudioTrackClientProxy(mCblk, mBuffer, frameCount, mFrameSize,
true /*clientInServer*/) : nullptr,
playbackThread, timeout)
@@ -3508,7 +3549,8 @@
bool isOut,
const android::content::AttributionSourceState& attributionSource,
pid_t creatorPid,
- audio_port_handle_t portId)
+ audio_port_handle_t portId,
+ float volume)
{
return sp<MmapTrack>::make(
thread,
@@ -3520,7 +3562,8 @@
isOut,
attributionSource,
creatorPid,
- portId);
+ portId,
+ volume);
}
MmapTrack::MmapTrack(IAfThreadBase* thread,
@@ -3532,7 +3575,8 @@
bool isOut,
const AttributionSourceState& attributionSource,
pid_t creatorPid,
- audio_port_handle_t portId)
+ audio_port_handle_t portId,
+ float volume)
: TrackBase(thread, NULL, attr, sampleRate, format,
channelMask, (size_t)0 /* frameCount */,
nullptr /* buffer */, (size_t)0 /* bufferSize */,
@@ -3543,10 +3587,15 @@
TYPE_DEFAULT, portId,
std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_MMAP) + std::to_string(portId)),
mPid(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.pid))),
- mSilenced(false), mSilencedNotified(false)
+ mSilenced(false), mSilencedNotified(false), mVolume(volume)
{
// Once this item is logged by the server, the client can add properties.
mTrackMetrics.logConstructor(creatorPid, uid(), id());
+ if (isOut && (attr.usage == AUDIO_USAGE_CALL_ASSISTANT
+ || attr.usage == AUDIO_USAGE_VIRTUAL_SOURCE)) {
+ // Audio patch and call assistant volume are always max
+ mVolume = 1.0f;
+ }
}
MmapTrack::~MmapTrack()
@@ -3625,8 +3674,8 @@
void MmapTrack::appendDumpHeader(String8& result) const
{
- result.appendFormat("Client Session Port Id Format Chn mask SRate Flags %s\n",
- isOut() ? "Usg CT": "Source");
+ result.appendFormat("Client Session Port Id Format Chn mask SRate Flags %s %s\n",
+ isOut() ? "Usg CT": "Source", isOut() ? "PortVol dB" : "");
}
void MmapTrack::appendDump(String8& result, bool active __unused) const
@@ -3641,6 +3690,7 @@
mAttr.flags);
if (isOut()) {
result.appendFormat("%3x %2x", mAttr.usage, mAttr.content_type);
+ result.appendFormat("%11.2g", 20.0 * log10(mVolume));
} else {
result.appendFormat("%6x", mAttr.source);
}
diff --git a/services/audioflinger/datapath/VolumePortInterface.h b/services/audioflinger/datapath/VolumePortInterface.h
new file mode 100644
index 0000000..fb1c463
--- /dev/null
+++ b/services/audioflinger/datapath/VolumePortInterface.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+
+namespace android {
+
+class VolumePortInterface : public virtual RefBase {
+public:
+ virtual void setPortVolume(float volume) = 0;
+ virtual float getPortVolume() const = 0;
+};
+
+} // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 35973c1..edcb805 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -147,7 +147,8 @@
std::vector<audio_io_handle_t> *secondaryOutputs,
output_type_t *outputType,
bool *isSpatialized,
- bool *isBitPerfect) = 0;
+ bool *isBitPerfect,
+ float *volume) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding
// stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
@@ -515,6 +516,18 @@
// for each output (destination device) it is attached to.
virtual status_t setStreamVolume(audio_stream_type_t stream, float volume,
audio_io_handle_t output, int delayMs = 0) = 0;
+ /**
+ * Set volume for given AudioTrack port ids for a particular output.
+ * For the same user setting, a given volume group and associated output port id
+ * can have different volumes for each output (destination device) it is attached to.
+ * @param ports to consider
+ * @param volume to apply
+ * @param output to consider
+ * @param delayMs to use
+ * @return NO_ERROR if successful
+ */
+ virtual status_t setPortsVolume(const std::vector<audio_port_handle_t>& ports, float volume,
+ audio_io_handle_t output, int delayMs = 0) = 0;
// function enabling to send proprietary informations directly from audio policy manager to
// audio hardware interface.
diff --git a/services/audiopolicy/common/include/SpatializerHelper.h b/services/audiopolicy/common/include/SpatializerHelper.h
new file mode 100644
index 0000000..2eb6613
--- /dev/null
+++ b/services/audiopolicy/common/include/SpatializerHelper.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <com_android_media_audio.h>
+#include <cutils/properties.h>
+
+namespace android {
+
+class SpatializerHelper {
+ public:
+ /**
+ * @brief Check if the stereo spatialization feature turned on by:
+ * - sysprop "ro.audio.stereo_spatialization_enabled" is true
+ * - com_android_media_audio_stereo_spatialization flag is on
+ *
+ * @return true if the stereo spatialization feature is enabled
+ * @return false if the stereo spatialization feature is not enabled
+ */
+ static bool isStereoSpatializationFeatureEnabled() {
+ static const bool stereoSpatializationEnabled =
+ property_get_bool("ro.audio.stereo_spatialization_enabled", false) &&
+ com_android_media_audio_stereo_spatialization();
+ return stereoSpatializationEnabled;
+ }
+};
+
+} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp
index 051e975..4dedcd6 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.bp
+++ b/services/audiopolicy/common/managerdefinitions/Android.bp
@@ -39,6 +39,8 @@
"android.media.audiopolicy-aconfig-cc",
"audioclient-types-aidl-cpp",
"audiopolicy-types-aidl-cpp",
+ "com.android.media.audioserver-aconfig-cc",
+ "libaconfig_storage_read_api_cc",
"libaudioclient_aidl_conversion",
"libaudiofoundation",
"libaudiopolicy",
@@ -51,6 +53,7 @@
"libmedia_helper",
"libutils",
"libxml2",
+ "server_configurable_flags",
],
export_shared_lib_headers: [
"libaudiofoundation",
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index c26ea10..0f2fe24 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -41,7 +41,8 @@
{
public:
AudioInputDescriptor(const sp<IOProfile>& profile,
- AudioPolicyClientInterface *clientInterface);
+ AudioPolicyClientInterface *clientInterface,
+ bool isPreemptor);
virtual ~AudioInputDescriptor() = default;
@@ -127,6 +128,8 @@
// active use case
void checkSuspendEffects();
+ bool isPreemptor() const { return mIsPreemptor; }
+
private:
void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
@@ -145,6 +148,7 @@
int32_t mGlobalActiveCount = 0; // non-client-specific activity ref count
EffectDescriptorCollection mEnabledEffects;
audio_input_flags_t& mFlags = AudioPortConfig::mFlags.input;
+ bool mIsPreemptor; // true if this input was opened after preemting another one
};
class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index a18cf1f..835fad2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -491,6 +491,13 @@
virtual std::string info() const override;
+ /**
+ * Finds all ports matching the given volume source.
+ * @param vs to be considered
+ * @return vector of ports following the given volume source.
+ */
+ std::vector<audio_port_handle_t> getPortsForVolumeSource(const VolumeSource& vs);
+
const sp<IOProfile> mProfile; // I/O profile this output derives from
audio_io_handle_t mIoHandle; // output handle
uint32_t mLatency; //
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 44f84b9..5a0fd97 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -30,9 +30,10 @@
namespace android {
AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile,
- AudioPolicyClientInterface *clientInterface)
+ AudioPolicyClientInterface *clientInterface,
+ bool isPreemptor)
: mProfile(profile)
- , mClientInterface(clientInterface)
+ , mClientInterface(clientInterface), mIsPreemptor(isPreemptor)
{
if (profile != NULL) {
profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
@@ -275,6 +276,9 @@
"%s invalid profile active count %u",
__func__, mProfile->curActiveCount);
mProfile->curActiveCount--;
+ // allow preemption again now that at least one client was able to
+ // capture on this input
+ mIsPreemptor = false;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 3c2f46a..2c41de4 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -27,6 +27,7 @@
#include "HwModule.h"
#include "TypeConverter.h"
#include "policy.h"
+#include <com_android_media_audioserver.h>
#include <media/AudioGain.h>
#include <media/AudioParameter.h>
#include <media/AudioPolicy.h>
@@ -34,6 +35,8 @@
// A device mask for all audio output devices that are considered "remote" when evaluating
// active output devices in isStreamActiveRemotely()
+namespace audioserver_flags = com::android::media::audioserver;
+
namespace android {
static const DeviceTypeSet& getAllOutRemoteDevices() {
@@ -498,17 +501,33 @@
const DeviceTypeSet& deviceTypes, uint32_t delayMs) {
// volume source active and more than one volume source is active, otherwise, no-op or let
// setVolume controlling SW and/or HW Gains
- if (!streamTypes.empty() && isActive(vs) && (getActiveVolumeSources().size() > 1)) {
- for (const auto& devicePort : devices()) {
- if (isSingleDeviceType(deviceTypes, devicePort->type()) &&
+ if (!audioserver_flags::portid_volume_management()) {
+ if (!streamTypes.empty() && isActive(vs) && (getActiveVolumeSources().size() > 1)) {
+ for (const auto& devicePort : devices()) {
+ if (isSingleDeviceType(deviceTypes, devicePort->type()) &&
devicePort->hasGainController(true /*canUseForVolume*/)) {
- float volumeAmpl = muted ? 0.0f : Volume::DbToAmpl(0);
- ALOGV("%s: output: %d, vs: %d, muted: %d, active vs count: %zu", __func__,
- mIoHandle, vs, muted, getActiveVolumeSources().size());
- for (const auto &stream : streamTypes) {
- mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ float volumeAmpl = muted ? 0.0f : Volume::DbToAmpl(0);
+ ALOGV("%s: output: %d, vs: %d, muted: %d, active vs count: %zu", __func__,
+ mIoHandle, vs, muted, getActiveVolumeSources().size());
+ for (const auto &stream : streamTypes) {
+ mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ }
+ return;
}
- return;
+ }
+ }
+ } else {
+ if (isActive(vs) && (getActiveVolumeSources().size() > 1)) {
+ for (const auto &devicePort: devices()) {
+ if (isSingleDeviceType(deviceTypes, devicePort->type()) &&
+ devicePort->hasGainController(true /*canUseForVolume*/)) {
+ float volumeAmpl = muted ? 0.0f : Volume::DbToAmpl(0);
+ ALOGV("%s: output: %d, vs: %d, muted: %d, active vs count: %zu", __func__,
+ mIoHandle, vs, muted, getActiveVolumeSources().size());
+ mClientInterface->setPortsVolume(
+ getPortsForVolumeSource(vs), volumeAmpl, mIoHandle, delayMs);
+ return;
+ }
}
}
}
@@ -524,6 +543,20 @@
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);
+ float volumeAmpl = Volume::DbToAmpl(volumeDb);
+ if (audioserver_flags::portid_volume_management()) {
+ mClientInterface->setPortsVolume(getPortsForVolumeSource(callVolSrc),
+ volumeAmpl, mIoHandle, delayMs);
+ } else {
+ mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL,
+ volumeAmpl, mIoHandle, delayMs);
+ }
+ }
+ }
return false;
}
if (streams.empty()) {
@@ -531,25 +564,34 @@
}
for (const auto& devicePort : devices()) {
// APM loops on all group, so filter on active group to set the port gain,
- // let the other groups set the stream volume as per legacy
+ // let the other groups set the sw volume as per legacy
// TODO: Pass in the device address and check against it.
if (isSingleDeviceType(deviceTypes, devicePort->type()) &&
devicePort->hasGainController(true) && isActive(vs)) {
ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
// @todo: here we might be in trouble if the SwOutput has several active clients with
// different Volume Source (or if we allow several curves within same volume group)
- //
- // @todo: default stream volume to max (0) when using HW Port gain?
- // Allows to set SW Gain on AudioFlinger if:
- // -volume group has explicit stream(s) associated
- // -volume group with no explicit stream(s) is the only active source on this output
- // Allows to mute SW Gain on AudioFlinger only for volume group with explicit stream(s)
- if (!streamTypes.empty() || (getActiveVolumeSources().size() == 1)) {
- const bool canMute = muted && (volumeDb != 0.0f) && !streamTypes.empty();
- float volumeAmpl = canMute ? 0.0f : Volume::DbToAmpl(0);
- for (const auto &stream : streams) {
- mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ if (!audioserver_flags::portid_volume_management()) {
+ // @todo: default stream volume to max (0) when using HW Port gain?
+ // Allows to set SW Gain on AudioFlinger if:
+ // -volume group has explicit stream(s) associated
+ // -volume group with no explicit stream(s) is the only active source on this
+ // output
+ // Allows to mute SW Gain on AudioFlinger only for volume group with explicit
+ // stream(s)
+ if (!streamTypes.empty() || (getActiveVolumeSources().size() == 1)) {
+ const bool canMute = muted && (volumeDb != 0.0f) && !streamTypes.empty();
+ float volumeAmpl = canMute ? 0.0f : Volume::DbToAmpl(0);
+ for (const auto &stream: streams) {
+ mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ }
}
+ } else {
+ float volumeAmpl = (muted && volumeDb != 0.0f) ? 0.0f : Volume::DbToAmpl(0);
+ ALOGV("%s: output: %d, vs: %d, active vs count: %zu", __func__,
+ mIoHandle, vs, getActiveVolumeSources().size());
+ mClientInterface->setPortsVolume(
+ getPortsForVolumeSource(vs), volumeAmpl, mIoHandle, delayMs);
}
AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb();
@@ -569,20 +611,47 @@
// Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled
float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
- mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
VolumeSource callVolSrc = getVoiceSource();
+ if (audioserver_flags::portid_volume_management()) {
+ if (callVolSrc != VOLUME_SOURCE_NONE) {
+ mClientInterface->setPortsVolume(getPortsForVolumeSource(callVolSrc), volumeAmpl,
+ mIoHandle, delayMs);
+ }
+ } else {
+ mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle,
+ delayMs);
+ }
if (callVolSrc != VOLUME_SOURCE_NONE) {
setCurVolume(callVolSrc, getCurVolume(vs), true);
}
}
- for (const auto &stream : streams) {
- ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
- mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
- mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ if (audioserver_flags::portid_volume_management()) {
+ ALOGV("%s output %d for volumeSource %d, volume %f, delay %d active=%d", __func__,
+ mIoHandle, vs, volumeDb, delayMs, isActive(vs));
+ mClientInterface->setPortsVolume(getPortsForVolumeSource(vs), volumeAmpl, mIoHandle,
+ delayMs);
+ } else {
+ for (const auto &stream : streams) {
+ ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
+ mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
+ mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ }
}
return true;
}
+std::vector<audio_port_handle_t> SwAudioOutputDescriptor::getPortsForVolumeSource(
+ const VolumeSource& vs)
+{
+ std::vector<audio_port_handle_t> portsForVolumeSource;
+ for (const auto& client : getClientIterable()) {
+ if (client->volumeSource() == vs) {
+ portsForVolumeSource.push_back(client->portId());
+ }
+ }
+ return portsForVolumeSource;
+}
+
status_t SwAudioOutputDescriptor::open(const audio_config_t *halConfig,
const audio_config_base_t *mixerConfig,
const DeviceVector &devices,
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp
index 3dc2229..c9a77a4 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp
@@ -38,6 +38,8 @@
shared_libs: [
"libaudiopolicycomponents",
"libaudiopolicyengineconfigurable",
+ "libbase",
+ "libcutils",
"liblog",
"libmedia_helper",
"libparameter",
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index b495f72..d42f2dc 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -449,10 +449,13 @@
// LE audio broadcast device is only used if:
// - No call is active
- // - either MEDIA or SONIFICATION_RESPECTFUL is the highest priority active strategy
- // OR the LE audio unicast device is not active
+ // - the highest priority active strategy is not PHONE or TRANSMITTED_THROUGH_SPEAKER
+ // OR the LE audio unicast device is not active
if (devices2.isEmpty() && !isInCall()
- && (strategy == STRATEGY_MEDIA || strategy == STRATEGY_SONIFICATION_RESPECTFUL)) {
+ // also skipping routing queries from PHONE and TRANSMITTED_THROUGH_SPEAKER here
+ // so this code is not dependent on breaks for other strategies above
+ && (strategy != STRATEGY_PHONE)
+ && (strategy != STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) {
legacy_strategy topActiveStrategy = STRATEGY_NONE;
for (const auto &ps : getOrderedProductStrategies()) {
if (outputs.isStrategyActive(ps)) {
@@ -462,8 +465,8 @@
}
}
- if (topActiveStrategy == STRATEGY_NONE || topActiveStrategy == STRATEGY_MEDIA
- || topActiveStrategy == STRATEGY_SONIFICATION_RESPECTFUL
+ if ((topActiveStrategy != STRATEGY_PHONE
+ && topActiveStrategy != STRATEGY_TRANSMITTED_THROUGH_SPEAKER)
|| !outputs.isAnyDeviceTypeActive(getAudioDeviceOutLeAudioUnicastSet())) {
devices2 =
availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_BLE_BROADCAST);
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 3583753..a9eff98 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -261,6 +261,7 @@
AudioPolicyInterface::output_type_t outputType;
bool isSpatialized;
bool isBitPerfect;
+ float volume;
// TODO b/182392769: use attribution source util
AttributionSourceState attributionSource;
@@ -268,7 +269,7 @@
attributionSource.token = sp<BBinder>::make();
if (mManager->getOutputForAttr(&attr, output, AUDIO_SESSION_NONE, &stream, attributionSource,
&config, &flags, selectedDeviceId, portId, {}, &outputType, &isSpatialized,
- &isBitPerfect) != OK) {
+ &isBitPerfect, &volume) != OK) {
return false;
}
if (*output == AUDIO_IO_HANDLE_NONE || *portId == AUDIO_PORT_HANDLE_NONE) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 74e77e8..558ce10 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -45,7 +45,6 @@
#include <android_media_audiopolicy.h>
#include <com_android_media_audioserver.h>
#include <cutils/bitops.h>
-#include <cutils/properties.h>
#include <media/AudioParameter.h>
#include <policy.h>
#include <private/android_filesystem_config.h>
@@ -55,6 +54,7 @@
#include <utils/Log.h>
#include "AudioPolicyManager.h"
+#include "SpatializerHelper.h"
#include "TypeConverter.h"
namespace android {
@@ -66,6 +66,7 @@
using android::media::audio::common::AudioDeviceAddress;
using android::media::audio::common::AudioPortDeviceExt;
using android::media::audio::common::AudioPortExt;
+using com::android::media::audioserver::fix_call_audio_patch;
using content::AttributionSourceState;
//FIXME: workaround for truncated touch sounds
@@ -721,8 +722,10 @@
audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
- disconnectTelephonyAudioSource(mCallRxSourceClient);
- disconnectTelephonyAudioSource(mCallTxSourceClient);
+ if (!fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
if (rxDevices.isEmpty()) {
ALOGW("%s() no selected output device", __func__);
@@ -775,6 +778,9 @@
// Use legacy routing method for voice calls via setOutputDevice() on primary output.
// Otherwise, create two audio patches for TX and RX path.
if (!createRxPatch) {
+ if (fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
if (!hasPrimaryOutput()) {
ALOGW("%s() no primary output available", __func__);
return INVALID_OPERATION;
@@ -797,6 +803,8 @@
}
}
connectTelephonyTxAudioSource(txSourceDevice, txSinkDevice, delayMs);
+ } else if (fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
}
if (waitMs != nullptr) {
*waitMs = muteWaitMs;
@@ -818,18 +826,38 @@
void AudioPolicyManager::connectTelephonyRxAudioSource(uint32_t delayMs)
{
- disconnectTelephonyAudioSource(mCallRxSourceClient);
+ const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
+
+ if (fix_call_audio_patch()) {
+ if (mCallRxSourceClient != nullptr) {
+ DeviceVector rxDevices =
+ mEngine->getOutputDevicesForAttributes(aa, nullptr, false /*fromCache*/);
+ ALOG_ASSERT(!rxDevices.isEmpty() || !mCallRxSourceClient->isConnected(),
+ "connectTelephonyRxAudioSource(): no device found for call RX source");
+ sp<DeviceDescriptor> rxDevice = rxDevices.itemAt(0);
+ if (mCallRxSourceClient->isConnected()
+ && mCallRxSourceClient->sinkDevice()->equals(rxDevice)) {
+ return;
+ }
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
+ } else {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
+
const struct audio_port_config source = {
.role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE,
.ext.device.type = AUDIO_DEVICE_IN_TELEPHONY_RX, .ext.device.address = ""
};
- const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
-
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+
status_t status = startAudioSourceInternal(&source, &aa, &portId, 0 /*uid*/,
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,
+ mCallRxSourceClient->srcDevice()->toString().c_str(),
+ mCallRxSourceClient->sinkDevice()->toString().c_str());
ALOGE_IF(mCallRxSourceClient == nullptr,
"%s failed to start Telephony Rx AudioSource", __func__);
}
@@ -848,15 +876,26 @@
const sp<DeviceDescriptor> &srcDevice, const sp<DeviceDescriptor> &sinkDevice,
uint32_t delayMs)
{
- disconnectTelephonyAudioSource(mCallTxSourceClient);
if (srcDevice == nullptr || sinkDevice == nullptr) {
ALOGW("%s could not create patch, invalid sink and/or source device(s)", __func__);
return;
}
+
+ if (fix_call_audio_patch()) {
+ if (mCallTxSourceClient != nullptr) {
+ if (mCallTxSourceClient->isConnected()
+ && mCallTxSourceClient->srcDevice()->equals(srcDevice)) {
+ return;
+ }
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
+ } else {
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
+
PatchBuilder patchBuilder;
patchBuilder.addSource(srcDevice).addSink(sinkDevice);
- ALOGV("%s between source %s and sink %s", __func__,
- srcDevice->toString().c_str(), sinkDevice->toString().c_str());
+
auto callTxSourceClientPortId = PolicyAudioPort::getNextUniqueId();
const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
@@ -873,6 +912,8 @@
mCallTxSourceClient, sinkDevice, patchBuilder.patch(), patchHandle, mUidCached,
delayMs);
ALOGE_IF(status != NO_ERROR, "%s() error %d creating TX audio patch", __func__, status);
+ ALOGV("%s portdID %d between source %s and sink %s", __func__, callTxSourceClientPortId,
+ srcDevice->toString().c_str(), sinkDevice->toString().c_str());
if (status == NO_ERROR) {
mAudioSources.add(callTxSourceClientPortId, mCallTxSourceClient);
}
@@ -1460,7 +1501,8 @@
std::vector<audio_io_handle_t> *secondaryOutputs,
output_type_t *outputType,
bool *isSpatialized,
- bool *isBitPerfect)
+ bool *isBitPerfect,
+ float *volume)
{
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1517,6 +1559,8 @@
outputDesc->mPolicyMix);
outputDesc->addClient(clientDesc);
+ *volume = Volume::DbToAmpl(outputDesc->getCurVolume(toVolumeSource(resultAttr)));
+
ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
*output, requestedPortId, *selectedDeviceId, *portId);
@@ -2353,11 +2397,15 @@
// If it is first bit-perfect client, reroute all clients that will be routed to
// the bit-perfect sink so that it is guaranteed only bit-perfect stream is active.
PortHandleVector clientsToInvalidate;
+ std::vector<sp<SwAudioOutputDescriptor>> outputsToResetDevice;
for (size_t i = 0; i < mOutputs.size(); i++) {
- if (mOutputs[i] == outputDesc ||
- mOutputs[i]->devices().filter(outputDesc->devices()).isEmpty()) {
+ if (mOutputs[i] == outputDesc || (!mOutputs[i]->devices().isEmpty() &&
+ mOutputs[i]->devices().filter(outputDesc->devices()).isEmpty())) {
continue;
}
+ if (mOutputs[i]->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) {
+ outputsToResetDevice.push_back(mOutputs[i]);
+ }
for (const auto& c : mOutputs[i]->getClientIterable()) {
clientsToInvalidate.push_back(c->portId());
}
@@ -2367,6 +2415,9 @@
__func__);
mpClientInterface->invalidateTracks(clientsToInvalidate);
}
+ for (const auto& output : outputsToResetDevice) {
+ resetOutputDevice(output, 0 /*delayMs*/, nullptr /*patchHandle*/);
+ }
}
}
}
@@ -3124,43 +3175,115 @@
}
}
+ bool isPreemptor = false;
if (!profile->canOpenNewIo()) {
- for (size_t i = 0; i < mInputs.size(); ) {
- sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
- if (desc->mProfile != profile) {
- i++;
- continue;
- }
- // if sound trigger, reuse input if used by other sound trigger on same session
- // else
- // reuse input if active client app is not in IDLE state
- //
- RecordClientVector clients = desc->clientsList();
- bool doClose = false;
- for (const auto& client : clients) {
- if (isSoundTrigger != client->isSoundTrigger()) {
+ if (com::android::media::audioserver::fix_input_sharing_logic()) {
+ // First pick best candidate for preemption (there may not be any):
+ // - Preempt and input if:
+ // - It has only strictly lower priority use cases than the new client
+ // - It has equal priority use cases than the new client, was not
+ // opened thanks to preemption or has been active since opened.
+ // - Order the preemption candidates by inactive first and priority second
+ sp<AudioInputDescriptor> closeCandidate;
+ int leastCloseRank = INT_MAX;
+ static const int sCloseActive = 0x100;
+
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
continue;
}
- if (client->isSoundTrigger()) {
- if (session == client->session()) {
+ sp<RecordClientDescriptor> topPrioClient = desc->getHighestPriorityClient();
+ if (topPrioClient == nullptr) {
+ continue;
+ }
+ int topPrio = source_priority(topPrioClient->source());
+ if (topPrio < source_priority(attributes.source)
+ || (topPrio == source_priority(attributes.source)
+ && !desc->isPreemptor())) {
+ int closeRank = (desc->isActive() ? sCloseActive : 0) + topPrio;
+ if (closeRank < leastCloseRank) {
+ leastCloseRank = closeRank;
+ closeCandidate = desc;
+ }
+ }
+ }
+
+ if (closeCandidate != nullptr) {
+ closeInput(closeCandidate->mIoHandle);
+ // Mark the new input as being issued from a preemption
+ // so that is will not be preempted later
+ isPreemptor = true;
+ } else {
+ // Then pick the best reusable input (There is always one)
+ // The order of preference is:
+ // 1) active inputs with same use case as the new client
+ // 2) inactive inputs with same use case
+ // 3) active inputs with different use cases
+ // 4) inactive inputs with different use cases
+ sp<AudioInputDescriptor> reuseCandidate;
+ int leastReuseRank = INT_MAX;
+ static const int sReuseDifferentUseCase = 0x100;
+
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
+ continue;
+ }
+ int reuseRank = sReuseDifferentUseCase;
+ for (const auto& client: desc->getClientIterable()) {
+ if (client->source() == attributes.source) {
+ reuseRank = 0;
+ break;
+ }
+ }
+ reuseRank += desc->isActive() ? 0 : 1;
+ if (reuseRank < leastReuseRank) {
+ leastReuseRank = reuseRank;
+ reuseCandidate = desc;
+ }
+ }
+ return reuseCandidate->mIoHandle;
+ }
+ } else { // fix_input_sharing_logic()
+ for (size_t i = 0; i < mInputs.size(); ) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
+ i++;
+ continue;
+ }
+ // if sound trigger, reuse input if used by other sound trigger on same session
+ // else
+ // reuse input if active client app is not in IDLE state
+ //
+ RecordClientVector clients = desc->clientsList();
+ bool doClose = false;
+ for (const auto& client : clients) {
+ if (isSoundTrigger != client->isSoundTrigger()) {
+ continue;
+ }
+ if (client->isSoundTrigger()) {
+ if (session == client->session()) {
+ return desc->mIoHandle;
+ }
+ continue;
+ }
+ if (client->active() && client->appState() != APP_STATE_IDLE) {
return desc->mIoHandle;
}
- continue;
+ doClose = true;
}
- if (client->active() && client->appState() != APP_STATE_IDLE) {
- return desc->mIoHandle;
+ if (doClose) {
+ closeInput(desc->mIoHandle);
+ } else {
+ i++;
}
- doClose = true;
- }
- if (doClose) {
- closeInput(desc->mIoHandle);
- } else {
- i++;
}
}
}
- sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile, mpClientInterface);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
+ profile, mpClientInterface, isPreemptor);
audio_config_t lConfig = AUDIO_CONFIG_INITIALIZER;
lConfig.sample_rate = profileSamplingRate;
@@ -3423,6 +3546,14 @@
bool enabled,
audio_stream_type_t streamToDriveAbs)
{
+ ALOGI("%s: deviceType 0x%X, enabled %d, streamToDriveAbs %d", __func__, deviceType, enabled,
+ 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__,
@@ -3430,12 +3561,7 @@
return BAD_VALUE;
}
- if (enabled) {
- mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
- } else {
- mAbsoluteVolumeDrivingStreams.erase(deviceType);
- }
-
+ mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
return NO_ERROR;
}
@@ -3713,9 +3839,10 @@
// 1: An offloaded output. If the effect ends up not being offloadable,
// AudioFlinger will invalidate the track and the offloaded output
// will be closed causing the effect to be moved to a PCM output.
- // 2: A deep buffer output
- // 3: The primary output
- // 4: the first output in the list
+ // 2: Spatializer output if the stereo spatializer feature enabled
+ // 3: A deep buffer output
+ // 4: The primary output
+ // 5: the first output in the list
DeviceVector devices = mEngine->getOutputDevicesForAttributes(
attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, false /*fromCache*/);
@@ -3730,28 +3857,36 @@
while (output == AUDIO_IO_HANDLE_NONE) {
audio_io_handle_t outputOffloaded = AUDIO_IO_HANDLE_NONE;
+ audio_io_handle_t outputSpatializer = AUDIO_IO_HANDLE_NONE;
audio_io_handle_t outputDeepBuffer = AUDIO_IO_HANDLE_NONE;
audio_io_handle_t outputPrimary = AUDIO_IO_HANDLE_NONE;
- for (audio_io_handle_t output : outputs) {
- sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
+ for (audio_io_handle_t outputLoop : outputs) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputLoop);
if (activeOnly && !desc->isActive(toVolumeSource(AUDIO_STREAM_MUSIC))) {
continue;
}
ALOGV("selectOutputForMusicEffects activeOnly %d output %d flags 0x%08x",
- activeOnly, output, desc->mFlags);
+ activeOnly, outputLoop, desc->mFlags);
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
- outputOffloaded = output;
+ outputOffloaded = outputLoop;
+ }
+ if ((desc->mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) {
+ if (SpatializerHelper::isStereoSpatializationFeatureEnabled()) {
+ outputSpatializer = outputLoop;
+ }
}
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
- outputDeepBuffer = output;
+ outputDeepBuffer = outputLoop;
}
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) != 0) {
- outputPrimary = output;
+ outputPrimary = outputLoop;
}
}
if (outputOffloaded != AUDIO_IO_HANDLE_NONE) {
output = outputOffloaded;
+ } else if (outputSpatializer != AUDIO_IO_HANDLE_NONE) {
+ output = outputSpatializer;
} else if (outputDeepBuffer != AUDIO_IO_HANDLE_NONE) {
output = outputDeepBuffer;
} else if (outputPrimary != AUDIO_IO_HANDLE_NONE) {
@@ -4278,8 +4413,9 @@
// As done in setDeviceConnectionState, we could also fix default device issue by
// preventing the force re-routing in case of default dev that distinguishes on address.
// Let's give back to engine full device choice decision however.
- bool forceRouting = !newDevices.isEmpty();
- if (outputDesc->mPreferredAttrInfo != nullptr && newDevices != outputDesc->devices()) {
+ bool newDevicesNotEmpty = !newDevices.isEmpty();
+ if (outputDesc->mPreferredAttrInfo != nullptr && newDevices != outputDesc->devices()
+ && newDevicesNotEmpty) {
// If the device is using preferred mixer attributes, the output need to reopen
// with default configuration when the new selected devices are different from
// current routing devices.
@@ -4287,9 +4423,10 @@
continue;
}
- waitMs = setOutputDevices(__func__, outputDesc, newDevices, forceRouting, delayMs,
- nullptr, !skipDelays /*requiresMuteCheck*/,
- !forceRouting /*requiresVolumeCheck*/, skipDelays);
+ waitMs = setOutputDevices(__func__, outputDesc, newDevices,
+ newDevicesNotEmpty /*force*/, delayMs,
+ nullptr /*patchHandle*/, !skipDelays /*requiresMuteCheck*/,
+ !newDevicesNotEmpty /*requiresVolumeCheck*/, skipDelays);
// Only apply special touch sound delay once
delayMs = 0;
}
@@ -5950,7 +6087,8 @@
float AudioPolicyManager::getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device)
{
- return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index, {device});
+ return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index,
+ {device}, /* adjustAttenuation= */false);
}
status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats,
@@ -6279,12 +6417,10 @@
// mode is not requested.
if (config != nullptr && *config != AUDIO_CONFIG_INITIALIZER) {
- static const bool stereo_spatialization_enabled =
- property_get_bool("ro.audio.stereo_spatialization_enabled", false);
const bool channel_mask_spatialized =
- (stereo_spatialization_enabled && com_android_media_audio_stereo_spatialization())
- ? audio_channel_mask_contains_stereo(config->channel_mask)
- : audio_is_channel_mask_spatialized(config->channel_mask);
+ SpatializerHelper::isStereoSpatializationFeatureEnabled()
+ ? audio_channel_mask_contains_stereo(config->channel_mask)
+ : audio_is_channel_mask_spatialized(config->channel_mask);
if (!channel_mask_spatialized) {
return false;
}
@@ -6699,8 +6835,8 @@
continue;
}
- sp<AudioInputDescriptor> inputDesc =
- new AudioInputDescriptor(inProfile, mpClientInterface);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
+ inProfile, mpClientInterface, false /*isPreemptor*/);
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
status_t status = inputDesc->open(nullptr,
@@ -7018,7 +7154,7 @@
continue;
}
- desc = new AudioInputDescriptor(profile, mpClientInterface);
+ desc = new AudioInputDescriptor(profile, mpClientInterface, false /*isPreemptor*/);
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
ALOGV("%s opening input for profile %s", __func__, profile->getTagName().c_str());
status = desc->open(nullptr, device, AUDIO_SOURCE_MIC,
@@ -8143,9 +8279,9 @@
float volumeDb = curves.volIndexToDb(deviceCategory, index);
if (com_android_media_audio_abs_volume_index_fix()) {
- if (mAbsoluteVolumeDrivingStreams.find(volumeDevice) !=
- mAbsoluteVolumeDrivingStreams.end()) {
- audio_attributes_t attributesToDriveAbs = mAbsoluteVolumeDrivingStreams[volumeDevice];
+ const auto it = mAbsoluteVolumeDrivingStreams.find(volumeDevice);
+ if (it != mAbsoluteVolumeDrivingStreams.end()) {
+ audio_attributes_t attributesToDriveAbs = it->second;
auto groupToDriveAbs = mEngine->getVolumeGroupForAttributes(attributesToDriveAbs);
if (groupToDriveAbs == VOLUME_GROUP_NONE) {
ALOGD("%s: no group matching with %s", __FUNCTION__,
@@ -8157,7 +8293,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});
@@ -8181,9 +8317,15 @@
VolumeSource volumeSource,
int index,
const DeviceTypeSet& deviceTypes,
+ bool adjustAttenuation,
bool computeInternalInteraction)
{
- float volumeDb = adjustDeviceAttenuationForAbsVolume(curves, volumeSource, index, deviceTypes);
+ float volumeDb;
+ if (adjustAttenuation) {
+ volumeDb = adjustDeviceAttenuationForAbsVolume(curves, volumeSource, index, deviceTypes);
+ } else {
+ volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(deviceTypes), index);
+ }
ALOGV("%s volume source %d, index %d, devices %s, compute internal %b ", __func__,
volumeSource, index, dumpDeviceTypes(deviceTypes).c_str(), computeInternalInteraction);
@@ -8204,7 +8346,8 @@
mOutputs.isActive(ringVolumeSrc, 0)) {
auto &ringCurves = getVolumeCurves(AUDIO_STREAM_RING);
const float ringVolumeDb = computeVolume(ringCurves, ringVolumeSrc, index, deviceTypes,
- /* computeInternalInteraction= */ false);
+ adjustAttenuation,
+ /* computeInternalInteraction= */false);
return ringVolumeDb - 4 > volumeDb ? ringVolumeDb - 4 : volumeDb;
}
@@ -8222,7 +8365,7 @@
int voiceVolumeIndex = voiceCurves.getVolumeIndex(deviceTypes);
const float maxVoiceVolDb =
computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, deviceTypes,
- /* computeInternalInteraction= */ false)
+ adjustAttenuation, /* computeInternalInteraction= */false)
+ IN_CALL_EARPIECE_HEADROOM_DB;
// FIXME: Workaround for call screening applications until a proper audio mode is defined
// to support this scenario : Exempt the RING stream from the audio cap if the audio was
@@ -8275,7 +8418,8 @@
musicVolumeSrc,
musicCurves.getVolumeIndex(musicDevice),
musicDevice,
- /* computeInternalInteraction= */ false);
+ adjustAttenuation,
+ /* computeInternalInteraction= */ false);
float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB;
if (volumeDb > minVolDb) {
@@ -8376,9 +8520,11 @@
}
float volumeDb = computeVolume(curves, volumeSource, index, deviceTypes);
+ const VolumeSource dtmfVolSrc = toVolumeSource(AUDIO_STREAM_DTMF, false);
if (outputDesc->isFixedVolume(deviceTypes) ||
// Force VoIP volume to max for bluetooth SCO/BLE device except if muted
- (index != 0 && (isVoiceVolSrc || isBtScoVolSrc) &&
+ (index != 0 && (isVoiceVolSrc || isBtScoVolSrc
+ || (isInCall() && (dtmfVolSrc == volumeSource))) &&
(isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device)
|| isSingleDeviceType(deviceTypes, audio_is_ble_out_device)))) {
volumeDb = 0.0f;
@@ -8417,11 +8563,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) &&
@@ -8969,6 +9123,13 @@
status_t AudioPolicyManager::getDevicesForAttributes(
const audio_attributes_t &attr, DeviceVector &devices, bool forVolume) {
+ // attr containing source set by AudioAttributes.Builder.setCapturePreset() has precedence
+ // over any usage or content type also present in attr.
+ if (com::android::media::audioserver::enable_audio_input_device_routing() &&
+ attr.source != AUDIO_SOURCE_INVALID) {
+ return getInputDevicesForAttributes(attr, devices);
+ }
+
// Devices are determined in the following precedence:
//
// 1) Devices associated with a dynamic policy matching the attributes. This is often
@@ -9032,6 +9193,15 @@
return NO_ERROR;
}
+status_t AudioPolicyManager::getInputDevicesForAttributes(
+ const audio_attributes_t &attr, DeviceVector &devices) {
+ devices = DeviceVector(
+ mEngine->getInputDeviceForAttributes(attr, 0 /*uid unknown here*/,
+ AUDIO_SESSION_NONE,
+ nullptr /* mix */));
+ return NO_ERROR;
+}
+
status_t AudioPolicyManager::getProfilesForDevices(const DeviceVector& devices,
AudioProfileVector& audioProfiles,
uint32_t flags,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 9d2166a..9ad2ea5 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -128,7 +128,8 @@
std::vector<audio_io_handle_t> *secondaryOutputs,
output_type_t *outputType,
bool *isSpatialized,
- bool *isBitPerfect) override;
+ bool *isBitPerfect,
+ float *volume) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual bool releaseOutput(audio_port_handle_t portId);
@@ -590,6 +591,8 @@
* @param index index to match in the volume curves for the calculation
* @param deviceTypes devices that should be considered in the volume curves for the
* calculation
+ * @param adjustAttenuation boolean indicating whether we should adjust the value to
+ * avoid double attenuation when controlling an avrcp device
* @param computeInternalInteraction boolean indicating whether recursive volume computation
* should continue within the volume computation. Defaults to {@code true} so the
* volume interactions can be computed. Calls within the method should always set the
@@ -598,6 +601,7 @@
*/
virtual float computeVolume(IVolumeCurves &curves, VolumeSource volumeSource,
int index, const DeviceTypeSet& deviceTypes,
+ bool adjustAttenuation = true,
bool computeInternalInteraction = true);
// rescale volume index from srcStream within range of dstStream
@@ -1362,6 +1366,11 @@
DeviceVector &devices,
bool forVolume);
+ // A helper method used by getDevicesForAttributes to retrieve input devices when
+ // capture preset is available in the given audio attributes parameter.
+ status_t getInputDevicesForAttributes(const audio_attributes_t &attr,
+ DeviceVector &devices);
+
status_t getProfilesForDevices(const DeviceVector& devices,
AudioProfileVector& audioProfiles,
uint32_t flags,
diff --git a/services/audiopolicy/permission/Android.bp b/services/audiopolicy/permission/Android.bp
index d5f59a0..cfbeaae 100644
--- a/services/audiopolicy/permission/Android.bp
+++ b/services/audiopolicy/permission/Android.bp
@@ -34,8 +34,8 @@
shared_libs: [
"libbase",
"libbinder",
- "libutils",
"liblog",
+ "libutils",
],
host_supported: true,
@@ -43,21 +43,21 @@
integer_overflow: true,
},
cflags: [
- "-Wall",
- "-Wdeprecated",
- "-Wextra",
- "-Werror=format",
- "-Wextra-semi",
- "-Wthread-safety",
- "-Wconditional-uninitialized",
- "-Wimplicit-fallthrough",
- "-Wreorder-init-list",
- "-Werror=reorder-init-list",
- "-Wshadow-all",
- "-Wunreachable-code-aggressive",
- "-Werror",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-Wall",
+ "-Wconditional-uninitialized",
+ "-Wdeprecated",
+ "-Werror",
+ "-Werror=format",
+ "-Werror=reorder-init-list",
+ "-Wextra",
+ "-Wextra-semi",
+ "-Wimplicit-fallthrough",
+ "-Wreorder-init-list",
+ "-Wshadow-all",
+ "-Wthread-safety",
+ "-Wunreachable-code-aggressive",
],
tidy: true,
tidy_checks: [
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/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 6d2c772..363dfa7 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -193,6 +193,16 @@
delay_ms);
}
+status_t AudioPolicyService::AudioPolicyClient::setPortsVolume(
+ const std::vector<audio_port_handle_t> &ports, float volume, audio_io_handle_t output,
+ int delayMs)
+{
+ if (ports.empty()) {
+ return NO_ERROR;
+ }
+ return mAudioPolicyService->setPortsVolume(ports, volume, output, delayMs);
+}
+
void AudioPolicyService::AudioPolicyClient::setParameters(audio_io_handle_t io_handle,
const String8& keyValuePairs,
int delay_ms)
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 768cd07..d436aac 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;
@@ -67,6 +86,10 @@
using media::audio::common::Int;
constexpr int kDefaultVirtualDeviceId = 0;
+namespace {
+constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED;
+constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED;
+}
const std::vector<audio_usage_t>& SYSTEM_USAGES = {
AUDIO_USAGE_CALL_ASSISTANT,
@@ -86,31 +109,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 +166,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 +222,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 +248,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 +300,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 +392,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 +401,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);
@@ -382,6 +427,7 @@
AudioPolicyInterface::output_type_t outputType;
bool isSpatialized = false;
bool isBitPerfect = false;
+ float volume;
status_t result = mAudioPolicyManager->getOutputForAttr(&attr, &output, session,
&stream,
attributionSource,
@@ -390,7 +436,8 @@
&secondaryOutputs,
&outputType,
&isSpatialized,
- &isBitPerfect);
+ &isBitPerfect,
+ &volume);
// FIXME: Introduce a way to check for the the telephony device before opening the output
if (result == NO_ERROR) {
@@ -400,18 +447,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 +483,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(
@@ -448,6 +501,7 @@
_aidl_return->isBitPerfect = isBitPerfect;
_aidl_return->attr = VALUE_OR_RETURN_BINDER_STATUS(
legacy2aidl_audio_attributes_t_AudioAttributes(attr));
+ _aidl_return->volume = volume;
} else {
_aidl_return->configBase.format = VALUE_OR_RETURN_BINDER_STATUS(
legacy2aidl_audio_format_t_AudioFormatDescription(config.format));
@@ -630,8 +684,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 +697,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 +709,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 +728,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 +751,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 +799,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",
@@ -823,13 +904,13 @@
std::stringstream msg;
msg << "Audio recording on session " << client->session;
+ const auto permitted = startRecording(client->attributionSource, client->virtualDeviceId,
+ String16(msg.str().c_str()), client->attributes.source);
// check calling permissions
- if (!(startRecording(client->attributionSource, client->virtualDeviceId,
- String16(msg.str().c_str()), client->attributes.source)
- || client->attributes.source == AUDIO_SOURCE_FM_TUNER
- || client->attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX
- || client->attributes.source == AUDIO_SOURCE_ECHO_REFERENCE)) {
+ if (permitted == PERMISSION_HARD_DENIED && client->attributes.source != AUDIO_SOURCE_FM_TUNER
+ && client->attributes.source != AUDIO_SOURCE_REMOTE_SUBMIX
+ && client->attributes.source != AUDIO_SOURCE_ECHO_REFERENCE) {
ALOGE("%s permission denied: recording not allowed for attribution source %s",
__func__, client->attributionSource.toString().c_str());
return binderStatusFromStatusT(PERMISSION_DENIED);
@@ -849,13 +930,17 @@
return binderStatusFromStatusT(INVALID_OPERATION);
}
- // Force the possibly silenced client to be unsilenced since we just called
- // startRecording (i.e. we have assumed it is unsilenced).
- // At this point in time, the client is inactive, so no calls to appops are sent in
- // setAppState_l.
- // This ensures existing clients have the same behavior as new clients (starting unsilenced).
+ // Force the possibly silenced client to match the state on the appops side
+ // following the call to startRecording (i.e. unsilenced iff call succeeded)
+ // At this point in time, the client is inactive, so no calls to appops are
+ // sent in setAppState_l. This ensures existing clients have the same
+ // behavior as new clients.
// TODO(b/282076713)
- setAppState_l(client, APP_STATE_TOP);
+ if (permitted == PERMISSION_GRANTED) {
+ setAppState_l(client, APP_STATE_TOP);
+ } else {
+ setAppState_l(client, APP_STATE_IDLE);
+ }
client->active = true;
client->startTimeNs = systemTime();
@@ -941,8 +1026,10 @@
client->active = false;
client->startTimeNs = 0;
updateUidStates_l();
- finishRecording(client->attributionSource, client->virtualDeviceId,
- client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, client->virtualDeviceId,
+ client->attributes.source);
+ }
}
return binderStatusFromStatusT(status);
@@ -971,7 +1058,11 @@
updateUidStates_l();
// finish the recording app op
- finishRecording(client->attributionSource, client->virtualDeviceId, client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, client->virtualDeviceId,
+ client->attributes.source);
+ }
+
AutoCallerClear acc;
return binderStatusFromStatusT(mAudioPolicyManager->stopInput(portId));
}
@@ -1025,8 +1116,15 @@
Status AudioPolicyService::setDeviceAbsoluteVolumeEnabled(const AudioDevice& deviceAidl,
bool enabled,
AudioStreamType streamToDriveAbsAidl) {
- audio_stream_type_t streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
- aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+ ALOGI("%s: deviceAidl %s, enabled %d, streamToDriveAbsAidl %d", __func__,
+ deviceAidl.toString().c_str(), enabled, streamToDriveAbsAidl);
+
+ audio_stream_type_t streamToDriveAbs = AUDIO_STREAM_DEFAULT;
+ if (enabled) {
+ streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+ }
+
audio_devices_t deviceType;
std::string address;
RETURN_BINDER_STATUS_IF_ERROR(
@@ -1035,12 +1133,12 @@
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) {
- return binderStatusFromStatusT(BAD_VALUE);
- }
+
audio_utils::lock_guard _l(mMutex);
AutoCallerClear acc;
return binderStatusFromStatusT(
@@ -1059,7 +1157,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 +1183,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 +1235,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 +1543,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 +1571,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 +1588,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 +1602,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 +1622,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 +1683,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 +1758,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 +1779,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 +1829,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 +1894,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 +1912,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 +1978,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 +1994,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 +2015,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 +2031,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 +2061,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 +2090,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 d529130..7b7275e 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -61,6 +61,10 @@
static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY");
+namespace {
+constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED;
+}
+
// Creates an association between Binder code to name for IAudioPolicyService.
#define IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST \
BINDER_METHOD_ENTRY(onNewAudioModulesAvailable) \
@@ -588,12 +592,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) {
@@ -611,17 +616,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() {
@@ -637,12 +642,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,
@@ -1215,9 +1220,10 @@
} else {
std::stringstream msg;
msg << "Audio recording un-silenced on session " << client->session;
- if (!startRecording(client->attributionSource, client->virtualDeviceId,
- String16(msg.str().c_str()), client->attributes.source)) {
- silenced = true;
+ if (startRecording(client->attributionSource, client->virtualDeviceId,
+ String16(msg.str().c_str()), client->attributes.source)
+ != PERMISSION_GRANTED) {
+ return;
}
}
}
@@ -1415,7 +1421,7 @@
}
}, mediautils::TimeCheck::getDefaultTimeoutDuration(),
mediautils::TimeCheck::getDefaultSecondChanceDuration(),
- true /* crashOnTimeout */);
+ !property_get_bool("audio.timecheck.disabled", false) /* crashOnTimeout */);
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
@@ -1814,6 +1820,16 @@
data->mIO);
ul.lock();
}break;
+ case SET_PORTS_VOLUME: {
+ VolumePortsData *data = (VolumePortsData *)command->mParam.get();
+ ALOGV("AudioCommandThread() processing set volume Ports %s volume %f, \
+ output %d", data->dumpPorts().c_str(), data->mVolume, data->mIO);
+ ul.unlock();
+ command->mStatus = AudioSystem::setPortsVolume(data->mPorts,
+ data->mVolume,
+ data->mIO);
+ ul.lock();
+ } break;
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam.get();
ALOGV("AudioCommandThread() processing set parameters string %s, io %d",
@@ -2147,6 +2163,23 @@
return sendCommand(command, delayMs);
}
+status_t AudioPolicyService::AudioCommandThread::volumePortsCommand(
+ const std::vector<audio_port_handle_t> &ports, float volume, audio_io_handle_t output,
+ int delayMs)
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = SET_PORTS_VOLUME;
+ sp<VolumePortsData> data = new VolumePortsData();
+ data->mPorts = ports;
+ data->mVolume = volume;
+ data->mIO = output;
+ command->mParam = data;
+ command->mWaitStatus = true;
+ ALOGV("AudioCommandThread() adding set volume ports %s, volume %f, output %d",
+ data->dumpPorts().c_str(), volume, output);
+ return sendCommand(command, delayMs);
+}
+
status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs,
int delayMs)
@@ -2477,6 +2510,31 @@
delayMs = 1;
} break;
+ case SET_PORTS_VOLUME: {
+ VolumePortsData *data = (VolumePortsData *)command->mParam.get();
+ VolumePortsData *data2 = (VolumePortsData *)command2->mParam.get();
+ if (data->mIO != data2->mIO) break;
+ // Can remove command only if port ids list is the same, otherwise, remove from
+ // command 2 all port whose volume will be replaced with command 1 volume.
+ std::vector<audio_port_handle_t> portsOnlyInCommand2{};
+ std::copy_if(data2->mPorts.begin(), data2->mPorts.end(),
+ std::back_inserter(portsOnlyInCommand2), [&](const auto &portId) {
+ return std::find(data->mPorts.begin(), data->mPorts.end(), portId) ==
+ data->mPorts.end();
+ });
+ if (!portsOnlyInCommand2.empty()) {
+ data2->mPorts = portsOnlyInCommand2;
+ break;
+ }
+ ALOGV("Filtering out volume command on output %d for ports %s",
+ data->mIO, data->dumpPorts().c_str());
+ removedCommands.add(command2);
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
+ } break;
+
case SET_VOICE_VOLUME: {
VoiceVolumeData *data = (VoiceVolumeData *)command->mParam.get();
VoiceVolumeData *data2 = (VoiceVolumeData *)command2->mParam.get();
@@ -2624,6 +2682,12 @@
output, delayMs);
}
+int AudioPolicyService::setPortsVolume(const std::vector<audio_port_handle_t> &ports, float volume,
+ audio_io_handle_t output, int delayMs)
+{
+ return (int)mAudioCommandThread->volumePortsCommand(ports, volume, output, delayMs);
+}
+
int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
{
return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 428e560..e22637f 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -47,6 +47,7 @@
#include <android/hardware/BnSensorPrivacyListener.h>
#include <android/content/AttributionSourceState.h>
+#include <numeric>
#include <unordered_map>
namespace android {
@@ -354,6 +355,21 @@
float volume,
audio_io_handle_t output,
int delayMs = 0);
+
+ /**
+ * Set a volume on AudioTrack port id(s) for a particular output.
+ * For the same user setting, a volume group (and associated given port of the
+ * client's track) can have different volumes for each output destination device
+ * it is attached to.
+ *
+ * @param ports to consider
+ * @param volume to set
+ * @param output to consider
+ * @param delayMs to use
+ * @return NO_ERROR if successful
+ */
+ virtual status_t setPortsVolume(const std::vector<audio_port_handle_t> &ports, float volume,
+ audio_io_handle_t output, int delayMs = 0);
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
void doOnNewAudioModulesAvailable();
@@ -452,8 +468,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();
@@ -577,6 +593,7 @@
// commands for tone AudioCommand
enum {
SET_VOLUME,
+ SET_PORTS_VOLUME,
SET_PARAMETERS,
SET_VOICE_VOLUME,
STOP_OUTPUT,
@@ -610,6 +627,8 @@
void exit();
status_t volumeCommand(audio_stream_type_t stream, float volume,
audio_io_handle_t output, int delayMs = 0);
+ status_t volumePortsCommand(const std::vector<audio_port_handle_t> &ports,
+ float volume, audio_io_handle_t output, int delayMs = 0);
status_t parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
@@ -684,6 +703,20 @@
audio_io_handle_t mIO;
};
+ class VolumePortsData : public AudioCommandData {
+ public:
+ std::vector<audio_port_handle_t> mPorts;
+ float mVolume;
+ audio_io_handle_t mIO;
+ std::string dumpPorts() {
+ return std::string("volume ") + std::to_string(mVolume) + " on IO " +
+ std::to_string(mIO) + " and ports " +
+ std::accumulate(std::begin(mPorts), std::end(mPorts), std::string{},
+ [] (const std::string& ls, int rs) {
+ return ls + std::to_string(rs) + " "; });
+ }
+ };
+
class ParametersData : public AudioCommandData {
public:
audio_io_handle_t mIO;
@@ -824,6 +857,19 @@
// set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes
// for each output (destination device) it is attached to.
virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0);
+ /**
+ * Set a volume on port(s) for a particular output. For the same user setting, a volume
+ * group (and associated given port of the client's track) can have different volumes for
+ * each output (destination device) it is attached to.
+ *
+ * @param ports to consider
+ * @param volume to set
+ * @param output to consider
+ * @param delayMs to use
+ * @return NO_ERROR if successful
+ */
+ status_t setPortsVolume(const std::vector<audio_port_handle_t> &ports, float volume,
+ audio_io_handle_t output, int delayMs = 0) override;
// function enabling to send proprietary informations directly from audio policy manager to audio hardware interface.
virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0);
@@ -977,13 +1023,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 +1062,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/AudioRecordClient.cpp b/services/audiopolicy/service/AudioRecordClient.cpp
index 6d8b3cf..733f0d6 100644
--- a/services/audiopolicy/service/AudioRecordClient.cpp
+++ b/services/audiopolicy/service/AudioRecordClient.cpp
@@ -18,15 +18,17 @@
#include "AudioRecordClient.h"
#include "AudioPolicyService.h"
+#include "binder/AppOpsManager.h"
#include <android_media_audiopolicy.h>
+#include <algorithm>
+
namespace android::media::audiopolicy {
namespace audiopolicy_flags = android::media::audiopolicy;
using android::AudioPolicyService;
namespace {
-bool isAppOpSource(audio_source_t source)
-{
+bool isAppOpSource(audio_source_t source) {
switch (source) {
case AUDIO_SOURCE_FM_TUNER:
case AUDIO_SOURCE_ECHO_REFERENCE:
@@ -55,7 +57,40 @@
bool doesPackageTargetAtLeastU(std::string_view packageName) {
return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__;
}
-}
+
+class AttrSourceItr {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = AttributionSourceState;
+ using pointer = const value_type*;
+ using reference = const value_type&;
+
+ AttrSourceItr() : mAttr(nullptr) {}
+
+ AttrSourceItr(const AttributionSourceState& attr) : mAttr(&attr) {}
+
+ reference operator*() const { return *mAttr; }
+ pointer operator->() const { return mAttr; }
+
+ AttrSourceItr& operator++() {
+ mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr;
+ return *this;
+ }
+
+ AttrSourceItr operator++(int) {
+ AttrSourceItr tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ friend bool operator==(const AttrSourceItr& a, const AttrSourceItr& b) = default;
+
+ static AttrSourceItr end() { return AttrSourceItr{}; }
+private:
+ const AttributionSourceState * mAttr;
+};
+} // anonymous
// static
sp<OpRecordAudioMonitor>
@@ -110,15 +145,24 @@
mOpCallback = new RecordAudioOpCallback(this);
ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str());
- int flags = doesPackageTargetAtLeastU(
- mAttributionSource.packageName.value_or("")) ?
- AppOpsManager::WATCH_FOREGROUND_CHANGES : 0;
- // TODO: We need to always watch AppOpsManager::OP_RECORD_AUDIO too
- // since it controls the mic permission for legacy apps.
- mAppOpsManager.startWatchingMode(mAppOp, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
- mAttributionSource.packageName.value_or(""))),
- flags,
- mOpCallback);
+ int flags = doesPackageTargetAtLeastU(mAttributionSource.packageName.value_or(""))
+ ? AppOpsManager::WATCH_FOREGROUND_CHANGES
+ : 0;
+
+ const auto reg = [&](int32_t op) {
+ std::for_each(AttrSourceItr{mAttributionSource}, AttrSourceItr::end(),
+ [&](const auto& attr) {
+ mAppOpsManager.startWatchingMode(
+ op,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ attr.packageName.value_or(""))),
+ flags, mOpCallback);
+ });
+ };
+ reg(mAppOp);
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) {
+ reg(AppOpsManager::OP_RECORD_AUDIO);
+ }
}
bool OpRecordAudioMonitor::hasOp() const {
@@ -131,14 +175,20 @@
// due to the UID in createIfNeeded(). As a result for those record track, it's:
// - not called from constructor,
// - not called from RecordAudioOpCallback because the callback is not installed in this case
-void OpRecordAudioMonitor::checkOp(bool updateUidStates)
-{
- // TODO: We need to always check AppOpsManager::OP_RECORD_AUDIO too
- // since it controls the mic permission for legacy apps.
- const int32_t mode = mAppOpsManager.checkOp(mAppOp,
- mAttributionSource.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
- mAttributionSource.packageName.value_or(""))));
- bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
+void OpRecordAudioMonitor::checkOp(bool updateUidStates) {
+ const auto check = [&](int32_t op) -> bool {
+ return std::all_of(
+ AttrSourceItr{mAttributionSource}, AttrSourceItr::end(), [&](const auto& x) {
+ return mAppOpsManager.checkOp(op, x.uid,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ x.packageName.value_or("")))) ==
+ AppOpsManager::MODE_ALLOWED;
+ });
+ };
+ bool hasIt = check(mAppOp);
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) {
+ hasIt = hasIt && check(AppOpsManager::OP_RECORD_AUDIO);
+ }
if (audiopolicy_flags::record_audio_device_aware_permission()) {
const bool canRecord = recordingAllowed(mAttributionSource, mVirtualDeviceId, mAttr.source);
@@ -173,4 +223,4 @@
}
}
-} // android::media::audiopolicy::internal
+} // namespace android::media::audiopolicy
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index c98f8df..c7740ad 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -29,9 +29,7 @@
#include <android/content/AttributionSourceState.h>
#include <android/sysprop/BluetoothProperties.sysprop.h>
#include <audio_utils/fixedfft.h>
-#include <com_android_media_audio.h>
#include <cutils/bitops.h>
-#include <cutils/properties.h>
#include <hardware/sensors.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -43,6 +41,7 @@
#include <utils/Thread.h>
#include "Spatializer.h"
+#include "SpatializerHelper.h"
namespace android {
@@ -398,12 +397,10 @@
return status;
}
for (const auto channelMask : channelMasks) {
- static const bool stereo_spatialization_enabled =
- property_get_bool("ro.audio.stereo_spatialization_enabled", false);
const bool channel_mask_spatialized =
- (stereo_spatialization_enabled && com_android_media_audio_stereo_spatialization())
- ? audio_channel_mask_contains_stereo(channelMask)
- : audio_is_channel_mask_spatialized(channelMask);
+ SpatializerHelper::isStereoSpatializationFeatureEnabled()
+ ? audio_channel_mask_contains_stereo(channelMask)
+ : audio_is_channel_mask_spatialized(channelMask);
if (!channel_mask_spatialized) {
ALOGW("%s: ignoring channelMask:%#x", __func__, channelMask);
continue;
@@ -936,7 +933,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 +942,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 +967,7 @@
outputChanged = mOutput != output;
mOutput = output;
- mNumActiveTracks = numActiveTracks;
+ mActiveTracksMasks = activeTracksMasks;
AudioSystem::addSupportedLatencyModesCallback(this);
std::vector<audio_latency_mode_t> latencyModes;
@@ -1008,7 +1006,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 +1050,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 +1115,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 +1147,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 +1257,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 +1269,9 @@
mDisplayOrientation);
// 4. Show flag or property state.
- base::StringAppendF(&ss, "%sStereo Spatialization: %s\n", prefixSpace.c_str(),
- com_android_media_audio_stereo_spatialization() ? "true" : "false");
+ base::StringAppendF(
+ &ss, "%sStereo Spatialization: %s\n", prefixSpace.c_str(),
+ SpatializerHelper::isStereoSpatializationFeatureEnabled() ? "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/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index 483f827..79c25ab 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -91,6 +91,7 @@
*input = mNextIoHandle++;
mOpenedInputs.insert(*input);
ALOGD("%s: opened input %d", __func__, *input);
+ mOpenInputCallsCount++;
return NO_ERROR;
}
@@ -105,6 +106,7 @@
return BAD_VALUE;
}
ALOGD("%s: closed input %d", __func__, input);
+ mCloseInputCallsCount++;
return NO_ERROR;
}
@@ -279,6 +281,18 @@
auto it = mTracksInternalMute.find(portId);
return it == mTracksInternalMute.end() ? false : it->second;
}
+ void resetInputApiCallsCounters() {
+ mOpenInputCallsCount = 0;
+ mCloseInputCallsCount = 0;
+ }
+
+ size_t getCloseInputCallsCount() const {
+ return mCloseInputCallsCount;
+ }
+
+ size_t getOpenInputCallsCount() const {
+ return mOpenInputCallsCount;
+ }
std::optional<audio_output_flags_t> getOpenOutputFlags(audio_io_handle_t output) const {
if (auto iter = mOpenedOutputs.find(output); iter != mOpenedOutputs.end()) {
@@ -301,6 +315,8 @@
std::set<audio_channel_mask_t> mSupportedChannelMasks;
std::map<audio_port_handle_t, bool> mTracksInternalMute;
std::set<audio_io_handle_t> mOpenedInputs;
+ size_t mOpenInputCallsCount = 0;
+ size_t mCloseInputCallsCount = 0;
std::map<audio_io_handle_t, audio_output_flags_t> mOpenedOutputs;
};
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index 6116eab..9ddfd6c 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -58,6 +58,10 @@
float /*volume*/,
audio_io_handle_t /*output*/,
int /*delayMs*/) override { return NO_INIT; }
+
+ status_t setPortsVolume(const std::vector<audio_port_handle_t>& /*ports*/, float /*volume*/,
+ audio_io_handle_t /*output*/, int /*delayMs*/) override { return NO_INIT; }
+
void setParameters(audio_io_handle_t /*ioHandle*/,
const String8& /*keyValuePairs*/,
int /*delayMs*/) override { }
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 6974a0b..7b454a3 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -311,11 +311,12 @@
AudioPolicyInterface::output_type_t outputType;
bool isSpatialized;
bool isBitPerfectInternal;
+ float volume;
AttributionSourceState attributionSource = createAttributionSourceState(uid);
ASSERT_EQ(OK, mManager->getOutputForAttr(
&attr, output, session, &stream, attributionSource, &config, &flags,
selectedDeviceId, portId, {}, &outputType, &isSpatialized,
- isBitPerfect == nullptr ? &isBitPerfectInternal : isBitPerfect));
+ isBitPerfect == nullptr ? &isBitPerfectInternal : isBitPerfect, &volume));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
}
@@ -2104,6 +2105,7 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
bool mIsSpatialized;
bool mIsBitPerfect;
+ float mVolume;
};
TEST_P(AudioPolicyManagerTestMMapPlaybackRerouting, MmapPlaybackStreamMatchingLoopbackDapMixFails) {
@@ -2122,7 +2124,7 @@
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
- &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect, &mVolume));
}
TEST_P(AudioPolicyManagerTestMMapPlaybackRerouting,
@@ -2141,7 +2143,7 @@
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
- &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect, &mVolume));
}
TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
@@ -2172,7 +2174,7 @@
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
- &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect, &mVolume));
ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
auto outputDesc = mManager->getOutputs().valueFor(mOutput);
ASSERT_NE(nullptr, outputDesc);
@@ -2188,7 +2190,7 @@
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
- &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect, &mVolume));
ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
outputDesc = mManager->getOutputs().valueFor(mOutput);
ASSERT_NE(nullptr, outputDesc);
@@ -2217,7 +2219,7 @@
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
- &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect, &mVolume));
}
INSTANTIATE_TEST_SUITE_P(
@@ -3870,7 +3872,7 @@
audio_port_handle_t routedPortId = devicePort.id;
ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &inputClientHandle, session, 1, &routedPortId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
- k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &portId));
+ 48000, AUDIO_INPUT_FLAG_NONE, &portId));
ASSERT_EQ(devicePort.id, routedPortId);
auto selectedDevice = availableDevices.getDeviceFromId(routedPortId);
ASSERT_NE(nullptr, selectedDevice);
@@ -3916,7 +3918,7 @@
const audio_format_t mBitPerfectFormat = AUDIO_FORMAT_PCM_16_BIT;
const audio_channel_mask_t mBitPerfectChannelMask = AUDIO_CHANNEL_OUT_STEREO;
- const uint32_t mBitPerfectSampleRate = 48000;
+ const uint32_t mBitPerfectSampleRate = k48000SamplingRate;
const uid_t mUid = 1234;
audio_port_handle_t mUsbPortId = AUDIO_PORT_HANDLE_NONE;
@@ -4017,11 +4019,12 @@
AudioPolicyInterface::output_type_t outputType;
bool isSpatialized;
bool isBitPerfect;
+ float volume;
EXPECT_EQ(expected,
mManager->getOutputForAttr(&sMediaAttr, &mBitPerfectOutput, AUDIO_SESSION_NONE,
&stream, attributionSource, &config, &flags,
&mSelectedDeviceId, &mBitPerfectPortId, {}, &outputType,
- &isSpatialized, &isBitPerfect));
+ &isSpatialized, &isBitPerfect, &volume));
}
class AudioPolicyManagerTestBitPerfect : public AudioPolicyManagerTestBitPerfectBase {
@@ -4191,8 +4194,8 @@
ASSERT_NO_FATAL_FAILURE(startBitPerfectOutput());
audio_attributes_t attr = {
+ .content_type = AUDIO_CONTENT_TYPE_UNKNOWN,
.usage = GetParam(),
- .content_type = AUDIO_CONTENT_TYPE_UNKNOWN
};
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
@@ -4216,6 +4219,95 @@
AUDIO_USAGE_ALARM)
);
+class AudioPolicyManagerInputPreemptionTest : public AudioPolicyManagerTestWithConfigurationFile {
+};
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ SameSessionReusesInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(0, mClient->getCloseInputCallsCount());
+ EXPECT_EQ(input1, input2);
+}
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ LesserPriorityReusesInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ attr.source = AUDIO_SOURCE_VOICE_RECOGNITION;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(0, mClient->getCloseInputCallsCount());
+ EXPECT_EQ(input1, input2);
+}
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ HigherPriorityPreemptsInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ attr.source = AUDIO_SOURCE_CAMCORDER;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate));
+
+ EXPECT_EQ(2, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(1, mClient->getCloseInputCallsCount());
+ EXPECT_NE(input1, input2);
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index bbc19fa..67e99f2 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -30,7 +30,7 @@
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
- <mixPort name="primary input" role="sink">
+ <mixPort name="primary input" role="sink" maxActiveCount="1" maxOpenCount="1">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
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/Android.bp b/services/camera/libcameraservice/Android.bp
index 0c4bfcb..3f2a617 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -58,6 +58,7 @@
"libcamera_metadata",
"libfmq",
"libgui",
+ "libguiflags",
"libhardware",
"libhidlbase",
"libimage_io",
@@ -169,6 +170,7 @@
"device3/Camera3OutputStreamInterface.cpp",
"device3/Camera3OutputUtils.cpp",
"device3/Camera3DeviceInjectionMethods.cpp",
+ "device3/deprecated/DeprecatedCamera3StreamSplitter.cpp",
"device3/UHRCropAndMeteringRegionMapper.cpp",
"device3/PreviewFrameSpacer.cpp",
"device3/hidl/HidlCamera3Device.cpp",
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 516e8f0..eb8708e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -784,12 +784,13 @@
return true;
}
-Status CameraService::getNumberOfCameras(int32_t type, int32_t deviceId, int32_t devicePolicy,
+Status CameraService::getNumberOfCameras(int32_t type,
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy,
int32_t* numCameras) {
ATRACE_CALL();
- if (vd_flags::camera_device_awareness() && (deviceId != kDefaultDeviceId)
+ if (vd_flags::camera_device_awareness() && (clientAttribution.deviceId != kDefaultDeviceId)
&& (devicePolicy != IVirtualDeviceManagerNative::DEVICE_POLICY_DEFAULT)) {
- *numCameras = mVirtualDeviceCameraIdMapper.getNumberOfCameras(deviceId);
+ *numCameras = mVirtualDeviceCameraIdMapper.getNumberOfCameras(clientAttribution.deviceId);
return Status::ok();
}
@@ -822,26 +823,22 @@
}
Status CameraService::createDefaultRequest(const std::string& unresolvedCameraId, int templateId,
- int32_t deviceId, int32_t devicePolicy,
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy,
/* out */
hardware::camera2::impl::CameraMetadataNative* request) {
ATRACE_CALL();
- if (!flags::feature_combination_query()) {
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
- "Camera subsystem doesn't support this method!");
- }
if (!mInitialized) {
ALOGE("%s: Camera subsystem is not available", __FUNCTION__);
logServiceError("Camera subsystem is not available", ERROR_DISCONNECTED);
return STATUS_ERROR(ERROR_DISCONNECTED, "Camera subsystem is not available");
}
- std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId, deviceId,
- devicePolicy);
+ std::optional<std::string> cameraIdOptional =
+ resolveCameraId(unresolvedCameraId, clientAttribution.deviceId, devicePolicy);
if (!cameraIdOptional.has_value()) {
std::string msg = fmt::sprintf("Camera %s: Invalid camera id for device id %d",
- unresolvedCameraId.c_str(), deviceId);
+ unresolvedCameraId.c_str(), clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -886,25 +883,21 @@
Status CameraService::isSessionConfigurationWithParametersSupported(
const std::string& unresolvedCameraId, int targetSdkVersion,
const SessionConfiguration& sessionConfiguration,
- int32_t deviceId, int32_t devicePolicy,
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy,
/*out*/ bool* supported) {
ATRACE_CALL();
- if (!flags::feature_combination_query()) {
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
- "Camera subsystem doesn't support this method!");
- }
if (!mInitialized) {
ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
logServiceError("Camera subsystem is not available", ERROR_DISCONNECTED);
return STATUS_ERROR(ERROR_DISCONNECTED, "Camera subsystem is not available");
}
- std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId, deviceId,
- devicePolicy);
+ std::optional<std::string> cameraIdOptional =
+ resolveCameraId(unresolvedCameraId, clientAttribution.deviceId, devicePolicy);
if (!cameraIdOptional.has_value()) {
std::string msg = fmt::sprintf("Camera %s: Invalid camera id for device id %d",
- unresolvedCameraId.c_str(), deviceId);
+ unresolvedCameraId.c_str(), clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -983,7 +976,8 @@
Status CameraService::getSessionCharacteristics(const std::string& unresolvedCameraId,
int targetSdkVersion, int rotationOverride,
- const SessionConfiguration& sessionConfiguration, int32_t deviceId, int32_t devicePolicy,
+ const SessionConfiguration& sessionConfiguration,
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy,
/*out*/ CameraMetadata* outMetadata) {
ATRACE_CALL();
@@ -1000,11 +994,11 @@
return STATUS_ERROR(ERROR_DISCONNECTED, "Camera subsystem is not available");
}
- std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId, deviceId,
- devicePolicy);
+ std::optional<std::string> cameraIdOptional =
+ resolveCameraId(unresolvedCameraId, clientAttribution.deviceId, devicePolicy);
if (!cameraIdOptional.has_value()) {
std::string msg = fmt::sprintf("Camera %s: Invalid camera id for device id %d",
- unresolvedCameraId.c_str(), deviceId);
+ unresolvedCameraId.c_str(), clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -1206,14 +1200,16 @@
return mVirtualDeviceCameraIdMapper.getActualCameraId(deviceId, inputCameraId);
}
-Status CameraService::getCameraInfo(int cameraId, int rotationOverride, int32_t deviceId,
- int32_t devicePolicy, CameraInfo* cameraInfo) {
+Status CameraService::getCameraInfo(int cameraId, int rotationOverride,
+ const AttributionSourceState& clientAttribution, int32_t devicePolicy,
+ CameraInfo* cameraInfo) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
- std::string cameraIdStr = cameraIdIntToStrLocked(cameraId, deviceId, devicePolicy);
+ std::string cameraIdStr =
+ cameraIdIntToStrLocked(cameraId, clientAttribution.deviceId, devicePolicy);
if (cameraIdStr.empty()) {
std::string msg = fmt::sprintf("Camera %d: Invalid camera id for device id %d",
- cameraId, deviceId);
+ cameraId, clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -1287,8 +1283,8 @@
}
Status CameraService::getCameraCharacteristics(const std::string& unresolvedCameraId,
- int targetSdkVersion, int rotationOverride, int32_t deviceId, int32_t devicePolicy,
- CameraMetadata* cameraInfo) {
+ int targetSdkVersion, int rotationOverride, const AttributionSourceState& clientAttribution,
+ int32_t devicePolicy, CameraMetadata* cameraInfo) {
ATRACE_CALL();
if (!cameraInfo) {
@@ -1303,11 +1299,11 @@
"Camera subsystem is not available");;
}
- std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId, deviceId,
- devicePolicy);
+ std::optional<std::string> cameraIdOptional =
+ resolveCameraId(unresolvedCameraId, clientAttribution.deviceId, devicePolicy);
if (!cameraIdOptional.has_value()) {
std::string msg = fmt::sprintf("Camera %s: Invalid camera id for device id %d",
- unresolvedCameraId.c_str(), deviceId);
+ unresolvedCameraId.c_str(), clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -1340,16 +1336,17 @@
return filterSensitiveMetadataIfNeeded(cameraId, cameraInfo);
}
-Status CameraService::getTorchStrengthLevel(const std::string& unresolvedCameraId, int32_t deviceId,
+Status CameraService::getTorchStrengthLevel(const std::string& unresolvedCameraId,
+ const AttributionSourceState& clientAttribution,
int32_t devicePolicy, int32_t* torchStrength) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
- std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId, deviceId,
- devicePolicy);
+ std::optional<std::string> cameraIdOptional = resolveCameraId(unresolvedCameraId,
+ clientAttribution.deviceId, devicePolicy);
if (!cameraIdOptional.has_value()) {
std::string msg = fmt::sprintf("Camera %s: Invalid camera id for device id %d",
- unresolvedCameraId.c_str(), deviceId);
+ unresolvedCameraId.c_str(), clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -1606,13 +1603,17 @@
std::string cameraIdStr = std::to_string(cameraId);
Status ret = Status::ok();
sp<Client> tmp = nullptr;
+
+ int callingPid = getCallingPid();
+ logConnectionAttempt(callingPid, kServiceName, cameraIdStr, API_1);
+
if (!(ret = connectHelper<ICameraClient,Client>(
sp<ICameraClient>{nullptr}, cameraIdStr, cameraId,
- kServiceName, /*systemNativeClient*/ false, {}, uid, USE_CALLING_PID,
+ kServiceName, /*systemNativeClient*/ false, {}, uid, callingPid,
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());
}
@@ -1682,17 +1683,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, int clientUid, 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;
}
@@ -1729,37 +1727,24 @@
return Status::ok();
}
-Status CameraService::validateClientPermissionsLocked(const std::string& cameraId,
- const std::string& clientName, int& clientUid, int& clientPid,
- /*out*/int& originalClientPid) const {
+Status CameraService::errorNotTrusted(int clientPid, int clientUid, const std::string& cameraId,
+ const std::string& clientName, bool isPid) const {
int callingPid = getCallingPid();
int callingUid = getCallingUid();
+ ALOGE("CameraService::connect X (calling PID %d, calling UID %d) rejected "
+ "(don't trust %s %d)", callingPid, callingUid, isPid ? "clientPid" : "clientUid",
+ isPid ? clientPid : clientUid);
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "Untrusted caller (calling PID %d, UID %d) trying to "
+ "forward camera access to camera %s for client %s (PID %d, UID %d)",
+ getCallingPid(), getCallingUid(), cameraId.c_str(),
+ clientName.c_str(), clientPid, clientUid);
+}
- // Check if we can trust clientUid
- if (clientUid == USE_CALLING_UID) {
- clientUid = callingUid;
- } else if (!isTrustedCallingUid(callingUid)) {
- ALOGE("CameraService::connect X (calling PID %d, calling UID %d) rejected "
- "(don't trust clientUid %d)", callingPid, callingUid, clientUid);
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "Untrusted caller (calling PID %d, UID %d) trying to "
- "forward camera access to camera %s for client %s (PID %d, UID %d)",
- callingPid, callingUid, cameraId.c_str(),
- clientName.c_str(), clientPid, clientUid);
- }
-
- // Check if we can trust clientPid
- if (clientPid == USE_CALLING_PID) {
- clientPid = callingPid;
- } else if (!isTrustedCallingUid(callingUid)) {
- ALOGE("CameraService::connect X (calling PID %d, calling UID %d) rejected "
- "(don't trust clientPid %d)", callingPid, callingUid, clientPid);
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "Untrusted caller (calling PID %d, UID %d) trying to "
- "forward camera access to camera %s for client %s (PID %d, UID %d)",
- callingPid, callingUid, cameraId.c_str(),
- clientName.c_str(), clientPid, clientUid);
- }
+Status CameraService::validateClientPermissionsLocked(const std::string& cameraId,
+ const std::string& clientName, int clientUid, int clientPid) const {
+ int callingPid = getCallingPid();
+ int callingUid = getCallingUid();
if (shouldRejectSystemCameraConnection(cameraId)) {
ALOGW("Attempting to connect to system-only camera id %s, connection rejected",
@@ -1814,12 +1799,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.
@@ -1840,11 +1823,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());
}
}
@@ -2129,35 +2112,59 @@
Status CameraService::connect(
const sp<ICameraClient>& cameraClient,
int api1CameraId,
- const std::string& clientPackageName,
- int clientUid,
- int clientPid,
int targetSdkVersion,
int rotationOverride,
bool forceSlowJpegMode,
- int32_t deviceId,
+ const AttributionSourceState& clientAttribution,
int32_t devicePolicy,
/*out*/
sp<ICamera>* device) {
ATRACE_CALL();
Status ret = Status::ok();
- std::string cameraIdStr = cameraIdIntToStr(api1CameraId, deviceId, devicePolicy);
+ std::string cameraIdStr =
+ cameraIdIntToStr(api1CameraId, clientAttribution.deviceId, devicePolicy);
if (cameraIdStr.empty()) {
std::string msg = fmt::sprintf("Camera %d: Invalid camera id for device id %d",
- api1CameraId, deviceId);
+ api1CameraId, clientAttribution.deviceId);
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
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);
+
+ int clientUid = clientAttribution.uid;
+ int clientPid = clientAttribution.pid;
+
+ // Resolve the client identity. In the near future, we will no longer rely on USE_CALLING_*, and
+ // need a way to guarantee the caller identity early.
+
+ // Check if we can trust clientUid
+ if (!resolveClientUid(clientUid)) {
+ return errorNotTrusted(clientPid, clientUid, cameraIdStr, clientPackageName,
+ /* isPid=*/ false);
+ }
+
+ // Check if we can trust clientUid
+ if (!resolveClientPid(clientPid)) {
+ return errorNotTrusted(clientPid, clientUid, cameraIdStr, clientPackageName,
+ /* isPid= */ true);
+ }
+
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, cameraIdStr, api1CameraId,
- clientPackageName, /*systemNativeClient*/ false, {}, clientUid, clientPid, API_1,
+ clientPackageName, /*systemNativeClient*/ false, {},
+ clientUid, clientPid, API_1,
/*shimUpdateOnly*/ false, /*oomScoreOffset*/ 0, tar