Merge "media.c2 aidl: Add AHardwareBuffer based C2Allocator" into main am: 8b4488192d am: 12f558e0f4 am: f9d066f6c0 am: 8564cfb796 am: ee56c2625d
Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/2753212
Change-Id: I579fb13e88ab646890ed8f16b5053d2967c0fd24
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/camera/Android.bp b/camera/Android.bp
index a3fd7f9..7de8a62 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -43,6 +43,22 @@
],
}
+aconfig_declarations {
+ name: "camera_platform_flags",
+ package: "com.android.internal.camera.flags",
+ srcs: ["camera_platform.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "camera_platform_flags_c_lib",
+ aconfig_declarations: "camera_platform_flags",
+}
+
+java_aconfig_library {
+ name: "camera_platform_flags_java_lib",
+ aconfig_declarations: "camera_platform_flags",
+}
+
cc_library_headers {
name: "camera_headers",
export_include_dirs: ["include"],
@@ -85,6 +101,7 @@
],
shared_libs: [
+ "camera_platform_flags_c_lib",
"libbase",
"libcutils",
"libutils",
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 1af899d..6759f3b 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -104,7 +104,6 @@
namespace {
sp<::android::hardware::ICameraService> gCameraService;
- const int kCameraServicePollDelay = 500000; // 0.5s
const char* kCameraServiceName = "media.camera";
Mutex gLock;
@@ -142,14 +141,10 @@
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
- do {
- binder = sm->getService(toString16(kCameraServiceName));
- if (binder != 0) {
- break;
- }
- ALOGW("CameraService not published, waiting...");
- usleep(kCameraServicePollDelay);
- } while(true);
+ binder = sm->waitForService(toString16(kCameraServiceName));
+ if (binder == nullptr) {
+ return nullptr;
+ }
if (gDeathNotifier == NULL) {
gDeathNotifier = new DeathNotifier();
}
diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp
index 36bf24c..f0630dd 100644
--- a/camera/CameraSessionStats.cpp
+++ b/camera/CameraSessionStats.cpp
@@ -16,6 +16,7 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "CameraSessionStats"
+
#include <utils/Log.h>
#include <utils/String16.h>
@@ -414,6 +415,12 @@
return err;
}
+ bool usedUltraWide = false;
+ if ((err = parcel->readBool(&usedUltraWide)) != OK) {
+ ALOGE("%s: Failed to read ultrawide usage from parcel", __FUNCTION__);
+ return err;
+ }
+
int32_t sessionIdx;
if ((err = parcel->readInt32(&sessionIdx)) != OK) {
ALOGE("%s: Failed to read session index from parcel", __FUNCTION__);
@@ -443,6 +450,7 @@
mStreamStats = std::move(streamStats);
mUserTag = toStdString(userTag);
mVideoStabilizationMode = videoStabilizationMode;
+ mUsedUltraWide = usedUltraWide;
mSessionIndex = sessionIdx;
mCameraExtensionSessionStats = extStats;
@@ -541,6 +549,10 @@
ALOGE("%s: Failed to write video stabilization mode!", __FUNCTION__);
return err;
}
+ if ((err = parcel->writeBool(mUsedUltraWide)) != OK) {
+ ALOGE("%s: Failed to write ultrawide usage!", __FUNCTION__);
+ return err;
+ }
if ((err = parcel->writeInt32(mSessionIndex)) != OK) {
ALOGE("%s: Failed to write session index!", __FUNCTION__);
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
new file mode 100644
index 0000000..074413f
--- /dev/null
+++ b/camera/camera_platform.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.internal.camera.flags"
+
+flag {
+ namespace: "camera_platform"
+ name: "camera_hsum_permission"
+ description: "Camera access by headless system user"
+ bug: "273539631"
+}
+
+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"
+ description: "Flash brightness level control in manual flash mode"
+ bug: "238348881"
+}
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index 8472562..13b705c 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -26,12 +26,15 @@
srcs: ["main_cameraserver.cpp"],
+ defaults: [
+ "libcameraservice_deps",
+ ],
+
header_libs: [
"libmedia_headers",
],
shared_libs: [
- "libcameraservice",
"liblog",
"libutils",
"libui",
@@ -40,15 +43,13 @@
"libbinder_ndk",
"libhidlbase",
"android.hardware.camera.common@1.0",
- "android.hardware.camera.provider@2.4",
- "android.hardware.camera.provider@2.5",
- "android.hardware.camera.provider@2.6",
- "android.hardware.camera.provider@2.7",
- "android.hardware.camera.provider-V2-ndk",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.4",
],
+ static_libs: [
+ "libcameraservice",
+ ],
compile_multilib: "first",
cflags: [
"-Wall",
diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h
index 70ca0b3..158ac2a 100644
--- a/camera/include/camera/CameraSessionStats.h
+++ b/camera/include/camera/CameraSessionStats.h
@@ -161,6 +161,7 @@
std::vector<CameraStreamStats> mStreamStats;
std::string mUserTag;
int mVideoStabilizationMode;
+ bool mUsedUltraWide;
int mSessionIndex;
CameraExtensionSessionStats mCameraExtensionSessionStats;
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 61c7551..b6b8012 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -540,6 +540,7 @@
case ACAMERA_CONTROL_AUTOFRAMING:
case ACAMERA_EDGE_MODE:
case ACAMERA_FLASH_MODE:
+ case ACAMERA_FLASH_STRENGTH_LEVEL:
case ACAMERA_HOT_PIXEL_MODE:
case ACAMERA_JPEG_GPS_COORDINATES:
case ACAMERA_JPEG_GPS_PROCESSING_METHOD:
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index fe0ef67..1db0196 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2364,6 +2364,125 @@
*/
ACAMERA_FLASH_STATE = // byte (acamera_metadata_enum_android_flash_state_t)
ACAMERA_FLASH_START + 5,
+ /**
+ * <p>Flash strength level to be used when manual flash control is active.</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>Flash strength level to use in capture mode i.e. when the applications control
+ * flash with either SINGLE or TORCH mode.</p>
+ * <p>Use android.flash.info.singleStrengthMaxLevel and
+ * android.flash.info.torchStrengthMaxLevel to check whether the device supports
+ * flash strength control or not.
+ * If the values of android.flash.info.singleStrengthMaxLevel and
+ * android.flash.info.torchStrengthMaxLevel are greater than 1,
+ * then the device supports manual flash strength control.</p>
+ * <p>If the ACAMERA_FLASH_MODE <code>==</code> TORCH the value must be >= 1
+ * and <= android.flash.info.torchStrengthMaxLevel.
+ * If the application doesn't set the key and
+ * android.flash.info.torchStrengthMaxLevel > 1,
+ * then the flash will be fired at the default level set by HAL in
+ * android.flash.info.torchStrengthDefaultLevel.
+ * If the ACAMERA_FLASH_MODE <code>==</code> SINGLE, then the value must be >= 1
+ * and <= android.flash.info.singleStrengthMaxLevel.
+ * If the application does not set this key and
+ * android.flash.info.singleStrengthMaxLevel > 1,
+ * then the flash will be fired at the default level set by HAL
+ * in android.flash.info.singleStrengthDefaultLevel.
+ * If ACAMERA_CONTROL_AE_MODE is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
+ * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
+ *
+ * @see ACAMERA_CONTROL_AE_MODE
+ * @see ACAMERA_FLASH_MODE
+ */
+ ACAMERA_FLASH_STRENGTH_LEVEL = // int32
+ ACAMERA_FLASH_START + 6,
+ /**
+ * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Maximum flash brightness level in camera capture mode and
+ * ACAMERA_FLASH_MODE set to SINGLE.
+ * Value will be > 1 if the manual flash strength control feature is supported,
+ * otherwise the value will be equal to 1.
+ * Note that this level is just a number of supported levels (the granularity of control).
+ * There is no actual physical power units tied to this level.</p>
+ *
+ * @see ACAMERA_FLASH_MODE
+ */
+ ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL = // int32
+ ACAMERA_FLASH_START + 7,
+ /**
+ * <p>Default flash brightness level for manual flash control in SINGLE mode.</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>If flash unit is available this will be greater than or equal to 1 and less
+ * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>.
+ * Note for devices that do not support the manual flash strength control
+ * feature, this level will always be equal to 1.</p>
+ */
+ ACAMERA_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL = // int32
+ ACAMERA_FLASH_START + 8,
+ /**
+ * <p>Maximum flash brightness level for manual flash control in TORCH mode</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Maximum flash brightness level in camera capture mode and
+ * ACAMERA_FLASH_MODE set to TORCH.
+ * Value will be > 1 if the manual flash strength control feature is supported,
+ * otherwise the value will be equal to 1.</p>
+ * <p>Note that this level is just a number of supported levels(the granularity of control).
+ * There is no actual physical power units tied to this level.
+ * There is no relation between android.flash.info.torchStrengthMaxLevel and
+ * android.flash.info.singleStrengthMaxLevel i.e. the ratio of
+ * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel
+ * is not guaranteed to be the ratio of actual brightness.</p>
+ *
+ * @see ACAMERA_FLASH_MODE
+ */
+ ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL = // int32
+ ACAMERA_FLASH_START + 9,
+ /**
+ * <p>Default flash brightness level for manual flash control in TORCH mode</p>
+ *
+ * <p>Type: int32</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>If flash unit is available this will be greater than or equal to 1 and less
+ * or equal to android.flash.info.torchStrengthMaxLevel.
+ * Note for the devices that do not support the manual flash strength control feature,
+ * this level will always be equal to 1.</p>
+ */
+ ACAMERA_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL = // int32
+ ACAMERA_FLASH_START + 10,
ACAMERA_FLASH_END,
/**
diff --git a/camera/tests/fuzzer/Android.bp b/camera/tests/fuzzer/Android.bp
index bae8706..9aecba4 100644
--- a/camera/tests/fuzzer/Android.bp
+++ b/camera/tests/fuzzer/Android.bp
@@ -28,6 +28,7 @@
"libcamera_client",
],
shared_libs: [
+ "camera_platform_flags_c_lib",
"libbase",
"libcutils",
"libutils",
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index 17d7046..a19ef8e 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -90,9 +90,12 @@
status_t Overlay::stop() {
ALOGV("Overlay::stop");
- Mutex::Autolock _l(mMutex);
- mState = STOPPING;
- mEventCond.signal();
+ {
+ Mutex::Autolock _l(mMutex);
+ mState = STOPPING;
+ mEventCond.signal();
+ }
+ join();
return NO_ERROR;
}
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 11e1704..ec99fac 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -37,10 +37,12 @@
// will calculate frame buffer size if |hasData| is set to true.
VideoFrame(uint32_t width, uint32_t height,
uint32_t displayWidth, uint32_t displayHeight,
+ uint32_t displayLeft, uint32_t displayTop,
uint32_t tileWidth, uint32_t tileHeight,
uint32_t angle, uint32_t bpp, uint32_t bitDepth, bool hasData, size_t iccSize):
mWidth(width), mHeight(height),
mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
+ mDisplayLeft(displayLeft), mDisplayTop(displayTop),
mTileWidth(tileWidth), mTileHeight(tileHeight), mDurationUs(0),
mRotationAngle(angle), mBytesPerPixel(bpp), mIccSize(iccSize),
mBitDepth(bitDepth) {
@@ -82,6 +84,8 @@
uint32_t mHeight; // Decoded image height before rotation
uint32_t mDisplayWidth; // Display width before rotation
uint32_t mDisplayHeight; // Display height before rotation
+ uint32_t mDisplayLeft; // Display left (column coordinate) before rotation
+ uint32_t mDisplayTop; // Display top (row coordinate) before rotation
uint32_t mTileWidth; // Tile width (0 if image doesn't have grid)
uint32_t mTileHeight; // Tile height (0 if image doesn't have grid)
int64_t mDurationUs; // Frame duration in microseconds
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
new file mode 100644
index 0000000..ed7f426
--- /dev/null
+++ b/media/audio/aconfig/Android.bp
@@ -0,0 +1,25 @@
+// media_audio namespace flags
+
+// aconfig_audio_flags is for general multi-project audio flags.
+aconfig_declarations {
+ name: "aconfig_audio_flags",
+ package: "com.android.media.audio.flags",
+ srcs: ["audio_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "aconfig_audio_flags_c_lib",
+ aconfig_declarations: "aconfig_audio_flags",
+
+ // This does not properly link with the
+ // libaudioutils library -- the vndk
+ // requirement needs a signature update.
+
+ double_loadable: true,
+ host_supported: true,
+ product_available: true,
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/media/audio/aconfig/audio_flags.aconfig b/media/audio/aconfig/audio_flags.aconfig
new file mode 100644
index 0000000..58a8d41
--- /dev/null
+++ b/media/audio/aconfig/audio_flags.aconfig
@@ -0,0 +1,17 @@
+package: "com.android.media.audio.flags"
+
+# General multi-project audio flags.
+#
+# Please add flags in alphabetical order.
+
+flag {
+ name: "mutex_priority_inheritance"
+ namespace: "media_audio"
+ description: "\
+Enable mutex priority inheritance in audioserver \
+(std::mutex does not normally transfer priority from \
+the blocked thread to the blocking thread). \
+This feature helps reduce audio glitching caused by low priority \
+blocking threads."
+ bug: "209491695"
+}
diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp
index 828d861..2030dc7 100644
--- a/media/audioserver/Android.bp
+++ b/media/audioserver/Android.bp
@@ -25,21 +25,31 @@
"libmediametrics_headers",
],
- shared_libs: [
- "packagemanager_aidl-cpp",
+ defaults: [
+ "libaaudioservice_dependencies",
+ "libaudioflinger_dependencies",
+ "libaudiopolicyservice_dependencies",
+ "latest_android_media_audio_common_types_cpp_shared",
+ "latest_android_hardware_audio_core_sounddose_ndk_shared",
+ ],
+
+ static_libs: [
"libaaudioservice",
- "libaudioclient",
"libaudioflinger",
"libaudiopolicyservice",
+ "libmedialogservice",
+ "libnbaio",
+ ],
+
+ shared_libs: [
+ "libaudioclient",
"libaudioprocessing",
"libbinder",
"libcutils",
"libhidlbase",
"liblog",
"libmedia",
- "libmedialogservice",
"libmediautils",
- "libnbaio",
"libnblog",
"libpowermanager",
"libutils",
@@ -59,9 +69,9 @@
"frameworks/av/services/audiopolicy/engine/interface",
"frameworks/av/services/audiopolicy/service",
"frameworks/av/services/medialog",
+ "frameworks/av/services/oboeservice", // TODO oboeservice is the old folder name for aaudioservice. It will be changed.
- // TODO oboeservice is the old folder name for aaudioservice. It will be changed.
- "frameworks/av/services/oboeservice",
+
],
init_rc: ["audioserver.rc"],
diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp
index d865ab2..721a12a 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.cpp
+++ b/media/codec2/components/aac/C2SoftAacEnc.cpp
@@ -69,7 +69,7 @@
addParameter(
DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
.withDefault(new C2StreamChannelCountInfo::input(0u, 1))
- .withFields({C2F(mChannelCount, value).inRange(1, 6)})
+ .withFields({C2F(mChannelCount, value).inRange(1, kMaxChannelCount)})
.withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
.build());
@@ -198,10 +198,17 @@
}
c2_status_t C2SoftAacEnc::onFlush_sm() {
+ if (mAACEncoder != nullptr) {
+ /* encoder's internal input buffer needs to be reset during flush */
+ if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CONTROL_STATE, AACENC_INIT_ALL)) {
+ ALOGE("Failed to reset AAC encoder");
+ }
+ }
mSentCodecSpecificData = false;
mInputSize = 0u;
mNextFrameTimestampUs.reset();
mLastFrameEndTimestampUs.reset();
+ mRemainderLen = 0;
return C2_OK;
}
@@ -562,6 +569,11 @@
inBufferSize[0] -= outargs.numInSamples * sizeof(int16_t);
inargs.numInSamples -= outargs.numInSamples;
}
+ } else {
+ // In case of error in encode call, discard remaining input bytes.
+ inBuffer[0] = nullptr;
+ inBufferSize[0] = 0;
+ inargs.numInSamples = 0;
}
ALOGV("encoderErr = %d mInputSize = %zu "
"inargs.numInSamples = %d, mNextFrameTimestampUs = %lld",
@@ -597,10 +609,19 @@
&outBufDesc,
&inargs,
&outargs);
+
+ // after flush, discard remaining input bytes.
+ inBuffer[0] = nullptr;
inBufferSize[0] = 0;
}
if (inBufferSize[0] > 0) {
+ if (inBufferSize[0] > kRemainderBufSize) {
+ ALOGE("Remaining bytes %d greater than remainder buffer size %zu", inBufferSize[0],
+ kRemainderBufSize);
+ work->result = C2_CORRUPTED;
+ return;
+ }
for (size_t i = 0; i < inBufferSize[0]; ++i) {
mRemainder[i] = static_cast<uint8_t *>(inBuffer[0])[i];
}
diff --git a/media/codec2/components/aac/C2SoftAacEnc.h b/media/codec2/components/aac/C2SoftAacEnc.h
index 9a28280..c79526c 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.h
+++ b/media/codec2/components/aac/C2SoftAacEnc.h
@@ -47,6 +47,9 @@
const std::shared_ptr<C2BlockPool> &pool) override;
private:
+ static constexpr size_t kMaxChannelCount = 6;
+ static constexpr size_t kRemainderBufSize = kMaxChannelCount * sizeof(int16_t);
+
std::shared_ptr<IntfImpl> mIntf;
HANDLE_AACENCODER mAACEncoder;
@@ -63,7 +66,7 @@
std::atomic_uint64_t mOutIndex;
// We support max 6 channels
- uint8_t mRemainder[6 * sizeof(int16_t)];
+ uint8_t mRemainder[kRemainderBufSize];
size_t mRemainderLen;
status_t initEncoder();
diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp
index 664647a..4b189b4 100644
--- a/media/codec2/components/base/Android.bp
+++ b/media/codec2/components/base/Android.bp
@@ -42,6 +42,10 @@
"libnativewindow_headers",
],
+ static_libs: [
+ "libyuv_static", // for conversion routines
+ ],
+
shared_libs: [
"libcutils", // for properties
"liblog", // for ALOG
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index 55a1164..06a21f6 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -21,8 +21,10 @@
#include <android/hardware_buffer.h>
#include <cutils/properties.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <inttypes.h>
+#include <libyuv.h>
#include <C2Config.h>
#include <C2Debug.h>
@@ -32,6 +34,15 @@
#include <SimpleC2Component.h>
namespace android {
+
+// libyuv version required for I410ToAB30Matrix and I210ToAB30Matrix.
+#if LIBYUV_VERSION >= 1780
+#include <algorithm>
+#define HAVE_LIBYUV_I410_I210_TO_AB30 1
+#else
+#define HAVE_LIBYUV_I410_I210_TO_AB30 0
+#endif
+
constexpr uint8_t kNeutralUVBitDepth8 = 128;
constexpr uint16_t kNeutralUVBitDepth10 = 512;
@@ -506,6 +517,120 @@
}
}
+void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
+ const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
+ size_t srcVStride, size_t dstStride, size_t width,
+ size_t height,
+ std::shared_ptr<const C2ColorAspectsStruct> aspects,
+ CONV_FORMAT_T format) {
+ bool processed = false;
+#if HAVE_LIBYUV_I410_I210_TO_AB30
+ if (format == CONV_FORMAT_I444) {
+ libyuv::I410ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dst,
+ dstStride, &libyuv::kYuvV2020Constants, width, height);
+ processed = true;
+ } else if (format == CONV_FORMAT_I422) {
+ libyuv::I210ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dst,
+ dstStride, &libyuv::kYuvV2020Constants, width, height);
+ processed = true;
+ }
+#endif // HAVE_LIBYUV_I410_I210_TO_AB30
+ if (!processed) {
+ convertYUV420Planar16ToY410OrRGBA1010102(
+ (uint32_t*)dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ dstStride / sizeof(uint32_t), width, height,
+ std::static_pointer_cast<const C2ColorAspectsStruct>(aspects));
+ }
+}
+
+void convertPlanar16ToP010(uint16_t* dstY, uint16_t* dstUV, const uint16_t* srcY,
+ const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+ bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+ size_t tmpFrameBufferSize) {
+#if LIBYUV_VERSION >= 1779
+ if ((format == CONV_FORMAT_I444) || (format == CONV_FORMAT_I422)) {
+ // TODO(https://crbug.com/libyuv/952): replace this block with libyuv::I410ToP010
+ // and libyuv::I210ToP010 when they are available. Note it may be safe to alias dstY
+ // in I010ToP010, but the libyuv API doesn't make any guarantees.
+ const size_t tmpSize = dstYStride * height + dstUStride * align(height, 2);
+ CHECK(tmpSize <= tmpFrameBufferSize);
+
+ uint16_t* const tmpY = tmpFrameBuffer;
+ uint16_t* const tmpU = tmpY + dstYStride * height;
+ uint16_t* const tmpV = tmpU + dstUStride * align(height, 2) / 2;
+ if (format == CONV_FORMAT_I444) {
+ libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+ dstYStride, tmpU, dstUStride, tmpV, dstUStride, width, height);
+ } else {
+ libyuv::I210ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+ dstYStride, tmpU, dstUStride, tmpV, dstUStride, width, height);
+ }
+ libyuv::I010ToP010(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstVStride, dstY, dstYStride,
+ dstUV, dstUStride, width, height);
+ } else {
+ convertYUV420Planar16ToP010(dstY, dstUV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, width, height,
+ isMonochrome);
+ }
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToP010(dstY, dstUV, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ dstYStride, dstUStride, width, height, isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
+}
+
+void convertPlanar16ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint16_t* srcY,
+ const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+ bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+ size_t tmpFrameBufferSize) {
+#if LIBYUV_VERSION >= 1779
+ if (format == CONV_FORMAT_I444) {
+ // TODO(https://crbug.com/libyuv/950): replace this block with libyuv::I410ToI420
+ // when it's available.
+ const size_t tmpSize = dstYStride * height + dstUStride * align(height, 2);
+ CHECK(tmpSize <= tmpFrameBufferSize);
+
+ uint16_t* const tmpY = tmpFrameBuffer;
+ uint16_t* const tmpU = tmpY + dstYStride * height;
+ uint16_t* const tmpV = tmpU + dstUStride * align(height, 2) / 2;
+ libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY, dstYStride,
+ tmpU, dstUStride, tmpV, dstVStride, width, height);
+ libyuv::I010ToI420(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstUStride, dstY, dstYStride,
+ dstU, dstUStride, dstV, dstVStride, width, height);
+ } else if (format == CONV_FORMAT_I422) {
+ libyuv::I210ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+ dstU, dstUStride, dstV, dstVStride, width, height);
+ } else {
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, width, height,
+ isMonochrome);
+ }
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, width, height, isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
+}
+
+void convertPlanar8ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint8_t* srcY,
+ const uint8_t* srcU, const uint8_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, uint32_t width, uint32_t height,
+ bool isMonochrome, CONV_FORMAT_T format) {
+ if (format == CONV_FORMAT_I444) {
+ libyuv::I444ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+ dstU, dstUStride, dstV, dstVStride, width, height);
+ } else if (format == CONV_FORMAT_I422) {
+ libyuv::I422ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY, dstYStride,
+ dstU, dstUStride, dstV, dstVStride, width, height);
+ } else {
+ convertYUV420Planar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, dstVStride, width, height,
+ isMonochrome);
+ }
+}
std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
mQueue.pop_front();
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
index bc27474..b28c47e 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -31,6 +31,12 @@
namespace android {
+typedef enum {
+ CONV_FORMAT_I420,
+ CONV_FORMAT_I422,
+ CONV_FORMAT_I444,
+} CONV_FORMAT_T;
+
void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint8_t *srcY,
const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride,
size_t srcUStride, size_t srcVStride, size_t dstYStride,
@@ -66,6 +72,30 @@
const uint32_t* srcRGBA, size_t srcRGBStride, size_t width,
size_t height, C2Color::matrix_t colorMatrix,
C2Color::range_t colorRange);
+void convertPlanar16ToY410OrRGBA1010102(uint8_t* dst, const uint16_t* srcY, const uint16_t* srcU,
+ const uint16_t* srcV, size_t srcYStride, size_t srcUStride,
+ size_t srcVStride, size_t dstStride, size_t width,
+ size_t height,
+ std::shared_ptr<const C2ColorAspectsStruct> aspects,
+ CONV_FORMAT_T format);
+
+void convertPlanar16ToP010(uint16_t* dstY, uint16_t* dstUV, const uint16_t* srcY,
+ const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+ bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+ size_t tmpFrameBufferSize);
+void convertPlanar16ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint16_t* srcY,
+ const uint16_t* srcU, const uint16_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, size_t width, size_t height,
+ bool isMonochrome, CONV_FORMAT_T format, uint16_t* tmpFrameBuffer,
+ size_t tmpFrameBufferSize);
+void convertPlanar8ToYV12(uint8_t* dstY, uint8_t* dstU, uint8_t* dstV, const uint8_t* srcY,
+ const uint8_t* srcU, const uint8_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, size_t dstYStride,
+ size_t dstUStride, size_t dstVStride, uint32_t width, uint32_t height,
+ bool isMonochrome, CONV_FORMAT_T format);
class SimpleC2Component
: public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
diff --git a/media/codec2/components/dav1d/Android.bp b/media/codec2/components/dav1d/Android.bp
new file mode 100644
index 0000000..c9387dd
--- /dev/null
+++ b/media/codec2/components/dav1d/Android.bp
@@ -0,0 +1,37 @@
+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"],
+}
+
+cc_library {
+ name: "libcodec2_soft_av1dec_dav1d",
+ // TODO: b/277797541 - enable once ready
+ enabled: false,
+
+ defaults: [
+ "libcodec2_soft-defaults",
+ "libcodec2_soft_sanitize_all-defaults",
+ "libcodec2_soft_sanitize_cfi-defaults",
+ ],
+
+ cflags: [
+ "-DCODECNAME=\"c2.android.dav1d-av1.decoder\"",
+ "-Wno-unused-variable",
+ ],
+
+ srcs: ["C2SoftDav1dDec.cpp", "C2SoftDav1dDump.cpp"],
+ static_libs: [
+ "libdav1d_8bit",
+ "libdav1d_16bit",
+ ],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+
+}
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.cpp b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
new file mode 100644
index 0000000..3f96cb3
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftDav1dDec"
+#include <android-base/properties.h>
+#include <cutils/properties.h>
+#include <thread>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <Codec2CommonUtils.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+#include <log/log.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include "C2SoftDav1dDec.h"
+
+namespace android {
+
+// The number of threads used for the dav1d decoder.
+static const int NUM_THREADS_DAV1D_DEFAULT = 0;
+static const char NUM_THREADS_DAV1D_PROPERTY[] = "debug.dav1d.numthreads";
+
+// codecname set and passed in as a compile flag from Android.bp
+constexpr char COMPONENT_NAME[] = CODECNAME;
+
+constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+
+class C2SoftDav1dDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+ public:
+ explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
+ : SimpleInterface<void>::BaseParams(helper, COMPONENT_NAME, C2Component::KIND_DECODER,
+ C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) {
+ noPrivateBuffers();
+ noInputReferences();
+ noOutputReferences();
+ noInputLatency();
+ noTimeStretch();
+
+ addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+ .withConstValue(new C2ComponentAttributesSetting(
+ C2Component::ATTRIB_IS_TEMPORAL))
+ .build());
+
+ addParameter(DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+ .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 4096),
+ C2F(mSize, height).inRange(2, 4096),
+ })
+ .withSetter(SizeSetter)
+ .build());
+
+ addParameter(DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+ .withDefault(new C2StreamProfileLevelInfo::input(
+ 0u, C2Config::PROFILE_AV1_0, C2Config::LEVEL_AV1_2_1))
+ .withFields({C2F(mProfileLevel, profile)
+ .oneOf({C2Config::PROFILE_AV1_0,
+ C2Config::PROFILE_AV1_1}),
+ C2F(mProfileLevel, level)
+ .oneOf({
+ C2Config::LEVEL_AV1_2,
+ C2Config::LEVEL_AV1_2_1,
+ C2Config::LEVEL_AV1_2_2,
+ C2Config::LEVEL_AV1_2_3,
+ C2Config::LEVEL_AV1_3,
+ C2Config::LEVEL_AV1_3_1,
+ C2Config::LEVEL_AV1_3_2,
+ C2Config::LEVEL_AV1_3_3,
+ C2Config::LEVEL_AV1_4,
+ C2Config::LEVEL_AV1_4_1,
+ C2Config::LEVEL_AV1_4_2,
+ C2Config::LEVEL_AV1_4_3,
+ C2Config::LEVEL_AV1_5,
+ C2Config::LEVEL_AV1_5_1,
+ C2Config::LEVEL_AV1_5_2,
+ C2Config::LEVEL_AV1_5_3,
+ })})
+ .withSetter(ProfileLevelSetter, mSize)
+ .build());
+
+ mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
+ addParameter(DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
+ .withDefault(mHdr10PlusInfoInput)
+ .withFields({
+ C2F(mHdr10PlusInfoInput, m.value).any(),
+ })
+ .withSetter(Hdr10PlusInfoInputSetter)
+ .build());
+
+ mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
+ addParameter(DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
+ .withDefault(mHdr10PlusInfoOutput)
+ .withFields({
+ C2F(mHdr10PlusInfoOutput, m.value).any(),
+ })
+ .withSetter(Hdr10PlusInfoOutputSetter)
+ .build());
+
+ // default static info
+ C2HdrStaticMetadataStruct defaultStaticInfo{};
+ helper->addStructDescriptors<C2MasteringDisplayColorVolumeStruct, C2ColorXyStruct>();
+ addParameter(
+ DefineParam(mHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withDefault(new C2StreamHdrStaticInfo::output(0u, defaultStaticInfo))
+ .withFields({C2F(mHdrStaticInfo, mastering.red.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.red.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.green.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.green.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.blue.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.blue.y).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.white.x).inRange(0, 1),
+ C2F(mHdrStaticInfo, mastering.maxLuminance).inRange(0, 65535),
+ C2F(mHdrStaticInfo, mastering.minLuminance).inRange(0, 6.5535),
+ C2F(mHdrStaticInfo, maxCll).inRange(0, 0XFFFF),
+ C2F(mHdrStaticInfo, maxFall).inRange(0, 0XFFFF)})
+ .withSetter(HdrStaticInfoSetter)
+ .build());
+
+ addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+ .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 2048, 2),
+ C2F(mSize, height).inRange(2, 2048, 2),
+ })
+ .withSetter(MaxPictureSizeSetter, mSize)
+ .build());
+
+ addParameter(
+ DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize))
+ .withFields({
+ C2F(mMaxInputSize, value).any(),
+ })
+ .calculatedAs(MaxInputSizeSetter, mMaxSize)
+ .build());
+
+ C2ChromaOffsetStruct locations[1] = {C2ChromaOffsetStruct::ITU_YUV_420_0()};
+ std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+ C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+ memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+ defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+ {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+ helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+ addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+ .withConstValue(defaultColorInfo)
+ .build());
+
+ addParameter(DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsTuning::output(
+ 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({C2F(mDefaultColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED,
+ C2Color::RANGE_OTHER),
+ C2F(mDefaultColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mDefaultColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mDefaultColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(DefaultColorAspectsSetter)
+ .build());
+
+ addParameter(DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(
+ 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({C2F(mCodedColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED,
+ C2Color::RANGE_OTHER),
+ C2F(mCodedColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mCodedColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mCodedColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(CodedColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(
+ 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields(
+ {C2F(mColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+ C2F(mColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
+ .build());
+
+ std::vector<uint32_t> pixelFormats = {HAL_PIXEL_FORMAT_YCBCR_420_888};
+ if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
+ pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+ }
+ // If color format surface isn't added to supported formats, there is no way to know
+ // when the color-format is configured to surface. This is necessary to be able to
+ // choose 10-bit format while decoding 10-bit clips in surface mode.
+ pixelFormats.push_back(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+ // TODO: support more formats?
+ addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+ .withDefault(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+ .withFields({C2F(mPixelFormat, value).oneOf(pixelFormats)})
+ .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
+ .build());
+ }
+
+ static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output>& oldMe,
+ C2P<C2StreamPictureSizeInfo::output>& me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+ me.set().width = oldMe.v.width;
+ }
+ if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+ me.set().height = oldMe.v.height;
+ }
+ return res;
+ }
+
+ static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output>& me,
+ const C2P<C2StreamPictureSizeInfo::output>& size) {
+ (void)mayBlock;
+ // TODO: get max width/height from the size's field helpers vs.
+ // hardcoding
+ me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+ me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+ return C2R::Ok();
+ }
+
+ static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input>& me,
+ const C2P<C2StreamMaxPictureSizeTuning::output>& maxSize) {
+ (void)mayBlock;
+ // assume compression ratio of 2, but enforce a floor
+ me.set().value =
+ c2_max((((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072),
+ kMinInputBufferSize);
+ return C2R::Ok();
+ }
+
+ static C2R DefaultColorAspectsSetter(bool mayBlock,
+ C2P<C2StreamColorAspectsTuning::output>& me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+ }
+
+ static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input>& me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+ }
+
+ static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
+ const C2P<C2StreamColorAspectsTuning::output>& def,
+ const C2P<C2StreamColorAspectsInfo::input>& coded) {
+ (void)mayBlock;
+ // take default values for all unspecified fields, and coded values for specified ones
+ me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+ me.set().primaries =
+ coded.v.primaries == PRIMARIES_UNSPECIFIED ? def.v.primaries : coded.v.primaries;
+ me.set().transfer =
+ coded.v.transfer == TRANSFER_UNSPECIFIED ? def.v.transfer : coded.v.transfer;
+ me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
+ return C2R::Ok();
+ }
+
+ static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input>& me,
+ const C2P<C2StreamPictureSizeInfo::output>& size) {
+ (void)mayBlock;
+ (void)size;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
+ std::shared_ptr<C2StreamColorAspectsTuning::output> getDefaultColorAspects_l() {
+ return mDefaultColorAspects;
+ }
+
+ std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() { return mColorAspects; }
+
+ static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::input>& me) {
+ (void)mayBlock;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
+ static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::output>& me) {
+ (void)mayBlock;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
+ // unsafe getters
+ std::shared_ptr<C2StreamPixelFormatInfo::output> getPixelFormat_l() const {
+ return mPixelFormat;
+ }
+
+ static C2R HdrStaticInfoSetter(bool mayBlock, C2P<C2StreamHdrStaticInfo::output>& me) {
+ (void)mayBlock;
+ if (me.v.mastering.red.x > 1) {
+ me.set().mastering.red.x = 1;
+ }
+ if (me.v.mastering.red.y > 1) {
+ me.set().mastering.red.y = 1;
+ }
+ if (me.v.mastering.green.x > 1) {
+ me.set().mastering.green.x = 1;
+ }
+ if (me.v.mastering.green.y > 1) {
+ me.set().mastering.green.y = 1;
+ }
+ if (me.v.mastering.blue.x > 1) {
+ me.set().mastering.blue.x = 1;
+ }
+ if (me.v.mastering.blue.y > 1) {
+ me.set().mastering.blue.y = 1;
+ }
+ if (me.v.mastering.white.x > 1) {
+ me.set().mastering.white.x = 1;
+ }
+ if (me.v.mastering.white.y > 1) {
+ me.set().mastering.white.y = 1;
+ }
+ if (me.v.mastering.maxLuminance > 65535.0) {
+ me.set().mastering.maxLuminance = 65535.0;
+ }
+ if (me.v.mastering.minLuminance > 6.5535) {
+ me.set().mastering.minLuminance = 6.5535;
+ }
+ if (me.v.maxCll > 65535.0) {
+ me.set().maxCll = 65535.0;
+ }
+ if (me.v.maxFall > 65535.0) {
+ me.set().maxFall = 65535.0;
+ }
+ return C2R::Ok();
+ }
+
+ private:
+ std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+ std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+ std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+ std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+ std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+ std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+ std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+ std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+ std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
+ std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
+ std::shared_ptr<C2StreamHdrStaticInfo::output> mHdrStaticInfo;
+};
+
+C2SoftDav1dDec::C2SoftDav1dDec(const char* name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl>& intfImpl)
+ : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+ mIntf(intfImpl) {
+ mTimeStart = mTimeEnd = systemTime();
+}
+
+C2SoftDav1dDec::~C2SoftDav1dDec() {
+ onRelease();
+}
+
+c2_status_t C2SoftDav1dDec::onInit() {
+ return initDecoder() ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftDav1dDec::onStop() {
+ // TODO: b/277797541 - investigate if the decoder needs to be flushed.
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ return C2_OK;
+}
+
+void C2SoftDav1dDec::onReset() {
+ (void)onStop();
+ c2_status_t err = onFlush_sm();
+ if (err != C2_OK) {
+ ALOGW("Failed to flush the av1 decoder. Trying to hard reset.");
+ destroyDecoder();
+ if (!initDecoder()) {
+ ALOGE("Hard reset failed.");
+ }
+ }
+}
+
+void C2SoftDav1dDec::flushDav1d() {
+ if (mDav1dCtx) {
+ Dav1dPicture p;
+
+ while (mDecodedPictures.size() > 0) {
+ p = mDecodedPictures.front();
+ mDecodedPictures.pop_front();
+
+ dav1d_picture_unref(&p);
+ }
+
+ int res = 0;
+ while (true) {
+ memset(&p, 0, sizeof(p));
+
+ if ((res = dav1d_get_picture(mDav1dCtx, &p)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ ALOGE("Error decoding frame: %s\n", strerror(DAV1D_ERR(res)));
+ break;
+ } else {
+ res = 0;
+ break;
+ }
+ } else {
+ dav1d_picture_unref(&p);
+ }
+ }
+
+ dav1d_flush(mDav1dCtx);
+ }
+}
+
+void C2SoftDav1dDec::onRelease() {
+ destroyDecoder();
+}
+
+c2_status_t C2SoftDav1dDec::onFlush_sm() {
+ flushDav1d();
+
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+
+ return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+ int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGV("Number of CPU cores: %d", cpuCoreCount);
+ return cpuCoreCount;
+}
+
+bool C2SoftDav1dDec::initDecoder() {
+#ifdef FILE_DUMP_ENABLE
+ mC2SoftDav1dDump.initDumping();
+#endif
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mHalPixelFormat = HAL_PIXEL_FORMAT_YV12;
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ mPixelFormatInfo = mIntf->getPixelFormat_l();
+ }
+
+ const char* version = dav1d_version();
+
+ Dav1dSettings lib_settings;
+ dav1d_default_settings(&lib_settings);
+ int cpu_count = GetCPUCoreCount();
+ lib_settings.n_threads = std::max(cpu_count / 2, 1); // use up to half the cores by default.
+
+ int32_t numThreads =
+ android::base::GetIntProperty(NUM_THREADS_DAV1D_PROPERTY, NUM_THREADS_DAV1D_DEFAULT);
+ if (numThreads > 0) lib_settings.n_threads = numThreads;
+
+ int res = 0;
+ if ((res = dav1d_open(&mDav1dCtx, &lib_settings))) {
+ ALOGE("dav1d_open failed. status: %d.", res);
+ return false;
+ } else {
+ ALOGD("dav1d_open succeeded(n_threads=%d,version=%s).", lib_settings.n_threads, version);
+ }
+
+ return true;
+}
+
+void C2SoftDav1dDec::destroyDecoder() {
+ if (mDav1dCtx) {
+ Dav1dPicture p;
+ while (mDecodedPictures.size() > 0) {
+ memset(&p, 0, sizeof(p));
+ p = mDecodedPictures.front();
+ mDecodedPictures.pop_front();
+
+ dav1d_picture_unref(&p);
+ }
+
+ dav1d_close(&mDav1dCtx);
+ mDav1dCtx = nullptr;
+ mOutputBufferIndex = 0;
+ mInputBufferIndex = 0;
+ }
+#ifdef FILE_DUMP_ENABLE
+ mC2SoftDav1dDump.destroyDumping();
+#endif
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling end_of_stream.");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftDav1dDec::finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2GraphicBlock>& block) {
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block, C2Rect(mWidth, mHeight));
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ buffer->setInfo(mIntf->getColorAspects_l());
+ }
+ auto fillWork = [buffer, index](const std::unique_ptr<C2Work>& work) {
+ uint32_t flags = 0;
+ if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+ (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling end_of_stream.");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+ fillWork(work);
+ } else {
+ finish(index, fillWork);
+ }
+}
+
+static void freeCallback(const uint8_t */*data*/, void */*cookie*/) {
+ return;
+}
+
+void C2SoftDav1dDec::process(const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2BlockPool>& pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ work->worklets.front()->output.configUpdate.clear();
+ work->worklets.front()->output.flags = work->input.flags;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ size_t inOffset = 0u;
+ size_t inSize = 0u;
+ C2ReadView rView = mDummyReadView;
+ if (!work->input.buffers.empty()) {
+ rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ inSize = rView.capacity();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+
+ bool codecConfig = ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0);
+ bool end_of_stream = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+ if (codecConfig) {
+ fillEmptyWork(work);
+ return;
+ }
+
+ int64_t in_frameIndex = work->input.ordinal.frameIndex.peekll();
+ if (inSize) {
+ mInputBufferIndex = in_frameIndex;
+
+ uint8_t* bitstream = const_cast<uint8_t*>(rView.data() + inOffset);
+
+ mTimeStart = systemTime();
+ nsecs_t delay = mTimeStart - mTimeEnd;
+
+ // Send the bitstream data (inputBuffer) to dav1d.
+ if (mDav1dCtx) {
+ int i_ret = 0;
+
+ Dav1dSequenceHeader seq;
+ int res = dav1d_parse_sequence_header(&seq, bitstream, inSize);
+ if (res == 0) {
+ ALOGV("dav1d found a sequenceHeader (%dx%d) for in_frameIndex=%ld.", seq.max_width,
+ seq.max_height, (long)in_frameIndex);
+ }
+
+ Dav1dData data;
+
+ res = dav1d_data_wrap(&data, bitstream, inSize, freeCallback, nullptr);
+ if (res != 0) {
+ ALOGE("Decoder wrap error %s!", strerror(DAV1D_ERR(res)));
+ i_ret = -1;
+ } else {
+ data.m.timestamp = in_frameIndex;
+ // ALOGV("inSize=%ld, in_frameIndex=%ld, timestamp=%ld",
+ // inSize, frameIndex, data.m.timestamp);
+
+
+ // Dump the bitstream data (inputBuffer) if dumping is enabled.
+#ifdef FILE_DUMP_ENABLE
+ mC2SoftDav1dDump.dumpInput(ptr, new_Size);
+#endif
+
+ bool b_draining = false;
+
+ do {
+ res = dav1d_send_data(mDav1dCtx, &data);
+ if (res < 0 && res != DAV1D_ERR(EAGAIN)) {
+ ALOGE("Decoder feed error %s!", strerror(DAV1D_ERR(res)));
+ /* bitstream decoding errors (typically DAV1D_ERR(EINVAL), are assumed
+ * to be recoverable. Other errors returned from this function are
+ * either unexpected, or considered critical failures.
+ */
+ i_ret = res == DAV1D_ERR(EINVAL) ? 0 : -1;
+ break;
+ }
+
+ bool b_output_error = false;
+
+ do {
+ Dav1dPicture img;
+ memset(&img, 0, sizeof(img));
+
+ res = dav1d_get_picture(mDav1dCtx, &img);
+ if (res == 0) {
+ mDecodedPictures.push_back(img);
+
+ if (!end_of_stream) break;
+ } else if (res == DAV1D_ERR(EAGAIN)) {
+ /* the decoder needs more data to be able to output something.
+ * if there is more data pending, continue the loop below or
+ * otherwise break */
+ if (data.sz != 0) res = 0;
+ break;
+ } else {
+ ALOGE("warning! Decoder error %d!", res);
+ b_output_error = true;
+ break;
+ }
+ } while (res == 0);
+
+ if (b_output_error) break;
+
+ /* on drain, we must ignore the 1st EAGAIN */
+ if (!b_draining && (res == DAV1D_ERR(EAGAIN) || res == 0) &&
+ (end_of_stream)) {
+ b_draining = true;
+ res = 0;
+ }
+ } while (res == 0 && ((data.sz != 0) || b_draining));
+
+ if (data.sz > 0) {
+ ALOGE("unexpected data.sz=%zu after dav1d_send_data", data.sz);
+ dav1d_data_unref(&data);
+ }
+ }
+
+ mTimeEnd = systemTime();
+ nsecs_t decodeTime = mTimeEnd - mTimeStart;
+ // ALOGV("decodeTime=%4" PRId64 " delay=%4" PRId64 "\n", decodeTime, delay);
+
+ if (i_ret != 0) {
+ ALOGE("av1 decoder failed to decode frame. status: %d.", i_ret);
+ work->result = C2_CORRUPTED;
+ work->workletsProcessed = 1u;
+ mSignalledError = true;
+ return;
+ }
+ }
+ }
+
+ (void)outputBuffer(pool, work);
+
+ if (end_of_stream) {
+ drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+ mSignalledOutputEos = true;
+ } else if (!inSize) {
+ fillEmptyWork(work);
+ }
+}
+
+void C2SoftDav1dDec::getHDRStaticParams(Dav1dPicture* picture,
+ const std::unique_ptr<C2Work>& work) {
+ C2StreamHdrStaticMetadataInfo::output hdrStaticMetadataInfo{};
+ bool infoPresent = false;
+
+ if (picture != nullptr) {
+ if (picture->mastering_display != nullptr) {
+ hdrStaticMetadataInfo.mastering.red.x =
+ picture->mastering_display->primaries[0][0] / 65536.0;
+ hdrStaticMetadataInfo.mastering.red.y =
+ picture->mastering_display->primaries[0][1] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.green.x =
+ picture->mastering_display->primaries[1][0] / 65536.0;
+ hdrStaticMetadataInfo.mastering.green.y =
+ picture->mastering_display->primaries[1][1] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.blue.x =
+ picture->mastering_display->primaries[2][0] / 65536.0;
+ hdrStaticMetadataInfo.mastering.blue.y =
+ picture->mastering_display->primaries[2][1] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.white.x =
+ picture->mastering_display->white_point[0] / 65536.0;
+ hdrStaticMetadataInfo.mastering.white.y =
+ picture->mastering_display->white_point[1] / 65536.0;
+
+ hdrStaticMetadataInfo.mastering.maxLuminance =
+ picture->mastering_display->max_luminance / 256.0;
+ hdrStaticMetadataInfo.mastering.minLuminance =
+ picture->mastering_display->min_luminance / 16384.0;
+
+ infoPresent = true;
+ }
+
+ if (picture->content_light != nullptr) {
+ hdrStaticMetadataInfo.maxCll = picture->content_light->max_content_light_level;
+ hdrStaticMetadataInfo.maxFall = picture->content_light->max_frame_average_light_level;
+ infoPresent = true;
+ }
+ }
+
+ // if (infoPresent) {
+ // ALOGD("received a hdrStaticMetadataInfo (mastering.red=%f,%f mastering.green=%f,%f
+ // mastering.blue=%f,%f mastering.white=%f,%f mastering.maxLuminance=%f
+ // mastering.minLuminance=%f maxCll=%f maxFall=%f) at mOutputBufferIndex=%d.",
+ // hdrStaticMetadataInfo.mastering.red.x,hdrStaticMetadataInfo.mastering.red.y,
+ // hdrStaticMetadataInfo.mastering.green.x,hdrStaticMetadataInfo.mastering.green.y,
+ // hdrStaticMetadataInfo.mastering.blue.x,hdrStaticMetadataInfo.mastering.blue.y,
+ // hdrStaticMetadataInfo.mastering.white.x,hdrStaticMetadataInfo.mastering.white.y,
+ // hdrStaticMetadataInfo.mastering.maxLuminance,hdrStaticMetadataInfo.mastering.minLuminance,
+ // hdrStaticMetadataInfo.maxCll,
+ // hdrStaticMetadataInfo.maxFall,
+ // mOutputBufferIndex);
+ // }
+
+ // config if static info has changed
+ if (infoPresent && !(hdrStaticMetadataInfo == mHdrStaticMetadataInfo)) {
+ mHdrStaticMetadataInfo = hdrStaticMetadataInfo;
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(mHdrStaticMetadataInfo));
+ }
+}
+
+void C2SoftDav1dDec::getHDR10PlusInfoData(Dav1dPicture* picture,
+ const std::unique_ptr<C2Work>& work) {
+ if (picture != nullptr) {
+ if (picture->itut_t35 != nullptr) {
+ std::vector<uint8_t> payload;
+ size_t payloadSize = picture->itut_t35->payload_size;
+ if (payloadSize > 0) {
+ payload.push_back(picture->itut_t35->country_code);
+ if (picture->itut_t35->country_code == 0xFF) {
+ payload.push_back(picture->itut_t35->country_code_extension_byte);
+ }
+ payload.insert(payload.end(), picture->itut_t35->payload,
+ picture->itut_t35->payload + picture->itut_t35->payload_size);
+ }
+
+ std::unique_ptr<C2StreamHdr10PlusInfo::output> hdr10PlusInfo =
+ C2StreamHdr10PlusInfo::output::AllocUnique(payload.size());
+ if (!hdr10PlusInfo) {
+ ALOGE("Hdr10PlusInfo allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ memcpy(hdr10PlusInfo->m.value, payload.data(), payload.size());
+
+ // ALOGD("Received a hdr10PlusInfo from picture->itut_t32
+ // (payload_size=%ld,country_code=%d) at mOutputBufferIndex=%d.",
+ // picture->itut_t35->payload_size,
+ // picture->itut_t35->country_code,
+ // mOutputBufferIndex);
+
+ // config if hdr10Plus info has changed
+ if (nullptr == mHdr10PlusInfo || !(*hdr10PlusInfo == *mHdr10PlusInfo)) {
+ mHdr10PlusInfo = std::move(hdr10PlusInfo);
+ work->worklets.front()->output.configUpdate.push_back(std::move(mHdr10PlusInfo));
+ }
+ }
+ }
+}
+
+void C2SoftDav1dDec::getVuiParams(Dav1dPicture* picture) {
+ VuiColorAspects vuiColorAspects;
+
+ if (picture) {
+ vuiColorAspects.primaries = picture->seq_hdr->pri;
+ vuiColorAspects.transfer = picture->seq_hdr->trc;
+ vuiColorAspects.coeffs = picture->seq_hdr->mtrx;
+ vuiColorAspects.fullRange = picture->seq_hdr->color_range;
+
+ // ALOGD("Received a vuiColorAspects from dav1d
+ // (primaries = % d, transfer = % d, coeffs = % d, fullRange = % d)
+ // at mOutputBufferIndex = % d,
+ // out_frameIndex = % ld.",
+ // vuiColorAspects.primaries,
+ // vuiColorAspects.transfer, vuiColorAspects.coeffs, vuiColorAspects.fullRange,
+ // mOutputBufferIndex, picture->m.timestamp);
+ }
+
+ // convert vui aspects to C2 values if changed
+ if (!(vuiColorAspects == mBitstreamColorAspects)) {
+ mBitstreamColorAspects = vuiColorAspects;
+ ColorAspects sfAspects;
+ C2StreamColorAspectsInfo::input codedAspects = {0u};
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+ vuiColorAspects.fullRange, sfAspects);
+ if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+ codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+ codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+ codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+ codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+ }
+}
+
+void C2SoftDav1dDec::setError(const std::unique_ptr<C2Work>& work, c2_status_t error) {
+ mSignalledError = true;
+ work->result = error;
+ work->workletsProcessed = 1u;
+}
+
+bool C2SoftDav1dDec::allocTmpFrameBuffer(size_t size) {
+ if (size > mTmpFrameBufferSize) {
+ mTmpFrameBuffer = std::make_unique<uint16_t[]>(size);
+ if (mTmpFrameBuffer == nullptr) {
+ mTmpFrameBufferSize = 0;
+ return false;
+ }
+ mTmpFrameBufferSize = size;
+ }
+ return true;
+}
+
+bool C2SoftDav1dDec::outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work) {
+ if (!(work && pool)) return false;
+ if (mDav1dCtx == nullptr) return false;
+
+ // Get a decoded picture from dav1d if it is enabled.
+ Dav1dPicture img;
+ memset(&img, 0, sizeof(img));
+
+ int res = 0;
+ if (mDecodedPictures.size() > 0) {
+ img = mDecodedPictures.front();
+ mDecodedPictures.pop_front();
+ // ALOGD("Got a picture(out_frameIndex=%ld,timestamp=%ld) from the deque for
+ // outputBuffer.",img.m.timestamp,img.m.timestamp);
+ } else {
+ res = dav1d_get_picture(mDav1dCtx, &img);
+ if (res == 0) {
+ // ALOGD("Got a picture(out_frameIndex=%ld,timestamp=%ld) from dav1d for
+ // outputBuffer.",img.m.timestamp,img.m.timestamp);
+ } else {
+ ALOGE("failed to get a picture from dav1d for outputBuffer.");
+ }
+ }
+
+ if (res == DAV1D_ERR(EAGAIN)) {
+ ALOGD("Not enough data to output a picture.");
+ return false;
+ }
+ if (res != 0) {
+ ALOGE("The AV1 decoder failed to get a picture (res=%s).", strerror(DAV1D_ERR(res)));
+ return false;
+ }
+
+ const int width = img.p.w;
+ const int height = img.p.h;
+ if (width != mWidth || height != mHeight) {
+ mWidth = width;
+ mHeight = height;
+
+ C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(size));
+ } else {
+ ALOGE("Config update size failed");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ work->workletsProcessed = 1u;
+ return false;
+ }
+ }
+
+ getVuiParams(&img);
+ getHDRStaticParams(&img, work);
+ getHDR10PlusInfoData(&img, work);
+
+ // out_frameIndex that the decoded picture returns from dav1d.
+ int64_t out_frameIndex = img.m.timestamp;
+
+ const bool isMonochrome = img.p.layout == DAV1D_PIXEL_LAYOUT_I400;
+
+ int bitdepth = img.p.bpc;
+
+ std::shared_ptr<C2GraphicBlock> block;
+ uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> codedColorAspects;
+ if (bitdepth == 10 && mPixelFormatInfo->value != HAL_PIXEL_FORMAT_YCBCR_420_888) {
+ IntfImpl::Lock lock = mIntf->lock();
+ codedColorAspects = mIntf->getColorAspects_l();
+ bool allowRGBA1010102 = false;
+ if (codedColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
+ codedColorAspects->matrix == C2Color::MATRIX_BT2020 &&
+ codedColorAspects->transfer == C2Color::TRANSFER_ST2084) {
+ allowRGBA1010102 = true;
+ }
+ format = getHalPixelFormatForBitDepth10(allowRGBA1010102);
+ }
+
+ if (mHalPixelFormat != format) {
+ C2StreamPixelFormatInfo::output pixelFormat(0u, format);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&pixelFormat}, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(pixelFormat));
+ } else {
+ ALOGE("Config update pixelFormat failed");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return UNKNOWN_ERROR;
+ }
+ mHalPixelFormat = format;
+ }
+
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+ // We always create a graphic block that is width aligned to 16 and height
+ // aligned to 2. We set the correct "crop" value of the image in the call to
+ // createGraphicBuffer() by setting the correct image dimensions.
+ c2_status_t err =
+ pool->fetchGraphicBlock(align(mWidth, 16), align(mHeight, 2), format, usage, &block);
+
+ if (err != C2_OK) {
+ ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+ work->result = err;
+ return false;
+ }
+
+ C2GraphicView wView = block->map().get();
+
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return false;
+ }
+
+ // ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d", block->width(),
+ // block->height(), mWidth, mHeight, (int)out_frameIndex);
+
+ mOutputBufferIndex = out_frameIndex;
+
+ uint8_t* dstY = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t* dstU = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_U]);
+ uint8_t* dstV = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_V]);
+
+ C2PlanarLayout layout = wView.layout();
+ size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ size_t dstUStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+ size_t dstVStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+ CONV_FORMAT_T convFormat;
+ switch (img.p.layout) {
+ case DAV1D_PIXEL_LAYOUT_I444:
+ convFormat = CONV_FORMAT_I444;
+ break;
+ case DAV1D_PIXEL_LAYOUT_I422:
+ convFormat = CONV_FORMAT_I422;
+ break;
+ default:
+ convFormat = CONV_FORMAT_I420;
+ break;
+ }
+
+ if (bitdepth == 10) {
+ // TODO: b/277797541 - Investigate if we can ask DAV1D to output the required format during
+ // decompression to avoid color conversion.
+ const uint16_t* srcY = (const uint16_t*)img.data[0];
+ const uint16_t* srcU = (const uint16_t*)img.data[1];
+ const uint16_t* srcV = (const uint16_t*)img.data[2];
+ size_t srcYStride = img.stride[0] / 2;
+ size_t srcUStride = img.stride[1] / 2;
+ size_t srcVStride = img.stride[1] / 2;
+
+ if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
+ if (isMonochrome) {
+ const size_t tmpSize = mWidth;
+ const bool needFill = tmpSize > mTmpFrameBufferSize;
+ if (!allocTmpFrameBuffer(tmpSize)) {
+ ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ setError(work, C2_NO_MEMORY);
+ return false;
+ }
+ srcU = srcV = mTmpFrameBuffer.get();
+ srcUStride = srcVStride = 0;
+ if (needFill) {
+ std::fill_n(mTmpFrameBuffer.get(), tmpSize, 512);
+ }
+ }
+ convertPlanar16ToY410OrRGBA1010102(
+ dstY, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ dstYStride, mWidth, mHeight,
+ std::static_pointer_cast<const C2ColorAspectsStruct>(codedColorAspects),
+ convFormat);
+ } else if (format == HAL_PIXEL_FORMAT_YCBCR_P010) {
+ dstYStride /= 2;
+ dstUStride /= 2;
+ dstVStride /= 2;
+ size_t tmpSize = 0;
+ if ((img.p.layout == DAV1D_PIXEL_LAYOUT_I444) ||
+ (img.p.layout == DAV1D_PIXEL_LAYOUT_I422)) {
+ tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
+ if (!allocTmpFrameBuffer(tmpSize)) {
+ ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ setError(work, C2_NO_MEMORY);
+ return false;
+ }
+ }
+ convertPlanar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV, srcYStride,
+ srcUStride, srcVStride, dstYStride, dstUStride, dstVStride,
+ mWidth, mHeight, isMonochrome, convFormat, mTmpFrameBuffer.get(),
+ tmpSize);
+ } else {
+ size_t tmpSize = 0;
+ if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
+ tmpSize = dstYStride * mHeight + dstUStride * align(mHeight, 2);
+ if (!allocTmpFrameBuffer(tmpSize)) {
+ ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ setError(work, C2_NO_MEMORY);
+ return false;
+ }
+ }
+ convertPlanar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, dstVStride, mWidth, mHeight,
+ isMonochrome, convFormat, mTmpFrameBuffer.get(), tmpSize);
+ }
+
+ // if(mOutputBufferIndex % 100 == 0)
+ ALOGV("output a 10bit picture %dx%d from dav1d "
+ "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
+ mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
+
+ // Dump the output buffer if dumping is enabled (debug only).
+#ifdef FILE_DUMP_ENABLE
+ mC2SoftDav1dDump.dumpOutput<uint16_t>(srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ mWidth, mHeight);
+#endif
+ } else {
+ const uint8_t* srcY = (const uint8_t*)img.data[0];
+ const uint8_t* srcU = (const uint8_t*)img.data[1];
+ const uint8_t* srcV = (const uint8_t*)img.data[2];
+
+ size_t srcYStride = img.stride[0];
+ size_t srcUStride = img.stride[1];
+ size_t srcVStride = img.stride[1];
+
+ // if(mOutputBufferIndex % 100 == 0)
+ ALOGV("output a 8bit picture %dx%d from dav1d "
+ "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
+ mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
+
+ // Dump the output buffer is dumping is enabled (debug only)
+#ifdef FILE_DUMP_ENABLE
+ mC2SoftDav1dDump.dumpOutput<uint8_t>(srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ mWidth, mHeight);
+#endif
+ convertPlanar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride,
+ dstYStride, dstUStride, dstVStride, mWidth, mHeight, isMonochrome,
+ convFormat);
+ }
+
+ dav1d_picture_unref(&img);
+
+ finishWork(out_frameIndex, work, std::move(block));
+ block = nullptr;
+ return true;
+}
+
+c2_status_t C2SoftDav1dDec::drainInternal(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work) {
+ if (drainMode == NO_DRAIN) {
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ }
+ if (drainMode == DRAIN_CHAIN) {
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ }
+
+ while (outputBuffer(pool, work)) {
+ }
+
+ if (drainMode == DRAIN_COMPONENT_WITH_EOS && work && work->workletsProcessed == 0u) {
+ fillEmptyWork(work);
+ }
+
+ return C2_OK;
+}
+
+c2_status_t C2SoftDav1dDec::drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) {
+ return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftDav1dFactory : public C2ComponentFactory {
+ public:
+ C2SoftDav1dFactory()
+ : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+ virtual c2_status_t createComponent(c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(
+ new C2SoftDav1dDec(COMPONENT_NAME, id,
+ std::make_shared<C2SoftDav1dDec::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual c2_status_t createInterface(
+ c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = std::shared_ptr<C2ComponentInterface>(
+ new SimpleInterface<C2SoftDav1dDec::IntfImpl>(
+ COMPONENT_NAME, id, std::make_shared<C2SoftDav1dDec::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftDav1dFactory() override = default;
+
+ private:
+ std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+} // namespace android
+
+__attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftDav1dFactory();
+}
+
+__attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(
+ ::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDec.h b/media/codec2/components/dav1d/C2SoftDav1dDec.h
new file mode 100644
index 0000000..e3d2a93
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_DAV1D_DEC_H_
+#define ANDROID_C2_SOFT_DAV1D_DEC_H_
+
+#include <inttypes.h>
+
+#include <memory>
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <C2Config.h>
+#include <SimpleC2Component.h>
+
+#include <dav1d/dav1d.h>
+#include <deque>
+#include <C2SoftDav1dDump.h>
+
+//#define FILE_DUMP_ENABLE 1
+
+namespace android {
+
+struct C2SoftDav1dDec : public SimpleC2Component {
+ class IntfImpl;
+
+ C2SoftDav1dDec(const char* name, c2_node_id_t id, const std::shared_ptr<IntfImpl>& intfImpl);
+ ~C2SoftDav1dDec();
+
+ // Begin SimpleC2Component overrides.
+ c2_status_t onInit() override;
+ c2_status_t onStop() override;
+ void onReset() override;
+ void onRelease() override;
+ c2_status_t onFlush_sm() override;
+ void process(const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2BlockPool>& pool) override;
+ c2_status_t drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) override;
+ // End SimpleC2Component overrides.
+
+ private:
+ std::shared_ptr<IntfImpl> mIntf;
+
+ int mInputBufferIndex = 0;
+ int mOutputBufferIndex = 0;
+
+ Dav1dContext* mDav1dCtx = nullptr;
+ std::deque<Dav1dPicture> mDecodedPictures;
+
+ // configurations used by component in process
+ // (TODO: keep this in intf but make them internal only)
+ std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormatInfo;
+
+ uint32_t mHalPixelFormat;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ bool mSignalledOutputEos;
+ bool mSignalledError;
+ // Used during 10-bit I444/I422 to 10-bit P010 & 8-bit I420 conversions.
+ std::unique_ptr<uint16_t[]> mTmpFrameBuffer;
+ size_t mTmpFrameBufferSize = 0;
+
+ C2StreamHdrStaticMetadataInfo::output mHdrStaticMetadataInfo;
+ std::unique_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfo = nullptr;
+
+ // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+ // converting them to C2 values for each frame
+ struct VuiColorAspects {
+ uint8_t primaries;
+ uint8_t transfer;
+ uint8_t coeffs;
+ uint8_t fullRange;
+
+ // default color aspects
+ VuiColorAspects()
+ : primaries(C2Color::PRIMARIES_UNSPECIFIED),
+ transfer(C2Color::TRANSFER_UNSPECIFIED),
+ coeffs(C2Color::MATRIX_UNSPECIFIED),
+ fullRange(C2Color::RANGE_UNSPECIFIED) {}
+
+ bool operator==(const VuiColorAspects& o) {
+ return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs &&
+ fullRange == o.fullRange;
+ }
+ } mBitstreamColorAspects;
+
+ nsecs_t mTimeStart = 0; // Time at the start of decode()
+ nsecs_t mTimeEnd = 0; // Time at the end of decode()
+
+ bool initDecoder();
+ void getHDRStaticParams(Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
+ void getHDR10PlusInfoData(Dav1dPicture* picture, const std::unique_ptr<C2Work>& work);
+ void getVuiParams(Dav1dPicture* picture);
+ void destroyDecoder();
+ void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2GraphicBlock>& block);
+ // Sets |work->result| and mSignalledError. Returns false.
+ void setError(const std::unique_ptr<C2Work>& work, c2_status_t error);
+ bool allocTmpFrameBuffer(size_t size);
+ bool outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work);
+
+ c2_status_t drainInternal(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work);
+
+ void flushDav1d();
+
+#ifdef FILE_DUMP_ENABLE
+ C2SoftDav1dDump mC2SoftDav1dDump;
+#endif
+
+ C2_DO_NOT_COPY(C2SoftDav1dDec);
+};
+
+} // namespace android
+
+#endif // ANDROID_C2_SOFT_DAV1D_DEC_H_
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDump.cpp b/media/codec2/components/dav1d/C2SoftDav1dDump.cpp
new file mode 100644
index 0000000..ec8d6cd
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDump.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftDav1dDump"
+#include "C2SoftDav1dDump.h"
+
+namespace android {
+
+// Flag to enable dumping the bitsteram and the decoded pictures to files.
+static const bool ENABLE_DUMPING_FILES_DEFAULT = true;
+static const char ENABLE_DUMPING_FILES_PROPERTY[] = "debug.dav1d.enabledumping";
+
+// The number of frames to dump to a file
+static const int NUM_FRAMES_TO_DUMP_DEFAULT = INT_MAX;
+static const char NUM_FRAMES_TO_DUMP_PROPERTY[] = "debug.dav1d.numframestodump";
+
+// start dumping from this frame
+static const int STARTING_FRAME_TO_DUMP_DEFAULT = 0;
+static const char STARTING_FRAME_TO_DUMP_PROPERTY[] = "debug.dav1d.startingframetodump";
+
+void C2SoftDav1dDump::initDumping() {
+ nsecs_t now = systemTime();
+ snprintf(mInDataFileName, kFileNameLength, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now,
+ INPUT_DATA_DUMP_EXT);
+ snprintf(mInSizeFileName, kFileNameLength, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now,
+ INPUT_SIZE_DUMP_EXT);
+ snprintf(mDav1dOutYuvFileName, kFileNameLength, "%s_%" PRId64 "dx.%s", DUMP_FILE_PATH, now,
+ OUTPUT_YUV_DUMP_EXT);
+
+ mFramesToDump =
+ android::base::GetIntProperty(NUM_FRAMES_TO_DUMP_PROPERTY, NUM_FRAMES_TO_DUMP_DEFAULT);
+ mFirstFrameToDump = android::base::GetIntProperty(STARTING_FRAME_TO_DUMP_PROPERTY,
+ STARTING_FRAME_TO_DUMP_DEFAULT);
+ bool enableDumping = android::base::GetBoolProperty(ENABLE_DUMPING_FILES_PROPERTY,
+ ENABLE_DUMPING_FILES_DEFAULT);
+ ALOGD("enableDumping = %d, mFramesToDump = %d", enableDumping, mFramesToDump);
+
+ if (enableDumping) {
+ mInDataFile = fopen(mInDataFileName, "wb");
+ if (mInDataFile == nullptr) {
+ ALOGD("Could not open file %s", mInDataFileName);
+ }
+
+ mInSizeFile = fopen(mInSizeFileName, "wb");
+ if (mInSizeFile == nullptr) {
+ ALOGD("Could not open file %s", mInSizeFileName);
+ }
+
+ mDav1dOutYuvFile = fopen(mDav1dOutYuvFileName, "wb");
+ if (mDav1dOutYuvFile == nullptr) {
+ ALOGD("Could not open file %s", mDav1dOutYuvFileName);
+ }
+ }
+}
+
+void C2SoftDav1dDump::destroyDumping() {
+ if (mInDataFile != nullptr) {
+ fclose(mInDataFile);
+ mInDataFile = nullptr;
+ }
+
+ if (mInSizeFile != nullptr) {
+ fclose(mInSizeFile);
+ mInSizeFile = nullptr;
+ }
+
+ if (mDav1dOutYuvFile != nullptr) {
+ fclose(mDav1dOutYuvFile);
+ mDav1dOutYuvFile = nullptr;
+ }
+}
+
+void C2SoftDav1dDump::dumpInput(uint8_t* ptr, int size) {
+ if (mInDataFile) {
+ int ret = fwrite(ptr, 1, size, mInDataFile);
+
+ if (ret != size) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mInDataFileName, size, ret);
+ }
+ }
+
+ // Dump the size per inputBuffer if dumping is enabled.
+ if (mInSizeFile) {
+ int ret = fwrite(&size, 1, 4, mInSizeFile);
+
+ if (ret != 4) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mInSizeFileName, 4, ret);
+ }
+ }
+}
+
+template <typename T>
+void C2SoftDav1dDump::dumpOutput(const T* srcY, const T* srcU, const T* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, int width, int height) {
+ mOutputCount++;
+ FILE* fp_out = mDav1dOutYuvFile;
+ int typeSize = sizeof(T);
+ if (fp_out && mOutputCount >= mFirstFrameToDump &&
+ mOutputCount <= (mFirstFrameToDump + mFramesToDump - 1)) {
+ for (int i = 0; i < height; i++) {
+ int ret =
+ fwrite((uint8_t*)srcY + i * srcYStride * typeSize, 1, width * typeSize, fp_out);
+ if (ret != width * typeSize) {
+ ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < height / 2; i++) {
+ int ret = fwrite((uint8_t*)srcU + i * srcUStride * typeSize, 1, width * typeSize / 2,
+ fp_out);
+ if (ret != width * typeSize / 2) {
+ ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize / 2, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < height / 2; i++) {
+ int ret = fwrite((uint8_t*)srcV + i * srcVStride * typeSize, 1, width * typeSize / 2,
+ fp_out);
+ if (ret != width * typeSize / 2) {
+ ALOGE("Error in fwrite, requested %d, returned %d", width * typeSize / 2, ret);
+ break;
+ }
+ }
+ }
+}
+
+void C2SoftDav1dDump::writeDav1dOutYuvFile(const Dav1dPicture& p) {
+ if (mDav1dOutYuvFile != NULL) {
+ uint8_t* ptr;
+ const int hbd = p.p.bpc > 8;
+
+ ptr = (uint8_t*)p.data[0];
+ for (int y = 0; y < p.p.h; y++) {
+ int iSize = p.p.w << hbd;
+ int ret = fwrite(ptr, 1, iSize, mDav1dOutYuvFile);
+ if (ret != iSize) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName, iSize,
+ ret);
+ break;
+ }
+
+ ptr += p.stride[0];
+ }
+
+ if (p.p.layout != DAV1D_PIXEL_LAYOUT_I400) {
+ // u/v
+ const int ss_ver = p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
+ const int ss_hor = p.p.layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int cw = (p.p.w + ss_hor) >> ss_hor;
+ const int ch = (p.p.h + ss_ver) >> ss_ver;
+ for (int pl = 1; pl <= 2; pl++) {
+ ptr = (uint8_t*)p.data[pl];
+ for (int y = 0; y < ch; y++) {
+ int iSize = cw << hbd;
+ int ret = fwrite(ptr, 1, cw << hbd, mDav1dOutYuvFile);
+ if (ret != iSize) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mDav1dOutYuvFileName,
+ iSize, ret);
+ break;
+ }
+ ptr += p.stride[1];
+ }
+ }
+ }
+ }
+}
+
+template void C2SoftDav1dDump::dumpOutput<uint8_t>(const uint8_t* srcY, const uint8_t* srcU,
+ const uint8_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, int width,
+ int height);
+template void C2SoftDav1dDump::dumpOutput<uint16_t>(const uint16_t* srcY, const uint16_t* srcU,
+ const uint16_t* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, int width,
+ int height);
+} // namespace android
\ No newline at end of file
diff --git a/media/codec2/components/dav1d/C2SoftDav1dDump.h b/media/codec2/components/dav1d/C2SoftDav1dDump.h
new file mode 100644
index 0000000..ea7a48a
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDump.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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/properties.h>
+#include <Codec2CommonUtils.h>
+#include <Codec2Mapper.h>
+#include <dav1d/dav1d.h>
+
+#define DUMP_FILE_PATH "/data/local/tmp/dump"
+#define INPUT_DATA_DUMP_EXT "av1"
+#define INPUT_SIZE_DUMP_EXT "size"
+#define OUTPUT_YUV_DUMP_EXT "yuv"
+
+namespace android {
+constexpr size_t kFileNameLength = 256;
+
+class C2SoftDav1dDump {
+ public:
+ void initDumping();
+ void destroyDumping();
+ void dumpInput(uint8_t* ptr, int new_size);
+ template <typename T>
+ void dumpOutput(const T* srcY, const T* srcU, const T* srcV, size_t srcYStride,
+ size_t srcUStride, size_t srcVStride, int width, int height);
+ void writeDav1dOutYuvFile(const Dav1dPicture& p);
+
+ private:
+ int mFramesToDump = 0;
+ int mFirstFrameToDump = 0;
+ int mOutputCount = 0;
+
+ char mInDataFileName[kFileNameLength];
+ char mInSizeFileName[kFileNameLength];
+ char mDav1dOutYuvFileName[kFileNameLength];
+
+ FILE* mInDataFile = nullptr;
+ FILE* mInSizeFile = nullptr;
+ FILE* mDav1dOutYuvFile = nullptr;
+};
+} // namespace android
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index 3e4247b..5f5f05d 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -752,6 +752,19 @@
return true;
}
+bool C2SoftGav1Dec::fillMonochromeRow(int value) {
+ const size_t tmpSize = mWidth;
+ const bool needFill = tmpSize > mTmpFrameBufferSize;
+ if (!allocTmpFrameBuffer(tmpSize)) {
+ ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ return false;
+ }
+ if (needFill) {
+ std::fill_n(mTmpFrameBuffer.get(), tmpSize, value);
+ }
+ return true;
+}
+
bool C2SoftGav1Dec::outputBuffer(const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work) {
if (!(work && pool)) return false;
@@ -773,6 +786,16 @@
return false;
}
+#if LIBYUV_VERSION < 1871
+ if (buffer->bitdepth > 10) {
+ ALOGE("bitdepth %d is not supported", buffer->bitdepth);
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return false;
+ }
+#endif
+
const int width = buffer->displayed_width[0];
const int height = buffer->displayed_height[0];
if (width != mWidth || height != mHeight) {
@@ -816,7 +839,7 @@
std::shared_ptr<C2GraphicBlock> block;
uint32_t format = HAL_PIXEL_FORMAT_YV12;
std::shared_ptr<C2StreamColorAspectsInfo::output> codedColorAspects;
- if (buffer->bitdepth == 10 && mPixelFormatInfo->value != HAL_PIXEL_FORMAT_YCBCR_420_888) {
+ if (buffer->bitdepth >= 10 && mPixelFormatInfo->value != HAL_PIXEL_FORMAT_YCBCR_420_888) {
IntfImpl::Lock lock = mIntf->lock();
codedColorAspects = mIntf->getColorAspects_l();
bool allowRGBA1010102 = false;
@@ -828,8 +851,9 @@
format = getHalPixelFormatForBitDepth10(allowRGBA1010102);
#if !HAVE_LIBYUV_I410_I210_TO_AB30
if ((format == HAL_PIXEL_FORMAT_RGBA_1010102) &&
- (buffer->image_format != libgav1::kImageFormatYuv420)) {
- ALOGE("Only YUV420 output is supported when targeting RGBA_1010102");
+ (buffer->image_format != libgav1::kImageFormatYuv420) &&
+ (buffer->bitdepth == 10)) {
+ ALOGE("Only YUV420 output is supported for 10-bit when targeting RGBA_1010102");
mSignalledError = true;
work->result = C2_OMITTED;
work->workletsProcessed = 1u;
@@ -837,6 +861,18 @@
}
#endif
}
+ if (buffer->bitdepth == 12 && format == HAL_PIXEL_FORMAT_RGBA_1010102 &&
+ (buffer->image_format == libgav1::kImageFormatYuv422 ||
+ buffer->image_format == libgav1::kImageFormatYuv444)) {
+ // There are no 12-bit color conversion functions from YUV422/YUV444 to
+ // RGBA_1010102. Use 8-bit YV12 in this case.
+ format = HAL_PIXEL_FORMAT_YV12;
+ }
+ if (buffer->bitdepth == 12 && format == HAL_PIXEL_FORMAT_YCBCR_P010) {
+ // There are no 12-bit color conversion functions to P010. Use 8-bit YV12
+ // in this case.
+ format = HAL_PIXEL_FORMAT_YV12;
+ }
if (mHalPixelFormat != format) {
C2StreamPixelFormatInfo::output pixelFormat(0u, format);
@@ -890,7 +926,41 @@
size_t dstUStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
size_t dstVStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
- if (buffer->bitdepth == 10) {
+ if (buffer->bitdepth == 12) {
+#if LIBYUV_VERSION >= 1871
+ const uint16_t *srcY = (const uint16_t *)buffer->plane[0];
+ const uint16_t *srcU = (const uint16_t *)buffer->plane[1];
+ const uint16_t *srcV = (const uint16_t *)buffer->plane[2];
+ size_t srcYStride = buffer->stride[0] / 2;
+ size_t srcUStride = buffer->stride[1] / 2;
+ size_t srcVStride = buffer->stride[2] / 2;
+ if (isMonochrome) {
+ if (!fillMonochromeRow(2048)) {
+ setError(work, C2_NO_MEMORY);
+ return false;
+ }
+ srcU = srcV = mTmpFrameBuffer.get();
+ srcUStride = srcVStride = 0;
+ }
+ if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
+ libyuv::I012ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride,
+ dstY, dstYStride, &libyuv::kYuvV2020Constants,
+ mWidth, mHeight);
+ } else if (isMonochrome || buffer->image_format == libgav1::kImageFormatYuv420) {
+ libyuv::I012ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride,
+ dstY, dstYStride, dstU, dstUStride, dstV, dstVStride,
+ mWidth, mHeight);
+ } else if (buffer->image_format == libgav1::kImageFormatYuv444) {
+ libyuv::I412ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride,
+ dstY, dstYStride, dstU, dstUStride, dstV, dstVStride,
+ mWidth, mHeight);
+ } else {
+ libyuv::I212ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride,
+ dstY, dstYStride, dstU, dstUStride, dstV, dstVStride,
+ mWidth, mHeight);
+ }
+#endif // LIBYUV_VERSION >= 1871
+ } else if (buffer->bitdepth == 10) {
const uint16_t *srcY = (const uint16_t *)buffer->plane[0];
const uint16_t *srcU = (const uint16_t *)buffer->plane[1];
const uint16_t *srcV = (const uint16_t *)buffer->plane[2];
@@ -915,18 +985,12 @@
#endif // HAVE_LIBYUV_I410_I210_TO_AB30
if (!processed) {
if (isMonochrome) {
- const size_t tmpSize = mWidth;
- const bool needFill = tmpSize > mTmpFrameBufferSize;
- if (!allocTmpFrameBuffer(tmpSize)) {
- ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ if (!fillMonochromeRow(512)) {
setError(work, C2_NO_MEMORY);
return false;
}
srcU = srcV = mTmpFrameBuffer.get();
srcUStride = srcVStride = 0;
- if (needFill) {
- std::fill_n(mTmpFrameBuffer.get(), tmpSize, 512);
- }
}
convertYUV420Planar16ToY410OrRGBA1010102(
(uint32_t *)dstY, srcY, srcU, srcV, srcYStride,
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index c3b27ea..0e09fcc 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -105,6 +105,7 @@
// Sets |work->result| and mSignalledError. Returns false.
void setError(const std::unique_ptr<C2Work> &work, c2_status_t error);
bool allocTmpFrameBuffer(size_t size);
+ bool fillMonochromeRow(int value);
bool outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
const std::unique_ptr<C2Work>& work);
c2_status_t drainInternal(uint32_t drainMode,
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index e3f8b1c..e7dd7c2 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -16,8 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2Client"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <android-base/logging.h>
-
+#include <utils/Trace.h>
#include <codec2/hidl/client.h>
#include <C2Debug.h>
#include <C2BufferPriv.h>
@@ -2321,6 +2322,7 @@
const C2ConstGraphicBlock& block,
const QueueBufferInput& input,
QueueBufferOutput* output) {
+ ScopedTrace trace(ATRACE_TAG,"Codec2Client::Component::queueToOutputSurface");
return mOutputBufferQueue->outputBuffer(block, input, output);
}
diff --git a/media/codec2/hal/client/output.cpp b/media/codec2/hal/client/output.cpp
index 4eebd1c..2f9773e 100644
--- a/media/codec2/hal/client/output.cpp
+++ b/media/codec2/hal/client/output.cpp
@@ -16,7 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-OutputBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <android-base/logging.h>
+#include <utils/Trace.h>
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
#include <codec2/hidl/output.h>
@@ -388,6 +390,7 @@
uint32_t generation;
uint64_t bqId;
int32_t bqSlot;
+ ScopedTrace trace(ATRACE_TAG,"Codec2-OutputBufferQueue::outputBuffer");
bool display = V1_0::utils::displayBufferQueueBlock(block);
if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
bqId == 0) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index a2f9b26..718bc8f 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -600,6 +600,8 @@
size_t bufferSize = 0;
c2_status_t blockRes = C2_OK;
bool copied = false;
+ ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf(
+ "CCodecBufferChannel::decrypt(%s)", mName).c_str());
if (mSendEncryptedInfoBuffer) {
static const C2MemoryUsage kDefaultReadWriteUsage{
C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index c351b6d..775bbbf 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -62,8 +62,8 @@
void setCrypto(const sp<ICrypto> &crypto) override;
void setDescrambler(const sp<IDescrambler> &descrambler) override;
- virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
- virtual status_t queueSecureInputBuffer(
+ status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ status_t queueSecureInputBuffer(
const sp<MediaCodecBuffer> &buffer,
bool secure,
const uint8_t *key,
@@ -73,10 +73,10 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) override;
- virtual status_t attachBuffer(
+ status_t attachBuffer(
const std::shared_ptr<C2Buffer> &c2Buffer,
const sp<MediaCodecBuffer> &buffer) override;
- virtual status_t attachEncryptedBuffer(
+ status_t attachEncryptedBuffer(
const sp<hardware::HidlMemory> &memory,
bool secure,
const uint8_t *key,
@@ -88,12 +88,12 @@
size_t numSubSamples,
const sp<MediaCodecBuffer> &buffer,
AString* errorDetailMsg) override;
- virtual status_t renderOutputBuffer(
+ status_t renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
- virtual void pollForRenderedBuffers() override;
- virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
- virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
- virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+ void pollForRenderedBuffers() override;
+ status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+ void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
// Methods below are interface for CCodec to use.
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index cbef644..a3754c6 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -1079,8 +1079,7 @@
public:
FlexOutputBuffers(const char *componentName, const char *name = "Output[]")
: OutputBuffers(componentName, name),
- mImpl(mName),
- mPixelFormat(0) { }
+ mImpl(mName) { }
status_t registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index 4bf6cd0..a57c0f1 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -28,8 +28,7 @@
class InputSurfaceWrapper {
public:
InputSurfaceWrapper()
- : mDataSpace(HAL_DATASPACE_UNKNOWN),
- mPixelFormat(PIXEL_FORMAT_UNKNOWN) {
+ : mDataSpace(HAL_DATASPACE_UNKNOWN) {
}
virtual ~InputSurfaceWrapper() = default;
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index 9004bcf..261fd05 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -621,8 +621,8 @@
uint8_t maxLvlChroma = colorRange == C2Color::RANGE_FULL ? 255 : 240;
#define CLIP3(min,v,max) (((v) < (min)) ? (min) : (((max) > (v)) ? (v) : (max)))
- for (size_t y = 0; y < src.height(); ++y) {
- for (size_t x = 0; x < src.width(); ++x) {
+ for (size_t y = 0; y < src.crop().height; ++y) {
+ for (size_t x = 0; x < src.crop().width; ++x) {
uint8_t r = *pRed;
uint8_t g = *pGreen;
uint8_t b = *pBlue;
diff --git a/media/codec2/vndk/C2Buffer.cpp b/media/codec2/vndk/C2Buffer.cpp
index 018e269..a56a216 100644
--- a/media/codec2/vndk/C2Buffer.cpp
+++ b/media/codec2/vndk/C2Buffer.cpp
@@ -16,7 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2Buffer"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <list>
#include <map>
@@ -33,6 +35,7 @@
namespace {
+using android::ScopedTrace;
using android::C2AllocatorBlob;
using android::C2AllocatorGralloc;
using android::C2AllocatorIon;
@@ -1159,6 +1162,7 @@
uint32_t capacity,
C2MemoryUsage usage,
std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+ ScopedTrace trace(ATRACE_TAG,"C2PooledBlockPool::fetchLinearBlock");
if (mBufferPoolVer == VER_HIDL && mImpl) {
return mImpl->fetchLinearBlock(capacity, usage, block);
}
@@ -1174,6 +1178,7 @@
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block) {
+ ScopedTrace trace(ATRACE_TAG,"C2PooledBlockPool::fetchGraphicBlock");
if (mBufferPoolVer == VER_HIDL && mImpl) {
return mImpl->fetchGraphicBlock(width, height, format, usage, block);
}
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 5fb0c8f..960fa79 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -16,8 +16,10 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2BqBuffer"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <android/hardware_buffer.h>
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
@@ -37,6 +39,7 @@
#include <map>
#include <mutex>
+using ::android::ScopedTrace;
using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
using ::android::C2AllocatorGralloc;
using ::android::C2AndroidMemoryUsage;
@@ -1063,6 +1066,7 @@
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+ ScopedTrace trace(ATRACE_TAG,"C2BufferQueueBlockPool::fetchGraphicBlock");
if (mImpl) {
return mImpl->fetchGraphicBlock(width, height, format, usage, block, nullptr);
}
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 7648c76..c3b32e6 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -539,6 +539,22 @@
* Available since API level 29.
*/
AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE = 10,
+
+ /**
+ * Use this preset for an echo canceller to capture the reference signal.
+ * Reserved for system components.
+ * Requires CAPTURE_AUDIO_OUTPUT permission
+ * Available since API level 35.
+ */
+ AAUDIO_INPUT_PRESET_SYSTEM_ECHO_REFERENCE = 1997,
+
+ /**
+ * Use this preset for preemptible, low-priority software hotword detection.
+ * Reserved for system components.
+ * Requires CAPTURE_AUDIO_HOTWORD permission.
+ * Available since API level 35.
+ */
+ AAUDIO_INPUT_PRESET_SYSTEM_HOTWORD = 1999,
};
typedef int32_t aaudio_input_preset_t;
@@ -623,6 +639,11 @@
* (e.g. a USB audio interface, a DAC connected to headphones) to
* specify allowable configurations of a particular device.
*
+ * Channel masks are for input only, output only, or both input and output.
+ * These channel masks are different than those defined in AudioFormat.java.
+ * If an app gets a channel mask from Java API and wants to use it in AAudio,
+ * conversion should be done by the app.
+ *
* Added in API level 32.
*/
enum {
@@ -630,10 +651,6 @@
* Invalid channel mask
*/
AAUDIO_CHANNEL_INVALID = -1,
-
- /**
- * Output audio channel mask
- */
AAUDIO_CHANNEL_FRONT_LEFT = 1 << 0,
AAUDIO_CHANNEL_FRONT_RIGHT = 1 << 1,
AAUDIO_CHANNEL_FRONT_CENTER = 1 << 2,
@@ -661,62 +678,112 @@
AAUDIO_CHANNEL_FRONT_WIDE_LEFT = 1 << 24,
AAUDIO_CHANNEL_FRONT_WIDE_RIGHT = 1 << 25,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_MONO = AAUDIO_CHANNEL_FRONT_LEFT,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_STEREO = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_2POINT1 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_LOW_FREQUENCY,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_TRI = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_TRI_BACK = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_BACK_CENTER,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_3POINT1 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_LOW_FREQUENCY,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_2POINT0POINT2 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_TOP_SIDE_LEFT |
AAUDIO_CHANNEL_TOP_SIDE_RIGHT,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_2POINT1POINT2 = AAUDIO_CHANNEL_2POINT0POINT2 |
AAUDIO_CHANNEL_LOW_FREQUENCY,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_3POINT0POINT2 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_TOP_SIDE_LEFT |
AAUDIO_CHANNEL_TOP_SIDE_RIGHT,
+ /**
+ * Supported for Input and Output
+ */
AAUDIO_CHANNEL_3POINT1POINT2 = AAUDIO_CHANNEL_3POINT0POINT2 |
AAUDIO_CHANNEL_LOW_FREQUENCY,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_QUAD = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_BACK_LEFT |
AAUDIO_CHANNEL_BACK_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_QUAD_SIDE = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_SIDE_LEFT |
AAUDIO_CHANNEL_SIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_SURROUND = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_BACK_CENTER,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_PENTA = AAUDIO_CHANNEL_QUAD |
AAUDIO_CHANNEL_FRONT_CENTER,
- // aka 5POINT1_BACK
+ /**
+ * Supported for Input and Output. aka 5POINT1_BACK
+ */
AAUDIO_CHANNEL_5POINT1 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_LOW_FREQUENCY |
AAUDIO_CHANNEL_BACK_LEFT |
AAUDIO_CHANNEL_BACK_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_5POINT1_SIDE = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_LOW_FREQUENCY |
AAUDIO_CHANNEL_SIDE_LEFT |
AAUDIO_CHANNEL_SIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_6POINT1 = AAUDIO_CHANNEL_FRONT_LEFT |
AAUDIO_CHANNEL_FRONT_RIGHT |
AAUDIO_CHANNEL_FRONT_CENTER |
@@ -724,32 +791,55 @@
AAUDIO_CHANNEL_BACK_LEFT |
AAUDIO_CHANNEL_BACK_RIGHT |
AAUDIO_CHANNEL_BACK_CENTER,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_7POINT1 = AAUDIO_CHANNEL_5POINT1 |
AAUDIO_CHANNEL_SIDE_LEFT |
AAUDIO_CHANNEL_SIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_5POINT1POINT2 = AAUDIO_CHANNEL_5POINT1 |
AAUDIO_CHANNEL_TOP_SIDE_LEFT |
AAUDIO_CHANNEL_TOP_SIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_5POINT1POINT4 = AAUDIO_CHANNEL_5POINT1 |
AAUDIO_CHANNEL_TOP_FRONT_LEFT |
AAUDIO_CHANNEL_TOP_FRONT_RIGHT |
AAUDIO_CHANNEL_TOP_BACK_LEFT |
AAUDIO_CHANNEL_TOP_BACK_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_7POINT1POINT2 = AAUDIO_CHANNEL_7POINT1 |
AAUDIO_CHANNEL_TOP_SIDE_LEFT |
AAUDIO_CHANNEL_TOP_SIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_7POINT1POINT4 = AAUDIO_CHANNEL_7POINT1 |
AAUDIO_CHANNEL_TOP_FRONT_LEFT |
AAUDIO_CHANNEL_TOP_FRONT_RIGHT |
AAUDIO_CHANNEL_TOP_BACK_LEFT |
AAUDIO_CHANNEL_TOP_BACK_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_9POINT1POINT4 = AAUDIO_CHANNEL_7POINT1POINT4 |
AAUDIO_CHANNEL_FRONT_WIDE_LEFT |
AAUDIO_CHANNEL_FRONT_WIDE_RIGHT,
+ /**
+ * Supported for only Output
+ */
AAUDIO_CHANNEL_9POINT1POINT6 = AAUDIO_CHANNEL_9POINT1POINT4 |
AAUDIO_CHANNEL_TOP_SIDE_LEFT |
AAUDIO_CHANNEL_TOP_SIDE_RIGHT,
-
+ /**
+ * Supported for only Input
+ */
AAUDIO_CHANNEL_FRONT_BACK = AAUDIO_CHANNEL_FRONT_CENTER |
AAUDIO_CHANNEL_BACK_CENTER,
};
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 5444565..b7e0ae6 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -39,18 +39,21 @@
aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
int32_t sourceChannelCount,
+ int32_t sourceSampleRate,
audio_format_t sinkFormat,
int32_t sinkChannelCount,
+ int32_t sinkSampleRate,
bool useMonoBlend,
+ bool useVolumeRamps,
float audioBalance,
- bool isExclusive) {
+ aaudio::resampler::MultiChannelResampler::Quality resamplerQuality) {
FlowGraphPortFloatOutput *lastOutput = nullptr;
- // TODO change back to ALOGD
- ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, "
- "useMonoBlend = %d, audioBalance = %f, isExclusive %d",
- __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount,
- useMonoBlend, audioBalance, isExclusive);
+ ALOGD("%s() source format = 0x%08x, channels = %d, sample rate = %d, "
+ "sink format = 0x%08x, channels = %d, sample rate = %d, "
+ "useMonoBlend = %d, audioBalance = %f, useVolumeRamps %d",
+ __func__, sourceFormat, sourceChannelCount, sourceSampleRate, sinkFormat,
+ sinkChannelCount, sinkSampleRate, useMonoBlend, audioBalance, useVolumeRamps);
switch (sourceFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
@@ -85,6 +88,15 @@
lastOutput = &mLimiter->output;
}
+ if (sourceSampleRate != sinkSampleRate) {
+ mResampler.reset(aaudio::resampler::MultiChannelResampler::make(sourceChannelCount,
+ sourceSampleRate, sinkSampleRate, resamplerQuality));
+ mRateConverter = std::make_unique<SampleRateConverter>(sourceChannelCount,
+ *mResampler);
+ lastOutput->connect(&mRateConverter->input);
+ lastOutput = &mRateConverter->output;
+ }
+
// Expand the number of channels if required.
if (sourceChannelCount == 1 && sinkChannelCount > 1) {
mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
@@ -95,8 +107,7 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- // Apply volume ramps for only exclusive streams.
- if (isExclusive) {
+ if (useVolumeRamps) {
// Apply volume ramps to set the left/right audio balance and target volumes.
// The signals will be decoupled, volume ramps will be applied, before the signals are
// combined again.
@@ -137,9 +148,14 @@
return AAUDIO_OK;
}
-void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) {
- mSource->setData(source, numFrames);
- mSink->read(destination, numFrames);
+int32_t AAudioFlowGraph::pull(void *destination, int32_t targetFramesToRead) {
+ return mSink->read(destination, targetFramesToRead);
+}
+
+int32_t AAudioFlowGraph::process(const void *source, int32_t numFramesToWrite, void *destination,
+ int32_t targetFramesToRead) {
+ mSource->setData(source, numFramesToWrite);
+ return mSink->read(destination, targetFramesToRead);
}
/**
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index 35fef37..e1d517e 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -30,6 +30,7 @@
#include <flowgraph/MonoToMultiConverter.h>
#include <flowgraph/MultiToManyConverter.h>
#include <flowgraph/RampLinear.h>
+#include <flowgraph/SampleRateConverter.h>
class AAudioFlowGraph {
public:
@@ -38,23 +39,57 @@
*
* @param sourceFormat
* @param sourceChannelCount
+ * @param sourceSampleRate
* @param sinkFormat
* @param sinkChannelCount
+ * @param sinkSampleRate
* @param useMonoBlend
+ * @param useVolumeRamps
* @param audioBalance
- * @param channelMask
- * @param isExclusive
+ * @param resamplerQuality
* @return
*/
aaudio_result_t configure(audio_format_t sourceFormat,
int32_t sourceChannelCount,
+ int32_t sourceSampleRate,
audio_format_t sinkFormat,
int32_t sinkChannelCount,
+ int32_t sinkSampleRate,
bool useMonoBlend,
+ bool useVolumeRamps,
float audioBalance,
- bool isExclusive);
+ aaudio::resampler::MultiChannelResampler::Quality resamplerQuality);
- void process(const void *source, void *destination, int32_t numFrames);
+ /**
+ * Attempt to read targetFramesToRead from the flowgraph.
+ * This function returns the number of frames actually read.
+ *
+ * This function does nothing if process() was not called before.
+ *
+ * @param destination
+ * @param targetFramesToRead
+ * @return numFramesRead
+ */
+ int32_t pull(void *destination, int32_t targetFramesToRead);
+
+ /**
+ * Set numFramesToWrite frames from the source into the flowgraph.
+ * Then, attempt to read targetFramesToRead from the flowgraph.
+ * This function returns the number of frames actually read.
+ *
+ * There may be data still in the flowgraph if targetFramesToRead is not large enough.
+ * Before calling process() again, pull() must be called until until all the data is consumed.
+ *
+ * TODO: b/289510598 - Calculate the exact number of input frames needed for Y output frames.
+ *
+ * @param source
+ * @param numFramesToWrite
+ * @param destination
+ * @param targetFramesToRead
+ * @return numFramesRead
+ */
+ int32_t process(const void *source, int32_t numFramesToWrite, void *destination,
+ int32_t targetFramesToRead);
/**
* @param volume between 0.0 and 1.0
@@ -73,6 +108,8 @@
private:
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::FlowGraphSourceBuffered> mSource;
+ std::unique_ptr<RESAMPLER_OUTER_NAMESPACE::resampler::MultiChannelResampler> mResampler;
+ std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::SampleRateConverter> mRateConverter;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::MonoBlend> mMonoBlend;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::Limiter> mLimiter;
std::unique_ptr<FLOWGRAPH_OUTER_NAMESPACE::flowgraph::MonoToMultiConverter> mChannelConverter;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 84c715f..9b1ad72 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -63,6 +63,8 @@
#define LOG_TIMESTAMPS 0
+#define ENABLE_SAMPLE_RATE_CONVERTER 1
+
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
, mClockModel()
@@ -132,12 +134,6 @@
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
- request.getConfiguration().setHardwareSamplesPerFrame(builder.getHardwareSamplesPerFrame());
- request.getConfiguration().setHardwareSampleRate(builder.getHardwareSampleRate());
- request.getConfiguration().setHardwareFormat(builder.getHardwareFormat());
-
- mDeviceChannelCount = getSamplesPerFrame(); // Assume it will be the same. Update if not.
-
mServiceStreamHandleInfo = mServiceInterface.openStream(request, configurationOutput);
if (getServiceHandle() < 0
&& (request.getConfiguration().getSamplesPerFrame() == 1
@@ -181,9 +177,6 @@
setChannelMask(configurationOutput.getChannelMask());
}
- mDeviceChannelCount = configurationOutput.getSamplesPerFrame();
-
- setSampleRate(configurationOutput.getSampleRate());
setDeviceId(configurationOutput.getDeviceId());
setSessionId(configurationOutput.getSessionId());
setSharingMode(configurationOutput.getSharingMode());
@@ -194,8 +187,21 @@
setIsContentSpatialized(configurationOutput.isContentSpatialized());
setInputPreset(configurationOutput.getInputPreset());
+ setDeviceSampleRate(configurationOutput.getSampleRate());
+
+ if (getSampleRate() == AAUDIO_UNSPECIFIED) {
+ setSampleRate(configurationOutput.getSampleRate());
+ }
+
+#if !ENABLE_SAMPLE_RATE_CONVERTER
+ if (getSampleRate() != getDeviceSampleRate()) {
+ goto error;
+ }
+#endif
+
// Save device format so we can do format conversion and volume scaling together.
setDeviceFormat(configurationOutput.getFormat());
+ setDeviceSamplesPerFrame(configurationOutput.getSamplesPerFrame());
setHardwareSamplesPerFrame(configurationOutput.getHardwareSamplesPerFrame());
setHardwareSampleRate(configurationOutput.getHardwareSampleRate());
@@ -233,39 +239,46 @@
}
aaudio_result_t AudioStreamInternal::configureDataInformation(int32_t callbackFrames) {
- int32_t framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
+ int32_t deviceFramesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
// Scale up the burst size to meet the minimum equivalent in microseconds.
// This is to avoid waking the CPU too often when the HW burst is very small
- // or at high sample rates.
- int32_t framesPerBurst = framesPerHardwareBurst;
+ // or at high sample rates. The actual number of frames that we call back to
+ // the app with will be 0 < N <= framesPerBurst so round up the division.
+ int32_t framesPerBurst = (static_cast<int64_t>(deviceFramesPerBurst) * getSampleRate() +
+ getDeviceSampleRate() - 1) / getDeviceSampleRate();
int32_t burstMicros = 0;
const int32_t burstMinMicros = android::AudioSystem::getAAudioHardwareBurstMinUsec();
do {
if (burstMicros > 0) { // skip first loop
+ deviceFramesPerBurst *= 2;
framesPerBurst *= 2;
}
burstMicros = framesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
} while (burstMicros < burstMinMicros);
ALOGD("%s() original HW burst = %d, minMicros = %d => SW burst = %d\n",
- __func__, framesPerHardwareBurst, burstMinMicros, framesPerBurst);
+ __func__, deviceFramesPerBurst, burstMinMicros, framesPerBurst);
// Validate final burst size.
if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+ setDeviceFramesPerBurst(deviceFramesPerBurst);
setFramesPerBurst(framesPerBurst); // only save good value
- mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+ mDeviceBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+
+ mBufferCapacityInFrames = static_cast<int64_t>(mDeviceBufferCapacityInFrames)
+ * getSampleRate() / getDeviceSampleRate();
if (mBufferCapacityInFrames < getFramesPerBurst()
|| mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
- mClockModel.setSampleRate(getSampleRate());
- mClockModel.setFramesPerBurst(framesPerHardwareBurst);
+ mClockModel.setSampleRate(getDeviceSampleRate());
+ mClockModel.setFramesPerBurst(deviceFramesPerBurst);
if (isDataCallbackSet()) {
mCallbackFrames = callbackFrames;
@@ -315,7 +328,8 @@
mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
}
- setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q
+ // Default buffer size to match Q
+ setBufferSize(mBufferCapacityInFrames / 2);
return AAUDIO_OK;
}
@@ -374,9 +388,9 @@
// Cache the buffer size which may be from client.
const int32_t previousBufferSize = mBufferSizeInFrames;
// Copy all available data from current data queue.
- uint8_t buffer[getBufferCapacity() * getBytesPerFrame()];
- android::fifo_frames_t fullFramesAvailable =
- mAudioEndpoint->read(buffer, getBufferCapacity());
+ uint8_t buffer[getDeviceBufferCapacity() * getBytesPerFrame()];
+ android::fifo_frames_t fullFramesAvailable = mAudioEndpoint->read(buffer,
+ getDeviceBufferCapacity());
mEndPointParcelable.closeDataFileDescriptor();
aaudio_result_t result = mServiceInterface.exitStandby(
mServiceStreamHandleInfo, endpointParcelable);
@@ -408,7 +422,7 @@
goto exit;
}
// Write data from previous data buffer to new endpoint.
- if (android::fifo_frames_t framesWritten =
+ if (const android::fifo_frames_t framesWritten =
mAudioEndpoint->write(buffer, fullFramesAvailable);
framesWritten != fullFramesAvailable) {
ALOGW("Some data lost after exiting standby, frames written: %d, "
@@ -448,7 +462,7 @@
ALOGD("requestStart() but DISCONNECTED");
return AAUDIO_ERROR_DISCONNECTED;
}
- aaudio_stream_state_t originalState = getState();
+ const aaudio_stream_state_t originalState = getState();
setState(AAUDIO_STREAM_STATE_STARTING);
// Clear any stale timestamps from the previous run.
@@ -605,7 +619,11 @@
// Generated in server and passed to client. Return latest.
if (mAtomicInternalTimestamp.isValid()) {
Timestamp timestamp = mAtomicInternalTimestamp.read();
- int64_t position = timestamp.getPosition() + mFramesOffsetFromService;
+ // This should not overflow as timestamp.getPosition() should be a position in a buffer and
+ // not the actual timestamp. timestamp.getNanoseconds() below uses the actual timestamp.
+ // At 48000 Hz we can run for over 100 years before overflowing the int64_t.
+ int64_t position = (timestamp.getPosition() + mFramesOffsetFromService) * getSampleRate() /
+ getDeviceSampleRate();
if (position >= 0) {
*framePosition = position;
*timeNanoseconds = timestamp.getNanoseconds();
@@ -889,7 +907,8 @@
adjustedFrames = maximumSize;
} else {
// Round to the next highest burst size.
- int32_t numBursts = (adjustedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+ int32_t numBursts = (static_cast<int64_t>(adjustedFrames) + getFramesPerBurst() - 1) /
+ getFramesPerBurst();
adjustedFrames = numBursts * getFramesPerBurst();
// Clip just in case maximumSize is not a multiple of getFramesPerBurst().
adjustedFrames = std::min(maximumSize, adjustedFrames);
@@ -897,23 +916,32 @@
if (mAudioEndpoint) {
// Clip against the actual size from the endpoint.
- int32_t actualFrames = 0;
+ int32_t actualFramesDevice = 0;
+ int32_t maximumFramesDevice = (static_cast<int64_t>(maximumSize) * getDeviceSampleRate()
+ + getSampleRate() - 1) / getSampleRate();
// Set to maximum size so we can write extra data when ready in order to reduce glitches.
// The amount we keep in the buffer is controlled by mBufferSizeInFrames.
- mAudioEndpoint->setBufferSizeInFrames(maximumSize, &actualFrames);
+ mAudioEndpoint->setBufferSizeInFrames(maximumFramesDevice, &actualFramesDevice);
+ int32_t actualFrames = (static_cast<int64_t>(actualFramesDevice) * getSampleRate() +
+ getDeviceSampleRate() - 1) / getDeviceSampleRate();
// actualFrames should be <= actual maximum size of endpoint
adjustedFrames = std::min(actualFrames, adjustedFrames);
}
- if (adjustedFrames != mBufferSizeInFrames) {
+ const int32_t bufferSizeInFrames = adjustedFrames;
+ const int32_t deviceBufferSizeInFrames = static_cast<int64_t>(bufferSizeInFrames) *
+ getDeviceSampleRate() / getSampleRate();
+
+ if (deviceBufferSizeInFrames != mDeviceBufferSizeInFrames) {
android::mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
- .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, adjustedFrames)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, deviceBufferSizeInFrames)
.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
.record();
}
- mBufferSizeInFrames = adjustedFrames;
+ mBufferSizeInFrames = bufferSizeInFrames;
+ mDeviceBufferSizeInFrames = deviceBufferSizeInFrames;
ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
return (aaudio_result_t) adjustedFrames;
}
@@ -922,10 +950,18 @@
return mBufferSizeInFrames;
}
+int32_t AudioStreamInternal::getDeviceBufferSize() const {
+ return mDeviceBufferSizeInFrames;
+}
+
int32_t AudioStreamInternal::getBufferCapacity() const {
return mBufferCapacityInFrames;
}
+int32_t AudioStreamInternal::getDeviceBufferCapacity() const {
+ return mDeviceBufferCapacityInFrames;
+}
+
bool AudioStreamInternal::isClockModelInControl() const {
return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 9c06121..a5981b1 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -22,8 +22,9 @@
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceInterface.h"
-#include "client/IsochronousClockModel.h"
+#include "client/AAudioFlowGraph.h"
#include "client/AudioEndpoint.h"
+#include "client/IsochronousClockModel.h"
#include "core/AudioStream.h"
#include "utility/AudioClock.h"
@@ -56,8 +57,12 @@
int32_t getBufferSize() const override;
+ int32_t getDeviceBufferSize() const;
+
int32_t getBufferCapacity() const override;
+ int32_t getDeviceBufferCapacity() const override;
+
int32_t getXRunCount() const override {
return mXRunCount;
}
@@ -133,8 +138,6 @@
// Calculate timeout for an operation involving framesPerOperation.
int64_t calculateReasonableTimeout(int32_t framesPerOperation);
- int32_t getDeviceChannelCount() const { return mDeviceChannelCount; }
-
/**
* @return true if running in audio service, versus in app process
*/
@@ -177,6 +180,8 @@
int64_t mLastFramesWritten = 0;
int64_t mLastFramesRead = 0;
+ AAudioFlowGraph mFlowGraph;
+
private:
/*
* Asynchronous write with data conversion.
@@ -206,13 +211,10 @@
int64_t mServiceLatencyNanos = 0;
- // Sometimes the hardware is operating with a different channel count from the app.
- // Then we require conversion in AAudio.
- int32_t mDeviceChannelCount = 0;
-
int32_t mBufferSizeInFrames = 0; // local threshold to control latency
+ int32_t mDeviceBufferSizeInFrames = 0;
int32_t mBufferCapacityInFrames = 0;
-
+ int32_t mDeviceBufferCapacityInFrames = 0;
};
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index f5cc2be..7d7b4ef 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -47,6 +47,27 @@
}
+aaudio_result_t AudioStreamInternalCapture::open(const AudioStreamBuilder &builder) {
+ aaudio_result_t result = AudioStreamInternal::open(builder);
+ if (result == AAUDIO_OK) {
+ result = mFlowGraph.configure(getDeviceFormat(),
+ getDeviceSamplesPerFrame(),
+ getDeviceSampleRate(),
+ getFormat(),
+ getSamplesPerFrame(),
+ getSampleRate(),
+ getRequireMonoBlend(),
+ false /* useVolumeRamps */,
+ getAudioBalance(),
+ aaudio::resampler::MultiChannelResampler::Quality::Medium);
+
+ if (result != AAUDIO_OK) {
+ safeReleaseClose();
+ }
+ }
+ return result;
+}
+
void AudioStreamInternalCapture::advanceClientToMatchServerPosition(int32_t serverMargin) {
int64_t readCounter = mAudioEndpoint->getDataReadCounter();
int64_t writeCounter = mAudioEndpoint->getDataWriteCounter() + serverMargin;
@@ -149,7 +170,8 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + getFramesPerBurst();
+ const int64_t nextPosition = mAudioEndpoint->getDataReadCounter() +
+ getDeviceFramesPerBurst();
wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
@@ -166,42 +188,73 @@
aaudio_result_t AudioStreamInternalCapture::readNowWithConversion(void *buffer,
int32_t numFrames) {
- // ALOGD("readNowWithConversion(%p, %d)",
- // buffer, numFrames);
WrappingBuffer wrappingBuffer;
- uint8_t *destination = (uint8_t *) buffer;
- int32_t framesLeft = numFrames;
+ uint8_t *byteBuffer = (uint8_t *) buffer;
+ int32_t framesLeftInByteBuffer = numFrames;
+
+ if (framesLeftInByteBuffer > 0) {
+ // Pull data from the flowgraph in case there is residual data.
+ const int32_t framesActuallyWrittenToByteBuffer = mFlowGraph.pull(
+ (void *)byteBuffer,
+ framesLeftInByteBuffer);
+
+ const int32_t numBytesActuallyWrittenToByteBuffer =
+ framesActuallyWrittenToByteBuffer * getBytesPerFrame();
+ byteBuffer += numBytesActuallyWrittenToByteBuffer;
+ framesLeftInByteBuffer -= framesActuallyWrittenToByteBuffer;
+ }
mAudioEndpoint->getFullFramesAvailable(&wrappingBuffer);
- // Read data in one or two parts.
- for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
- int32_t framesToProcess = framesLeft;
- const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
- if (framesAvailable <= 0) break;
+ // Write data in one or two parts.
+ int partIndex = 0;
+ int framesReadFromAudioEndpoint = 0;
+ while (framesLeftInByteBuffer > 0 && partIndex < WrappingBuffer::SIZE) {
+ const int32_t totalFramesInWrappingBuffer = wrappingBuffer.numFrames[partIndex];
+ int32_t framesAvailableInWrappingBuffer = totalFramesInWrappingBuffer;
+ uint8_t *currentWrappingBuffer = (uint8_t *) wrappingBuffer.data[partIndex];
- if (framesToProcess > framesAvailable) {
- framesToProcess = framesAvailable;
+ // Put data from the wrapping buffer into the flowgraph 8 frames at a time.
+ // Continuously pull as much data as possible from the flowgraph into the byte buffer.
+ // The return value of mFlowGraph.process is the number of frames actually pulled.
+ while (framesAvailableInWrappingBuffer > 0 && framesLeftInByteBuffer > 0) {
+ const int32_t framesToReadFromWrappingBuffer = std::min(flowgraph::kDefaultBufferSize,
+ framesAvailableInWrappingBuffer);
+
+ const int32_t numBytesToReadFromWrappingBuffer = getBytesPerDeviceFrame() *
+ framesToReadFromWrappingBuffer;
+
+ // If framesActuallyWrittenToByteBuffer < framesLeftInByteBuffer, it is guaranteed
+ // that all the data is pulled. If there is no more space in the byteBuffer, the
+ // remaining data will be pulled in the following readNowWithConversion().
+ const int32_t framesActuallyWrittenToByteBuffer = mFlowGraph.process(
+ (void *)currentWrappingBuffer,
+ framesToReadFromWrappingBuffer,
+ (void *)byteBuffer,
+ framesLeftInByteBuffer);
+
+ const int32_t numBytesActuallyWrittenToByteBuffer =
+ framesActuallyWrittenToByteBuffer * getBytesPerFrame();
+ byteBuffer += numBytesActuallyWrittenToByteBuffer;
+ framesLeftInByteBuffer -= framesActuallyWrittenToByteBuffer;
+ currentWrappingBuffer += numBytesToReadFromWrappingBuffer;
+ framesAvailableInWrappingBuffer -= framesToReadFromWrappingBuffer;
+
+ //ALOGD("%s() numBytesActuallyWrittenToByteBuffer %d, framesLeftInByteBuffer %d"
+ // "framesAvailableInWrappingBuffer %d, framesReadFromAudioEndpoint %d"
+ // , __func__, numBytesActuallyWrittenToByteBuffer, framesLeftInByteBuffer,
+ // framesAvailableInWrappingBuffer, framesReadFromAudioEndpoint);
}
-
- const int32_t numBytes = getBytesPerFrame() * framesToProcess;
- const int32_t numSamples = framesToProcess * getSamplesPerFrame();
-
- const audio_format_t sourceFormat = getDeviceFormat();
- const audio_format_t destinationFormat = getFormat();
-
- memcpy_by_audio_format(destination, destinationFormat,
- wrappingBuffer.data[partIndex], sourceFormat, numSamples);
-
- destination += numBytes;
- framesLeft -= framesToProcess;
+ framesReadFromAudioEndpoint += totalFramesInWrappingBuffer -
+ framesAvailableInWrappingBuffer;
+ partIndex++;
}
- int32_t framesProcessed = numFrames - framesLeft;
- mAudioEndpoint->advanceReadIndex(framesProcessed);
+ // The audio endpoint should reference the number of frames written to the wrapping buffer.
+ mAudioEndpoint->advanceReadIndex(framesReadFromAudioEndpoint);
- //ALOGD("readNowWithConversion() returns %d", framesProcessed);
- return framesProcessed;
+ // The internal code should use the number of frames read from the app.
+ return numFrames - framesLeftInByteBuffer;
}
int64_t AudioStreamInternalCapture::getFramesWritten() {
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 87017de..10e247d 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -32,6 +32,8 @@
bool inService = false);
virtual ~AudioStreamInternalCapture() = default;
+ aaudio_result_t open(const AudioStreamBuilder &builder) override;
+
aaudio_result_t read(void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 89dd8ff..ac927ae 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -48,14 +48,18 @@
aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AudioStreamInternal::open(builder);
+ const bool useVolumeRamps = (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE);
if (result == AAUDIO_OK) {
result = mFlowGraph.configure(getFormat(),
getSamplesPerFrame(),
+ getSampleRate(),
getDeviceFormat(),
- getDeviceChannelCount(),
+ getDeviceSamplesPerFrame(),
+ getDeviceSampleRate(),
getRequireMonoBlend(),
+ useVolumeRamps,
getAudioBalance(),
- (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE));
+ aaudio::resampler::MultiChannelResampler::Quality::Medium);
if (result != AAUDIO_OK) {
safeReleaseClose();
@@ -186,7 +190,7 @@
// Sleep if there is too much data in the buffer.
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr
- && (mAudioEndpoint->getFullFramesAvailable() >= getBufferSize())) {
+ && (mAudioEndpoint->getFullFramesAvailable() >= getDeviceBufferSize())) {
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
@@ -206,12 +210,12 @@
// If the appBufferSize is smaller than the endpointBufferSize then
// we will have room to write data beyond the appBufferSize.
// That is a technique used to reduce glitches without adding latency.
- const int32_t appBufferSize = getBufferSize();
+ const int64_t appBufferSize = getDeviceBufferSize();
// The endpoint buffer size is set to the maximum that can be written.
// If we use it then we must carve out some room to write data when we wake up.
- const int32_t endBufferSize = mAudioEndpoint->getBufferSizeInFrames()
- - getFramesPerBurst();
- const int32_t bestBufferSize = std::min(appBufferSize, endBufferSize);
+ const int64_t endBufferSize = mAudioEndpoint->getBufferSizeInFrames()
+ - getDeviceFramesPerBurst();
+ const int64_t bestBufferSize = std::min(appBufferSize, endBufferSize);
int64_t targetReadPosition = mAudioEndpoint->getDataWriteCounter() - bestBufferSize;
wakeTime = mClockModel.convertPositionToTime(targetReadPosition);
}
@@ -232,37 +236,78 @@
int32_t numFrames) {
WrappingBuffer wrappingBuffer;
uint8_t *byteBuffer = (uint8_t *) buffer;
- int32_t framesLeft = numFrames;
+ int32_t framesLeftInByteBuffer = numFrames;
mAudioEndpoint->getEmptyFramesAvailable(&wrappingBuffer);
// Write data in one or two parts.
int partIndex = 0;
- while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
- int32_t framesToWrite = framesLeft;
- int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
- if (framesAvailable > 0) {
- if (framesToWrite > framesAvailable) {
- framesToWrite = framesAvailable;
- }
+ int framesWrittenToAudioEndpoint = 0;
+ while (framesLeftInByteBuffer > 0 && partIndex < WrappingBuffer::SIZE) {
+ int32_t framesAvailableInWrappingBuffer = wrappingBuffer.numFrames[partIndex];
+ uint8_t *currentWrappingBuffer = (uint8_t *) wrappingBuffer.data[partIndex];
- int32_t numBytes = getBytesPerFrame() * framesToWrite;
+ if (framesAvailableInWrappingBuffer > 0) {
+ // Pull data from the flowgraph in case there is residual data.
+ const int32_t framesActuallyWrittenToWrappingBuffer = mFlowGraph.pull(
+ (void*) currentWrappingBuffer,
+ framesAvailableInWrappingBuffer);
- mFlowGraph.process((void *)byteBuffer,
- wrappingBuffer.data[partIndex],
- framesToWrite);
+ const int32_t numBytesActuallyWrittenToWrappingBuffer =
+ framesActuallyWrittenToWrappingBuffer * getBytesPerDeviceFrame();
+ currentWrappingBuffer += numBytesActuallyWrittenToWrappingBuffer;
+ framesAvailableInWrappingBuffer -= framesActuallyWrittenToWrappingBuffer;
+ framesWrittenToAudioEndpoint += framesActuallyWrittenToWrappingBuffer;
+ }
- byteBuffer += numBytes;
- framesLeft -= framesToWrite;
- } else {
- break;
+ // Put data from byteBuffer into the flowgraph one buffer (8 frames) at a time.
+ // Continuously pull as much data as possible from the flowgraph into the wrapping buffer.
+ // The return value of mFlowGraph.process is the number of frames actually pulled.
+ while (framesAvailableInWrappingBuffer > 0 && framesLeftInByteBuffer > 0) {
+ const int32_t framesToWriteFromByteBuffer = std::min(flowgraph::kDefaultBufferSize,
+ framesLeftInByteBuffer);
+
+ const int32_t numBytesToWriteFromByteBuffer = getBytesPerFrame() *
+ framesToWriteFromByteBuffer;
+
+ //ALOGD("%s() framesLeftInByteBuffer %d, framesAvailableInWrappingBuffer %d"
+ // "framesToWriteFromByteBuffer %d, numBytesToWriteFromByteBuffer %d"
+ // , __func__, framesLeftInByteBuffer, framesAvailableInWrappingBuffer,
+ // framesToWriteFromByteBuffer, numBytesToWriteFromByteBuffer);
+
+ const int32_t framesActuallyWrittenToWrappingBuffer = mFlowGraph.process(
+ (void *)byteBuffer,
+ framesToWriteFromByteBuffer,
+ (void *)currentWrappingBuffer,
+ framesAvailableInWrappingBuffer);
+
+ byteBuffer += numBytesToWriteFromByteBuffer;
+ framesLeftInByteBuffer -= framesToWriteFromByteBuffer;
+ const int32_t numBytesActuallyWrittenToWrappingBuffer =
+ framesActuallyWrittenToWrappingBuffer * getBytesPerDeviceFrame();
+ currentWrappingBuffer += numBytesActuallyWrittenToWrappingBuffer;
+ framesAvailableInWrappingBuffer -= framesActuallyWrittenToWrappingBuffer;
+ framesWrittenToAudioEndpoint += framesActuallyWrittenToWrappingBuffer;
+
+ //ALOGD("%s() numBytesActuallyWrittenToWrappingBuffer %d, framesLeftInByteBuffer %d"
+ // "framesActuallyWrittenToWrappingBuffer %d, numBytesToWriteFromByteBuffer %d"
+ // "framesWrittenToAudioEndpoint %d"
+ // , __func__, numBytesActuallyWrittenToWrappingBuffer, framesLeftInByteBuffer,
+ // framesActuallyWrittenToWrappingBuffer, numBytesToWriteFromByteBuffer,
+ // framesWrittenToAudioEndpoint);
}
partIndex++;
}
- int32_t framesWritten = numFrames - framesLeft;
- mAudioEndpoint->advanceWriteIndex(framesWritten);
+ //ALOGD("%s() framesWrittenToAudioEndpoint %d, numFrames %d"
+ // "framesLeftInByteBuffer %d"
+ // , __func__, framesWrittenToAudioEndpoint, numFrames,
+ // framesLeftInByteBuffer);
- return framesWritten;
+ // The audio endpoint should reference the number of frames written to the wrapping buffer.
+ mAudioEndpoint->advanceWriteIndex(framesWrittenToAudioEndpoint);
+
+ // The internal code should use the number of frames read from the app.
+ return numFrames - framesLeftInByteBuffer;
}
int64_t AudioStreamInternalPlay::getFramesRead() {
@@ -284,7 +329,6 @@
return mLastFramesWritten;
}
-
// Render audio in the application callback and then write the data to the stream.
void *AudioStreamInternalPlay::callbackLoop() {
ALOGD("%s() entering >>>>>>>>>>>>>>>", __func__);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index e761807..b51b5d0 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -21,7 +21,6 @@
#include <aaudio/AAudio.h>
#include "binding/AAudioServiceInterface.h"
-#include "client/AAudioFlowGraph.h"
#include "client/AudioStreamInternal.h"
using android::sp;
@@ -89,13 +88,11 @@
* Asynchronous write with data conversion.
* @param buffer
* @param numFrames
- * @return fdrames written or negative error
+ * @return frames written or negative error
*/
aaudio_result_t writeNowWithConversion(const void *buffer,
int32_t numFrames);
- AAudioFlowGraph mFlowGraph;
-
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 8a13a6f..1e27a81 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -571,13 +571,15 @@
AAUDIO_API int64_t AAudioStream_getFramesWritten(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- return audioStream->getFramesWritten();
+ return audioStream->getFramesWritten() * audioStream->getSampleRate() /
+ audioStream->getDeviceSampleRate();
}
AAUDIO_API int64_t AAudioStream_getFramesRead(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- return audioStream->getFramesRead();
+ return audioStream->getFramesRead() * audioStream->getSampleRate() /
+ audioStream->getDeviceSampleRate();
}
AAUDIO_API aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream,
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index f305e46..1db62f3 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -184,6 +184,8 @@
case AAUDIO_INPUT_PRESET_VOICE_RECOGNITION:
case AAUDIO_INPUT_PRESET_UNPROCESSED:
case AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE:
+ case AAUDIO_INPUT_PRESET_SYSTEM_ECHO_REFERENCE:
+ case AAUDIO_INPUT_PRESET_SYSTEM_HOTWORD:
break; // valid
default:
ALOGD("input preset not valid = %d", mInputPreset);
@@ -317,4 +319,4 @@
ALOGD("mHardwareSamplesPerFrame = %6d", mHardwareSamplesPerFrame);
ALOGD("mHardwareSampleRate = %6d", mHardwareSampleRate);
ALOGD("mHardwareAudioFormat = %6d", (int)mHardwareAudioFormat);
-}
\ No newline at end of file
+}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 56ef1e6..e0fd325 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -134,7 +134,8 @@
.set(AMEDIAMETRICS_PROP_ENCODINGHARDWARE,
android::toString(getHardwareFormat()).c_str())
.set(AMEDIAMETRICS_PROP_CHANNELCOUNTHARDWARE, (int32_t)getHardwareSamplesPerFrame())
- .set(AMEDIAMETRICS_PROP_SAMPLERATEHARDWARE, (int32_t)getHardwareSampleRate());
+ .set(AMEDIAMETRICS_PROP_SAMPLERATEHARDWARE, (int32_t)getHardwareSampleRate())
+ .set(AMEDIAMETRICS_PROP_SAMPLERATECLIENT, (int32_t)getSampleRate());
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
item.set(AMEDIAMETRICS_PROP_PLAYERIID, mPlayerBase->getPlayerIId());
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 9b4b734..f2f5cac 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -204,10 +204,18 @@
return mBufferCapacity;
}
+ virtual int32_t getDeviceBufferCapacity() const {
+ return mDeviceBufferCapacity;
+ }
+
virtual int32_t getFramesPerBurst() const {
return mFramesPerBurst;
}
+ virtual int32_t getDeviceFramesPerBurst() const {
+ return mDeviceFramesPerBurst;
+ }
+
virtual int32_t getXRunCount() const {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
@@ -224,6 +232,10 @@
return mSampleRate;
}
+ aaudio_result_t getDeviceSampleRate() const {
+ return mDeviceSampleRate;
+ }
+
aaudio_result_t getHardwareSampleRate() const {
return mHardwareSampleRate;
}
@@ -240,6 +252,10 @@
return mSamplesPerFrame;
}
+ aaudio_result_t getDeviceSamplesPerFrame() const {
+ return mDeviceSamplesPerFrame;
+ }
+
aaudio_result_t getHardwareSamplesPerFrame() const {
return mHardwareSamplesPerFrame;
}
@@ -322,10 +338,10 @@
}
/**
- * This is only valid after setChannelMask() and setDeviceFormat() have been called.
+ * This is only valid after setDeviceSamplesPerFrame() and setDeviceFormat() have been called.
*/
int32_t getBytesPerDeviceFrame() const {
- return getSamplesPerFrame() * audio_bytes_per_sample(getDeviceFormat());
+ return getDeviceSamplesPerFrame() * audio_bytes_per_sample(getDeviceFormat());
}
virtual int64_t getFramesWritten() = 0;
@@ -365,6 +381,11 @@
mSamplesPerFrame = AAudioConvert_channelMaskToCount(channelMask);
}
+ void setDeviceSamplesPerFrame(int32_t deviceSamplesPerFrame) {
+ mDeviceSamplesPerFrame = deviceSamplesPerFrame;
+ }
+
+
/**
* @return true if data callback has been specified
*/
@@ -542,6 +563,11 @@
}
// This should not be called after the open() call.
+ void setDeviceSampleRate(int32_t deviceSampleRate) {
+ mDeviceSampleRate = deviceSampleRate;
+ }
+
+ // This should not be called after the open() call.
void setHardwareSampleRate(int32_t hardwareSampleRate) {
mHardwareSampleRate = hardwareSampleRate;
}
@@ -552,11 +578,21 @@
}
// This should not be called after the open() call.
+ void setDeviceFramesPerBurst(int32_t deviceFramesPerBurst) {
+ mDeviceFramesPerBurst = deviceFramesPerBurst;
+ }
+
+ // This should not be called after the open() call.
void setBufferCapacity(int32_t bufferCapacity) {
mBufferCapacity = bufferCapacity;
}
// This should not be called after the open() call.
+ void setDeviceBufferCapacity(int32_t deviceBufferCapacity) {
+ mDeviceBufferCapacity = deviceBufferCapacity;
+ }
+
+ // This should not be called after the open() call.
void setSharingMode(aaudio_sharing_mode_t sharingMode) {
mSharingMode = sharingMode;
}
@@ -721,9 +757,11 @@
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceSamplesPerFrame = AAUDIO_UNSPECIFIED;
int32_t mHardwareSamplesPerFrame = AAUDIO_UNSPECIFIED;
aaudio_channel_mask_t mChannelMask = AAUDIO_UNSPECIFIED;
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceSampleRate = AAUDIO_UNSPECIFIED;
int32_t mHardwareSampleRate = AAUDIO_UNSPECIFIED;
int32_t mDeviceId = AAUDIO_UNSPECIFIED;
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
@@ -732,7 +770,9 @@
audio_format_t mHardwareFormat = AUDIO_FORMAT_DEFAULT;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
int32_t mFramesPerBurst = 0;
+ int32_t mDeviceFramesPerBurst = 0;
int32_t mBufferCapacity = 0;
+ int32_t mDeviceBufferCapacity = 0;
aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED;
aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/core/VersionExperiment.txt b/media/libaaudio/src/core/VersionExperiment.txt
deleted file mode 100644
index 071239b..0000000
--- a/media/libaaudio/src/core/VersionExperiment.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-
-// TODO Experiment with versioning. This may be removed or changed dramatically.
-// Please ignore for now. Do not review.
-#define OBOE_VERSION_EXPERIMENT 0
-#if OBOE_VERSION_EXPERIMENT
-
-#define OBOE_EARLIEST_SUPPORTED_VERSION 1
-#define OBOE_CURRENT_VERSION 2
-
-typedef struct OboeInterface_s {
- int32_t size; // do not use size_t because its size can vary
- int32_t version;
- int32_t reserved1;
- void * reserved2;
- oboe_result_t (*createStreamBuilder)(OboeStreamBuilder *);
-} OboeInterface_t;
-
-OboeInterface_t s_oboe_template = {
- .size = sizeof(OboeInterface_t),
- .version = OBOE_CURRENT_VERSION,
- .reserved1 = 0,
- .reserved2 = NULL,
- .createStreamBuilder = Oboe_createStreamBuilder
-};
-
-oboe_result_t Oboe_Unimplemented(OboeInterface_t *oboe) {
- (void) oboe;
- return OBOE_ERROR_UNIMPLEMENTED;
-}
-
-typedef oboe_result_t (*OboeFunction_t)(OboeInterface_t *oboe);
-
-int32_t Oboe_Initialize(OboeInterface_t *oboe, uint32_t flags) {
- if (oboe->version < OBOE_EARLIEST_SUPPORTED_VERSION) {
- return OBOE_ERROR_INCOMPATIBLE;
- }
- // Fill in callers vector table.
- uint8_t *start = (uint8_t*)&oboe->reserved1;
- uint8_t *end;
- if (oboe->size <= s_oboe_template.size) {
- end = ((uint8_t *)oboe) + oboe->size;
- } else {
- end = ((uint8_t *)oboe) + s_oboe_template.size;
- // Assume the rest of the structure is vectors.
- // Point them all to OboeInternal_Unimplemented()
- // Point to first vector past end of the known structure.
- OboeFunction_t *next = (OboeFunction_t*)end;
- while ((((uint8_t *)next) - ((uint8_t *)oboe)) < oboe->size) {
- *next++ = Oboe_Unimplemented;
- }
- }
- memcpy(&oboe->reserved1, &s_oboe_template.reserved1, end - start);
- return OBOE_OK;
-}
-#endif /* OBOE_VERSION_EXPERIMENT -------------------------- */
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp
index ad6d041..e79bf96 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.cpp
+++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp
@@ -21,7 +21,8 @@
#include <stdint.h>
#include "FifoControllerBase.h"
-using namespace android; // TODO just import names needed
+using android::FifoControllerBase;
+using android::fifo_frames_t;
FifoControllerBase::FifoControllerBase(fifo_frames_t capacity, fifo_frames_t threshold)
: mCapacity(capacity)
diff --git a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp
index a3ce58c..611ddcd 100644
--- a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp
+++ b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp
@@ -135,10 +135,9 @@
int coefficientIndex = 0;
double phase = 0.0; // ranges from 0.0 to 1.0, fraction between samples
// Stretch the sinc function for low pass filtering.
- const float cutoffScaler = normalizedCutoff *
- ((outputRate < inputRate)
- ? ((float)outputRate / inputRate)
- : ((float)inputRate / outputRate));
+ const float cutoffScaler = (outputRate < inputRate)
+ ? (normalizedCutoff * (float)outputRate / inputRate)
+ : 1.0f; // Do not filter when upsampling.
const int numTapsHalf = getNumTaps() / 2; // numTaps must be even.
const float numTapsHalfInverse = 1.0f / numTapsHalf;
for (int i = 0; i < numRows; i++) {
diff --git a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h
index 717f3fd..9e47335 100644
--- a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h
+++ b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h
@@ -111,6 +111,9 @@
* Set lower to reduce aliasing.
* Default is 0.70.
*
+ * Note that this value is ignored when upsampling, which is when
+ * the outputRate is higher than the inputRate.
+ *
* @param normalizedCutoff anti-aliasing filter cutoff
* @return address of this builder for chaining calls
*/
@@ -227,6 +230,10 @@
/**
* Generate the filter coefficients in optimal order.
+ *
+ * Note that normalizedCutoff is ignored when upsampling, which is when
+ * the outputRate is higher than the inputRate.
+ *
* @param inputRate sample rate of the input stream
* @param outputRate sample rate of the output stream
* @param numRows number of rows in the array that contain a set of tap coefficients
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index e760dab..fe4bf2c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -208,6 +208,12 @@
setBufferCapacity(getBufferCapacityFromDevice());
setFramesPerBurst(getFramesPerBurstFromDevice());
+ // Use the same values for device values.
+ setDeviceSamplesPerFrame(getSamplesPerFrame());
+ setDeviceSampleRate(mAudioRecord->getSampleRate());
+ setDeviceBufferCapacity(getBufferCapacityFromDevice());
+ setDeviceFramesPerBurst(getFramesPerBurstFromDevice());
+
setHardwareSamplesPerFrame(mAudioRecord->getHalChannelCount());
setHardwareSampleRate(mAudioRecord->getHalSampleRate());
setHardwareFormat(mAudioRecord->getHalFormat());
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 67ee42e..59fdabc 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -203,6 +203,12 @@
setBufferCapacity(getBufferCapacityFromDevice());
setFramesPerBurst(getFramesPerBurstFromDevice());
+ // Use the same values for device values.
+ setDeviceSamplesPerFrame(getSamplesPerFrame());
+ setDeviceSampleRate(mAudioTrack->getSampleRate());
+ setDeviceBufferCapacity(getBufferCapacityFromDevice());
+ setDeviceFramesPerBurst(getFramesPerBurstFromDevice());
+
setHardwareSamplesPerFrame(mAudioTrack->getHalChannelCount());
setHardwareSampleRate(mAudioTrack->getHalSampleRate());
setHardwareFormat(mAudioTrack->getHalFormat());
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index e8324a8..0cbf79d 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -383,11 +383,10 @@
return AUDIO_CHANNEL_OUT_7POINT1POINT2;
case AAUDIO_CHANNEL_7POINT1POINT4:
return AUDIO_CHANNEL_OUT_7POINT1POINT4;
- // TODO: add 9point1point4 and 9point1point6 when they are added in audio-hal-enums.h
- // case AAUDIO_CHANNEL_9POINT1POINT4:
- // return AUDIO_CHANNEL_OUT_9POINT1POINT4;
- // case AAUDIO_CHANNEL_9POINT1POINT6:
- // return AUDIO_CHANNEL_OUT_9POINT1POINT6;
+ case AAUDIO_CHANNEL_9POINT1POINT4:
+ return AUDIO_CHANNEL_OUT_9POINT1POINT4;
+ case AAUDIO_CHANNEL_9POINT1POINT6:
+ return AUDIO_CHANNEL_OUT_9POINT1POINT6;
default:
ALOGE("%s() %#x unrecognized", __func__, channelMask);
return AUDIO_CHANNEL_INVALID;
@@ -465,11 +464,10 @@
return AAUDIO_CHANNEL_7POINT1POINT2;
case AUDIO_CHANNEL_OUT_7POINT1POINT4:
return AAUDIO_CHANNEL_7POINT1POINT4;
- // TODO: add 9point1point4 and 9point1point6 when they are added in audio-hal-enums.h
- // case AUDIO_CHANNEL_OUT_9POINT1POINT4:
- // return AAUDIO_CHANNEL_9POINT1POINT4;
- // case AUDIO_CHANNEL_OUT_9POINT1POINT6:
- // return AAUDIO_CHANNEL_9POINT1POINT6;
+ case AUDIO_CHANNEL_OUT_9POINT1POINT4:
+ return AAUDIO_CHANNEL_9POINT1POINT4;
+ case AUDIO_CHANNEL_OUT_9POINT1POINT6:
+ return AAUDIO_CHANNEL_9POINT1POINT6;
default:
ALOGE("%s() %#x unrecognized", __func__, channelMask);
return AAUDIO_CHANNEL_INVALID;
diff --git a/media/libaaudio/src/utility/AudioClock.h b/media/libaaudio/src/utility/AudioClock.h
index d5d4ef4..37f5b39 100644
--- a/media/libaaudio/src/utility/AudioClock.h
+++ b/media/libaaudio/src/utility/AudioClock.h
@@ -33,7 +33,7 @@
public:
static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
struct timespec time;
- int result = clock_gettime(clockId, &time);
+ const int result = clock_gettime(clockId, &time);
if (result < 0) {
return -errno;
}
@@ -56,7 +56,7 @@
time.tv_sec = nanoTime / AAUDIO_NANOS_PER_SECOND;
// Calculate the fractional nanoseconds. Avoids expensive % operation.
time.tv_nsec = nanoTime - (time.tv_sec * AAUDIO_NANOS_PER_SECOND);
- int err = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
+ const int err = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
switch (err) {
case EINTR:
return 1;
@@ -86,7 +86,7 @@
// Calculate the fractional nanoseconds. Avoids expensive % operation.
time.tv_nsec = nanoseconds - (time.tv_sec * AAUDIO_NANOS_PER_SECOND);
const int flags = 0; // documented as relative sleep
- int err = clock_nanosleep(clockId, flags, &time, nullptr);
+ const int err = clock_nanosleep(clockId, flags, &time, nullptr);
switch (err) {
case EINTR:
return 1;
diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h
index 51eb69b..b58634f 100644
--- a/media/libaaudio/src/utility/MonotonicCounter.h
+++ b/media/libaaudio/src/utility/MonotonicCounter.h
@@ -104,7 +104,7 @@
*/
void roundUp64(int32_t period) {
if (period > 0) {
- int64_t numPeriods = (mCounter64 + period - 1) / period;
+ const int64_t numPeriods = (mCounter64 + period - 1) / period;
mCounter64 = numPeriods * period;
}
}
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 24041bc..0cfdfb2 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -145,6 +145,7 @@
srcs: ["test_flowgraph.cpp"],
shared_libs: [
"libaaudio_internal",
+ "libaudioutils",
"libbinder",
"libcutils",
"libutils",
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 6f75f5a..7eb8b0d 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -25,6 +25,8 @@
#include <gtest/gtest.h>
+#include <aaudio/AAudio.h>
+#include "client/AAudioFlowGraph.h"
#include "flowgraph/ClipToRange.h"
#include "flowgraph/Limiter.h"
#include "flowgraph/MonoBlend.h"
@@ -37,8 +39,18 @@
#include "flowgraph/SinkI32.h"
#include "flowgraph/SourceI16.h"
#include "flowgraph/SourceI24.h"
+#include "flowgraph/resampler/IntegerRatio.h"
using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+using namespace RESAMPLER_OUTER_NAMESPACE::resampler;
+
+using TestFlowgraphResamplerParams = std::tuple<int32_t, int32_t, MultiChannelResampler::Quality>;
+
+enum {
+ PARAM_SOURCE_SAMPLE_RATE = 0,
+ PARAM_SINK_SAMPLE_RATE,
+ PARAM_RESAMPLER_QUALITY
+};
constexpr int kBytesPerI24Packed = 3;
@@ -394,3 +406,240 @@
EXPECT_NEAR(expected[i], output[i], tolerance);
}
}
+
+TEST(test_flowgraph, module_sinki16_multiple_reads) {
+ static constexpr int kNumSamples = 8;
+ std::array<int16_t, kNumSamples + 10> output; // larger than input
+
+ SourceFloat sourceFloat{1};
+ SinkI16 sinkI16{1};
+
+ sourceFloat.setData(kInputFloat.data(), kNumSamples);
+ sourceFloat.output.connect(&sinkI16.input);
+
+ output.fill(777);
+
+ // Read the first half of the data
+ int32_t numRead = sinkI16.read(output.data(), kNumSamples / 2);
+ ASSERT_EQ(kNumSamples / 2, numRead);
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_EQ(kExpectedI16.at(i), output.at(i)) << ", i = " << i;
+ }
+
+ // Read the rest of the data
+ numRead = sinkI16.read(output.data(), output.size());
+ ASSERT_EQ(kNumSamples / 2, numRead);
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_EQ(kExpectedI16.at(i + kNumSamples / 2), output.at(i)) << ", i = " << i;
+ }
+}
+
+void checkSampleRateConversionVariedSizes(int32_t sourceSampleRate,
+ int32_t sinkSampleRate,
+ MultiChannelResampler::Quality resamplerQuality) {
+ AAudioFlowGraph flowgraph;
+ aaudio_result_t result = flowgraph.configure(AUDIO_FORMAT_PCM_FLOAT /* sourceFormat */,
+ 1 /* sourceChannelCount */,
+ sourceSampleRate,
+ AUDIO_FORMAT_PCM_FLOAT /* sinkFormat */,
+ 1 /* sinkChannelCount */,
+ sinkSampleRate,
+ false /* useMonoBlend */,
+ false /* useVolumeRamps */,
+ 0.0f /* audioBalance */,
+ resamplerQuality);
+
+ IntegerRatio ratio(sourceSampleRate, sinkSampleRate);
+ ratio.reduce();
+
+ ASSERT_EQ(AAUDIO_OK, result);
+
+ const int inputSize = ratio.getNumerator();
+ const int outputSize = ratio.getDenominator();
+ float input[inputSize];
+ float output[outputSize];
+
+ for (int i = 0; i < inputSize; i++) {
+ input[i] = i * 1.0f / inputSize;
+ }
+
+ int inputUsed = 0;
+ int outputRead = 0;
+ int curInputSize = 1;
+
+ // Process the data with larger and larger input buffer sizes.
+ while (inputUsed < inputSize) {
+ outputRead += flowgraph.process((void *) (input + inputUsed),
+ curInputSize,
+ (void *) (output + outputRead),
+ outputSize - outputRead);
+ inputUsed += curInputSize;
+ curInputSize = std::min(curInputSize + 5, inputSize - inputUsed);
+ }
+
+ ASSERT_EQ(outputSize, outputRead);
+
+ for (int i = 1; i < outputSize; i++) {
+ // The first values of the flowgraph will be close to zero.
+ // Besides those, the values should be strictly increasing.
+ if (output[i - 1] > 0.01f) {
+ EXPECT_GT(output[i], output[i - 1]);
+ }
+ }
+}
+
+TEST(test_flowgraph, flowgraph_varied_sizes_all) {
+ const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000, 64000, 88200, 96000};
+ const MultiChannelResampler::Quality qualities[] =
+ {
+ MultiChannelResampler::Quality::Fastest,
+ MultiChannelResampler::Quality::Low,
+ MultiChannelResampler::Quality::Medium,
+ MultiChannelResampler::Quality::High,
+ MultiChannelResampler::Quality::Best
+ };
+ for (int srcRate : rates) {
+ for (int destRate : rates) {
+ for (auto quality : qualities) {
+ if (srcRate != destRate) {
+ checkSampleRateConversionVariedSizes(srcRate, destRate, quality);
+ }
+ }
+ }
+ }
+}
+
+void checkSampleRateConversionPullLater(int32_t sourceSampleRate,
+ int32_t sinkSampleRate,
+ MultiChannelResampler::Quality resamplerQuality) {
+ AAudioFlowGraph flowgraph;
+ aaudio_result_t result = flowgraph.configure(AUDIO_FORMAT_PCM_FLOAT /* sourceFormat */,
+ 1 /* sourceChannelCount */,
+ sourceSampleRate,
+ AUDIO_FORMAT_PCM_FLOAT /* sinkFormat */,
+ 1 /* sinkChannelCount */,
+ sinkSampleRate,
+ false /* useMonoBlend */,
+ false /* useVolumeRamps */,
+ 0.0f /* audioBalance */,
+ resamplerQuality);
+
+ IntegerRatio ratio(sourceSampleRate, sinkSampleRate);
+ ratio.reduce();
+
+ ASSERT_EQ(AAUDIO_OK, result);
+
+ const int inputSize = ratio.getNumerator();
+ const int outputSize = ratio.getDenominator();
+ float input[inputSize];
+ float output[outputSize];
+
+ for (int i = 0; i < inputSize; i++) {
+ input[i] = i * 1.0f / inputSize;
+ }
+
+ // Read half the data with process.
+ int outputRead = flowgraph.process((void *) input,
+ inputSize,
+ (void *) output,
+ outputSize / 2);
+
+ ASSERT_EQ(outputSize / 2, outputRead);
+
+ // Now read the other half of the data with pull.
+ outputRead += flowgraph.pull(
+ (void *) (output + outputRead),
+ outputSize - outputRead);
+
+ ASSERT_EQ(outputSize, outputRead);
+ for (int i = 1; i < outputSize; i++) {
+ // The first values of the flowgraph will be close to zero.
+ // Besides those, the values should be strictly increasing.
+ if (output[i - 1] > 0.01f) {
+ EXPECT_GT(output[i], output[i - 1]);
+ }
+ }
+}
+
+// TODO: b/289508408 - Remove non-parameterized tests if they get noisy.
+TEST(test_flowgraph, flowgraph_pull_later_all) {
+ const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000, 64000, 88200, 96000};
+ const MultiChannelResampler::Quality qualities[] =
+ {
+ MultiChannelResampler::Quality::Fastest,
+ MultiChannelResampler::Quality::Low,
+ MultiChannelResampler::Quality::Medium,
+ MultiChannelResampler::Quality::High,
+ MultiChannelResampler::Quality::Best
+ };
+ for (int srcRate : rates) {
+ for (int destRate : rates) {
+ for (auto quality : qualities) {
+ if (srcRate != destRate) {
+ checkSampleRateConversionPullLater(srcRate, destRate, quality);
+ }
+ }
+ }
+ }
+}
+
+class TestFlowgraphSampleRateConversion : public ::testing::Test,
+ public ::testing::WithParamInterface<TestFlowgraphResamplerParams> {
+};
+
+const char* resamplerQualityToString(MultiChannelResampler::Quality quality) {
+ switch (quality) {
+ case MultiChannelResampler::Quality::Fastest: return "FASTEST";
+ case MultiChannelResampler::Quality::Low: return "LOW";
+ case MultiChannelResampler::Quality::Medium: return "MEDIUM";
+ case MultiChannelResampler::Quality::High: return "HIGH";
+ case MultiChannelResampler::Quality::Best: return "BEST";
+ }
+ return "UNKNOWN";
+}
+
+static std::string getTestName(
+ const ::testing::TestParamInfo<TestFlowgraphResamplerParams>& info) {
+ return std::string()
+ + std::to_string(std::get<PARAM_SOURCE_SAMPLE_RATE>(info.param))
+ + "__" + std::to_string(std::get<PARAM_SINK_SAMPLE_RATE>(info.param))
+ + "__" + resamplerQualityToString(std::get<PARAM_RESAMPLER_QUALITY>(info.param));
+}
+
+TEST_P(TestFlowgraphSampleRateConversion, test_flowgraph_pull_later) {
+ checkSampleRateConversionPullLater(std::get<PARAM_SOURCE_SAMPLE_RATE>(GetParam()),
+ std::get<PARAM_SINK_SAMPLE_RATE>(GetParam()),
+ std::get<PARAM_RESAMPLER_QUALITY>(GetParam()));
+}
+
+TEST_P(TestFlowgraphSampleRateConversion, test_flowgraph_varied_sizes) {
+ checkSampleRateConversionVariedSizes(std::get<PARAM_SOURCE_SAMPLE_RATE>(GetParam()),
+ std::get<PARAM_SINK_SAMPLE_RATE>(GetParam()),
+ std::get<PARAM_RESAMPLER_QUALITY>(GetParam()));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ test_flowgraph,
+ TestFlowgraphSampleRateConversion,
+ ::testing::Values(
+ TestFlowgraphResamplerParams({8000, 11025, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({8000, 48000, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({8000, 44100, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({11025, 24000, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({11025, 48000,
+ MultiChannelResampler::Quality::Fastest}),
+ TestFlowgraphResamplerParams({11025, 48000, MultiChannelResampler::Quality::Low}),
+ TestFlowgraphResamplerParams({11025, 48000,
+ MultiChannelResampler::Quality::Medium}),
+ TestFlowgraphResamplerParams({11025, 48000, MultiChannelResampler::Quality::High}),
+ TestFlowgraphResamplerParams({11025, 48000, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({11025, 44100, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({11025, 88200, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({16000, 48000, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({44100, 48000, MultiChannelResampler::Quality::Low}),
+ TestFlowgraphResamplerParams({44100, 48000, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({48000, 11025, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({48000, 44100, MultiChannelResampler::Quality::Best}),
+ TestFlowgraphResamplerParams({44100, 11025, MultiChannelResampler::Quality::Best})),
+ &getTestName
+);
diff --git a/media/libaaudio/tests/test_resampler.cpp b/media/libaaudio/tests/test_resampler.cpp
index 1e4f59c..13e4a20 100644
--- a/media/libaaudio/tests/test_resampler.cpp
+++ b/media/libaaudio/tests/test_resampler.cpp
@@ -101,14 +101,20 @@
}
}
+ // Flush out remaining frames from the flowgraph
+ while (!mcResampler->isWriteNeeded()) {
+ mcResampler->readNextFrame(output);
+ output++;
+ numRead++;
+ }
+
ASSERT_LE(numRead, kNumOutputSamples);
// Some frames are lost priming the FIR filter.
- const int kMaxAlgorithmicFrameLoss = 16;
+ const int kMaxAlgorithmicFrameLoss = 5;
EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
- // Some cycles may get chopped off at the end.
- const int kMaxZeroCrossingDelta = 3;
+ const int kMaxZeroCrossingDelta = std::max(sinkRate / sourceRate / 2, 1);
EXPECT_LE(abs(sourceZeroCrossingCount - sinkZeroCrossingCount), kMaxZeroCrossingDelta);
// Detect glitches by looking for spikes in the second derivative.
@@ -136,8 +142,7 @@
TEST(test_resampler, resampler_scan_all) {
- // TODO Add 64000, 88200, 96000 when they work. Failing now.
- const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000};
+ const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000, 64000, 88200, 96000};
const MultiChannelResampler::Quality qualities[] =
{
MultiChannelResampler::Quality::Fastest,
@@ -193,10 +198,9 @@
checkResampler(11025, 44100, MultiChannelResampler::Quality::Best);
}
-// TODO This fails because the output is very low.
-//TEST(test_resampler, resampler_11025_88200_best) {
-// checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
-//}
+TEST(test_resampler, resampler_11025_88200_best) {
+ checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
+}
TEST(test_resampler, resampler_16000_48000_best) {
checkResampler(16000, 48000, MultiChannelResampler::Quality::Best);
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 01e3d53..d35708c 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -336,6 +336,7 @@
srcs: [
"aidl/android/media/AudioAttributesEx.aidl",
"aidl/android/media/AudioMix.aidl",
+ "aidl/android/media/AudioMixUpdate.aidl",
"aidl/android/media/AudioMixerAttributesInternal.aidl",
"aidl/android/media/AudioMixerBehavior.aidl",
"aidl/android/media/AudioMixCallbackFlag.aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 6616197..5bfdd5f 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -20,6 +20,7 @@
#include <utils/Log.h>
#include <android/media/IAudioPolicyService.h>
+#include <android/media/AudioMixUpdate.h>
#include <android/media/BnCaptureStateListener.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
@@ -108,7 +109,7 @@
}
// establish binder interface to AudioFlinger service
-const sp<IAudioFlinger> AudioSystem::get_audio_flinger() {
+const sp<IAudioFlinger> AudioSystem::getAudioFlingerImpl(bool canStartThreadPool = true) {
sp<IAudioFlinger> af;
sp<AudioFlingerClient> afc;
bool reportNoError = false;
@@ -132,12 +133,10 @@
binder = gAudioFlingerBinder;
} else {
sp<IServiceManager> sm = defaultServiceManager();
- do {
- binder = sm->getService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
- if (binder != nullptr) break;
- ALOGW("AudioFlinger not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
+ binder = sm->waitForService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
+ if (binder == nullptr) {
+ return nullptr;
+ }
}
binder->linkToDeath(gAudioFlingerClient);
const auto afs = interface_cast<media::IAudioFlingerService>(binder);
@@ -147,7 +146,9 @@
afc = gAudioFlingerClient;
af = gAudioFlinger;
// Make sure callbacks can be received by gAudioFlingerClient
- ProcessState::self()->startThreadPool();
+ if(canStartThreadPool) {
+ ProcessState::self()->startThreadPool();
+ }
}
const int64_t token = IPCThreadState::self()->clearCallingIdentity();
af->registerClient(afc);
@@ -156,6 +157,14 @@
return af;
}
+const sp<IAudioFlinger> AudioSystem:: get_audio_flinger() {
+ return getAudioFlingerImpl();
+}
+
+const sp<IAudioFlinger> AudioSystem:: get_audio_flinger_for_fuzzer() {
+ return getAudioFlingerImpl(false);
+}
+
const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient() {
// calling get_audio_flinger() will initialize gAudioFlingerClient if needed
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -870,14 +879,10 @@
Mutex::Autolock _l(gLockAPS);
if (gAudioPolicyService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.audio_policy"));
- if (binder != 0)
- break;
- ALOGW("AudioPolicyService not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
+ sp<IBinder> binder = sm->waitForService(String16("media.audio_policy"));
+ if (binder == nullptr) {
+ return nullptr;
+ }
if (gAudioPolicyServiceClient == NULL) {
gAudioPolicyServiceClient = new AudioPolicyServiceClient();
}
@@ -1838,6 +1843,27 @@
return statusTFromBinderStatus(aps->registerPolicyMixes(mixesAidl, registration));
}
+status_t AudioSystem::updatePolicyMixes(
+ const std::vector<std::pair<AudioMix, std::vector<AudioMixMatchCriterion>>>&
+ mixesWithUpdates) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+
+ std::vector<media::AudioMixUpdate> updatesAidl;
+ updatesAidl.reserve(mixesWithUpdates.size());
+
+ for (const auto& update : mixesWithUpdates) {
+ media::AudioMixUpdate updateAidl;
+ updateAidl.audioMix = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioMix(update.first));
+ RETURN_STATUS_IF_ERROR(convertRange(update.second.begin(), update.second.end(),
+ std::back_inserter(updateAidl.newCriteria),
+ legacy2aidl_AudioMixMatchCriterion));
+ updatesAidl.emplace_back(updateAidl);
+ }
+
+ return statusTFromBinderStatus(aps->updatePolicyMixes(updatesAidl));
+}
+
status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -2093,8 +2119,7 @@
return BAD_VALUE;
}
- const sp<IAudioPolicyService>
- & aps = AudioSystem::get_audio_policy_service();
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
std::vector<AudioFormatDescription> formatsAidl;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index f3539a1..8b8f7cd 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -817,7 +817,7 @@
(void) updateAndGetPosition_l();
// save start timestamp
- if (isOffloadedOrDirect_l()) {
+ if (isAfTrackOffloadedOrDirect_l()) {
if (getTimestamp_l(mStartTs) != OK) {
mStartTs.mPosition = 0;
}
@@ -838,7 +838,7 @@
mTimestampStaleTimeReported = false;
mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
- if (!isOffloadedOrDirect_l()
+ if (!isAfTrackOffloadedOrDirect_l()
&& mStartEts.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) {
// Server side has consumed something, but is it finished consuming?
// It is possible since flush and stop are asynchronous that the server
@@ -1917,6 +1917,7 @@
mAfChannelCount = audio_channel_count_from_out_mask(output.afChannelMask);
mAfFormat = output.afFormat;
mAfLatency = output.afLatencyMs;
+ mAfTrackFlags = output.afTrackFlags;
mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;
@@ -3182,7 +3183,7 @@
// To avoid a race, read the presented frames first. This ensures that presented <= consumed.
status_t status;
- if (isOffloadedOrDirect_l()) {
+ if (isAfTrackOffloadedOrDirect_l()) {
// use Binder to get timestamp
media::AudioTimestampInternal ts;
mAudioTrack->getTimestamp(&ts, &status);
@@ -3294,7 +3295,7 @@
ALOGV_IF(status != WOULD_BLOCK, "%s(%d): getTimestamp error:%#x", __func__, mPortId, status);
return status;
}
- if (isOffloadedOrDirect_l()) {
+ if (isAfTrackOffloadedOrDirect_l()) {
if (isOffloaded_l() && (mState == STATE_PAUSED || mState == STATE_PAUSED_STOPPING)) {
// use cached paused position in case another offloaded track is running.
timestamp.mPosition = mPausedPosition;
@@ -3740,7 +3741,7 @@
// This is conservatively figured - if we encounter an unexpected error
// then we will not wait.
bool wait = false;
- if (isOffloadedOrDirect_l()) {
+ if (isAfTrackOffloadedOrDirect_l()) {
AudioTimestamp ts;
status_t status = getTimestamp_l(ts);
if (status == WOULD_BLOCK) {
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 4bd12b8..515e708 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -116,6 +116,8 @@
legacy2aidl_audio_channel_mask_t_AudioChannelLayout(afChannelMask, false /*isInput*/));
aidl.afFormat = VALUE_OR_RETURN(
legacy2aidl_audio_format_t_AudioFormatDescription(afFormat));
+ aidl.afTrackFlags = VALUE_OR_RETURN(
+ legacy2aidl_audio_output_flags_t_int32_t_mask(afTrackFlags));
aidl.outputId = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(outputId));
aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(portId));
aidl.audioTrack = audioTrack;
@@ -144,6 +146,8 @@
false /*isInput*/));
legacy.afFormat = VALUE_OR_RETURN(
aidl2legacy_AudioFormatDescription_audio_format_t(aidl.afFormat));
+ legacy.afTrackFlags = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_output_flags_t_mask(aidl.afTrackFlags));
legacy.outputId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.outputId));
legacy.portId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
legacy.audioTrack = aidl.audioTrack;
diff --git a/media/libaudioclient/aidl/android/media/AudioMixUpdate.aidl b/media/libaudioclient/aidl/android/media/AudioMixUpdate.aidl
new file mode 100644
index 0000000..d481b1c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixUpdate.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 android.media;
+
+import android.media.AudioMix;
+import android.media.AudioMixMatchCriterion;
+
+
+/**
+ * {@hide}
+ */
+parcelable AudioMixUpdate {
+ // Audio mix to update.
+ AudioMix audioMix;
+ // Updated audio mixing rule.
+ AudioMixMatchCriterion[] newCriteria;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
index 42e0bb4..ab60461 100644
--- a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
@@ -43,6 +43,7 @@
AudioChannelLayout afChannelMask;
AudioFormatDescription afFormat;
int afLatencyMs;
+ int afTrackFlags;
/** Interpreted as audio_io_handle_t. */
int outputId;
/** Interpreted as audio_port_handle_t. */
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 3e9b27f..52c8da0 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -20,6 +20,7 @@
import android.media.AudioDirectMode;
import android.media.AudioMix;
+import android.media.AudioMixUpdate;
import android.media.AudioMixerAttributesInternal;
import android.media.AudioOffloadMode;
import android.media.AudioPatchFw;
@@ -262,6 +263,8 @@
void registerPolicyMixes(in AudioMix[] mixes, boolean registration);
+ void updatePolicyMixes(in AudioMixUpdate[] updates);
+
void setUidDeviceAffinities(int /* uid_t */ uid, in AudioDevice[] devices);
void removeUidDeviceAffinities(int /* uid_t */ uid);
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
new file mode 100644
index 0000000..67258d9
--- /dev/null
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_defaults {
+ name: "libaudioclient_aidl_fuzzer_defaults",
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "effect-aidl-cpp",
+ "liblog",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libjsoncpp",
+ "libmediametricsservice",
+ "libmedia_helper",
+ "libprocessgroup",
+ "shared-file-region-aidl-cpp",
+ "libfakeservicemanager"
+ ],
+ shared_libs: [
+ "libaudioclient",
+ "libaudioflinger",
+ "libmediautils",
+ "libnblog",
+ "libaudioprocessing",
+ "libnbaio",
+ "libpowermanager",
+ "libvibrator",
+ "packagemanager_aidl-cpp",
+ "android.hardware.audio.common-util",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "capture_state_listener-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ "libaudiofoundation",
+ "libaudiomanager",
+ "libaudiopolicy",
+ "libaudioutils",
+ "libdl",
+ "libxml2",
+ "mediametricsservice-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ "libvndksupport",
+ "libmediametrics",
+ "libfakeservicemanager",
+ "libactivitymanager_aidl",
+ "libheadtracking",
+ "libaudiopolicyservice",
+ "libsensorprivacy",
+ "libaudiopolicymanagerdefault",
+ "libaudiohal",
+ "libhidlbase",
+ "libpermission",
+ "libaudiohal@7.0",
+ ],
+ header_libs: [
+ "libaudiopolicymanager_interface_headers",
+ "libbinder_headers",
+ "libaudiofoundation_headers",
+ "libmedia_headers",
+ "libaudiohal_headers",
+ "libaudioflinger_headers",
+ "mediautils_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ hotlists: ["4593311"],
+ description: "The fuzzer targets the APIs of libaudioflinger",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
+ },
+}
+
+cc_fuzz {
+ name: "audioflinger_aidl_fuzzer",
+ srcs: ["audioflinger_aidl_fuzzer.cpp"],
+ defaults: [
+ "libaudioclient_aidl_fuzzer_defaults",
+ "service_fuzzer_defaults"
+ ],
+}
diff --git a/media/libaudioclient/aidl/fuzzer/audioflinger_aidl_fuzzer.cpp b/media/libaudioclient/aidl/fuzzer/audioflinger_aidl_fuzzer.cpp
new file mode 100644
index 0000000..fac5f53
--- /dev/null
+++ b/media/libaudioclient/aidl/fuzzer/audioflinger_aidl_fuzzer.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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 <AudioFlinger.h>
+#include <ISchedulingPolicyService.h>
+#include <fakeservicemanager/FakeServiceManager.h>
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/media/IAudioPolicyService.h>
+#include <binder/IActivityManager.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionController.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/IAudioFlinger.h>
+#include <mediautils/SchedulingPolicyService.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
+#include <service/AudioPolicyService.h>
+
+using namespace android;
+using namespace android::binder;
+using android::fuzzService;
+
+static sp<media::IAudioFlingerService> gAudioFlingerService;
+
+class FuzzerSchedulingPolicyService : public BnInterface<ISchedulingPolicyService> {
+ int32_t requestPriority(int32_t /*pid_t*/, int32_t /*tid*/, int32_t /*prio*/, bool /*isForApp*/,
+ bool /*asynchronous*/) {
+ return 0;
+ }
+
+ int32_t requestCpusetBoost(bool /*enable*/, const sp<IBinder>& /*client*/) { return 0; }
+};
+
+class FuzzerPermissionController : public BnInterface<IPermissionController> {
+ public:
+ bool checkPermission(const String16& /*permission*/, int32_t /*pid*/, int32_t /*uid*/) {
+ return true;
+ }
+ int32_t noteOp(const String16& /*op*/, int32_t /*uid*/, const String16& /*packageName*/) {
+ return 0;
+ }
+ void getPackagesForUid(const uid_t /*uid*/, Vector<String16>& /*packages*/) {}
+ bool isRuntimePermission(const String16& /*permission*/) { return true; }
+ int32_t getPackageUid(const String16& /*package*/, int /*flags*/) { return 0; }
+};
+
+class FuzzerSensorPrivacyManager : public BnInterface<hardware::ISensorPrivacyManager> {
+ public:
+ Status supportsSensorToggle(int32_t /*toggleType*/, int32_t /*sensor*/,
+ bool* /*_aidl_return*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status addSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& /*listener*/) override {
+ return Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ Status addToggleSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& /*listener*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status removeSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& /*listener*/) override {
+ return Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ Status removeToggleSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& /*listener*/) override {
+ return Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ Status isSensorPrivacyEnabled(bool* /*_aidl_return*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status isCombinedToggleSensorPrivacyEnabled(int32_t /*sensor*/,
+ bool* /*_aidl_return*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status isToggleSensorPrivacyEnabled(int32_t /*toggleType*/, int32_t /*sensor*/,
+ bool* /*_aidl_return*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status setSensorPrivacy(bool /*enable*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status setToggleSensorPrivacy(int32_t /*userId*/, int32_t /*source*/, int32_t /*sensor*/,
+ bool /*enable*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+ Status setToggleSensorPrivacyForProfileGroup(int32_t /*userId*/, int32_t /*source*/,
+ int32_t /*sensor*/, bool /*enable*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+};
+
+class FuzzerActivityManager : public BnInterface<IActivityManager> {
+ public:
+ int32_t openContentUri(const String16& /*stringUri*/) override { return 0; }
+
+ status_t registerUidObserver(const sp<IUidObserver>& /*observer*/, const int32_t /*event*/,
+ const int32_t /*cutpoint*/,
+ const String16& /*callingPackage*/) override {
+ return OK;
+ }
+
+ status_t unregisterUidObserver(const sp<IUidObserver>& /*observer*/) override { return OK; }
+
+ bool isUidActive(const uid_t /*uid*/, const String16& /*callingPackage*/) override {
+ return true;
+ }
+
+ int32_t getUidProcessState(const uid_t /*uid*/, const String16& /*callingPackage*/) override {
+ return ActivityManager::PROCESS_STATE_UNKNOWN;
+ }
+
+ status_t checkPermission(const String16& /*permission*/, const pid_t /*pid*/,
+ const uid_t /*uid*/, int32_t* /*outResult*/) override {
+ return NO_ERROR;
+ }
+
+ status_t registerUidObserverForUids(const sp<IUidObserver>& /*observer*/ ,
+ const int32_t /*event*/ ,
+ const int32_t /*cutpoint*/ ,
+ const String16& /*callingPackage*/ ,
+ const int32_t uids[] ,
+ size_t /*nUids*/ ,
+ /*out*/ sp<IBinder>& /*observerToken*/ ) {
+ (void)uids;
+ return OK;
+ }
+
+ status_t addUidToObserver(const sp<IBinder>& /*observerToken*/ ,
+ const String16& /*callingPackage*/ ,
+ int32_t /*uid*/ ) override {
+ return NO_ERROR;
+ }
+
+ status_t removeUidFromObserver(const sp<IBinder>& /*observerToken*/ ,
+ const String16& /*callingPackage*/ ,
+ int32_t /*uid*/ ) override {
+ return NO_ERROR;
+ }
+
+ status_t logFgsApiBegin(int32_t /*apiType*/ , int32_t /*appUid*/ ,
+ int32_t /*appPid*/ ) override {
+ return NO_ERROR;
+ }
+ status_t logFgsApiEnd(int32_t /*apiType*/ , int32_t /*appUid*/ ,
+ int32_t /*appPid*/ ) override {
+ return NO_ERROR;
+ }
+ status_t logFgsApiStateChanged(int32_t /*apiType*/ , int32_t /*state*/ ,
+ int32_t /*appUid*/ ,
+ int32_t /*appPid*/ ) override {
+ return NO_ERROR;
+ }
+};
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+ /* Create a FakeServiceManager instance and add required services */
+ sp<FakeServiceManager> fakeServiceManager = new FakeServiceManager();
+ setDefaultServiceManager(fakeServiceManager);
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ sp<FuzzerActivityManager> am = new FuzzerActivityManager();
+ fakeServiceManager->addService(String16("activity"), IInterface::asBinder(am));
+
+ sp<FuzzerSensorPrivacyManager> sensorPrivacyManager = new FuzzerSensorPrivacyManager();
+ fakeServiceManager->addService(String16("sensor_privacy"),
+ IInterface::asBinder(sensorPrivacyManager));
+ sp<FuzzerPermissionController> permissionController = new FuzzerPermissionController();
+ fakeServiceManager->addService(String16("permission"),
+ IInterface::asBinder(permissionController));
+
+ sp<FuzzerSchedulingPolicyService> schedulingService = new FuzzerSchedulingPolicyService();
+ fakeServiceManager->addService(String16("scheduling_policy"),
+ IInterface::asBinder(schedulingService));
+
+ const auto audioFlingerObj = sp<AudioFlinger>::make();
+ const auto afAdapter = sp<AudioFlingerServerAdapter>::make(audioFlingerObj);
+
+ fakeServiceManager->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME),
+ IInterface::asBinder(afAdapter), false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+ const auto audioPolicyService = sp<AudioPolicyService>::make();
+ fakeServiceManager->addService(String16("media.audio_policy"), audioPolicyService,
+ false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+ sp<IBinder> binder =
+ fakeServiceManager->getService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
+ gAudioFlingerService = interface_cast<media::IAudioFlingerService>(binder);
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (!gAudioFlingerService) {
+ return 0;
+ }
+
+ fuzzService(media::IAudioFlingerService::asBinder(gAudioFlingerService),
+ FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 8f8c9dd..a1f7941 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -178,6 +178,7 @@
// helper function to obtain AudioFlinger service handle
static const sp<IAudioFlinger> get_audio_flinger();
+ static const sp<IAudioFlinger> get_audio_flinger_for_fuzzer();
static float linearToLog(int volume);
static int logToLinear(float volume);
@@ -461,6 +462,10 @@
static status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration);
+ static status_t updatePolicyMixes(
+ const std::vector<
+ std::pair<AudioMix, std::vector<AudioMixMatchCriterion>>>& mixesWithUpdates);
+
static status_t setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices);
static status_t removeUidDeviceAffinities(uid_t uid);
@@ -876,6 +881,7 @@
static audio_io_handle_t getOutput(audio_stream_type_t stream);
static const sp<AudioFlingerClient> getAudioFlingerClient();
static sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+ static const sp<IAudioFlinger> getAudioFlingerImpl(bool canStartThreadPool);
// Invokes all registered error callbacks with the given error code.
static void reportError(status_t err);
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 8f712db..523383f 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1238,6 +1238,11 @@
bool isDirect_l() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+ bool isAfTrackOffloadedOrDirect_l() const
+ { return isOffloadedOrDirect_l() ||
+ (mAfTrackFlags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|
+ AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+
// pure pcm data is mixable (which excludes HW_AV_SYNC, with embedded timing)
bool isPurePcmData_l() const
{ return audio_is_linear_pcm(mFormat)
@@ -1295,6 +1300,7 @@
uint32_t mAfSampleRate; // AudioFlinger sample rate
uint32_t mAfChannelCount; // AudioFlinger channel count
audio_format_t mAfFormat; // AudioFlinger format
+ audio_output_flags_t mAfTrackFlags; // AudioFlinger track flags
// constant after constructor or set()
audio_format_t mFormat; // as requested by client, not forced to 16-bit
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 3c96862..35a3208 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -119,6 +119,7 @@
uint32_t afLatencyMs;
audio_channel_mask_t afChannelMask;
audio_format_t afFormat;
+ audio_output_flags_t afTrackFlags;
audio_io_handle_t outputId;
audio_port_handle_t portId;
sp<media::IAudioTrack> audioTrack;
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index fa990b5..c101f00 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -56,7 +56,7 @@
ASSERT_NE(nullptr, ap);
ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
<< "Unable to open Resource";
- EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ ASSERT_EQ(OK, ap->create()) << "track creation failed";
sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
EXPECT_EQ(OK, ap->start()) << "audio track start failed";
@@ -94,7 +94,7 @@
sp<AudioCapture> capture = sp<AudioCapture>::make(
AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
ASSERT_NE(nullptr, capture);
- EXPECT_EQ(OK, capture->create()) << "record creation failed";
+ ASSERT_EQ(OK, capture->create()) << "record creation failed";
sp<OnAudioDeviceUpdateNotifier> cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture));
@@ -105,7 +105,7 @@
ASSERT_NE(nullptr, playback);
ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
<< "Unable to open Resource";
- EXPECT_EQ(OK, playback->create()) << "track creation failed";
+ ASSERT_EQ(OK, playback->create()) << "track creation failed";
sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
@@ -187,7 +187,7 @@
ASSERT_NE(nullptr, playback);
ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
<< "Unable to open Resource";
- EXPECT_EQ(OK, playback->create()) << "track creation failed";
+ ASSERT_EQ(OK, playback->create()) << "track creation failed";
sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
@@ -195,7 +195,7 @@
sp<AudioCapture> captureA = sp<AudioCapture>::make(
AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
ASSERT_NE(nullptr, captureA);
- EXPECT_EQ(OK, captureA->create()) << "record creation failed";
+ ASSERT_EQ(OK, captureA->create()) << "record creation failed";
sp<OnAudioDeviceUpdateNotifier> cbCaptureA = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, captureA->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureA));
@@ -206,7 +206,7 @@
AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
AUDIO_INPUT_FLAG_NONE, AUDIO_SESSION_ALLOCATE, AudioRecord::TRANSFER_CALLBACK, &attr);
ASSERT_NE(nullptr, captureB);
- EXPECT_EQ(OK, captureB->create()) << "record creation failed";
+ ASSERT_EQ(OK, captureB->create()) << "record creation failed";
sp<OnAudioDeviceUpdateNotifier> cbCaptureB = sp<OnAudioDeviceUpdateNotifier>::make();
EXPECT_EQ(OK, captureB->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureB));
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index df64676..1a37622 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -17,7 +17,7 @@
#ifndef LVM_FLOAT
typedef float LVM_FLOAT;
#endif
-#define LOG_TAG "Bundle"
+#define LOG_TAG "EffectBundle"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array)[0])
//#define LOG_NDEBUG 0
@@ -1191,11 +1191,13 @@
// 0 if the configuration is supported
//----------------------------------------------------------------------------
int VirtualizerIsDeviceSupported(audio_devices_t deviceType) {
+ ALOGV("%s: deviceType:%#x", __func__, deviceType);
switch (deviceType) {
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
case AUDIO_DEVICE_OUT_USB_HEADSET:
+ case AUDIO_DEVICE_OUT_BLE_HEADSET:
// case AUDIO_DEVICE_OUT_USB_DEVICE: // For USB testing of the virtualizer only.
return 0;
default:
@@ -3372,10 +3374,10 @@
if (pContext->EffectType == LVM_BASS_BOOST) {
if ((device == AUDIO_DEVICE_OUT_SPEAKER) ||
(device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
- (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) {
- ALOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_BASS_BOOST %d",
- *(int32_t*)pCmdData);
- ALOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_BAS_BOOST");
+ device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER ||
+ device == AUDIO_DEVICE_OUT_BLE_SPEAKER) {
+ ALOGV("%s: EFFECT_CMD_SET_DEVICE device %#x is invalid for LVM_BASS_BOOST",
+ __func__, device);
// If a device doesn't support bassboost the effect must be temporarily disabled
// the effect must still report its original state as this can only be changed
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 2ba1fc3..085a7e4 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -41,8 +41,8 @@
namespace android {
void initFrameInfo(HeifFrameInfo *info, const VideoFrame *videoFrame) {
- info->mWidth = videoFrame->mWidth;
- info->mHeight = videoFrame->mHeight;
+ info->mWidth = videoFrame->mDisplayWidth;
+ info->mHeight = videoFrame->mDisplayHeight;
info->mRotationAngle = videoFrame->mRotationAngle;
info->mBytesPerPixel = videoFrame->mBytesPerPixel;
info->mDurationUs = videoFrame->mDurationUs;
@@ -476,35 +476,37 @@
}
bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
- if (heifColor == (HeifColorFormat)mOutputColor) {
- return true;
- }
-
+ android_pixel_format_t outputColor;
switch(heifColor) {
case kHeifColorFormat_RGB565:
{
- mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
+ outputColor = HAL_PIXEL_FORMAT_RGB_565;
break;
}
case kHeifColorFormat_RGBA_8888:
{
- mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
+ outputColor = HAL_PIXEL_FORMAT_RGBA_8888;
break;
}
case kHeifColorFormat_BGRA_8888:
{
- mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
+ outputColor = HAL_PIXEL_FORMAT_BGRA_8888;
break;
}
case kHeifColorFormat_RGBA_1010102:
{
- mOutputColor = HAL_PIXEL_FORMAT_RGBA_1010102;
+ outputColor = HAL_PIXEL_FORMAT_RGBA_1010102;
break;
}
default:
ALOGE("Unsupported output color format %d", heifColor);
return false;
}
+ if (outputColor == mOutputColor) {
+ return true;
+ }
+
+ mOutputColor = outputColor;
if (mFrameDecoded) {
return reinit(nullptr);
@@ -740,8 +742,11 @@
// Either document why it is safe in this case or address the
// issue (e.g. by copying).
VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
- uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
- memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
+ uint8_t* src = videoFrame->getFlattenedData() +
+ (videoFrame->mRowBytes * (mCurScanline + videoFrame->mDisplayTop)) +
+ (videoFrame->mBytesPerPixel * videoFrame->mDisplayLeft);
+ mCurScanline++;
+ memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
return true;
}
diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp
index c43ef66..f498453 100644
--- a/media/libmedia/IMediaDeathNotifier.cpp
+++ b/media/libmedia/IMediaDeathNotifier.cpp
@@ -38,16 +38,10 @@
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.player"));
- if (binder != 0) {
- break;
- }
- ALOGW("Media player service not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
-
+ sp<IBinder> binder = sm->waitForService(String16("media.player"));
+ if (binder == nullptr) {
+ return nullptr;
+ }
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 427a3bd..9731ea4 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -41,14 +41,10 @@
if (sService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.player"));
- if (binder != 0) {
- break;
- }
- ALOGW("MediaPlayerService not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
+ binder = sm->waitForService(String16("media.player"));
+ if (binder == nullptr) {
+ return nullptr;
+ }
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index 2240223..26fe306 100644
--- a/media/libmediametrics/MediaMetrics.cpp
+++ b/media/libmediametrics/MediaMetrics.cpp
@@ -87,7 +87,7 @@
}
void mediametrics_setString(mediametrics_handle_t handle, attr_t attr,
- const std::string &string) {
+ const std::string &string) {
mediametrics_setCString(handle, attr, string.c_str());
}
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index f80a467..26aa375 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -184,6 +184,7 @@
#define AMEDIAMETRICS_PROP_PLAYERIID "playerIId" // int32 (-1 invalid/unset IID)
#define AMEDIAMETRICS_PROP_ROUTEDDEVICEID "routedDeviceId" // int32
#define AMEDIAMETRICS_PROP_SAMPLERATE "sampleRate" // int32
+#define AMEDIAMETRICS_PROP_SAMPLERATECLIENT "sampleRateClient" // int32
#define AMEDIAMETRICS_PROP_SAMPLERATEHARDWARE "sampleRateHardware" // int32
#define AMEDIAMETRICS_PROP_SELECTEDDEVICEID "selectedDeviceId" // int32
#define AMEDIAMETRICS_PROP_SELECTEDMICDIRECTION "selectedMicDirection" // int32
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 84d772d..605e659 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -672,6 +672,18 @@
if (trackMeta->findInt32(kKeyWidth, &imageWidth)
&& trackMeta->findInt32(kKeyHeight, &imageHeight)) {
imagePrimary = imageCount;
+ int32_t displayLeft;
+ int32_t displayTop;
+ int32_t displayRight;
+ int32_t displayBottom;
+ if (trackMeta->findRect(kKeyCropRect, &displayLeft, &displayTop,
+ &displayRight, &displayBottom)
+ && displayLeft >= 0 && displayTop >= 0 && displayRight < imageWidth
+ && displayBottom < imageHeight && displayLeft <= displayRight
+ && displayTop <= displayBottom) {
+ imageWidth = displayRight - displayLeft + 1;
+ imageHeight = displayBottom - displayTop + 1;
+ }
} else {
ALOGE("primary image track ignored for missing dimensions");
}
diff --git a/media/libmediaplayerservice/fuzzer/Android.bp b/media/libmediaplayerservice/fuzzer/Android.bp
index c3d6c89..b511372 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -174,6 +174,7 @@
"libnetd_client",
"libpowermanager",
"libstagefright_httplive",
+ "libaudiohal@7.0",
],
}
diff --git a/media/libnbaio/Android.bp b/media/libnbaio/Android.bp
index 89e9806..434ae00 100644
--- a/media/libnbaio/Android.bp
+++ b/media/libnbaio/Android.bp
@@ -49,7 +49,7 @@
defaults: ["libnbaio_mono_defaults"],
}
-cc_library_shared {
+cc_library {
name: "libnbaio",
defaults: ["libnbaio_mono_defaults"],
srcs: [
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index a26fcbe..0af9d12 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -270,10 +270,10 @@
"SurfaceUtils.cpp",
"ThrottledSource.cpp",
"Utils.cpp",
- "VideoRenderQualityTracker.cpp",
"VideoFrameSchedulerBase.cpp",
"VideoFrameScheduler.cpp",
- ],
+ "VideoRenderQualityTracker.cpp",
+ ],
shared_libs: [
"libstagefright_framecapture_utils",
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index b5bd975..1a0bb7f 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -16,7 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "FrameDecoder"
-
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include "include/FrameDecoder.h"
#include "include/FrameCaptureLayer.h"
#include "include/HevcUtils.h"
@@ -41,6 +41,7 @@
#include <media/stagefright/Utils.h>
#include <private/media/VideoFrame.h>
#include <utils/Log.h>
+#include <utils/Trace.h>
namespace android {
@@ -85,6 +86,22 @@
displayWidth = width;
displayHeight = height;
}
+ int32_t displayLeft = 0;
+ int32_t displayTop = 0;
+ int32_t displayRight;
+ int32_t displayBottom;
+ if (trackMeta->findRect(kKeyCropRect, &displayLeft, &displayTop, &displayRight,
+ &displayBottom)) {
+ if (displayLeft >= 0 && displayTop >= 0 && displayRight < width && displayBottom < height &&
+ displayLeft <= displayRight && displayTop <= displayBottom) {
+ displayWidth = displayRight - displayLeft + 1;
+ displayHeight = displayBottom - displayTop + 1;
+ } else {
+ // Crop rectangle is invalid, use the whole frame.
+ displayLeft = 0;
+ displayTop = 0;
+ }
+ }
if (allocRotated) {
if (rotationAngle == 90 || rotationAngle == 270) {
@@ -107,8 +124,8 @@
}
}
- VideoFrame frame(width, height, displayWidth, displayHeight,
- tileWidth, tileHeight, rotationAngle, dstBpp, bitDepth, !metaOnly, iccSize);
+ VideoFrame frame(width, height, displayWidth, displayHeight, displayLeft, displayTop, tileWidth,
+ tileHeight, rotationAngle, dstBpp, bitDepth, !metaOnly, iccSize);
size_t size = frame.getFlattenedSize();
sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient");
@@ -340,6 +357,7 @@
}
sp<IMemory> FrameDecoder::extractFrame(FrameRect *rect) {
+ ScopedTrace trace(ATRACE_TAG, "FrameDecoder::ExtractFrame");
status_t err = onExtractRect(rect);
if (err == OK) {
err = extractInternal();
@@ -713,6 +731,7 @@
}
converter.setSrcColorSpace(standard, range, transfer);
if (converter.isValid()) {
+ ScopedTrace trace(ATRACE_TAG, "FrameDecoder::ColorConverter");
converter.convert(
(const uint8_t *)videoFrameBuffer->data(),
width, height, stride,
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 3a25a11..6765a6f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -31,7 +31,6 @@
#include "include/SoftwareRenderer.h"
#include <android/api-level.h>
-#include <android/binder_manager.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
@@ -74,7 +73,6 @@
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
@@ -245,7 +243,7 @@
"android.media.mediacodec.judder-score-histogram-buckets";
// Freeze event
static const char *kCodecFreezeEventCount = "android.media.mediacodec.freeze-event-count";
-static const char *kFreezeEventKeyName = "freeze";
+static const char *kFreezeEventKeyName = "videofreeze";
static const char *kFreezeEventInitialTimeUs = "android.media.mediacodec.freeze.initial-time-us";
static const char *kFreezeEventDurationMs = "android.media.mediacodec.freeze.duration-ms";
static const char *kFreezeEventCount = "android.media.mediacodec.freeze.count";
@@ -257,7 +255,7 @@
"android.media.mediacodec.freeze.details-distance-ms";
// Judder event
static const char *kCodecJudderEventCount = "android.media.mediacodec.judder-event-count";
-static const char *kJudderEventKeyName = "judder";
+static const char *kJudderEventKeyName = "videojudder";
static const char *kJudderEventInitialTimeUs = "android.media.mediacodec.judder.initial-time-us";
static const char *kJudderEventDurationMs = "android.media.mediacodec.judder.duration-ms";
static const char *kJudderEventCount = "android.media.mediacodec.judder.count";
diff --git a/media/libstagefright/VideoRenderQualityTracker.cpp b/media/libstagefright/VideoRenderQualityTracker.cpp
index e920bd1..90d5405 100644
--- a/media/libstagefright/VideoRenderQualityTracker.cpp
+++ b/media/libstagefright/VideoRenderQualityTracker.cpp
@@ -15,7 +15,11 @@
*/
#define LOG_TAG "VideoRenderQualityTracker"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
+
#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Mutex.h>
#include <media/stagefright/VideoRenderQualityTracker.h>
@@ -25,8 +29,10 @@
#include <stdio.h>
#include <sys/time.h>
+#include <android-base/macros.h>
#include <android-base/parsebool.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
namespace android {
@@ -38,6 +44,7 @@
typedef VideoRenderQualityTracker::Configuration::GetServerConfigurableFlagFn
GetServerConfigurableFlagFn;
+typedef VideoRenderQualityTracker::TraceTriggerFn TraceTriggerFn;
static void getServerConfigurableFlag(GetServerConfigurableFlagFn getServerConfigurableFlagFn,
char const *flagNameSuffix, bool *value) {
@@ -149,6 +156,10 @@
getFlag(judderEventMax, "judder_event_max");
getFlag(judderEventDetailsMax, "judder_event_details_max");
getFlag(judderEventDistanceToleranceMs, "judder_event_distance_tolerance_ms");
+ getFlag(traceTriggerEnabled, "trace_trigger_enabled");
+ getFlag(traceTriggerThrottleMs, "trace_trigger_throttle_ms");
+ getFlag(traceMinFreezeDurationMs, "trace_minimum_freeze_duration_ms");
+ getFlag(traceMaxFreezeDurationMs, "trace_maximum_freeze_duration_ms");
#undef getFlag
return c;
}
@@ -186,15 +197,25 @@
judderEventMax = 0; // enabled only when debugging
judderEventDetailsMax = 20;
judderEventDistanceToleranceMs = 5000; // lump judder occurrences together when 5s or less
+
+ // Perfetto trigger configuration.
+ traceTriggerEnabled = android::base::GetProperty(
+ "ro.build.type", "user") != "user"; // Enabled for non-user builds for debugging.
+ traceTriggerThrottleMs = 5 * 60 * 1000; // 5 mins.
+ traceMinFreezeDurationMs = 400;
+ traceMaxFreezeDurationMs = 1500;
}
-VideoRenderQualityTracker::VideoRenderQualityTracker() : mConfiguration(Configuration()) {
+VideoRenderQualityTracker::VideoRenderQualityTracker()
+ : mConfiguration(Configuration()), mTraceTriggerFn(triggerTrace) {
configureHistograms(mMetrics, mConfiguration);
clear();
}
-VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration) :
- mConfiguration(configuration) {
+VideoRenderQualityTracker::VideoRenderQualityTracker(const Configuration &configuration,
+ const TraceTriggerFn traceTriggerFn)
+ : mConfiguration(configuration),
+ mTraceTriggerFn(traceTriggerFn == nullptr ? triggerTrace : traceTriggerFn) {
configureHistograms(mMetrics, mConfiguration);
clear();
}
@@ -231,6 +252,11 @@
resetIfDiscontinuity(contentTimeUs, -1);
+ if (mTraceFrameSkippedToken == -1) {
+ mTraceFrameSkippedToken = contentTimeUs;
+ ATRACE_ASYNC_BEGIN("Video frame(s) skipped", mTraceFrameSkippedToken);
+ }
+
// Frames skipped at the end of playback shouldn't be counted as skipped frames, since the
// app could be terminating the playback. The pending count will be added to the metrics if and
// when the next frame is rendered.
@@ -261,11 +287,25 @@
return;
}
+ if (mTraceFrameSkippedToken != -1) {
+ ATRACE_ASYNC_END("Video frame(s) skipped", mTraceFrameSkippedToken);
+ mTraceFrameSkippedToken = -1;
+ }
+
int64_t actualRenderTimeUs = actualRenderTimeNs / 1000;
if (mLastRenderTimeUs != -1) {
- mRenderDurationMs += (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+ int64_t frameRenderDurationMs = (actualRenderTimeUs - mLastRenderTimeUs) / 1000;
+ mRenderDurationMs += frameRenderDurationMs;
+ if (mConfiguration.traceTriggerEnabled
+ // Threshold for visible video freeze.
+ && frameRenderDurationMs >= mConfiguration.traceMinFreezeDurationMs
+ // Threshold for removing long render durations which could be video pause.
+ && frameRenderDurationMs < mConfiguration.traceMaxFreezeDurationMs) {
+ triggerTraceWithThrottle(mTraceTriggerFn, mConfiguration, actualRenderTimeUs);
+ }
}
+
// Now that a frame has been rendered, the previously skipped frames can be processed as skipped
// frames since the app is not skipping them to terminate playback.
for (int64_t contentTimeUs : mPendingSkippedFrameContentTimeUsList) {
@@ -738,4 +778,42 @@
return false;
}
+void VideoRenderQualityTracker::triggerTraceWithThrottle(const TraceTriggerFn traceTriggerFn,
+ const Configuration &c,
+ const int64_t triggerTimeUs) {
+ static int64_t lastTriggerUs = -1;
+ static Mutex updateLastTriggerLock;
+
+ Mutex::Autolock autoLock(updateLastTriggerLock);
+ if (lastTriggerUs != -1) {
+ int32_t sinceLastTriggerMs = int32_t((triggerTimeUs - lastTriggerUs) / 1000);
+ // Throttle the trace trigger calls to reduce continuous PID fork calls in a short time
+ // to impact device performance, and reduce spamming trace reports.
+ if (sinceLastTriggerMs < c.traceTriggerThrottleMs) {
+ ALOGI("Not triggering trace - not enough time since last trigger");
+ return;
+ }
+ }
+ lastTriggerUs = triggerTimeUs;
+ (*traceTriggerFn)();
+}
+
+void VideoRenderQualityTracker::triggerTrace() {
+ // Trigger perfetto to stop always-on-tracing (AOT) to collect trace into a file for video
+ // freeze event, the collected trace categories are configured by AOT.
+ const char* args[] = {"/system/bin/trigger_perfetto", "com.android.codec-video-freeze", NULL};
+ pid_t pid = fork();
+ if (pid < 0) {
+ ALOGI("Failed to fork for triggering trace");
+ return;
+ }
+ if (pid == 0) {
+ // child process.
+ execvp(args[0], const_cast<char**>(args));
+ ALOGW("Failed to trigger trace %s", args[1]);
+ _exit(1);
+ }
+ ALOGI("Triggered trace %s", args[1]);
+}
+
} // namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index a4b3e2f..e091cb8 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -286,9 +286,11 @@
return;
}
- // decoder deals in ms, OMX in us.
- outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp);
- mPvToOmxTimeMap.removeItem(timestamp);
+ if (mPvToOmxTimeMap.indexOfKey(timestamp) >= 0) {
+ // decoder deals in ms, OMX in us.
+ outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp);
+ mPvToOmxTimeMap.removeItem(timestamp);
+ }
inHeader->nOffset += bufferSize;
inHeader->nFilledLen = 0;
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index a464504..946d533 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -72,8 +72,8 @@
void setCrypto(const sp<ICrypto> &crypto) override;
void setDescrambler(const sp<IDescrambler> &descrambler) override;
- virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
- virtual status_t queueSecureInputBuffer(
+ status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ status_t queueSecureInputBuffer(
const sp<MediaCodecBuffer> &buffer,
bool secure,
const uint8_t *key,
@@ -83,10 +83,10 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) override;
- virtual status_t attachBuffer(
+ status_t attachBuffer(
const std::shared_ptr<C2Buffer> &c2Buffer,
const sp<MediaCodecBuffer> &buffer) override;
- virtual status_t attachEncryptedBuffer(
+ status_t attachEncryptedBuffer(
const sp<hardware::HidlMemory> &memory,
bool secure,
const uint8_t *key,
@@ -98,12 +98,12 @@
size_t numSubSamples,
const sp<MediaCodecBuffer> &buffer,
AString* errorDetailMsg) override;
- virtual status_t renderOutputBuffer(
+ status_t renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
- virtual void pollForRenderedBuffers() override;
- virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
- virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
- virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+ void pollForRenderedBuffers() override;
+ status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+ void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
// Methods below are interface for ACodec to use.
diff --git a/media/libstagefright/include/media/stagefright/MediaHistogram.h b/media/libstagefright/include/media/stagefright/MediaHistogram.h
index 50fa258..46ee288 100644
--- a/media/libstagefright/include/media/stagefright/MediaHistogram.h
+++ b/media/libstagefright/include/media/stagefright/MediaHistogram.h
@@ -29,11 +29,11 @@
public:
MediaHistogram();
void clear();
- bool setup(int bucketCount, T width, T floor = 0);
+ bool setup(size_t bucketCount, T width, T floor = 0);
bool setup(const std::vector<T> &bucketLimits);
void insert(T sample);
- size_t size();
- int64_t operator[](int);
+ size_t size() const;
+ int64_t operator[](int) const;
T getMin() const { return mMin; }
T getMax() const { return mMax; }
T getCount() const { return mCount; }
@@ -45,7 +45,7 @@
private:
MediaHistogram(const MediaHistogram &); // disallow
- bool allocate(int bucketCount, bool withBucketLimits);
+ void allocate(size_t bucketCount, bool withBucketLimits);
T mFloor, mCeiling, mWidth;
T mMin, mMax, mSum;
@@ -73,13 +73,12 @@
}
template<typename T>
-bool MediaHistogram<T>::setup(int bucketCount, T width, T floor) {
+bool MediaHistogram<T>::setup(size_t bucketCount, T width, T floor) {
if (bucketCount <= 0 || width <= 0) {
return false;
}
- if (!allocate(bucketCount, false)) {
- return false;
- }
+ allocate(bucketCount, false);
+
mWidth = width;
mFloor = floor;
mCeiling = floor + bucketCount * width;
@@ -92,14 +91,14 @@
if (bucketLimits.size() <= 1) {
return false;
}
- int bucketCount = bucketLimits.size() - 1;
- if (!allocate(bucketCount, true)) {
- return false;
- }
+ // The floor is the first bucket limit value, so offset by 1
+ size_t bucketCount = bucketLimits.size() - 1;
+ allocate(bucketCount, true);
mWidth = -1;
mFloor = bucketLimits[0];
- for (int i = 0; i < bucketCount; ++i) {
+ for (size_t i = 0; i < bucketCount; ++i) {
+ // The floor is the first bucket, so offset by 1
mBucketLimits[i] = bucketLimits[i + 1];
}
mCeiling = bucketLimits[bucketCount];
@@ -108,7 +107,7 @@
}
template<typename T>
-bool MediaHistogram<T>::allocate(int bucketCount, bool withBucketLimits) {
+void MediaHistogram<T>::allocate(size_t bucketCount, bool withBucketLimits) {
assert(bucketCount > 0);
if (bucketCount != mBuckets.size()) {
mBuckets = std::vector<T>(bucketCount, 0);
@@ -116,7 +115,6 @@
if (withBucketLimits && mBucketLimits.size() != bucketCount) {
mBucketLimits = std::vector<T>(bucketCount, 0);
}
- return true;
}
template<typename T>
@@ -128,8 +126,8 @@
mCount++;
mSum += sample;
- if (mMin > sample) mMin = sample;
- if (mMax < sample) mMax = sample;
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
if (sample < mFloor) {
mBelow++;
@@ -138,7 +136,7 @@
} else if (mWidth == -1) {
// A binary search might be more efficient for large number of buckets, but it is expected
// that there will never be a large amount of buckets, so keep the code simple.
- for (int slot = 0; slot < mBucketLimits.size(); ++slot) {
+ for (size_t slot = 0; slot < mBucketLimits.size(); ++slot) {
if (sample < mBucketLimits[slot]) {
mBuckets[slot]++;
break;
@@ -153,12 +151,12 @@
}
template<typename T>
-size_t MediaHistogram<T>::size() {
+size_t MediaHistogram<T>::size() const {
return mBuckets.size() + 1;
}
template<typename T>
-int64_t MediaHistogram<T>::operator[](int i) {
+int64_t MediaHistogram<T>::operator[](int i) const {
assert(i >= 0);
assert(i <= mBuckets.size());
if (i == mBuckets.size()) {
@@ -179,7 +177,7 @@
} else {
ss << mFloor << "," << mWidth << "," << mBelow << "{";
}
- for (int i = 0; i < mBuckets.size(); i++) {
+ for (size_t i = 0; i < mBuckets.size(); i++) {
if (i != 0) {
ss << ",";
}
@@ -194,12 +192,12 @@
std::stringstream ss("");
if (mWidth == -1) {
ss << mFloor;
- for (int i = 0; i < mBucketLimits.size(); ++i) {
+ for (size_t i = 0; i < mBucketLimits.size(); ++i) {
ss << ',' << mBucketLimits[i];
}
} else {
ss << mFloor;
- for (int i = 1; i <= mBuckets.size(); ++i) {
+ for (size_t i = 1; i <= mBuckets.size(); ++i) {
ss << ',' << (mFloor + i * mWidth);
}
}
diff --git a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
index a656e6e..cf53f27 100644
--- a/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
+++ b/media/libstagefright/include/media/stagefright/VideoRenderQualityTracker.h
@@ -200,6 +200,21 @@
// The maximum distance in time between two judder occurrences such that both will be
// lumped into the same judder event.
int32_t judderEventDistanceToleranceMs;
+ //
+ // Whether or not Perfetto trace trigger is enabled.
+ bool traceTriggerEnabled;
+ //
+ // The throttle time for Perfetto trace trigger to avoid triggering multiple traces for
+ // the same event in a short time.
+ int32_t traceTriggerThrottleMs;
+ //
+ // The minimum frame render duration to recognize video freeze event to collect trace.
+ int32_t traceMinFreezeDurationMs;
+ //
+ // The maximum frame render duration to recognize video freeze event. A frame render
+ // duration that is larger than the max duration would not trigger trace collection for
+ // video freeze because it's highly possible a video pause.
+ int32_t traceMaxFreezeDurationMs;
};
struct FreezeEvent {
@@ -256,8 +271,11 @@
Details details;
};
+ typedef void (*TraceTriggerFn)();
+
VideoRenderQualityTracker();
- VideoRenderQualityTracker(const Configuration &configuration);
+ VideoRenderQualityTracker(const Configuration &configuration,
+ const TraceTriggerFn traceTriggerFn = nullptr);
// Called when a tunnel mode frame has been queued.
void onTunnelFrameQueued(int64_t contentTimeUs);
@@ -376,6 +394,14 @@
JudderEvent &e, const VideoRenderQualityMetrics & m,
const Configuration &c, JudderEvent *judderEventOut);
+ // Trigger trace collection for video freeze.
+ static void triggerTrace();
+
+ // Trigger collection of a Perfetto Always-On-Tracing (AOT) trace file for video freeze,
+ // triggerTimeUs is used as a throttle to avoid triggering multiple traces in a short time.
+ static void triggerTraceWithThrottle(TraceTriggerFn traceTriggerFn,
+ const Configuration &c, const int64_t triggerTimeUs);
+
// Check to see if a discontinuity has occurred by examining the content time and the
// app-desired render time. If so, reset some internal state.
bool resetIfDiscontinuity(int64_t contentTimeUs, int64_t desiredRenderTimeUs);
@@ -394,6 +420,9 @@
// Configurable elements of the metrics algorithms.
const Configuration mConfiguration;
+ // The function for triggering trace collection for video freeze.
+ const TraceTriggerFn mTraceTriggerFn;
+
// Metrics are updated every time a frame event occurs - skipped, dropped, rendered.
VideoRenderQualityMetrics mMetrics;
@@ -445,6 +474,9 @@
// Frame durations derived from timestamps captured by the display subsystem, indicating the
// wall clock atime at which the frame is actually rendered.
FrameDurationUs mActualFrameDurationUs;
+
+ // Token of async atrace for video frame dropped/skipped by the app.
+ int64_t mTraceFrameSkippedToken= -1;
};
} // namespace android
diff --git a/media/libstagefright/rtsp/fuzzer/Android.bp b/media/libstagefright/rtsp/fuzzer/Android.bp
new file mode 100644
index 0000000..a2791ba
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/Android.bp
@@ -0,0 +1,89 @@
+/*
+* Copyright (C) 2023 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+cc_defaults {
+ name: "libstagefright_rtsp_fuzzer_defaults",
+ shared_libs: [
+ "liblog",
+ "libmedia",
+ "libutils",
+ "libstagefright_foundation",
+ ],
+ static_libs: [
+ "libdatasource",
+ "libstagefright_rtsp",
+ ],
+ header_libs: [
+ "libstagefright_rtsp_headers",
+ ],
+ fuzz_config:{
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_fuzz {
+ name: "sdploader_fuzzer",
+ srcs: [
+ "sdploader_fuzzer.cpp",
+ ],
+ defaults: [
+ "libstagefright_rtsp_fuzzer_defaults",
+ ]
+}
+
+cc_fuzz {
+ name: "rtp_writer_fuzzer",
+ srcs: [
+ "rtp_writer_fuzzer.cpp",
+ ],
+ defaults: [
+ "libstagefright_rtsp_fuzzer_defaults",
+ ],
+ shared_libs:[
+ "libandroid_net",
+ "libbase",
+ "libstagefright",
+ "libcutils",
+ ],
+}
+
+cc_fuzz {
+ name: "packet_source_fuzzer",
+ srcs: [
+ "packet_source_fuzzer.cpp",
+ ],
+ defaults: [
+ "libstagefright_rtsp_fuzzer_defaults",
+ ],
+}
+
+cc_fuzz {
+ name: "rtsp_connection_fuzzer",
+ srcs: [
+ "rtsp_connection_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "libcutils",
+ "libnetd_client",
+ ],
+ defaults: [
+ "libstagefright_rtsp_fuzzer_defaults",
+ ],
+}
diff --git a/media/libstagefright/rtsp/fuzzer/README.md b/media/libstagefright/rtsp/fuzzer/README.md
new file mode 100644
index 0000000..bc7be29
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/README.md
@@ -0,0 +1,117 @@
+# Fuzzers for libstagefright_rtsp
+
+## Table of contents
++ [sdploader_fuzzer](#SDPLoader)
++ [rtp_writer_fuzzer](#ARTPWriter)
++ [packet_source_fuzzer](#packetSource)
++ [rtsp_connection_fuzzer](#ARTSPConnection)
+
+# <a name="SDPLoader"></a> Fuzzer for SDPLoader
+
+SDPLoader supports the following parameters:
+1. Flag (parameter name: "flags")
+2. URL (parameter name: "url")
+3. Header (parameter name: "headers")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`flags`| `UINT32_MIN` to `UINT32_MAX` |Value obtained from FuzzedDataProvider|
+|`url`| `String` |Value obtained from FuzzedDataProvider|
+|`headers`| `String` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) sdploader_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/sdploader_fuzzer/sdploader_fuzzer
+```
+
+# <a name="ARTPWriter"></a> Fuzzer for ARTPWriter
+
+ARTPWriter supports the following parameters:
+1. File descriptor (parameter name: "fd")
+2. Local Ip (parameter name: "localIp")
+3. Local Port (parameter name: "localPort")
+4. Remote Ip (parameter name: "remoteIp")
+5. Remote Port (parameter name: "remotePort")
+6. Sequence No (parameter name: "seqNo")
+7. OpponentID (parameter name: "opponentID")
+8. Bit Rate (parameter name: "bitrate")
+9. kKeyMIMETypeArray (parameter name: "mimeType")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`localIp`| `String` |Value obtained from FuzzedDataProvider|
+|`localPort`| `UINT32_MIN` to `UINT32_MAX` |Value obtained from FuzzedDataProvider|
+|`remoteIp`| `String` |Value obtained from FuzzedDataProvider|
+|`remotePort`| `UINT32_MIN` to `UINT32_MAX` |Value obtained from FuzzedDataProvider|
+|`seqNo`| `0` to `10000000` |Value obtained from FuzzedDataProvider|
+|`opponentID`| `UINT32_MIN` to `UINT32_MAX` |Value obtained from FuzzedDataProvider|
+|`bitrate`| `UINT32_MIN` to `UINT32_MAX` |Value obtained from FuzzedDataProvider|
+|`mimeType`| 0. `MEDIA_MIMETYPE_VIDEO_AVC`<br> 1. `MEDIA_MIMETYPE_VIDEO_HEVC`<br> 2. `MEDIA_MIMETYPE_VIDEO_H263`<br> 3. `MEDIA_MIMETYPE_AUDIO_AMR_NB`<br> 4. `MEDIA_MIMETYPE_AUDIO_AMR_WB`|Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) rtp_writer_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/rtp_writer_fuzzer/rtp_writer_fuzzer
+```
+
+# <a name="packetSource"></a> Fuzzer for PacketSource
+
+ PacketSource supports the following parameters:
+1. Codec (parameter name: "kCodecs")
+2. Format (parameter name: "kFmtp")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kCodecs`| 0. `opus`<br/>1. `ISAC`<br/>2. `VP8`<br/>3. `google-data`<br/>4. `G722`<br/>5. `PCMU`<br/>6. `PCMA`<br/>7. `CN`<br/>8. `telephone-event`<br/>9. `VP9`<br/>10. `red`<br/>11. `ulpfec`<br/>12. `rtx`<br/>13. `H264`<br/>14. `iLBC`<br/>15. `H261`<br/>16. `MPV`<br/>17. `H263`<br/>18. `AMR`<br/>19. `AC3`<br/>20. `G723`<br/>21. `G729A`<br/>22. `MP4V-ES`<br/>23. `H265`<br/>24. `H263-2000`<br/>25. `H263-1998`<br/>26. `AMR-WB`<br/>27. `MP4A-LATM`<br/>28. `MP2T`<br/>29. `mpeg4-generic` |Value obtained from FuzzedDataProvider|
+|`kFmtp`| <br/>0. `br=`<br/>1. `bw=`<br/>2. `ch-aw-recv=`<br/>3. `mode-change-capability=`<br/>4. `max-red =`<br/>5. `octet-align=`<br/>6. `mode-change-capability=`<br/>7. `profile-level-id=`<br/>8. `packetization-mode=`<br/>9. `profile=`<br/>10. `level=` <br/>11. `apt=`<br/>12. `annexb=`<br/>13. `protocol=`<br/>14. `config=`<br/>15. `streamtype=`<br/>16. `mode=`<br/>17. `sizelength=`<br/>18. `indexlength=`<br/>19. `indexdeltalength=`<br/>20. `minptime=`<br/>21. `useinbandfec=`<br/>22. `maxplaybackrate=`<br/>23. `stereo=`<br/>24. `level-asymmetry-allowed=`<br/>25. `max-fs=`<br/>26. `max-fr=`|Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) packet_source_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/packet_source_fuzzer/packet_source_fuzzer
+```
+
+# <a name="ARTSPConnection"></a> Fuzzer for ARTSPConnection
+
+## Design Considerations
+This fuzzer aims at covering ARTSPConnection.cpp. A server is implemented in the fuzzer. After accepting a connect request, the server accepts the connections and handles them in a seperate thread. The threads are maintained in a ThreadPool which limits the maximum number of threads alive at a time. When the fuzzer process ends, all the threads in the ThreadPool are joined to the main thread.
+The inputs to the server are generated using FuzzedDataProvider and stored in a variable 'mFuzzData'. As this variable is shared among multiple threads, mutex is used to ensure synchronization.
+### Fuzzer Inputs:
+The inputs generated in the fuzzer using FuzzzedDataProvider have been randomized as much as possible. Due to the constraints in the module source code, the inputs have to be limited and arranged in some specific format.
+
+ARTSPConnection supports the following parameters:
+1. Authentication Type (parameter name: "kAuthType")
+2. FuzzData (parameter name: "mFuzzData")
+3. RequestData (parameter name: "mFuzzRequestData")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kAuthType`| 0.`Basic`<br/>1.`Digest`|Value obtained from FuzzedDataProvider|
+|`mFuzzData`| `String` |Value obtained from FuzzedDataProvider|
+|`mFuzzRequestData`| `String` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) rtsp_connection_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/rtsp_connection_fuzzer/rtsp_connection_fuzzer
diff --git a/media/libstagefright/rtsp/fuzzer/packet_source_fuzzer.cpp b/media/libstagefright/rtsp/fuzzer/packet_source_fuzzer.cpp
new file mode 100644
index 0000000..a3d7535
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/packet_source_fuzzer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 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 <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/rtsp/APacketSource.h>
+#include <media/stagefright/rtsp/ASessionDescription.h>
+
+using namespace android;
+
+static constexpr int32_t kMinValue = 0;
+static constexpr int32_t kMaxIPAddress = 255;
+static constexpr int32_t kMaxFmt = 255;
+static constexpr int32_t kMinAPICase = 0;
+static constexpr int32_t kMaxPacketSourceAPI = 5;
+static constexpr size_t kMinIndex = 1;
+static constexpr size_t kMaxCodecConfigs = 4;
+
+std::string kCodecs[] = {"opus", "ISAC", "VP8",
+ "google-data", "G722", "PCMU",
+ "PCMA", "CN", "telephone-event",
+ "VP9", "red", "ulpfec",
+ "rtx", "H264", "iLBC",
+ "H261", "MPV", "H263",
+ "AMR", "AC3", "G723",
+ "G729A", "H264", "MP4V-ES",
+ "H265", "H263-2000", "H263-1998",
+ "AMR", "AMR-WB", "MP4A-LATM",
+ "MP2T", "mpeg4-generic"};
+
+std::string kFmtp[] = {"br=",
+ "bw=",
+ "ch-aw-recv=",
+ "mode-change-capability=",
+ "max-red =",
+ "octet-align=",
+ "mode-change-capability=",
+ "max-red=",
+ "profile-level-id=",
+ "packetization-mode=",
+ "profile=",
+ "level=",
+ "apt=",
+ "annexb=",
+ "protocol=",
+ "streamtype=",
+ "mode=",
+ "sizelength=",
+ "indexlength=",
+ "indexdeltalength=",
+ "minptime=",
+ "useinbandfec=",
+ "maxplaybackrate=",
+ "stereo=",
+ "level-asymmetry-allowed=",
+ "max-fs=",
+ "max-fr="};
+
+std::string kCodecConfigString[kMaxCodecConfigs][2] = {{"H264", "profile-level-id="},
+ {"MP4A-LATM", "config="},
+ {"MP4V-ES", "config="},
+ {"mpeg4-generic", "mode="}};
+
+class ASessionPacketFuzzer {
+ public:
+ ASessionPacketFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+};
+
+bool checkFormatSupport(const std::string& codec, const std::string& format) {
+ for (int i = 0; i < kMaxCodecConfigs; ++i) {
+ if (codec == kCodecConfigString[i][0]) {
+ if (format == kCodecConfigString[i][1]) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void ASessionPacketFuzzer::process() {
+ AString inputString;
+ const sp<ASessionDescription> sessionPacket = sp<ASessionDescription>::make();
+ std::string codec = mFdp.PickValueInArray(kCodecs);
+ std::string ipAddress =
+ std::to_string(mFdp.ConsumeIntegralInRange(kMinValue, kMaxIPAddress)) + "." +
+ std::to_string(mFdp.ConsumeIntegralInRange(kMinValue, kMaxIPAddress)) + "." +
+ std::to_string(mFdp.ConsumeIntegralInRange(kMinValue, kMaxIPAddress)) + "." + "0";
+ std::string format = mFdp.PickValueInArray(kFmtp);
+ std::string fmptStr = format + std::to_string(mFdp.ConsumeIntegralInRange(kMinValue, kMaxFmt)) +
+ ";" + mFdp.PickValueInArray(kFmtp) +
+ std::to_string(mFdp.ConsumeIntegralInRange(kMinValue, kMaxFmt));
+ sessionPacket->SDPStringFactory(
+ inputString, ipAddress.c_str() /* ip */, mFdp.ConsumeBool() /* isAudio */,
+ mFdp.ConsumeIntegral<unsigned int>() /* port */,
+ mFdp.ConsumeIntegral<unsigned int>() /* payloadType */,
+ mFdp.ConsumeIntegral<unsigned int>() /* as */, codec.c_str(), /* codec */
+ fmptStr.c_str() /* fmtp */, mFdp.ConsumeIntegral<int32_t>() /* width */,
+ mFdp.ConsumeIntegral<int32_t>() /* height */,
+ mFdp.ConsumeIntegral<int32_t>() /* cvoExtMap */);
+ sessionPacket->setTo(inputString.c_str(), inputString.size());
+ size_t trackSize = sessionPacket->countTracks();
+ AString desc = nullptr;
+ while (mFdp.remaining_bytes()) {
+ int32_t packetSourceAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxPacketSourceAPI);
+ switch (packetSourceAPI) {
+ case 0: {
+ unsigned long payload = 0;
+ AString params = nullptr;
+ sessionPacket->getFormatType(mFdp.ConsumeIntegralInRange(kMinIndex, trackSize - 1),
+ &payload, &desc, ¶ms);
+ break;
+ }
+ case 1: {
+ int32_t width, height;
+ unsigned long payload = mFdp.ConsumeIntegral<unsigned long>();
+ sessionPacket->getDimensions(mFdp.ConsumeIntegralInRange(kMinIndex, trackSize - 1),
+ payload, &width, &height);
+ break;
+ }
+ case 2: {
+ int32_t cvoExtMap = mFdp.ConsumeIntegral<int32_t>();
+ sessionPacket->getCvoExtMap(mFdp.ConsumeIntegralInRange(kMinIndex, trackSize - 1),
+ &cvoExtMap);
+ break;
+ }
+ case 3: {
+ int64_t durationUs = mFdp.ConsumeIntegral<int64_t>();
+ sessionPacket->getDurationUs(&durationUs);
+ break;
+ }
+ case 4: {
+ int32_t timeScale, numChannels;
+ if (desc != nullptr) {
+ sessionPacket->ParseFormatDesc(desc.c_str(), &timeScale, &numChannels);
+ }
+ break;
+ }
+ case 5: {
+ if (checkFormatSupport(codec, format)) {
+ sp<APacketSource> packetSource = sp<APacketSource>::make(
+ sessionPacket, mFdp.ConsumeIntegralInRange(kMinIndex, trackSize - 1));
+ }
+ break;
+ }
+ }
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ ASessionPacketFuzzer packetSourceFuzzer(data, size);
+ packetSourceFuzzer.process();
+ return 0;
+}
diff --git a/media/libstagefright/rtsp/fuzzer/rtp_writer_fuzzer.cpp b/media/libstagefright/rtsp/fuzzer/rtp_writer_fuzzer.cpp
new file mode 100644
index 0000000..8d9f923
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/rtp_writer_fuzzer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 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 <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/rtsp/ARTPWriter.h>
+
+constexpr int32_t kMinSize = 0;
+constexpr int32_t kMaxSize = 65536;
+constexpr int32_t kMaxTime = 1000;
+constexpr int32_t kMaxBytes = 128;
+constexpr int32_t kAMRNBFrameSizes[] = {13, 14, 16, 18, 20, 21, 27, 32};
+constexpr int32_t kAMRWBFrameSizes[] = {18, 24, 33, 37, 41, 47, 51, 59, 61};
+constexpr int32_t kAMRIndexOffset = 8;
+
+using namespace android;
+
+const char* kKeyMimeTypeArray[] = {MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_HEVC,
+ MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AMR_NB,
+ MEDIA_MIMETYPE_AUDIO_AMR_WB};
+
+struct TestMediaSource : public MediaSource {
+ public:
+ TestMediaSource(FuzzedDataProvider& mFdp) : mTestMetaData(new MetaData) {
+ int32_t vectorSize = 0;
+ mAllowRead = mFdp.ConsumeBool();
+ mKeySps = mFdp.ConsumeIntegral<int32_t>();
+ mKeyVps = mFdp.ConsumeIntegral<int32_t>();
+ mKeyPps = mFdp.ConsumeIntegral<int32_t>();
+ mKeyTime = mFdp.ConsumeIntegralInRange<int64_t>(kMinSize, kMaxTime);
+
+ mMimeType = mFdp.PickValueInArray(kKeyMimeTypeArray);
+ mTestMetaData->setCString(kKeyMIMEType, mMimeType);
+ if (mMimeType == MEDIA_MIMETYPE_AUDIO_AMR_NB) {
+ int32_t index =
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, std::size(kAMRNBFrameSizes) - 1);
+ vectorSize = kAMRNBFrameSizes[index];
+ mData.push_back(kAMRIndexOffset * index);
+ } else if (mMimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB) {
+ int32_t index =
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, std::size(kAMRWBFrameSizes) - 1);
+ vectorSize = kAMRWBFrameSizes[index];
+ mData.push_back(kAMRIndexOffset * index);
+ } else if (mMimeType == MEDIA_MIMETYPE_VIDEO_H263) {
+ // Required format for H263 media data
+ mData.push_back(0);
+ mData.push_back(0);
+ vectorSize = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ } else {
+ vectorSize = mFdp.ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize);
+ }
+ for (size_t idx = mData.size(); idx < vectorSize; ++idx) {
+ mData.push_back(mFdp.ConsumeIntegral<uint8_t>());
+ }
+ }
+ virtual status_t start(MetaData* /*params*/) { return OK; }
+ virtual status_t stop() { return OK; }
+ virtual sp<MetaData> getFormat() { return mTestMetaData; }
+ virtual status_t read(MediaBufferBase** buffer, const ReadOptions* /*options*/) {
+ if (!mAllowRead) {
+ return -1;
+ }
+ *buffer = new MediaBuffer(mData.data() /*data*/, mData.size() /*size*/);
+ if (mKeySps) {
+ (*buffer)->meta_data().setInt32(kKeySps, mKeySps);
+ }
+ if (mKeyVps) {
+ (*buffer)->meta_data().setInt32(kKeyVps, mKeyVps);
+ }
+ if (mKeyPps) {
+ (*buffer)->meta_data().setInt32(kKeyPps, mKeyPps);
+ }
+ (*buffer)->meta_data().setInt64(kKeyTime, mKeyTime);
+ return OK;
+ }
+
+ private:
+ int32_t mKeySps;
+ int32_t mKeyVps;
+ int32_t mKeyPps;
+ int64_t mKeyTime;
+ bool mAllowRead;
+ const char* mMimeType;
+ sp<MetaData> mTestMetaData;
+ std::vector<uint8_t> mData;
+};
+
+class ARTPWriterFuzzer {
+ public:
+ ARTPWriterFuzzer(const uint8_t* data, size_t size)
+ : mDataSourceFd(memfd_create("InputFile", MFD_ALLOW_SEALING)), mFdp(data, size) {}
+ ~ARTPWriterFuzzer() { close(mDataSourceFd); }
+ void process();
+
+ private:
+ void createARTPWriter();
+ const int32_t mDataSourceFd;
+ FuzzedDataProvider mFdp;
+ sp<ARTPWriter> mArtpWriter;
+};
+
+void ARTPWriterFuzzer::createARTPWriter() {
+ String8 localIp = String8(mFdp.ConsumeRandomLengthString(kMaxBytes).c_str());
+ String8 remoteIp = String8(mFdp.ConsumeRandomLengthString(kMaxBytes).c_str());
+ mArtpWriter = sp<ARTPWriter>::make(
+ mDataSourceFd, localIp, mFdp.ConsumeIntegral<uint16_t>() /* localPort */, remoteIp,
+ mFdp.ConsumeIntegral<uint16_t>() /* remotePort */,
+ mFdp.ConsumeIntegralInRange<uint32_t>(kMinSize, kMaxSize) /* seqNo */);
+}
+
+void ARTPWriterFuzzer::process() {
+ if (mFdp.ConsumeBool()) {
+ mArtpWriter = sp<ARTPWriter>::make(mDataSourceFd);
+ if (mArtpWriter->getSequenceNum() > kMaxSize) {
+ createARTPWriter();
+ }
+ } else {
+ createARTPWriter();
+ }
+
+ mArtpWriter->addSource(sp<TestMediaSource>::make(mFdp) /* source */);
+
+ while (mFdp.remaining_bytes()) {
+ auto invokeRTPWriterFuzzer = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ sp<MetaData> metaData = sp<MetaData>::make();
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt32(kKeySelfID, mFdp.ConsumeIntegral<int32_t>());
+ }
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt32(kKeyPayloadType, mFdp.ConsumeIntegral<int32_t>());
+ }
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt32(kKeyRtpExtMap, mFdp.ConsumeIntegral<int32_t>());
+ }
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt32(kKeyRtpCvoDegrees, mFdp.ConsumeIntegral<int32_t>());
+ }
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt32(kKeyRtpDscp, mFdp.ConsumeIntegral<int32_t>());
+ }
+ if (mFdp.ConsumeBool()) {
+ metaData->setInt64(kKeySocketNetwork, mFdp.ConsumeIntegral<int64_t>());
+ }
+ mArtpWriter->start(metaData.get() /*param*/);
+ },
+ [&]() {
+ mArtpWriter->setTMMBNInfo(mFdp.ConsumeIntegral<uint32_t>() /* opponentID */,
+ mFdp.ConsumeIntegral<uint32_t>() /* bitrate */);
+ },
+ [&]() { mArtpWriter->stop(); },
+ [&]() {
+ mArtpWriter->updateCVODegrees(mFdp.ConsumeIntegral<int32_t>() /* cvoDegrees */);
+ },
+ [&]() {
+ mArtpWriter->updatePayloadType(
+ mFdp.ConsumeIntegral<int32_t>() /* payloadType */);
+ },
+
+ });
+ invokeRTPWriterFuzzer();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ ARTPWriterFuzzer artpWriterFuzzer(data, size);
+ artpWriterFuzzer.process();
+ return 0;
+}
diff --git a/media/libstagefright/rtsp/fuzzer/rtsp_connection_fuzzer.cpp b/media/libstagefright/rtsp/fuzzer/rtsp_connection_fuzzer.cpp
new file mode 100644
index 0000000..51c423e
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/rtsp_connection_fuzzer.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2023 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 <arpa/inet.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/rtsp/ARTSPConnection.h>
+#include <thread>
+
+using namespace android;
+
+const std::string kAuthType[] = {"Basic", "Digest"};
+const std::string kTab = "\t";
+const std::string kCSeq = "CSeq: ";
+const std::string kSpace = " ";
+const std::string kNewLine = "\n";
+const std::string kBinaryHeader = "$";
+const std::string kNonce = " nonce=\"\"";
+const std::string kRealm = " realm=\"\"";
+const std::string kHeaderBoundary = "\r\n\r\n";
+const std::string kContentLength = "content-length: ";
+const std::string kDefaultRequestValue = "INVALID_FORMAT";
+const std::string kUrlPrefix = "rtsp://root:pass@127.0.0.1:";
+const std::string kRequestMarker = "REQUEST_SENT";
+const std::string kQuitResponse = "\n\n\n\n";
+const std::string kRTSPVersion = "RTSP/1.0";
+const std::string kValidResponse = kRTSPVersion + " 200 \n";
+const std::string kAuthString = kRTSPVersion + " 401 \nwww-authenticate: ";
+constexpr char kNullValue = '\0';
+constexpr char kDefaultValue = '0';
+constexpr int32_t kWhat = 'resp';
+constexpr int32_t kMinPort = 100;
+constexpr int32_t kMaxPort = 999;
+constexpr int32_t kMinASCIIValue = 32;
+constexpr int32_t kMaxASCIIValue = 126;
+constexpr int32_t kMinContentLength = 0;
+constexpr int32_t kMaxContentLength = 1000;
+constexpr int32_t kBinaryVectorSize = 3;
+constexpr int32_t kDefaultCseqValue = 1;
+constexpr int32_t kBufferSize = 1024;
+constexpr int32_t kMaxLoopRuns = 5;
+constexpr int32_t kPort = 554;
+constexpr int32_t kMaxBytes = 128;
+constexpr int32_t kMaxThreads = 1024;
+
+struct FuzzAHandler : public AHandler {
+ public:
+ FuzzAHandler(std::function<void()> signalEosFunction)
+ : mSignalEosFunction(std::move(signalEosFunction)) {}
+ ~FuzzAHandler() = default;
+
+ protected:
+ void onMessageReceived(const sp<AMessage>& msg) override {
+ switch (msg->what()) {
+ case kWhat: {
+ mSignalEosFunction();
+ break;
+ }
+ }
+ }
+
+ private:
+ std::function<void()> mSignalEosFunction;
+};
+
+class RTSPConnectionFuzzer {
+ public:
+ RTSPConnectionFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ ~RTSPConnectionFuzzer() {
+ // wait for all the threads to join the main thread
+ for (auto& thread : mThreadPool) {
+ if (thread.joinable()) {
+ thread.join();
+ }
+ }
+ close(mServerFd);
+ }
+ void process();
+
+ private:
+ void signalEos();
+ void startServer();
+ void createFuzzData();
+ void acceptConnection();
+ void handleConnection(int32_t);
+ void handleClientResponse(int32_t);
+ void sendValidResponse(int32_t, int32_t);
+ int32_t checkSocket(int32_t);
+ size_t generateBinaryDataSize(std::string);
+ bool checkValidRequestData(const AString&);
+ bool mEosReached = false;
+ bool mServerFailure = false;
+ bool mNotifyResponseListener = false;
+ int32_t mServerFd;
+ std::string mFuzzData = "";
+ std::string mFuzzRequestData = "";
+ std::string mRequestData = kDefaultRequestValue;
+ std::mutex mFuzzDataMutex;
+ std::mutex mMsgPostCompleteMutex;
+ std::condition_variable mConditionalVariable;
+ std::vector<std::thread> mThreadPool;
+ FuzzedDataProvider mFdp;
+};
+
+size_t RTSPConnectionFuzzer::generateBinaryDataSize(std::string values) {
+ // computed the binary data size as done in ARTSPConnection.cpp
+ uint8_t x = values[0];
+ uint8_t y = values[1];
+ return x << 8 | y;
+}
+
+bool RTSPConnectionFuzzer::checkValidRequestData(const AString& request) {
+ if (request.find(kHeaderBoundary.c_str()) <= 0) {
+ return false;
+ }
+ ssize_t space = request.find(kSpace.c_str());
+ if (space <= 0) {
+ return false;
+ }
+ if (request.find(kSpace.c_str(), space + 1) <= 0) {
+ return false;
+ }
+ return true;
+}
+
+void RTSPConnectionFuzzer::createFuzzData() {
+ std::unique_lock fuzzLock(mFuzzDataMutex);
+ mFuzzData = "";
+ mFuzzRequestData = "";
+ int32_t contentLength = 0;
+ if (mFdp.ConsumeBool()) {
+ if (mFdp.ConsumeBool()) {
+ // if we want to handle server request
+ mFuzzData.append(kSpace + kSpace + kRTSPVersion);
+ } else {
+ // if we want to notify response listener
+ mFuzzData.append(
+ kRTSPVersion + kSpace +
+ std::to_string(mFdp.ConsumeIntegralInRange<uint16_t>(kMinPort, kMaxPort)) +
+ kSpace);
+ }
+ mFuzzData.append(kNewLine);
+ if (mFdp.ConsumeBool()) {
+ contentLength =
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinContentLength, kMaxContentLength);
+ mFuzzData.append(kContentLength + std::to_string(contentLength) + kNewLine);
+ if (mFdp.ConsumeBool()) {
+ mFdp.ConsumeBool() ? mFuzzData.append(kSpace + kNewLine)
+ : mFuzzData.append(kTab + kNewLine);
+ }
+ }
+ // new line to break out of infinite for loop
+ mFuzzData.append(kNewLine);
+ if (contentLength) {
+ std::string contentData = mFdp.ConsumeBytesAsString(contentLength);
+ contentData.resize(contentLength, kDefaultValue);
+ mFuzzData.append(contentData);
+ }
+ } else {
+ // for binary data
+ std::string randomValues(kBinaryVectorSize, kNullValue);
+ for (size_t idx = 0; idx < kBinaryVectorSize; ++idx) {
+ randomValues[idx] =
+ (char)mFdp.ConsumeIntegralInRange<uint8_t>(kMinASCIIValue, kMaxASCIIValue);
+ }
+ size_t binaryDataSize = generateBinaryDataSize(randomValues);
+ std::string data = mFdp.ConsumeBytesAsString(binaryDataSize);
+ data.resize(binaryDataSize, kDefaultValue);
+ mFuzzData.append(kBinaryHeader + randomValues + data);
+ }
+ if (mFdp.ConsumeBool()) {
+ mRequestData = mFdp.ConsumeRandomLengthString(kMaxBytes) + kSpace + kSpace +
+ kHeaderBoundary + mFdp.ConsumeRandomLengthString(kMaxBytes);
+ // Check if Request data is valid
+ if (checkValidRequestData(mRequestData.c_str())) {
+ if (mFdp.ConsumeBool()) {
+ if (mFdp.ConsumeBool()) {
+ // if we want to handle server request
+ mFuzzRequestData.append(kSpace + kSpace + kRTSPVersion + kNewLine);
+ } else {
+ // if we want to add authentication headers
+ mNotifyResponseListener = true;
+ mFuzzRequestData.append(kAuthString);
+ if (mFdp.ConsumeBool()) {
+ // for Authentication type: Basic
+ mFuzzRequestData.append(kAuthType[0]);
+ } else {
+ // for Authentication type: Digest
+ mFuzzRequestData.append(kAuthType[1]);
+ mFuzzRequestData.append(kNonce);
+ mFuzzRequestData.append(kRealm);
+ }
+ mFuzzRequestData.append(kNewLine);
+ }
+ } else {
+ mNotifyResponseListener = false;
+ mFuzzRequestData.append(kValidResponse);
+ }
+ } else {
+ mRequestData = kDefaultRequestValue;
+ }
+ } else {
+ mRequestData = kDefaultRequestValue;
+ mFuzzData.append(kNewLine);
+ }
+}
+
+void RTSPConnectionFuzzer::signalEos() {
+ mEosReached = true;
+ mConditionalVariable.notify_all();
+ return;
+}
+
+int32_t RTSPConnectionFuzzer::checkSocket(int32_t newSocket) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ fd_set rs;
+ FD_ZERO(&rs);
+ FD_SET(newSocket, &rs);
+
+ return select(newSocket + 1, &rs, nullptr, nullptr, &tv);
+}
+
+void RTSPConnectionFuzzer::sendValidResponse(int32_t newSocket, int32_t cseq = -1) {
+ std::string validResponse = kValidResponse;
+ if (cseq != -1) {
+ validResponse.append(kCSeq + std::to_string(cseq));
+ validResponse.append(kNewLine + kNewLine);
+ } else {
+ validResponse.append(kNewLine);
+ }
+ send(newSocket, validResponse.c_str(), validResponse.size(), 0);
+}
+
+void RTSPConnectionFuzzer::handleClientResponse(int32_t newSocket) {
+ char buffer[kBufferSize] = {0};
+ if (checkSocket(newSocket) == 1) {
+ read(newSocket, buffer, kBufferSize);
+ }
+}
+
+void RTSPConnectionFuzzer::handleConnection(int32_t newSocket) {
+ std::unique_lock fuzzLock(mFuzzDataMutex);
+ send(newSocket, mFuzzData.c_str(), mFuzzData.size(), 0);
+ if (mFuzzData[0] == kSpace[0]) {
+ handleClientResponse(newSocket);
+ }
+
+ if (mFuzzRequestData != "") {
+ char buffer[kBufferSize] = {0};
+ if (checkSocket(newSocket) == 1 && recv(newSocket, buffer, kBufferSize, MSG_DONTWAIT) > 0) {
+ // Extract the 'CSeq' value present at the end of header
+ std::string clientResponse(buffer);
+ std::string header = clientResponse.substr(0, clientResponse.find(kHeaderBoundary));
+ char cseq = header[header.rfind(kCSeq) + kCSeq.length()];
+ int32_t cseqValue = cseq ? cseq - '0' : kDefaultCseqValue;
+ std::string response = mFuzzRequestData;
+ response.append(kCSeq + std::to_string(cseqValue));
+ response.append(kNewLine + kNewLine);
+ send(newSocket, response.data(), response.length(), 0);
+
+ if (!mNotifyResponseListener) {
+ char buffer[kBufferSize] = {0};
+ if (checkSocket(newSocket) == 1) {
+ if (recv(newSocket, buffer, kBufferSize, MSG_DONTWAIT) > 0) {
+ // Extract the 'CSeq' value present at the end of header
+ std::string clientResponse(buffer);
+ std::string header =
+ clientResponse.substr(0, clientResponse.find(kHeaderBoundary));
+ char cseq = header[header.rfind(kCSeq) + kCSeq.length()];
+ int32_t cseqValue = cseq ? cseq - '0' : kDefaultCseqValue;
+ sendValidResponse(newSocket, cseqValue);
+ } else {
+ sendValidResponse(newSocket);
+ }
+ }
+ }
+ } else {
+ // If no data to read, then send a valid response
+ // to release the mutex lock in fuzzer
+ sendValidResponse(newSocket);
+ }
+ }
+ send(newSocket, kQuitResponse.c_str(), kQuitResponse.size(), 0);
+}
+
+void RTSPConnectionFuzzer::startServer() {
+ signal(SIGPIPE, SIG_IGN);
+ mServerFd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ struct sockaddr_in serverAddress;
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ serverAddress.sin_port = htons(kPort);
+
+ // Get rid of "Address in use" error
+ int32_t opt = 1;
+ if (setsockopt(mServerFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
+ mServerFailure = true;
+ }
+
+ // Bind the socket and set for listening.
+ if (bind(mServerFd, (struct sockaddr*)(&serverAddress), sizeof(serverAddress)) < 0) {
+ mServerFailure = true;
+ }
+
+ if (listen(mServerFd, 5) < 0) {
+ mServerFailure = true;
+ }
+}
+
+void RTSPConnectionFuzzer::acceptConnection() {
+ int32_t clientFd = accept4(mServerFd, nullptr, nullptr, SOCK_CLOEXEC);
+ handleConnection(clientFd);
+ close(clientFd);
+}
+
+void RTSPConnectionFuzzer::process() {
+ startServer();
+ if (mServerFailure) {
+ return;
+ }
+ sp<ALooper> looper = sp<ALooper>::make();
+ sp<FuzzAHandler> handler =
+ sp<FuzzAHandler>::make(std::bind(&RTSPConnectionFuzzer::signalEos, this));
+ sp<ARTSPConnection> rtspConnection =
+ sp<ARTSPConnection>::make(mFdp.ConsumeBool(), mFdp.ConsumeIntegral<uint64_t>());
+ looper->start();
+ looper->registerHandler(rtspConnection);
+ looper->registerHandler(handler);
+ sp<AMessage> replymsg = sp<AMessage>::make(kWhat, handler);
+ std::string url = kUrlPrefix + std::to_string(kPort) + "/";
+
+ while (mFdp.remaining_bytes() && mThreadPool.size() < kMaxThreads) {
+ createFuzzData();
+ mThreadPool.push_back(std::thread(&RTSPConnectionFuzzer::acceptConnection, this));
+ if (mFdp.ConsumeBool()) {
+ rtspConnection->observeBinaryData(replymsg);
+ }
+
+ {
+ rtspConnection->connect(url.c_str(), replymsg);
+ std::unique_lock waitForMsgPostComplete(mMsgPostCompleteMutex);
+ mConditionalVariable.wait(waitForMsgPostComplete, [this] {
+ if (mEosReached == true) {
+ mEosReached = false;
+ return true;
+ }
+ return mEosReached;
+ });
+ }
+
+ if (mRequestData != kDefaultRequestValue) {
+ rtspConnection->sendRequest(mRequestData.c_str(), replymsg);
+ std::unique_lock waitForMsgPostComplete(mMsgPostCompleteMutex);
+ mConditionalVariable.wait(waitForMsgPostComplete, [this] {
+ if (mEosReached == true) {
+ mEosReached = false;
+ return true;
+ }
+ return mEosReached;
+ });
+ }
+
+ if (mFdp.ConsumeBool()) {
+ rtspConnection->disconnect(replymsg);
+ std::unique_lock waitForMsgPostComplete(mMsgPostCompleteMutex);
+ mConditionalVariable.wait(waitForMsgPostComplete, [this] {
+ if (mEosReached == true) {
+ mEosReached = false;
+ return true;
+ }
+ return mEosReached;
+ });
+ }
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ RTSPConnectionFuzzer rtspFuzz(data, size);
+ rtspFuzz.process();
+ return 0;
+}
diff --git a/media/libstagefright/rtsp/fuzzer/sdploader_fuzzer.cpp b/media/libstagefright/rtsp/fuzzer/sdploader_fuzzer.cpp
new file mode 100644
index 0000000..748e5b6
--- /dev/null
+++ b/media/libstagefright/rtsp/fuzzer/sdploader_fuzzer.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 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 <datasource/HTTPBase.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/MediaHTTPConnection.h>
+#include <media/MediaHTTPService.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/rtsp/SDPLoader.h>
+
+using namespace android;
+
+constexpr int32_t kMinCapacity = 0;
+constexpr int32_t kMaxCapacity = 1000;
+constexpr int32_t kMaxStringLength = 20;
+constexpr int32_t kMaxBytes = 128;
+enum { kWhatLoad = 'load' };
+
+struct FuzzAHandler : public AHandler {
+ public:
+ FuzzAHandler(std::function<void()> signalEosFunction) : mSignalEosFunction(signalEosFunction) {}
+
+ protected:
+ void onMessageReceived(const sp<AMessage>& msg) override {
+ switch (msg->what()) {
+ case kWhatLoad: {
+ mSignalEosFunction();
+ break;
+ }
+ }
+ return;
+ }
+
+ private:
+ std::function<void()> mSignalEosFunction;
+};
+
+struct FuzzMediaHTTPConnection : public MediaHTTPConnection {
+ public:
+ FuzzMediaHTTPConnection(FuzzedDataProvider* fdp) : mFdp(fdp) {
+ mSize = mFdp->ConsumeIntegralInRange(kMinCapacity, kMaxCapacity);
+ mData = mFdp->ConsumeBytes<uint8_t>(mSize);
+ mSize = mData.size();
+ }
+ virtual bool connect(const char* /* uri */,
+ const KeyedVector<String8, String8>* /* headers */) {
+ return mFdp->ConsumeBool();
+ }
+ virtual void disconnect() { return; }
+ virtual ssize_t readAt(off64_t offset, void* data, size_t size) {
+ if ((size + offset <= mData.size()) && (offset >= 0)) {
+ memcpy(data, mData.data() + offset, size);
+ return size;
+ }
+ return 0;
+ }
+ virtual off64_t getSize() { return mSize; }
+ virtual status_t getMIMEType(String8* /*mimeType*/) {return mFdp->ConsumeIntegral<status_t>();}
+ virtual status_t getUri(String8* /*uri*/) {return mFdp->ConsumeIntegral<status_t>();}
+
+ private:
+ FuzzedDataProvider* mFdp = nullptr;
+ std::vector<uint8_t> mData;
+ size_t mSize = 0;
+};
+
+struct FuzzMediaHTTPService : public MediaHTTPService {
+ public:
+ FuzzMediaHTTPService(FuzzedDataProvider* fdp) : mFdp(fdp) {}
+ virtual sp<MediaHTTPConnection> makeHTTPConnection() {
+ mediaHTTPConnection = sp<FuzzMediaHTTPConnection>::make(mFdp);
+ return mediaHTTPConnection;
+ }
+
+ private:
+ sp<FuzzMediaHTTPConnection> mediaHTTPConnection = nullptr;
+ FuzzedDataProvider* mFdp = nullptr;
+};
+
+class SDPLoaderFuzzer {
+ public:
+ SDPLoaderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {}
+ void process();
+
+ private:
+ void signalEos();
+
+ bool mEosReached = false;
+ std::mutex mMsgPostCompleteMutex;
+ std::condition_variable mConditionalVariable;
+ FuzzedDataProvider mFdp;
+};
+
+void SDPLoaderFuzzer::signalEos() {
+ mEosReached = true;
+ mConditionalVariable.notify_one();
+ return;
+}
+
+void SDPLoaderFuzzer::process() {
+ sp<FuzzAHandler> handler = sp<FuzzAHandler>::make(std::bind(&SDPLoaderFuzzer::signalEos, this));
+ sp<ALooper> looper = sp<ALooper>::make();
+ looper->start();
+ looper->registerHandler(handler);
+ const sp<AMessage> notify = sp<AMessage>::make(kWhatLoad, handler);
+ sp<SDPLoader> sdpLoader =
+ sp<SDPLoader>::make(notify, mFdp.ConsumeIntegral<uint32_t>() /* flags */,
+ sp<FuzzMediaHTTPService>::make(&mFdp) /* httpService */);
+
+ KeyedVector<String8, String8> headers;
+ for (size_t idx = 0; idx < mFdp.ConsumeIntegralInRange<size_t>(kMinCapacity, kMaxCapacity);
+ ++idx) {
+ headers.add(String8(mFdp.ConsumeRandomLengthString(kMaxBytes).c_str()) /* key */,
+ String8(mFdp.ConsumeRandomLengthString(kMaxBytes).c_str()) /* value */);
+ }
+
+ sdpLoader->load(mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* url */, &headers);
+
+ std::unique_lock waitForMsgPostComplete(mMsgPostCompleteMutex);
+ mConditionalVariable.wait(waitForMsgPostComplete, [this] { return mEosReached; });
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ SDPLoaderFuzzer sdpLoaderFuzzer(data, size);
+ sdpLoaderFuzzer.process();
+ return 0;
+}
diff --git a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
index 7823922..3b70636 100644
--- a/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
+++ b/media/libstagefright/tests/VideoRenderQualityTracker_test.cpp
@@ -36,10 +36,11 @@
class Helper {
public:
Helper(double contentFrameDurationMs, const Configuration &configuration) :
- mVideoRenderQualityTracker(configuration) {
+ mVideoRenderQualityTracker(configuration, testTraceTrigger) {
mContentFrameDurationUs = int64_t(contentFrameDurationMs * 1000);
mMediaTimeUs = 0;
mClockTimeNs = 0;
+ sTraceTriggeredCount = 0;
}
void changeContentFrameDuration(double contentFrameDurationMs) {
@@ -100,6 +101,10 @@
return e;
}
+ int getTraceTriggeredCount() {
+ return sTraceTriggeredCount;
+ }
+
private:
VideoRenderQualityTracker mVideoRenderQualityTracker;
int64_t mContentFrameDurationUs;
@@ -107,8 +112,16 @@
int64_t mClockTimeNs;
VideoRenderQualityTracker::FreezeEvent mFreezeEvent;
VideoRenderQualityTracker::JudderEvent mJudderEvent;
+
+ static int sTraceTriggeredCount;
+
+ static void testTraceTrigger() {
+ sTraceTriggeredCount++;
+ };
};
+int Helper::sTraceTriggeredCount = 0;
+
class VideoRenderQualityTrackerTest : public ::testing::Test {
public:
VideoRenderQualityTrackerTest() {}
@@ -139,6 +152,10 @@
EXPECT_EQ(c.judderEventMax, d.judderEventMax);
EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+ EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+ EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+ EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+ EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
}
TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withEmpty) {
@@ -166,6 +183,10 @@
EXPECT_EQ(c.judderEventMax, d.judderEventMax);
EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+ EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+ EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+ EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+ EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
}
TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withInvalid) {
@@ -193,6 +214,10 @@
EXPECT_EQ(c.judderEventMax, d.judderEventMax);
EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+ EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+ EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+ EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+ EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
}
TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withAlmostValid) {
@@ -232,6 +257,14 @@
return "10*10";
} else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
return "140-a";
+ } else if (flag == "render_metrics_trace_trigger_enabled") {
+ return "fals";
+ } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+ return "12345678901234";
+ } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+ return "10b0";
+ } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+ return "100a";
}
return "";
}
@@ -255,6 +288,10 @@
EXPECT_EQ(c.judderEventMax, d.judderEventMax);
EXPECT_EQ(c.judderEventDetailsMax, d.judderEventDetailsMax);
EXPECT_EQ(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+ EXPECT_EQ(c.traceTriggerEnabled, d.traceTriggerEnabled);
+ EXPECT_EQ(c.traceTriggerThrottleMs, d.traceTriggerThrottleMs);
+ EXPECT_EQ(c.traceMinFreezeDurationMs, d.traceMinFreezeDurationMs);
+ EXPECT_EQ(c.traceMaxFreezeDurationMs, d.traceMaxFreezeDurationMs);
}
TEST_F(VideoRenderQualityTrackerTest, getFromServerConfigurableFlags_withValid) {
@@ -294,6 +331,14 @@
return "10000";
} else if (flag == "render_metrics_judder_event_distance_tolerance_ms") {
return "11000";
+ } else if (flag == "render_metrics_trace_trigger_enabled") {
+ return "true";
+ } else if (flag == "render_metrics_trace_trigger_throttle_ms") {
+ return "50000";
+ } else if (flag == "render_metrics_trace_minimum_freeze_duration_ms") {
+ return "1000";
+ } else if (flag == "render_metrics_trace_maximum_freeze_duration_ms") {
+ return "5000";
}
return "";
}
@@ -353,6 +398,11 @@
EXPECT_NE(c.judderEventDetailsMax, d.judderEventDetailsMax);
EXPECT_EQ(c.judderEventDistanceToleranceMs, 11000);
EXPECT_NE(c.judderEventDistanceToleranceMs, d.judderEventDistanceToleranceMs);
+
+ EXPECT_EQ(c.traceTriggerEnabled, true);
+ EXPECT_EQ(c.traceTriggerThrottleMs, 50000);
+ EXPECT_EQ(c.traceMinFreezeDurationMs, 1000);
+ EXPECT_EQ(c.traceMaxFreezeDurationMs, 5000);
}
TEST_F(VideoRenderQualityTrackerTest, countsReleasedFrames) {
@@ -1024,4 +1074,75 @@
EXPECT_EQ(h.getMetrics().judderScore, 10 + 300 + 2000);
}
+TEST_F(VideoRenderQualityTrackerTest,
+ freezesForTraceDuration_withThrottle_throttlesTraceTrigger) {
+ Configuration c;
+ c.enabled = true;
+ c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+ // The value of traceTriggerThrottleMs must be larger than traceMinFreezeDurationMs. Otherwise,
+ // the throttle does work.
+ c.traceTriggerThrottleMs = 200;
+ c.traceMinFreezeDurationMs = 40;
+ int32_t freeze = c.traceMinFreezeDurationMs;
+
+ Helper h(20, c);
+ // Freeze triggers separated by 80ms which is less than the threshold.
+ h.render({
+ freeze, // Freeze duration does not check trace trigger.
+ 20, // Trace triggered.
+ 20, // Throttle time: 20/200ms
+ 20, // Throttle time: 40/200ms
+ freeze, // Throttle time: 80/200ms
+ 20, // Throttle time: 100/200ms (Trace not triggered)
+ });
+ EXPECT_EQ(h.getTraceTriggeredCount(), 1);
+ // Next freeze trigger is separated by 200ms which breaks the throttle threshold.
+ h.render({
+ 20, // Throttle time: 120/200ms
+ 20, // Throttle time: 140/200ms
+ 20, // Throttle time: 160/200ms
+ freeze, // Throttle time: 200/200ms
+ 20, // Trace triggered.
+ });
+ EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+ // Next freeze trigger is separated by 80ms which is less than the threshold.
+ h.render({
+ 20, // Throttle time: 20/200ms
+ 20, // Throttle time: 40/200ms
+ freeze, // Throttle time: 80/200ms
+ 20, // Throttle time: 100/200ms (Trace not triggered)
+ });
+ EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest, freezeForTraceDuration_triggersTrace) {
+ Configuration c;
+ c.enabled = true;
+ c.traceTriggerEnabled = true; // The trigger is enabled, so traces should be triggered.
+ c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+ int32_t freeze1 = c.traceMinFreezeDurationMs;
+ int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+ int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+ Helper h(20, c);
+ h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+ EXPECT_EQ(h.getTraceTriggeredCount(), 2);
+}
+
+TEST_F(VideoRenderQualityTrackerTest,
+ freezeForTraceDuration_withTraceDisabled_doesNotTriggerTrace) {
+ Configuration c;
+ c.enabled = true;
+ c.traceTriggerEnabled = false; // The trigger is disabled, so no traces should be triggered.
+ c.traceTriggerThrottleMs = 0; // Disable throttle in the test case.
+ int32_t freeze1 = c.traceMinFreezeDurationMs;
+ int32_t freeze2 = c.traceMaxFreezeDurationMs - 1;
+ int32_t couldBeAPause = c.traceMaxFreezeDurationMs + 1;
+
+ Helper h(20, c);
+ h.render({freeze1, 20, freeze2, 20, couldBeAPause, 20});
+
+ EXPECT_EQ(h.getTraceTriggeredCount(), 0);
+}
} // android
diff --git a/media/module/extractors/mp4/Android.bp b/media/module/extractors/mp4/Android.bp
index 540d75d..8072002 100644
--- a/media/module/extractors/mp4/Android.bp
+++ b/media/module/extractors/mp4/Android.bp
@@ -30,6 +30,7 @@
srcs: [
"AC4Parser.cpp",
+ "HeifCleanAperture.cpp",
"ItemTable.cpp",
"MPEG4Extractor.cpp",
"SampleIterator.cpp",
diff --git a/media/module/extractors/mp4/HeifCleanAperture.cpp b/media/module/extractors/mp4/HeifCleanAperture.cpp
new file mode 100644
index 0000000..f0a0867
--- /dev/null
+++ b/media/module/extractors/mp4/HeifCleanAperture.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 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 <HeifCleanAperture.h>
+
+namespace android {
+namespace heif {
+namespace {
+
+// |a| and |b| hold int32_t values. The int64_t type is used so that we can negate INT32_MIN without
+// overflowing int32_t.
+int64_t calculateGreatestCommonDivisor(int64_t a, int64_t b) {
+ if (a < 0) {
+ a *= -1;
+ }
+ if (b < 0) {
+ b *= -1;
+ }
+ while (b != 0) {
+ int64_t r = a % b;
+ a = b;
+ b = r;
+ }
+ return a;
+}
+
+bool overflowsInt32(int64_t x) {
+ return (x < INT32_MIN) || (x > INT32_MAX);
+}
+
+Fraction calculateCenter(int32_t value) {
+ Fraction f(value, 2);
+ f.simplify();
+ return f;
+}
+
+} // namespace
+
+Fraction::Fraction(int32_t n, int32_t d) {
+ this->n = n;
+ this->d = d;
+}
+
+void Fraction::simplify() {
+ int64_t gcd = calculateGreatestCommonDivisor(n, d);
+ if (gcd > 1) {
+ n = static_cast<int32_t>(n / gcd);
+ d = static_cast<int32_t>(d / gcd);
+ }
+}
+
+bool Fraction::commonDenominator(Fraction* f) {
+ simplify();
+ f->simplify();
+ if (d == f->d) return true;
+ const int64_t this_d = d;
+ const int64_t fd = f->d;
+ const int64_t thisnNew = n * fd;
+ const int64_t thisdNew = d * fd;
+ const int64_t fnNew = f->n * this_d;
+ const int64_t fdNew = f->d * this_d;
+ if (overflowsInt32(thisnNew) || overflowsInt32(thisdNew) || overflowsInt32(fnNew) ||
+ overflowsInt32(fdNew)) {
+ return false;
+ }
+ n = static_cast<int32_t>(thisnNew);
+ d = static_cast<int32_t>(thisdNew);
+ f->n = static_cast<int32_t>(fnNew);
+ f->d = static_cast<int32_t>(fdNew);
+ return true;
+}
+
+bool Fraction::add(Fraction f) {
+ if (!commonDenominator(&f)) {
+ return false;
+ }
+
+ const int64_t result = static_cast<int64_t>(n) + f.n;
+ if (overflowsInt32(result)) {
+ return false;
+ }
+ n = static_cast<int32_t>(result);
+ simplify();
+ return true;
+}
+
+bool Fraction::subtract(Fraction f) {
+ if (!commonDenominator(&f)) {
+ return false;
+ }
+
+ const int64_t result = static_cast<int64_t>(n) - f.n;
+ if (overflowsInt32(result)) {
+ return false;
+ }
+ n = static_cast<int32_t>(result);
+ simplify();
+ return true;
+}
+
+bool convertCleanApertureToRect(uint32_t imageW, uint32_t imageH, const CleanAperture& clap,
+ int32_t* left, int32_t* top, int32_t* right, int32_t* bottom) {
+ // ISO/IEC 14496-12:2020, Section 12.1.4.1:
+ // For horizOff and vertOff, D shall be strictly positive and N may be
+ // positive or negative. For cleanApertureWidth and cleanApertureHeight,
+ // N shall be positive and D shall be strictly positive.
+ if (clap.width.d <= 0 || clap.height.d <= 0 || clap.horizOff.d <= 0 || clap.vertOff.d <= 0 ||
+ clap.width.n < 0 || clap.height.n < 0 || !clap.width.isInteger() ||
+ !clap.height.isInteger() || imageW > INT32_MAX || imageH > INT32_MAX) {
+ return false;
+ }
+
+ const int32_t clapW = clap.width.getInt32();
+ const int32_t clapH = clap.height.getInt32();
+ if (clapW == 0 || clapH == 0) {
+ return false;
+ }
+
+ Fraction centerX = calculateCenter(imageW);
+ Fraction centerY = calculateCenter(imageH);
+ Fraction halfW(clapW, 2);
+ Fraction halfH(clapH, 2);
+
+ if (!centerX.add(clap.horizOff) || !centerX.subtract(halfW) || !centerX.isInteger() ||
+ centerX.n < 0 || !centerY.add(clap.vertOff) || !centerY.subtract(halfH) ||
+ !centerY.isInteger() || centerY.n < 0) {
+ return false;
+ }
+
+ *left = centerX.getInt32();
+ *top = centerY.getInt32();
+ *right = *left + clapW;
+ *bottom = *top + clapH;
+
+ // Make sure that the crop rect is within the image bounds.
+ if (*left > (UINT32_MAX - clapW) || *right > imageW || *top > (UINT32_MAX - clapH) ||
+ *bottom > imageH) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace heif
+} // namespace android
diff --git a/media/module/extractors/mp4/ItemTable.cpp b/media/module/extractors/mp4/ItemTable.cpp
index 7fe5ba7..cf3df62 100644
--- a/media/module/extractors/mp4/ItemTable.cpp
+++ b/media/module/extractors/mp4/ItemTable.cpp
@@ -19,6 +19,7 @@
#include <unordered_set>
+#include <HeifCleanAperture.h>
#include <ItemTable.h>
#include <media/MediaExtractorPluginApi.h>
#include <media/MediaExtractorPluginHelper.h>
@@ -47,7 +48,7 @@
ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
type(_type), itemId(_id), hidden(_hidden),
rows(0), columns(0), width(0), height(0), rotation(0),
- offset(0), size(0), nextTileIndex(0) {}
+ offset(0), size(0), seenClap(false), nextTileIndex(0) {}
bool isGrid() const {
return type == FOURCC("grid");
@@ -77,6 +78,8 @@
sp<ABuffer> hvcc;
sp<ABuffer> icc;
sp<ABuffer> av1c;
+ bool seenClap;
+ CleanAperture clap;
Vector<uint32_t> thumbnails;
Vector<uint32_t> dimgRefs;
@@ -833,6 +836,47 @@
return OK;
}
+struct ClapBox : public Box, public ItemProperty {
+ ClapBox(DataSourceHelper *source) :
+ Box(source, FOURCC("clap")), mSeen(false) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.seenClap = mSeen;
+ if (!mSeen) return;
+ image.clap = mClap;
+ }
+
+private:
+ bool mSeen;
+ CleanAperture mClap;
+};
+
+status_t ClapBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ if (size < 32) {
+ return ERROR_MALFORMED;
+ }
+ mSeen = true;
+ uint32_t values[8];
+ for (int i = 0; i < 8; ++i, offset += 4) {
+ if (!source()->getUInt32(offset, &values[i])) {
+ return ERROR_IO;
+ }
+ }
+ mClap.width.n = values[0];
+ mClap.width.d = values[1];
+ mClap.height.n = values[2];
+ mClap.height.d = values[3];
+ mClap.horizOff.n = values[4];
+ mClap.horizOff.d = values[5];
+ mClap.vertOff.n = values[6];
+ mClap.vertOff.d = values[7];
+ return OK;
+}
+
struct ColrBox : public Box, public ItemProperty {
ColrBox(DataSourceHelper *source) :
Box(source, FOURCC("colr")) {}
@@ -992,6 +1036,11 @@
itemProperty = new IrotBox(source());
break;
}
+ case FOURCC("clap"):
+ {
+ itemProperty = new ClapBox(source());
+ break;
+ }
case FOURCC("colr"):
{
itemProperty = new ColrBox(source());
@@ -1599,6 +1648,12 @@
AMediaFormat_setInt32(meta,
AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
+ int32_t left, top, right, bottom;
+ if (image->seenClap && convertCleanApertureToRect(image->width, image->height, image->clap,
+ &left, &top, &right, &bottom)) {
+ AMediaFormat_setRect(meta, AMEDIAFORMAT_KEY_DISPLAY_CROP, left, top, right - 1, bottom - 1);
+ }
+
if (!image->thumbnails.empty()) {
ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
if (thumbItemIndex >= 0) {
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index ecd937d..9ab9f0f 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -2007,7 +2007,7 @@
uint8_t mhac_header[mhac_header_size];
off64_t data_offset = *offset;
- if (chunk_size < sizeof(mhac_header)) {
+ if (mLastTrack == NULL || chunk_size < sizeof(mhac_header)) {
return ERROR_MALFORMED;
}
diff --git a/media/module/extractors/mp4/include/HeifCleanAperture.h b/media/module/extractors/mp4/include/HeifCleanAperture.h
new file mode 100644
index 0000000..930197d
--- /dev/null
+++ b/media/module/extractors/mp4/include/HeifCleanAperture.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEIF_CLEAN_APERTURE_H_
+#define HEIF_CLEAN_APERTURE_H_
+
+#include <stdint.h>
+
+namespace android {
+namespace heif {
+
+struct Fraction {
+ Fraction() = default;
+ Fraction(int32_t n, int32_t d);
+
+ void simplify();
+ bool commonDenominator(Fraction* f);
+ bool add(Fraction f);
+ bool subtract(Fraction f);
+ bool isInteger() const { return n % d == 0; }
+ int32_t getInt32() const { return n / d; }
+ int32_t n;
+ int32_t d;
+};
+
+struct CleanAperture {
+ Fraction width;
+ Fraction height;
+ Fraction horizOff;
+ Fraction vertOff;
+};
+
+// Converts the CleanAperture value into a rectangle with bounds left, top, right and bottom.
+// Returns true on success, false otherwise.
+bool convertCleanApertureToRect(uint32_t imageW, uint32_t imageH, const CleanAperture& image,
+ int32_t* left, int32_t* top, int32_t* right, int32_t* bottom);
+
+} // namespace heif
+} // namespace android
+
+#endif // HEIF_CLEAN_APERTURE_H_
diff --git a/media/module/extractors/mp4/tests/Android.bp b/media/module/extractors/mp4/tests/Android.bp
new file mode 100644
index 0000000..252cec2
--- /dev/null
+++ b/media/module/extractors/mp4/tests/Android.bp
@@ -0,0 +1,24 @@
+package {
+ default_applicable_licenses: ["frameworks_av_media_extractors_mp4_license"],
+}
+
+cc_test_host {
+ name: "HeifCleanApertureUnitTest",
+ gtest: true,
+
+ srcs: ["HeifCleanApertureUnitTest.cpp"],
+
+ header_libs: [
+ "libmp4extractor_headers",
+ ],
+
+ static_libs: [
+ "libmp4extractor",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/module/extractors/mp4/tests/HeifCleanApertureUnitTest.cpp b/media/module/extractors/mp4/tests/HeifCleanApertureUnitTest.cpp
new file mode 100644
index 0000000..6a84ae3
--- /dev/null
+++ b/media/module/extractors/mp4/tests/HeifCleanApertureUnitTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 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 <stdint.h>
+
+#include <HeifCleanAperture.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+using android::heif::CleanAperture;
+using android::heif::convertCleanApertureToRect;
+using android::heif::Fraction;
+
+struct InvalidClapPropertyParam {
+ uint32_t width;
+ uint32_t height;
+ CleanAperture clap;
+};
+
+const InvalidClapPropertyParam kInvalidClapPropertyTestParams[] = {
+ // Zero or negative denominators.
+ {120, 160, {Fraction(96, 0), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, -1), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 0), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, -1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 1), Fraction(0, 0), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 1), Fraction(0, -1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 1), Fraction(0, 1), Fraction(0, 0)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 1), Fraction(0, 1), Fraction(0, -1)}},
+ // Zero or negative clean aperture width or height.
+ {120, 160, {Fraction(-96, 1), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(0, 1), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(-132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(0, 1), Fraction(0, 1), Fraction(0, 1)}},
+ // Clean aperture width or height is not an integer.
+ {120, 160, {Fraction(96, 5), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)}},
+ {120, 160, {Fraction(96, 1), Fraction(132, 5), Fraction(0, 1), Fraction(0, 1)}},
+ {722, 1024, {Fraction(385, 1), Fraction(330, 1), Fraction(103, 1), Fraction(-308, 1)}},
+ {1024, 722, {Fraction(330, 1), Fraction(385, 1), Fraction(-308, 1), Fraction(103, 1)}},
+};
+
+using InvalidClapPropertyTest = ::testing::TestWithParam<InvalidClapPropertyParam>;
+
+INSTANTIATE_TEST_SUITE_P(Parameterized, InvalidClapPropertyTest,
+ ::testing::ValuesIn(kInvalidClapPropertyTestParams));
+
+// Negative tests for the convertCleanApertureToRect() function.
+TEST_P(InvalidClapPropertyTest, ValidateClapProperty) {
+ const InvalidClapPropertyParam& param = GetParam();
+ int32_t left, top, right, bottom;
+ EXPECT_FALSE(convertCleanApertureToRect(param.width, param.height, param.clap, &left, &top,
+ &right, &bottom));
+}
+
+struct ValidClapPropertyParam {
+ uint32_t width;
+ uint32_t height;
+ CleanAperture clap;
+
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+};
+
+const ValidClapPropertyParam kValidClapPropertyTestParams[] = {
+ {120,
+ 160,
+ {Fraction(96, 1), Fraction(132, 1), Fraction(0, 1), Fraction(0, 1)},
+ 12,
+ 14,
+ 108,
+ 146},
+ {120,
+ 160,
+ {Fraction(60, 1), Fraction(80, 1), Fraction(-30, 1), Fraction(-40, 1)},
+ 0,
+ 0,
+ 60,
+ 80},
+};
+
+using ValidClapPropertyTest = ::testing::TestWithParam<ValidClapPropertyParam>;
+
+INSTANTIATE_TEST_SUITE_P(Parameterized, ValidClapPropertyTest,
+ ::testing::ValuesIn(kValidClapPropertyTestParams));
+
+// Positive tests for the convertCleanApertureToRect() function.
+TEST_P(ValidClapPropertyTest, ValidateClapProperty) {
+ const ValidClapPropertyParam& param = GetParam();
+ int32_t left, top, right, bottom;
+ EXPECT_TRUE(convertCleanApertureToRect(param.width, param.height, param.clap, &left, &top,
+ &right, &bottom));
+ EXPECT_EQ(left, param.left);
+ EXPECT_EQ(top, param.top);
+ EXPECT_EQ(right, param.right);
+ EXPECT_EQ(bottom, param.bottom);
+}
+
+} // namespace
diff --git a/media/module/foundation/MetaDataBase.cpp b/media/module/foundation/MetaDataBase.cpp
index da383fa..60478c9 100644
--- a/media/module/foundation/MetaDataBase.cpp
+++ b/media/module/foundation/MetaDataBase.cpp
@@ -23,6 +23,8 @@
#include <stdlib.h>
#include <string.h>
+#include <mutex>
+
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/hexdump.h>
@@ -78,6 +80,7 @@
struct MetaDataBase::MetaDataInternal {
+ std::mutex mLock;
KeyedVector<uint32_t, MetaDataBase::typed_data> mItems;
};
@@ -102,10 +105,12 @@
}
void MetaDataBase::clear() {
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
mInternalData->mItems.clear();
}
bool MetaDataBase::remove(uint32_t key) {
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
ssize_t i = mInternalData->mItems.indexOfKey(key);
if (i < 0) {
@@ -252,6 +257,7 @@
uint32_t key, uint32_t type, const void *data, size_t size) {
bool overwrote_existing = true;
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
ssize_t i = mInternalData->mItems.indexOfKey(key);
if (i < 0) {
typed_data item;
@@ -269,6 +275,7 @@
bool MetaDataBase::findData(uint32_t key, uint32_t *type,
const void **data, size_t *size) const {
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
ssize_t i = mInternalData->mItems.indexOfKey(key);
if (i < 0) {
@@ -283,6 +290,7 @@
}
bool MetaDataBase::hasData(uint32_t key) const {
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
ssize_t i = mInternalData->mItems.indexOfKey(key);
if (i < 0) {
@@ -429,6 +437,7 @@
String8 MetaDataBase::toString() const {
String8 s;
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
for (int i = mInternalData->mItems.size(); --i >= 0;) {
int32_t key = mInternalData->mItems.keyAt(i);
char cc[5];
@@ -443,6 +452,7 @@
}
void MetaDataBase::dumpToLog() const {
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
for (int i = mInternalData->mItems.size(); --i >= 0;) {
int32_t key = mInternalData->mItems.keyAt(i);
char cc[5];
@@ -455,6 +465,7 @@
#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
status_t MetaDataBase::writeToParcel(Parcel &parcel) {
status_t ret;
+ std::lock_guard<std::mutex> guard(mInternalData->mLock);
size_t numItems = mInternalData->mItems.size();
ret = parcel.writeUint32(uint32_t(numItems));
if (ret) {
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 5dbcd08..6068d68 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -73,14 +73,14 @@
}
bool MtpDataPacket::getUInt8(uint8_t& value) {
- if (mPacketSize - mOffset < sizeof(value))
+ if ((mPacketSize - mOffset < sizeof(value)) || (mOffset >= mBufferSize))
return false;
value = mBuffer[mOffset++];
return true;
}
bool MtpDataPacket::getUInt16(uint16_t& value) {
- if (mPacketSize - mOffset < sizeof(value))
+ if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+1) >= mBufferSize))
return false;
int offset = mOffset;
value = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
@@ -89,7 +89,7 @@
}
bool MtpDataPacket::getUInt32(uint32_t& value) {
- if (mPacketSize - mOffset < sizeof(value))
+ if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+3) >= mBufferSize))
return false;
int offset = mOffset;
value = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
@@ -99,7 +99,7 @@
}
bool MtpDataPacket::getUInt64(uint64_t& value) {
- if (mPacketSize - mOffset < sizeof(value))
+ if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+7) >= mBufferSize))
return false;
int offset = mOffset;
value = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
diff --git a/media/ndk/fuzzer/Android.bp b/media/ndk/fuzzer/Android.bp
index a3d6a96..ba92b19 100644
--- a/media/ndk/fuzzer/Android.bp
+++ b/media/ndk/fuzzer/Android.bp
@@ -56,6 +56,14 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 155276,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libmediandk library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
@@ -63,6 +71,11 @@
name: "ndk_crypto_fuzzer",
srcs: ["ndk_crypto_fuzzer.cpp"],
defaults: ["libmediandk_fuzzer_defaults"],
+ fuzz_config: {
+ libfuzzer_options: [
+ "max_len=10000",
+ ],
+ },
}
cc_fuzz {
@@ -116,3 +129,16 @@
header_libs: ["libnativewindow_headers",],
defaults: ["libmediandk_fuzzer_defaults",],
}
+
+cc_fuzz {
+ name: "ndk_async_codec_fuzzer",
+ srcs: [
+ "ndk_async_codec_fuzzer.cpp",
+ "NdkMediaCodecFuzzerBase.cpp",
+ ],
+ header_libs: [
+ "libnativewindow_headers",
+ "libutils_headers",
+ ],
+ defaults: ["libmediandk_fuzzer_defaults",],
+}
diff --git a/media/ndk/fuzzer/README.md b/media/ndk/fuzzer/README.md
index 0fd08b0..7f6bdd7 100644
--- a/media/ndk/fuzzer/README.md
+++ b/media/ndk/fuzzer/README.md
@@ -8,6 +8,7 @@
+ [ndk_drm_fuzzer](#NdkDrm)
+ [ndk_mediamuxer_fuzzer](#NdkMediaMuxer)
+ [ndk_sync_codec_fuzzer](#NdkSyncCodec)
++ [ndk_async_codec_fuzzer](#NdkAsyncCodec)
# <a name="NdkCrypto"></a> Fuzzer for NdkCrypto
@@ -156,3 +157,16 @@
$ adb sync data
$ adb shell /data/fuzz/arm64/ndk_sync_codec_fuzzer/ndk_sync_codec_fuzzer
```
+
+# <a name="NdkAsyncCodec"></a>Fuzzer for NdkAsyncCodec
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_async_codec_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_async_codec_fuzzer/ndk_sync_codec_fuzzer
+```
diff --git a/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp b/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp
new file mode 100644
index 0000000..28a38fe
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2022 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 <NdkMediaCodecFuzzerBase.h>
+#include <media/NdkMediaFormatPriv.h>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+using namespace android;
+using namespace std;
+
+constexpr int32_t kMaxCryptoInfoAPIs = 3;
+constexpr int32_t kMaxNdkCodecAPIs = 5;
+
+template <typename T>
+class CallBackQueue {
+ public:
+ void push(T elem) {
+ bool needsNotify = false;
+ {
+ unique_lock<mutex> lock(mMutex);
+ needsNotify = mQueue.empty();
+ mQueue.push(std::move(elem));
+ }
+ if (needsNotify) {
+ mQueueNotEmptyCondition.notify_one();
+ }
+ }
+
+ T pop() {
+ unique_lock<mutex> lock(mMutex);
+ if (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(lock, [this]() { return !mQueue.empty(); });
+ }
+ auto result = mQueue.front();
+ mQueue.pop();
+ return result;
+ }
+
+ private:
+ mutex mMutex;
+ std::queue<T> mQueue;
+ std::condition_variable mQueueNotEmptyCondition;
+};
+
+class CallBackHandle {
+ public:
+ CallBackHandle() : mSawError(false), mIsDone(false) {}
+
+ virtual ~CallBackHandle() {}
+
+ void ioThread();
+
+ // Implementation in child class (Decoder/Encoder)
+ virtual void invokeInputBufferAPI(AMediaCodec* codec, int32_t index) {
+ (void)codec;
+ (void)index;
+ }
+ virtual void onFormatChanged(AMediaCodec* codec, AMediaFormat* format) {
+ (void)codec;
+ (void)format;
+ }
+ virtual void receiveError(void) {}
+ virtual void invokeOutputBufferAPI(AMediaCodec* codec, int32_t index,
+ AMediaCodecBufferInfo* bufferInfo) {
+ (void)codec;
+ (void)index;
+ (void)bufferInfo;
+ }
+
+ // Keep a queue of all function callbacks.
+ typedef function<void()> IOTask;
+ CallBackQueue<IOTask> mIOQueue;
+ bool mSawError;
+ bool mIsDone;
+};
+
+void CallBackHandle::ioThread() {
+ while (!mIsDone && !mSawError) {
+ auto task = mIOQueue.pop();
+ task();
+ }
+}
+
+static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ self->mIOQueue.push([self, codec, index]() { self->invokeInputBufferAPI(codec, index); });
+}
+
+static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
+ AMediaCodecBufferInfo* bufferInfo) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ AMediaCodecBufferInfo bufferInfoCopy = *bufferInfo;
+ self->mIOQueue.push([self, codec, index, bufferInfoCopy]() {
+ AMediaCodecBufferInfo bc = bufferInfoCopy;
+ self->invokeOutputBufferAPI(codec, index, &bc);
+ });
+}
+
+static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
+ (void)codec;
+ (void)userdata;
+ (void)format;
+};
+
+static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t err, int32_t actionCode,
+ const char* detail) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ self->mSawError = true;
+ self->receiveError();
+ (void)codec;
+ (void)err;
+ (void)actionCode;
+ (void)detail;
+};
+
+class NdkAsyncCodecFuzzer : public NdkMediaCodecFuzzerBase, public CallBackHandle {
+ public:
+ NdkAsyncCodecFuzzer(const uint8_t* data, size_t size)
+ : NdkMediaCodecFuzzerBase(), mFdp(data, size) {
+ setFdp(&mFdp);
+ mStopCodec = false;
+ mSawInputEOS = false;
+ mSignalledError = false;
+ mIsEncoder = false;
+ mNumOfFrames = 0;
+ mNumInputFrames = 0;
+ };
+ ~NdkAsyncCodecFuzzer() {
+ mIOThreadPool->stop();
+ delete (mIOThreadPool);
+ };
+
+ void process();
+
+ static void codecOnFrameRendered(AMediaCodec* codec, void* userdata, int64_t mediaTimeUs,
+ int64_t systemNano) {
+ (void)codec;
+ (void)userdata;
+ (void)mediaTimeUs;
+ (void)systemNano;
+ };
+ class ThreadPool {
+ public:
+ void start();
+ void queueJob(const std::function<void()>& job);
+ void stop();
+
+ private:
+ void ThreadLoop();
+ bool mShouldTerminate = false;
+ std::vector<std::thread> mThreads;
+ std::mutex mQueueMutex;
+ std::condition_variable mQueueMutexCondition;
+ std::queue<std::function<void()>> mJobs;
+ };
+
+ private:
+ FuzzedDataProvider mFdp;
+ AMediaCodec* mCodec = nullptr;
+ void invokeCodecCryptoInfoAPI();
+ void invokekAsyncCodecAPIs(bool isEncoder);
+ void invokeAsyncCodeConfigAPI();
+ void invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex);
+ void invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
+ AMediaCodecBufferInfo* bufferInfo);
+ void invokeFormatAPI(AMediaCodec* codec);
+ void receiveError();
+ bool mStopCodec;
+ bool mSawInputEOS;
+ bool mSignalledError;
+ int32_t mNumOfFrames;
+ int32_t mNumInputFrames;
+ mutable Mutex mMutex;
+ bool mIsEncoder;
+ ThreadPool* mIOThreadPool = new ThreadPool();
+};
+
+void NdkAsyncCodecFuzzer::ThreadPool::start() {
+ const uint32_t numThreads = std::thread::hardware_concurrency();
+ mThreads.resize(numThreads);
+ for (uint32_t i = 0; i < numThreads; ++i) {
+ mThreads.at(i) = std::thread(&ThreadPool::ThreadLoop, this);
+ }
+}
+
+void NdkAsyncCodecFuzzer::ThreadPool::ThreadLoop() {
+ while (true) {
+ std::function<void()> job;
+ {
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ mQueueMutexCondition.wait(lock, [this] { return !mJobs.empty() || mShouldTerminate; });
+ if (mShouldTerminate) {
+ return;
+ }
+ job = mJobs.front();
+ mJobs.pop();
+ }
+ job();
+ }
+}
+
+void NdkAsyncCodecFuzzer::ThreadPool::queueJob(const std::function<void()>& job) {
+ {
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ mJobs.push(job);
+ }
+ mQueueMutexCondition.notify_one();
+}
+
+void NdkAsyncCodecFuzzer::ThreadPool::stop() {
+ {
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ mShouldTerminate = true;
+ }
+ mQueueMutexCondition.notify_all();
+ for (std::thread& active_thread : mThreads) {
+ active_thread.join();
+ }
+ mThreads.clear();
+}
+
+void NdkAsyncCodecFuzzer::receiveError(void) {
+ mSignalledError = true;
+}
+
+void NdkAsyncCodecFuzzer::invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex) {
+ size_t bufferSize = 0;
+ Mutex::Autolock autoLock(mMutex);
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ return;
+ }
+ if (mStopCodec || bufferIndex < 0 || mSawInputEOS) {
+ return;
+ }
+
+ uint8_t* buffer = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufferSize);
+ if (buffer) {
+ std::vector<uint8_t> bytesRead = mFdp.ConsumeBytes<uint8_t>(
+ std::min(mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes), bufferSize));
+ memcpy(buffer, bytesRead.data(), bytesRead.size());
+ bufferSize = bytesRead.size();
+ } else {
+ mSignalledError = true;
+ return;
+ }
+
+ uint32_t flag = 0;
+ if (!bufferSize || mNumInputFrames == mNumOfFrames) {
+ flag |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ AMediaCodec_queueInputBuffer(codec, bufferIndex, 0 /* offset */, bufferSize, 0 /* time */,
+ flag);
+ mNumInputFrames++;
+}
+
+void NdkAsyncCodecFuzzer::invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
+ AMediaCodecBufferInfo* bufferInfo) {
+ size_t bufferSize = 0;
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ return;
+ }
+
+ if (mStopCodec || bufferIndex < 0 || mIsDone) {
+ return;
+ }
+
+ if (!mIsEncoder) {
+ (void)AMediaCodec_getOutputBuffer(codec, bufferIndex, &bufferSize);
+ }
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, mFdp.ConsumeBool());
+ mIsDone = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
+}
+
+void NdkAsyncCodecFuzzer::invokeFormatAPI(AMediaCodec* codec) {
+ AMediaFormat* codecFormat = nullptr;
+ if (mFdp.ConsumeBool()) {
+ codecFormat = AMediaCodec_getInputFormat(codec);
+ } else {
+ codecFormat = AMediaCodec_getOutputFormat(codec);
+ }
+ if (codecFormat) {
+ AMediaFormat_delete(codecFormat);
+ }
+}
+
+void NdkAsyncCodecFuzzer::invokekAsyncCodecAPIs(bool isEncoder) {
+ ANativeWindow* nativeWindow = nullptr;
+
+ if (mFdp.ConsumeBool()) {
+ AMediaCodec_createInputSurface(mCodec, &nativeWindow);
+ }
+
+ if (AMEDIA_OK == AMediaCodec_configure(mCodec, getCodecFormat(), nativeWindow,
+ nullptr /* crypto */,
+ (isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0))) {
+ mNumOfFrames = mFdp.ConsumeIntegralInRange<size_t>(kMinIterations, kMaxIterations);
+ // Configure codecs to run in async mode.
+ AMediaCodecOnAsyncNotifyCallback callBack = {onAsyncInputAvailable, onAsyncOutputAvailable,
+ onAsyncFormatChanged, onAsyncError};
+ AMediaCodec_setAsyncNotifyCallback(mCodec, callBack, this);
+ mIOThreadPool->queueJob([this] { CallBackHandle::ioThread(); });
+
+ AMediaCodec_start(mCodec);
+ sleep(5);
+ int32_t count = 0;
+ while (++count <= mNumOfFrames) {
+ int32_t ndkcodecAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxNdkCodecAPIs);
+ switch (ndkcodecAPI) {
+ case 0: { // get input and output Format
+ invokeFormatAPI(mCodec);
+ break;
+ }
+ case 1: {
+ AMediaCodec_signalEndOfInputStream(mCodec);
+ mSawInputEOS = true;
+ break;
+ }
+ case 2: { // set parameters
+ // Create a new parameter and set
+ AMediaFormat* params = AMediaFormat_new();
+ AMediaFormat_setInt32(
+ params, "video-bitrate",
+ mFdp.ConsumeIntegralInRange<size_t>(kMinIntKeyValue, kMaxIntKeyValue));
+ AMediaCodec_setParameters(mCodec, params);
+ AMediaFormat_delete(params);
+ break;
+ }
+ case 3: { // flush codec
+ AMediaCodec_flush(mCodec);
+ if (mFdp.ConsumeBool()) {
+ AMediaCodec_start(mCodec);
+ }
+ break;
+ }
+ case 4: {
+ char* name = nullptr;
+ AMediaCodec_getName(mCodec, &name);
+ AMediaCodec_releaseName(mCodec, name);
+ break;
+ }
+ case 5:
+ default: {
+ std::vector<uint8_t> userData = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCodecOnFrameRendered callback = codecOnFrameRendered;
+ AMediaCodec_setOnFrameRenderedCallback(mCodec, callback, userData.data());
+ break;
+ }
+ }
+ }
+ {
+ Mutex::Autolock autoLock(mMutex);
+ mStopCodec = 1;
+ AMediaCodec_stop(mCodec);
+ }
+ }
+
+ if (nativeWindow) {
+ ANativeWindow_release(nativeWindow);
+ }
+}
+
+void NdkAsyncCodecFuzzer::invokeAsyncCodeConfigAPI() {
+ mIOThreadPool->start();
+
+ while (mFdp.remaining_bytes() > 0) {
+ mIsEncoder = mFdp.ConsumeBool();
+ mCodec = createCodec(mIsEncoder, mFdp.ConsumeBool() /* isCodecForClient */);
+ if (mCodec) {
+ invokekAsyncCodecAPIs(mIsEncoder);
+ AMediaCodec_delete(mCodec);
+ }
+ }
+ mIOThreadPool->stop();
+}
+
+void NdkAsyncCodecFuzzer::invokeCodecCryptoInfoAPI() {
+ while (mFdp.remaining_bytes() > 0) {
+ AMediaCodecCryptoInfo* cryptoInfo = getAMediaCodecCryptoInfo();
+ int32_t ndkCryptoInfoAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxCryptoInfoAPIs);
+ switch (ndkCryptoInfoAPI) {
+ case 0: {
+ size_t sizes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getEncryptedBytes(cryptoInfo, sizes);
+ break;
+ }
+ case 1: {
+ size_t sizes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getClearBytes(cryptoInfo, sizes);
+ break;
+ }
+ case 2: {
+ uint8_t bytes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getIV(cryptoInfo, bytes);
+ break;
+ }
+ case 3:
+ default: {
+ uint8_t bytes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getKey(cryptoInfo, bytes);
+ break;
+ }
+ }
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ }
+}
+
+void NdkAsyncCodecFuzzer::process() {
+ if (mFdp.ConsumeBool()) {
+ invokeCodecCryptoInfoAPI();
+ } else {
+ invokeAsyncCodeConfigAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ NdkAsyncCodecFuzzer ndkAsyncCodecFuzzer(data, size);
+ ndkAsyncCodecFuzzer.process();
+ return 0;
+}
diff --git a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
index 2b22f0f..a759ae7 100644
--- a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
+++ b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
@@ -20,10 +20,12 @@
constexpr size_t kMaxString = 256;
constexpr size_t kMinBytes = 0;
constexpr size_t kMaxBytes = 1000;
+constexpr size_t kMaxRuns = 100;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
AMediaUUID uuid = {};
+ size_t apiCount = 0;
int32_t maxLen = fdp.ConsumeIntegralInRange<size_t>(kMinBytes, (size_t)sizeof(AMediaUUID));
for (size_t idx = 0; idx < maxLen; ++idx) {
uuid[idx] = fdp.ConsumeIntegral<uint8_t>();
@@ -31,7 +33,14 @@
std::vector<uint8_t> initData =
fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
AMediaCrypto* crypto = AMediaCrypto_new(uuid, initData.data(), initData.size());
- while (fdp.remaining_bytes()) {
+ /*
+ * The AMediaCrypto_isCryptoSchemeSupported API doesn't consume any input bytes,
+ * so when PickValueInArray() selects it repeatedly, only one byte is consumed by 'fdp'.
+ * As a result, on larger inputs, AMediaCrypto_isCryptoSchemeSupported can run a large
+ * number of times, potentially causing a timeout crash.
+ * Therefore, to prevent this issue, while loop is limited to kMaxRuns.
+ */
+ while (fdp.remaining_bytes() && ++apiCount <= kMaxRuns) {
auto invokeNdkCryptoFuzzer = fdp.PickValueInArray<const std::function<void()>>({
[&]() {
AMediaCrypto_requiresSecureDecoderComponent(
diff --git a/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp b/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp
index c19ea13..23e2eaf 100644
--- a/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp
+++ b/media/ndk/fuzzer/ndk_mediaformat_fuzzer.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <media/NdkMediaFormat.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <sys/mman.h>
#include <unistd.h>
#include <utils/Log.h>
@@ -176,11 +177,13 @@
constexpr size_t kMaxBytes = 1000;
constexpr size_t kMinChoice = 0;
constexpr size_t kMaxChoice = 9;
+const size_t kMaxIteration = android::AMessage::maxAllowedEntries();
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
AMediaFormat* mediaFormat = AMediaFormat_new();
- while (fdp.remaining_bytes()) {
+ std::vector<std::string> nameCollection;
+ while (fdp.remaining_bytes() && nameCollection.size() < kMaxIteration) {
const char* name = nullptr;
std::string nameString;
if (fdp.ConsumeBool()) {
@@ -190,6 +193,11 @@
: fdp.ConsumeRandomLengthString(
fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
name = nameString.c_str();
+ std::vector<std::string>::iterator it =
+ find(nameCollection.begin(), nameCollection.end(), name);
+ if (it == nameCollection.end()) {
+ nameCollection.push_back(name);
+ }
}
switch (fdp.ConsumeIntegralInRange<int32_t>(kMinChoice, kMaxChoice)) {
case 0: {
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 9e0d5e4..e947ef6 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
@@ -43,6 +43,7 @@
private boolean mRender = false;
private ArrayList<BufferInfo> mInputBufferInfo;
private Stats mStats;
+ private String mMime;
private boolean mSawInputEOS;
private boolean mSawOutputEOS;
@@ -107,14 +108,14 @@
}
private MediaCodec createCodec(String codecName, MediaFormat format) throws IOException {
- String mime = format.getString(MediaFormat.KEY_MIME);
+ mMime = format.getString(MediaFormat.KEY_MIME);
try {
MediaCodec codec;
if (codecName.isEmpty()) {
- Log.i(TAG, "File mime type: " + mime);
- if (mime != null) {
- codec = MediaCodec.createDecoderByType(mime);
- Log.i(TAG, "Decoder created for mime type " + mime);
+ Log.i(TAG, "File mime type: " + mMime);
+ if (mMime != null) {
+ codec = MediaCodec.createDecoderByType(mMime);
+ Log.i(TAG, "Decoder created for mime type " + mMime);
return codec;
} else {
Log.e(TAG, "Mime type is null, please specify a mime type to create decoder");
@@ -122,12 +123,12 @@
}
} else {
codec = MediaCodec.createByCodecName(codecName);
- Log.i(TAG, "Decoder created with codec name: " + codecName + " mime: " + mime);
+ Log.i(TAG, "Decoder created with codec name: " + codecName + " mime: " + mMime);
return codec;
}
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
- Log.e(TAG, "Failed to create decoder for " + codecName + " mime:" + mime);
+ Log.e(TAG, "Failed to create decoder for " + codecName + " mime:" + mMime);
return null;
}
}
@@ -167,6 +168,7 @@
}
if (mFrameReleaseQueue != null) {
mFrameReleaseQueue.setMediaCodec(mCodec);
+ mFrameReleaseQueue.setMime(mMime);
}
if (asyncMode) {
mCodec.setCallback(new MediaCodec.Callback() {
@@ -322,7 +324,7 @@
ByteBuffer inputCodecBuffer = mediaCodec.getInputBuffer(inputBufferId);
BufferInfo bufInfo;
if (mNumInFramesProvided >= mNumInFramesRequired) {
- Log.i(TAG, "Input frame limit reached");
+ Log.i(TAG, "Input frame limit reached provided: " + mNumInFramesProvided);
mIndex = mInputBufferInfo.size() - 1;
bufInfo = mInputBufferInfo.get(mIndex);
if ((bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0) {
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 84554d3..90731ed 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
@@ -21,20 +21,32 @@
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
public class FrameReleaseQueue {
private static final String TAG = "FrameReleaseQueue";
+ private final String MIME_AV1 = "video/av01";
+ private final int AV1_SUPERFRAME_DELAY = 6;
+ private final int THRESHOLD_TIME = 5;
private MediaCodec mCodec;
private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
private ReleaseThread mReleaseThread;
- private boolean doFrameRelease = false;
+ private AtomicBoolean doFrameRelease = new AtomicBoolean(false);
+ private boolean mReleaseJobStarted = false;
private boolean mRender = false;
private int mWaitTime = 40; // milliseconds per frame
private int mWaitTimeCorrection = 0;
private int mCorrectionLoopCount;
private int firstReleaseTime = -1;
- private int THRESHOLD_TIME = 5;
+ private int mAllowedDelayTime = THRESHOLD_TIME;
+ private int mFrameDelay = 0;
+ private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
+
private static class FrameInfo {
private int number;
@@ -48,60 +60,69 @@
}
private class ReleaseThread extends Thread {
+ private int mLoopCount = 0;
+ private int mNextReleaseTime = 0;
+
+ @SuppressWarnings("FutureReturnValueIgnored")
public void run() {
- int nextReleaseTime = 0;
- int loopCount = 0;
- while (doFrameRelease || mFrameInfoQueue.size() > 0) {
+ /* Check if the release thread wakes up too late */
+ if (mLoopCount != 0) {
+ int delta = getCurSysTime() - mNextReleaseTime;
+ if (delta >= THRESHOLD_TIME) {
+ Log.d(TAG, "Release thread wake up late by " + delta);
+ /* For accidental late wake up, we should relax the timestamp
+ check for display time */
+ mAllowedDelayTime = 1 + delta;
+ } else {
+ mAllowedDelayTime = THRESHOLD_TIME;
+ }
+ }
+ if (doFrameRelease.get() || mFrameInfoQueue.size() > 0) {
FrameInfo curFrameInfo = mFrameInfoQueue.peek();
if (curFrameInfo == null) {
- nextReleaseTime += mWaitTime;
+ mNextReleaseTime += mWaitTime;
} else {
- if (curFrameInfo.displayTime == 0) {
+ if (firstReleaseTime == -1 || curFrameInfo.displayTime <= 0) {
// first frame of loop
firstReleaseTime = getCurSysTime();
- nextReleaseTime = firstReleaseTime + mWaitTime;
- popAndRelease(curFrameInfo, true);
- } else if (!doFrameRelease && mFrameInfoQueue.size() == 1) {
+ mNextReleaseTime = firstReleaseTime + mWaitTime;
+ popAndRelease(true);
+ } else if (!doFrameRelease.get() && mFrameInfoQueue.size() == 1) {
// EOS
Log.i(TAG, "EOS");
- popAndRelease(curFrameInfo, false);
+ popAndRelease(false);
} else {
- nextReleaseTime += mWaitTime;
+ mNextReleaseTime += mWaitTime;
int curSysTime = getCurSysTime();
int curMediaTime = curSysTime - firstReleaseTime;
while (curFrameInfo != null && curFrameInfo.displayTime > 0 &&
curFrameInfo.displayTime <= curMediaTime) {
- if (!((curMediaTime - curFrameInfo.displayTime) < THRESHOLD_TIME)) {
+ if (!((curMediaTime - curFrameInfo.displayTime) <= mAllowedDelayTime)) {
Log.d(TAG, "Dropping expired frame " + curFrameInfo.number +
" display time " + curFrameInfo.displayTime +
" current time " + curMediaTime);
- popAndRelease(curFrameInfo, false);
+ popAndRelease(false);
} else {
- popAndRelease(curFrameInfo, true);
+ popAndRelease(true);
}
curFrameInfo = mFrameInfoQueue.peek();
}
if (curFrameInfo != null && curFrameInfo.displayTime > curMediaTime) {
if ((curFrameInfo.displayTime - curMediaTime) < THRESHOLD_TIME) {
- popAndRelease(curFrameInfo, true);
+ // release the frame now as we are already there
+ popAndRelease(true);
}
}
}
}
- int sleepTime = nextReleaseTime - getCurSysTime();
- if (sleepTime > 0) {
- try {
- mReleaseThread.sleep(sleepTime);
- } catch (InterruptedException e) {
- Log.e(TAG, "Threw InterruptedException on sleep");
- }
- } else {
- Log.d(TAG, "Thread sleep time less than 1");
+
+ long sleepTime = (long)(mNextReleaseTime - getCurSysTime());
+ mScheduler.schedule(mReleaseThread, sleepTime, TimeUnit.MILLISECONDS);
+
+ if (mLoopCount % mCorrectionLoopCount == 0) {
+ mNextReleaseTime += mWaitTimeCorrection;
}
- if (loopCount % mCorrectionLoopCount == 0) {
- nextReleaseTime += mWaitTimeCorrection;
- }
- loopCount += 1;
+ mLoopCount += 1;
}
}
}
@@ -109,7 +130,7 @@
public FrameReleaseQueue(boolean render, int frameRate) {
this.mFrameInfoQueue = new LinkedBlockingQueue();
this.mReleaseThread = new ReleaseThread();
- this.doFrameRelease = true;
+ this.doFrameRelease.set(true);
this.mRender = render;
this.mWaitTime = 1000 / frameRate; // wait time in milliseconds per frame
int waitTimeRemainder = 1000 % frameRate;
@@ -127,6 +148,12 @@
this.mCodec = mediaCodec;
}
+ public void setMime(String mime) {
+ if (mime.equals(MIME_AV1)) {
+ mFrameDelay = AV1_SUPERFRAME_DELAY;
+ }
+ }
+
public boolean pushFrame(int frameNumber, int frameBufferId, long frameDisplayTime) {
int frameDisplayTimeMs = (int)(frameDisplayTime/1000);
FrameInfo curFrameInfo = new FrameInfo(frameNumber, frameBufferId, frameDisplayTimeMs);
@@ -135,8 +162,10 @@
Log.e(TAG, "Failed to push frame with buffer id " + curFrameInfo.bufferId);
return false;
}
- if (!mReleaseThread.isAlive()) {
- mReleaseThread.start();
+
+ if (!mReleaseJobStarted && frameNumber >= mFrameDelay) {
+ mScheduler.execute(mReleaseThread);
+ mReleaseJobStarted = true;
Log.i(TAG, "Started frame release thread");
}
return true;
@@ -146,29 +175,33 @@
return (int)(System.nanoTime()/1000000);
}
- private void popAndRelease(FrameInfo curFrameInfo, boolean renderThisFrame) {
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private void popAndRelease(boolean renderThisFrame) {
+ final boolean actualRender = (renderThisFrame && mRender);
try {
- curFrameInfo = mFrameInfoQueue.take();
+ final FrameInfo curFrameInfo = mFrameInfoQueue.take();
+
+ CompletableFuture.runAsync(() -> {
+ try {
+ mCodec.releaseOutputBuffer(curFrameInfo.bufferId, actualRender);
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ });
+
} catch (InterruptedException e) {
Log.e(TAG, "Threw InterruptedException on take");
}
- boolean actualRender = (renderThisFrame && mRender);
- try {
- mCodec.releaseOutputBuffer(curFrameInfo.bufferId, actualRender);
- } catch (IllegalStateException e) {
- Log.e(TAG,
- "Threw IllegalStateException on releaseOutputBuffer for frame "
- + curFrameInfo.number);
- }
}
public void stopFrameRelease() {
- doFrameRelease = false;
- try {
- mReleaseThread.join();
- Log.i(TAG, "Joined frame release thread");
- } catch (InterruptedException e) {
- Log.e(TAG, "Threw InterruptedException on thread join");
+ doFrameRelease.set(false);
+ while (mFrameInfoQueue.size() > 0) {
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Threw InterruptedException on sleep");
+ }
}
}
}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 7abb0b6..07dac5e 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -179,3 +179,8 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
+
+cc_library_headers {
+ name: "mediautils_headers",
+ export_include_dirs: ["include", "."],
+}
diff --git a/media/utils/include/mediautils/SharedMemoryAllocator.h b/media/utils/include/mediautils/SharedMemoryAllocator.h
index 79621e2..4243b9c 100644
--- a/media/utils/include/mediautils/SharedMemoryAllocator.h
+++ b/media/utils/include/mediautils/SharedMemoryAllocator.h
@@ -138,7 +138,7 @@
template <typename Allocator>
class ScopedAllocator {
public:
- static constexpr size_t alignment() { return Allocator::alignment(); }
+ static size_t alignment() { return Allocator::alignment(); }
explicit ScopedAllocator(const std::shared_ptr<Allocator>& allocator) : mAllocator(allocator) {}
@@ -218,7 +218,7 @@
template <typename Allocator, typename Policy>
class PolicyAllocator {
public:
- static constexpr size_t alignment() { return Allocator::alignment(); }
+ static size_t alignment() { return Allocator::alignment(); }
PolicyAllocator(Allocator allocator, Policy policy)
: mAllocator(allocator), mPolicy(std::move(policy)) {}
@@ -277,7 +277,7 @@
std::string name;
size_t allocation_number;
};
- static constexpr size_t alignment() { return Allocator::alignment(); }
+ static size_t alignment() { return Allocator::alignment(); }
SnoopingAllocator(Allocator allocator, std::string_view name)
: mName(name), mAllocator(std::move(allocator)) {}
@@ -362,16 +362,19 @@
template <class PrimaryAllocator, class SecondaryAllocator>
class FallbackAllocator {
public:
- static_assert(PrimaryAllocator::alignment() == SecondaryAllocator::alignment());
static_assert(shared_allocator_impl::has_owns<PrimaryAllocator>);
- static constexpr size_t alignment() { return PrimaryAllocator::alignment(); }
+ static size_t alignment() { return PrimaryAllocator::alignment(); }
FallbackAllocator(const PrimaryAllocator& primary, const SecondaryAllocator& secondary)
- : mPrimary(primary), mSecondary(secondary) {}
+ : mPrimary(primary), mSecondary(secondary) {
+ verify_alignment();
+ }
// Default construct primary and secondary allocator
- FallbackAllocator() = default;
+ FallbackAllocator() {
+ verify_alignment();
+ }
template <typename T>
AllocationType allocate(T&& request) {
@@ -414,6 +417,10 @@
}
private:
+ void verify_alignment() {
+ LOG_ALWAYS_FATAL_IF(PrimaryAllocator::alignment() != SecondaryAllocator::alignment(),
+ "PrimaryAllocator::alignment() != SecondaryAllocator::alignment()");
+ }
[[no_unique_address]] PrimaryAllocator mPrimary;
[[no_unique_address]] SecondaryAllocator mSecondary;
};
@@ -423,7 +430,7 @@
template <typename Allocator>
class IndirectAllocator {
public:
- static constexpr size_t alignment() { return Allocator::alignment(); }
+ static size_t alignment() { return Allocator::alignment(); }
explicit IndirectAllocator(const std::shared_ptr<Allocator>& allocator)
: mAllocator(allocator) {}
@@ -449,7 +456,7 @@
// a shared memory mapped anonymous file) as allocations.
class MemoryHeapBaseAllocator {
public:
- static constexpr size_t alignment() { return 4096; /* PAGE_SIZE */ }
+ static size_t alignment() { return kPageSize; }
static constexpr unsigned FLAGS = 0; // default flags
template <typename T>
@@ -475,5 +482,7 @@
// allocated by us, the underlying IMemoryHeap was a MemoryHeapBase
static_cast<MemoryHeapBase&>(*heap).dispose();
}
+ private:
+ static inline const size_t kPageSize = getpagesize();
};
} // namespace android::mediautils
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 0689083..3fdc6eb 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -200,7 +200,10 @@
name: "timerthread_tests",
defaults: ["libmediautils_tests_defaults"],
-
+ // TODO(b/270180838)
+ test_options: {
+ unit_test: false,
+ },
srcs: [
"TimerThread-test.cpp",
],
diff --git a/media/utils/tests/shared_memory_allocator_tests.cpp b/media/utils/tests/shared_memory_allocator_tests.cpp
index 11bc72a..c164cbd 100644
--- a/media/utils/tests/shared_memory_allocator_tests.cpp
+++ b/media/utils/tests/shared_memory_allocator_tests.cpp
@@ -19,21 +19,25 @@
#include <gtest/gtest.h>
#include <mediautils/SharedMemoryAllocator.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <utils/Log.h>
using namespace android;
using namespace android::mediautils;
namespace {
+const size_t kPageSize = getpagesize();
+constexpr size_t kMaxPageSize = 65536; // arm64 supports 4k, 16k and 64k pages
+
void validate_block(const AllocationType& block) {
ASSERT_TRUE(block != nullptr);
- memset(block->unsecurePointer(), 10, 4096);
+ memset(block->unsecurePointer(), 10, kPageSize);
EXPECT_EQ(*(static_cast<char*>(block->unsecurePointer()) + 100), static_cast<char>(10));
}
template <size_t N = 0, bool FatalOwn = true>
struct ValidateForwarding {
- static constexpr size_t alignment() { return 1337; }
+ static size_t alignment() { return 1337; }
bool owns(const AllocationType& allocation) const {
if (allocation == owned) return true;
@@ -48,9 +52,9 @@
static inline size_t deallocate_all_count = 0;
static inline const AllocationType owned =
- MemoryHeapBaseAllocator().allocate(BasicAllocRequest{4096});
+ MemoryHeapBaseAllocator().allocate(BasicAllocRequest{kMaxPageSize});
static inline const AllocationType not_owned =
- MemoryHeapBaseAllocator().allocate(BasicAllocRequest{4096});
+ MemoryHeapBaseAllocator().allocate(BasicAllocRequest{kMaxPageSize});
static inline const std::string dump_string = std::to_string(N) + "Test Dump Forwarding";
};
@@ -64,17 +68,14 @@
shared_allocator_impl::has_deallocate_all<SnoopingAllocator<MemoryHeapBaseAllocator>> ==
true);
static_assert(
- shared_allocator_impl::has_owns<
- PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>, SizePolicy<4096>>> ==
- true);
+ shared_allocator_impl::has_owns<PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
+ SizePolicy<kMaxPageSize>>> == true);
static_assert(
- shared_allocator_impl::has_dump<
- PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>, SizePolicy<4096>>> ==
- true);
-static_assert(
- shared_allocator_impl::has_deallocate_all<
- PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>, SizePolicy<4096>>> ==
- true);
+ shared_allocator_impl::has_dump<PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
+ SizePolicy<kMaxPageSize>>> == true);
+static_assert(shared_allocator_impl::has_deallocate_all<PolicyAllocator<
+ SnoopingAllocator<MemoryHeapBaseAllocator>, SizePolicy<kMaxPageSize>>> ==
+ true);
static_assert(shared_allocator_impl::has_owns<
FallbackAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
SnoopingAllocator<MemoryHeapBaseAllocator>>> == true);
@@ -93,7 +94,7 @@
const auto memory = allocator.allocate(BasicAllocRequest{500});
ASSERT_TRUE(memory != nullptr);
const auto fd = dup(memory->getMemory()->getHeapID());
- EXPECT_EQ(memory->size(), static_cast<unsigned>(4096));
+ EXPECT_EQ(memory->size(), static_cast<unsigned>(kPageSize));
EXPECT_EQ(memory->size(), memory->getMemory()->getSize());
validate_block(memory);
allocator.deallocate(memory);
@@ -108,7 +109,7 @@
}
TEST(shared_memory_allocator_tests, mheapbase_allocator_independence) {
- static_assert(MemoryHeapBaseAllocator::alignment() == 4096);
+ ASSERT_EQ(MemoryHeapBaseAllocator::alignment(), kPageSize);
MemoryHeapBaseAllocator allocator;
const auto first_memory = allocator.allocate(BasicAllocRequest{500});
const auto second_memory = allocator.allocate(BasicAllocRequest{500});
@@ -120,8 +121,8 @@
}
TEST(shared_memory_allocator_tests, snooping_allocator) {
- static_assert(SnoopingAllocator<ValidateForwarding<0>>::alignment() ==
- ValidateForwarding<0>::alignment());
+ ASSERT_EQ(SnoopingAllocator<ValidateForwarding<0>>::alignment(),
+ ValidateForwarding<0>::alignment());
SnoopingAllocator<MemoryHeapBaseAllocator> allocator{"allocator"};
const auto first_memory = allocator.allocate(NamedAllocRequest{{500}, "allocate_1"});
@@ -165,29 +166,29 @@
// TODO generic policy test
TEST(shared_memory_allocator_tests, size_policy_allocator_enforcement) {
PolicyAllocator allocator{MemoryHeapBaseAllocator{},
- SizePolicy<4096 * 7, 4096 * 2, 4096 * 4>{}};
+ SizePolicy<kMaxPageSize * 7, kMaxPageSize * 2, kMaxPageSize * 4>{}};
// Violate max size
- EXPECT_TRUE(allocator.allocate(BasicAllocRequest{4096 * 5}) == nullptr);
+ EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize * 5}) == nullptr);
// Violate min alloc size
- EXPECT_TRUE(allocator.allocate(BasicAllocRequest{4096}) == nullptr);
- const auto first_memory = allocator.allocate(BasicAllocRequest{4096 * 4});
+ EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize}) == nullptr);
+ const auto first_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 4});
validate_block(first_memory);
// Violate pool size
- EXPECT_TRUE(allocator.allocate(BasicAllocRequest{4096 * 4}) == nullptr);
- const auto second_memory = allocator.allocate(BasicAllocRequest{4096 * 3});
+ EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize * 4}) == nullptr);
+ const auto second_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 3});
validate_block(second_memory);
allocator.deallocate(second_memory);
// Check pool size update after deallocation
- const auto new_second_memory = allocator.allocate(BasicAllocRequest{4096 * 2});
+ const auto new_second_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 2});
validate_block(new_second_memory);
}
TEST(shared_memory_allocator_tests, indirect_allocator) {
- static_assert(IndirectAllocator<ValidateForwarding<0>>::alignment() ==
- ValidateForwarding<0>::alignment());
+ ASSERT_EQ(IndirectAllocator<ValidateForwarding<0>>::alignment(),
+ ValidateForwarding<0>::alignment());
const auto allocator_handle = std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>();
IndirectAllocator allocator{allocator_handle};
- const auto memory = allocator.allocate(NamedAllocRequest{{4096}, "allocation"});
+ const auto memory = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation"});
EXPECT_TRUE(allocator_handle->owns(memory));
EXPECT_TRUE(allocator_handle->getAllocations().size() == 1);
allocator.deallocate(memory);
@@ -199,35 +200,37 @@
// Test appropriate forwarding of allocator, deallocate
const auto primary_allocator =
std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>("allocator");
- PolicyAllocator allocator{IndirectAllocator(primary_allocator), SizePolicy<4096>{}};
- const auto memory = allocator.allocate(NamedAllocRequest{{4096}, "allocation"});
+ PolicyAllocator allocator{IndirectAllocator(primary_allocator), SizePolicy<kMaxPageSize>{}};
+ const auto memory = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation"});
EXPECT_TRUE(primary_allocator->owns(memory));
const auto& allocations = primary_allocator->getAllocations();
EXPECT_TRUE(allocations.size() == 1);
allocator.deallocate(memory);
EXPECT_TRUE(allocations.size() == 0);
- const auto memory2 = allocator.allocate(NamedAllocRequest{{4096}, "allocation_2"});
+ const auto memory2 = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation_2"});
EXPECT_TRUE(allocations.size() == 1);
EXPECT_TRUE(primary_allocator->owns(memory2));
allocator.deallocate(memory2);
EXPECT_FALSE(primary_allocator->owns(memory2));
EXPECT_TRUE(allocations.size() == 0);
// Test appropriate forwarding of own, dump, alignment, deallocate_all
- PolicyAllocator allocator2{ValidateForwarding<0>{}, SizePolicy<4096>{}};
+ PolicyAllocator allocator2{ValidateForwarding<0>{}, SizePolicy<kMaxPageSize>{}};
EXPECT_TRUE(allocator2.owns(ValidateForwarding<0>::owned));
EXPECT_FALSE(allocator2.owns(ValidateForwarding<0>::not_owned));
EXPECT_TRUE(allocator2.dump().find(ValidateForwarding<0>::dump_string) != std::string::npos);
- static_assert(decltype(allocator2)::alignment() == ValidateForwarding<0>::alignment());
+ ASSERT_EQ(decltype(allocator2)::alignment(), ValidateForwarding<0>::alignment());
size_t prev = ValidateForwarding<0>::deallocate_all_count;
allocator2.deallocate_all();
EXPECT_EQ(ValidateForwarding<0>::deallocate_all_count, prev + 1);
}
TEST(shared_memory_allocator_tests, snooping_allocator_nullptr) {
- SnoopingAllocator allocator{PolicyAllocator{MemoryHeapBaseAllocator{}, SizePolicy<4096 * 2>{}}};
- const auto memory = allocator.allocate(NamedAllocRequest{{3000}, "allocation_1"});
+ SnoopingAllocator allocator{
+ PolicyAllocator{MemoryHeapBaseAllocator{}, SizePolicy<kMaxPageSize * 2>{}}};
+ const auto memory = allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_1"});
validate_block(memory);
- ASSERT_TRUE(allocator.allocate(NamedAllocRequest{{5000}, "allocation_2"}) == nullptr);
+ ASSERT_TRUE(allocator.allocate(NamedAllocRequest{{kMaxPageSize * 3}, "allocation_2"}) ==
+ nullptr);
const auto& allocations = allocator.getAllocations();
EXPECT_EQ(allocations.size(), 1ul);
for (const auto& [key, val] : allocations) {
@@ -240,23 +243,26 @@
TEST(shared_memory_allocator_tests, fallback_allocator) {
// Construct Fallback Allocator
const auto primary_allocator = std::make_shared<
- SnoopingAllocator<PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<4096>>>>(
- PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<4096>>{}, "primary_allocator");
+ SnoopingAllocator<PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<kMaxPageSize>>>>(
+ PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<kMaxPageSize>>{},
+ "primary_allocator");
const auto secondary_allocator =
std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>("secondary_allocator");
FallbackAllocator fallback_allocator{SnoopingAllocator{IndirectAllocator{primary_allocator}},
SnoopingAllocator{IndirectAllocator{secondary_allocator}}};
- static_assert(decltype(fallback_allocator)::alignment() == 4096);
+ ASSERT_EQ(decltype(fallback_allocator)::alignment(), kPageSize);
// Basic Allocation Test
- const auto memory = fallback_allocator.allocate(NamedAllocRequest{{3000}, "allocation_1"});
+ const auto memory =
+ fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_1"});
validate_block(memory);
// Correct allocator selected
EXPECT_TRUE(fallback_allocator.owns(memory));
EXPECT_TRUE(primary_allocator->owns(memory));
EXPECT_FALSE(secondary_allocator->owns(memory));
// Test fallback allocation
- const auto memory2 = fallback_allocator.allocate(NamedAllocRequest{{3000}, "allocation_2"});
+ const auto memory2 =
+ fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_2"});
validate_block(memory2);
// Correct allocator selected
EXPECT_TRUE(fallback_allocator.owns(memory2));
@@ -276,7 +282,8 @@
EXPECT_TRUE(primary_allocator->getAllocations().size() == 0ul);
EXPECT_TRUE(secondary_allocator->getAllocations().size() == 1ul);
// Appropriate fallback after deallocation
- const auto memory3 = fallback_allocator.allocate(NamedAllocRequest{{3000}, "allocation_3"});
+ const auto memory3 =
+ fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_3"});
EXPECT_TRUE(fallback_allocator.owns(memory3));
EXPECT_TRUE(primary_allocator->owns(memory3));
EXPECT_FALSE(secondary_allocator->owns(memory3));
@@ -285,7 +292,8 @@
EXPECT_TRUE(secondary_allocator->getAllocations().size() == 1ul);
fallback_allocator.deallocate(memory2);
EXPECT_TRUE(secondary_allocator->getAllocations().size() == 0ul);
- const auto memory4 = fallback_allocator.allocate(NamedAllocRequest{{3000}, "allocation_4"});
+ const auto memory4 =
+ fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_4"});
EXPECT_TRUE(fallback_allocator.owns(memory4));
EXPECT_FALSE(primary_allocator->owns(memory4));
EXPECT_TRUE(secondary_allocator->owns(memory4));
@@ -311,7 +319,7 @@
EXPECT_FALSE(forward_test.owns(Alloc1::not_owned));
EXPECT_FALSE(forward_test.owns(Alloc2::not_owned));
// Test alignment forwarding
- static_assert(FallbackAllocator<Alloc1, Alloc2>::alignment() == Alloc1::alignment());
+ ASSERT_EQ(decltype(forward_test)::alignment(), Alloc1::alignment());
// Test deallocate_all forwarding
size_t prev1 = Alloc1::deallocate_all_count;
size_t prev2 = Alloc2::deallocate_all_count;
@@ -343,8 +351,8 @@
}
EXPECT_EQ(allocations.size(), 0ul);
// Test forwarding
- static_assert(ScopedAllocator<ValidateForwarding<0>>::alignment() ==
- ValidateForwarding<0>::alignment());
+ ASSERT_EQ(ScopedAllocator<ValidateForwarding<0>>::alignment(),
+ ValidateForwarding<0>::alignment());
ScopedAllocator<ValidateForwarding<0>> forwarding{};
EXPECT_EQ(forwarding.dump(), ValidateForwarding<0>::dump_string);
}
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 0cd6243..b001a34 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -138,10 +138,58 @@
],
}
-cc_library_shared {
+cc_defaults {
+ name: "libaudioflinger_dependencies",
+
+ shared_libs: [
+ "audioflinger-aidl-cpp",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "effect-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ "libactivitymanager_aidl",
+ "libaudioflinger_datapath",
+ "libaudioflinger_fastpath",
+ "libaudioflinger_timing",
+ "libaudioflinger_utils",
+ "libaudiofoundation",
+ "libaudiohal",
+ "libaudioprocessing",
+ "libaudioutils",
+ "libaudioutils_nonvndk",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libbinder",
+ "libbinder_ndk",
+ "libaudioclient",
+ "libaudiomanager",
+ "libmediametrics",
+ "libmediautils",
+ "libnbaio",
+ "libnblog",
+ "libpermission",
+ "libpowermanager",
+ "libmemunreachable",
+ "libmedia_helper",
+ "libshmemcompat",
+ "libsounddose",
+ "libvibrator",
+ "packagemanager_aidl-cpp",
+ ],
+
+ static_libs: [
+ "libmedialogservice",
+ "libaudiospdif",
+ ],
+}
+
+
+cc_library {
name: "libaudioflinger",
defaults: [
+ "libaudioflinger_dependencies",
"latest_android_media_audio_common_types_cpp_shared",
"latest_android_hardware_audio_core_sounddose_ndk_shared",
"audioflinger_flags_defaults",
@@ -164,44 +212,6 @@
"frameworks/av/services/medialog",
],
- shared_libs: [
- "audioflinger-aidl-cpp",
- "audioclient-types-aidl-cpp",
- "av-types-aidl-cpp",
- "effect-aidl-cpp",
- "libaudioclient_aidl_conversion",
- "libactivitymanager_aidl",
- "libaudioflinger_datapath",
- "libaudioflinger_fastpath",
- "libaudioflinger_timing",
- "libaudioflinger_utils",
- "libaudiofoundation",
- "libaudiohal",
- "libaudioprocessing",
- "libaudiospdif",
- "libaudioutils",
- "libcutils",
- "libutils",
- "liblog",
- "libbinder",
- "libbinder_ndk",
- "libaudioclient",
- "libaudiomanager",
- "libmedialogservice",
- "libmediametrics",
- "libmediautils",
- "libnbaio",
- "libnblog",
- "libpermission",
- "libpowermanager",
- "libmemunreachable",
- "libmedia_helper",
- "libshmemcompat",
- "libsounddose",
- "libvibrator",
- "packagemanager_aidl-cpp",
- ],
-
static_libs: [
"libcpustats",
"libpermission",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a98011d..e0b907f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1080,7 +1080,7 @@
input.sharedBuffer, sessionId, &output.flags,
callingPid, adjAttributionSource, input.clientInfo.clientTid,
&lStatus, portId, input.audioTrackCallback, isSpatialized,
- isBitPerfect);
+ isBitPerfect, &output.afTrackFlags);
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
@@ -2082,6 +2082,9 @@
}
if (removed) {
removedEffects = purgeStaleEffects_l();
+ std::vector< sp<IAfEffectModule> > removedOrphanEffects = purgeOrphanEffectChains_l();
+ removedEffects.insert(removedEffects.end(), removedOrphanEffects.begin(),
+ removedOrphanEffects.end());
}
}
for (auto& effect : removedEffects) {
@@ -2089,7 +2092,8 @@
}
}
-void AudioFlinger::ioConfigChanged(audio_io_config_event_t event,
+// Hold either AudioFlinger::mutex or ThreadBase::mutex
+void AudioFlinger::ioConfigChanged_l(audio_io_config_event_t event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid) {
media::AudioIoConfigEvent eventAidl = VALUE_OR_FATAL(
@@ -2943,7 +2947,7 @@
latencyMs = playbackThread->latency();
// notify client processes of the new output creation
- playbackThread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
+ playbackThread->ioConfigChanged_l(AUDIO_OUTPUT_OPENED);
// the first primary output opened designates the primary hw device if no HW module
// named "primary" was already loaded.
@@ -2957,7 +2961,7 @@
mHardwareStatus = AUDIO_HW_IDLE;
}
} else {
- thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
+ thread->ioConfigChanged_l(AUDIO_OUTPUT_OPENED);
}
response->output = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
response->config = VALUE_OR_RETURN_STATUS(
@@ -2990,7 +2994,7 @@
thread->addOutputTrack(thread2);
mPlaybackThreads.add(id, thread);
// notify client processes of the new output creation
- thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
+ thread->ioConfigChanged_l(AUDIO_OUTPUT_OPENED);
return id;
}
@@ -3050,7 +3054,7 @@
mMmapThreads.removeItem(output);
ALOGD("closing mmapThread %p", mmapThread.get());
}
- ioConfigChanged(AUDIO_OUTPUT_CLOSED, sp<AudioIoDescriptor>::make(output));
+ ioConfigChanged_l(AUDIO_OUTPUT_CLOSED, sp<AudioIoDescriptor>::make(output));
mPatchPanel->notifyStreamClosed(output);
}
// The thread entity (active unit of execution) is no longer running here,
@@ -3153,7 +3157,7 @@
if (thread != 0) {
// notify client processes of the new input creation
- thread->ioConfigChanged(AUDIO_INPUT_OPENED);
+ thread->ioConfigChanged_l(AUDIO_INPUT_OPENED);
return NO_ERROR;
}
return NO_INIT;
@@ -3312,7 +3316,7 @@
dumpToThreadLog_l(mmapThread);
mMmapThreads.removeItem(input);
}
- ioConfigChanged(AUDIO_INPUT_CLOSED, sp<AudioIoDescriptor>::make(input));
+ ioConfigChanged_l(AUDIO_INPUT_CLOSED, sp<AudioIoDescriptor>::make(input));
}
// FIXME: calling thread->exit() without mutex() held should not be needed anymore now that
// we have a different lock for notification client
@@ -3540,6 +3544,42 @@
return removedEffects;
}
+std::vector< sp<IAfEffectModule> > AudioFlinger::purgeOrphanEffectChains_l()
+{
+ ALOGV("purging stale effects from orphan chains");
+ std::vector< sp<IAfEffectModule> > removedEffects;
+ for (size_t index = 0; index < mOrphanEffectChains.size(); index++) {
+ sp<IAfEffectChain> chain = mOrphanEffectChains.valueAt(index);
+ audio_session_t session = mOrphanEffectChains.keyAt(index);
+ if (session == AUDIO_SESSION_OUTPUT_MIX || session == AUDIO_SESSION_DEVICE
+ || session == AUDIO_SESSION_OUTPUT_STAGE) {
+ continue;
+ }
+ size_t numSessionRefs = mAudioSessionRefs.size();
+ bool found = false;
+ for (size_t k = 0; k < numSessionRefs; k++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(k);
+ if (ref->mSessionid == session) {
+ ALOGV(" session %d still exists for %d with %d refs", session, ref->mPid,
+ ref->mCnt);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ for (size_t i = 0; i < chain->numberOfEffects(); i++) {
+ sp<IAfEffectModule> effect = chain->getEffectModule(i);
+ removedEffects.push_back(effect);
+ }
+ }
+ }
+ for (auto& effect : removedEffects) {
+ effect->unPin();
+ updateOrphanEffectChains_l(effect);
+ }
+ return removedEffects;
+}
+
// dumpToThreadLog_l() must be called with AudioFlinger::mutex() held
void AudioFlinger::dumpToThreadLog_l(const sp<IAfThreadBase> &thread)
{
@@ -3692,7 +3732,8 @@
return {};
}
- return thread->outDeviceTypes();
+ audio_utils::lock_guard l(thread->mutex());
+ return thread->outDeviceTypes_l();
}
IAfPlaybackThread* AudioFlinger::fastPlaybackThread_l() const
@@ -4269,7 +4310,7 @@
response->id = idOut;
response->enabled = enabledOut != 0;
- response->effect = handle->asIEffect();
+ response->effect = handle.get() ? handle->asIEffect() : nullptr;
response->desc = VALUE_OR_RETURN_STATUS(
legacy2aidl_effect_descriptor_t_EffectDescriptor(descOut));
@@ -4277,24 +4318,42 @@
return lStatus;
}
-status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
+status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ audio_io_handle_t dstIo)
+NO_THREAD_SAFETY_ANALYSIS
{
- ALOGV("%s() session %d, srcOutput %d, dstOutput %d",
- __func__, sessionId, srcOutput, dstOutput);
+ ALOGV("%s() session %d, srcIo %d, dstIo %d", __func__, sessionId, srcIo, dstIo);
audio_utils::lock_guard _l(mutex());
- if (srcOutput == dstOutput) {
- ALOGW("%s() same dst and src outputs %d", __func__, dstOutput);
+ if (srcIo == dstIo) {
+ ALOGW("%s() same dst and src outputs %d", __func__, dstIo);
return NO_ERROR;
}
- IAfPlaybackThread* const srcThread = checkPlaybackThread_l(srcOutput);
+ IAfRecordThread* const srcRecordThread = checkRecordThread_l(srcIo);
+ IAfRecordThread* const dstRecordThread = checkRecordThread_l(dstIo);
+ if (srcRecordThread != nullptr || dstRecordThread != nullptr) {
+ if (srcRecordThread != nullptr) {
+ srcRecordThread->mutex().lock();
+ }
+ if (dstRecordThread != nullptr) {
+ dstRecordThread->mutex().lock();
+ }
+ status_t ret = moveEffectChain_ll(sessionId, srcRecordThread, dstRecordThread);
+ if (srcRecordThread != nullptr) {
+ srcRecordThread->mutex().unlock();
+ }
+ if (dstRecordThread != nullptr) {
+ dstRecordThread->mutex().unlock();
+ }
+ return ret;
+ }
+ IAfPlaybackThread* const srcThread = checkPlaybackThread_l(srcIo);
if (srcThread == nullptr) {
- ALOGW("%s() bad srcOutput %d", __func__, srcOutput);
+ ALOGW("%s() bad srcIo %d", __func__, srcIo);
return BAD_VALUE;
}
- IAfPlaybackThread* const dstThread = checkPlaybackThread_l(dstOutput);
+ IAfPlaybackThread* const dstThread = checkPlaybackThread_l(dstIo);
if (dstThread == nullptr) {
- ALOGW("%s() bad dstOutput %d", __func__, dstOutput);
+ ALOGW("%s() bad dstIo %d", __func__, dstIo);
return BAD_VALUE;
}
@@ -4430,6 +4489,48 @@
return status;
}
+
+// moveEffectChain_ll must be called with both srcThread (if not null) and dstThread (if not null)
+// mutex()s held
+status_t AudioFlinger::moveEffectChain_ll(audio_session_t sessionId,
+ IAfRecordThread* srcThread, IAfRecordThread* dstThread)
+{
+ sp<IAfEffectChain> chain = nullptr;
+ if (srcThread != 0) {
+ const Vector<sp<IAfEffectChain>> effectChains = srcThread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ if (effectChains[i]->sessionId() == sessionId) {
+ chain = effectChains[i];
+ break;
+ }
+ }
+ ALOGV_IF(effectChains.size() == 0, "%s: no effect chain on io=%d", __func__,
+ srcThread->id());
+ if (chain == nullptr) {
+ ALOGE("%s wrong session id %d", __func__, sessionId);
+ return BAD_VALUE;
+ }
+ ALOGV("%s: removing effect chain for session=%d io=%d", __func__, sessionId,
+ srcThread->id());
+ srcThread->removeEffectChain_l(chain);
+ } else {
+ chain = getOrphanEffectChain_l(sessionId);
+ if (chain == nullptr) {
+ ALOGE("%s: no orphan effect chain found for session=%d", __func__, sessionId);
+ return BAD_VALUE;
+ }
+ }
+ if (dstThread != 0) {
+ ALOGV("%s: adding effect chain for session=%d on io=%d", __func__, sessionId,
+ dstThread->id());
+ dstThread->addEffectChain_l(chain);
+ return NO_ERROR;
+ }
+ ALOGV("%s: parking to orphan effect chain for session=%d", __func__, sessionId);
+ putOrphanEffectChain_l(chain);
+ return NO_ERROR;
+}
+
status_t AudioFlinger::moveAuxEffectToIo(int EffectId,
const sp<IAfPlaybackThread>& dstThread, sp<IAfPlaybackThread>* srcThread)
{
@@ -4486,8 +4587,9 @@
}
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- sp<IAfEffectChain> ec =
- mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ const auto thread = mPlaybackThreads.valueAt(i);
+ audio_utils::lock_guard l(thread->mutex());
+ const sp<IAfEffectChain> ec = thread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
if (ec != 0 && ec->isNonOffloadableEnabled()) {
return true;
}
@@ -4543,6 +4645,11 @@
bool AudioFlinger::updateOrphanEffectChains(const sp<IAfEffectModule>& effect)
{
audio_utils::lock_guard _l(mutex());
+ return updateOrphanEffectChains_l(effect);
+}
+
+bool AudioFlinger::updateOrphanEffectChains_l(const sp<IAfEffectModule>& effect)
+{
audio_session_t session = effect->sessionId();
ssize_t index = mOrphanEffectChains.indexOfKey(session);
ALOGV("updateOrphanEffectChains session %d index %zd", session, index);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 2c34144..6af8015 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -338,7 +338,8 @@
// ----- begin IAfThreadCallback interface
- bool isNonOffloadableGlobalEffectEnabled_l() const final REQUIRES(mutex());
+ bool isNonOffloadableGlobalEffectEnabled_l() const final
+ REQUIRES(mutex()) EXCLUDES_ThreadBase_Mutex;
bool btNrecIsOff() const final { return mBtNrecIsOff.load(); }
float masterVolume_l() const final REQUIRES(mutex());
bool masterMute_l() const final REQUIRES(mutex());
@@ -383,7 +384,8 @@
const audioflinger::SyncEventCallback& callBack,
const wp<IAfTrackBase>& cookie) final EXCLUDES_AudioFlinger_Mutex;
- void ioConfigChanged(audio_io_config_event_t event,
+ // Hold either AudioFlinger::mutex or ThreadBase::mutex
+ void ioConfigChanged_l(audio_io_config_event_t event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0) final EXCLUDES_AudioFlinger_ClientMutex;
void onNonOffloadableGlobalEffectEnable() final EXCLUDES_AudioFlinger_Mutex;
@@ -547,6 +549,10 @@
// used by IAfDeviceEffectManagerCallback, IAfPatchPanelCallback, IAfThreadCallback
audio_unique_id_t nextUniqueId(audio_unique_id_use_t use) final;
+ status_t moveEffectChain_ll(audio_session_t sessionId,
+ IAfRecordThread* srcThread, IAfRecordThread* dstThread)
+ REQUIRES(mutex(), audio_utils::ThreadBase_Mutex);
+
// return thread associated with primary hardware device, or NULL
DeviceTypeSet primaryOutputDevice_l() const REQUIRES(mutex());
@@ -579,6 +585,9 @@
std::vector< sp<IAfEffectModule> > purgeStaleEffects_l() REQUIRES(mutex());
+ std::vector< sp<IAfEffectModule> > purgeOrphanEffectChains_l() REQUIRES(mutex());
+ bool updateOrphanEffectChains_l(const sp<IAfEffectModule>& effect) REQUIRES(mutex());
+
void broadcastParametersToRecordThreads_l(const String8& keyValuePairs) REQUIRES(mutex());
void forwardParametersToDownstreamPatches_l(
audio_io_handle_t upStream, const String8& keyValuePairs,
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 0a7be75..201d147 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -60,18 +60,37 @@
__func__, handle, patch.mHalHandle,
patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
audio_utils::lock_guard _l(mutex());
- for (auto& effect : mDeviceEffects) {
- status_t status = effect.second->onCreatePatch(handle, patch);
- ALOGV("%s Effect onCreatePatch status %d", __func__, status);
- ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status);
+ for (auto& effectProxies : mDeviceEffects) {
+ for (auto& effect : effectProxies.second) {
+ const status_t status = effect->onCreatePatch(handle, patch);
+ ALOGV("%s Effect onCreatePatch status %d", __func__, status);
+ ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status);
+ }
}
}
void DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
audio_utils::lock_guard _l(mutex());
- for (auto& effect : mDeviceEffects) {
- effect.second->onReleasePatch(handle);
+ for (auto& effectProxies : mDeviceEffects) {
+ for (auto& effect : effectProxies.second) {
+ effect->onReleasePatch(handle);
+ }
+ }
+}
+
+void DeviceEffectManager::onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle, const IAfPatchPanel::Patch& patch) {
+ ALOGV("%s oldhandle %d newHandle %d mHalHandle %d device sink %08x",
+ __func__, oldHandle, newHandle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+ audio_utils::lock_guard _l(mutex());
+ for (auto& effectProxies : mDeviceEffects) {
+ for (auto& effect : effectProxies.second) {
+ const status_t status = effect->onUpdatePatch(oldHandle, newHandle, patch);
+ ALOGV("%s Effect onUpdatePatch status %d", __func__, status);
+ ALOGW_IF(status != NO_ERROR, "%s onUpdatePatch error %d", __func__, status);
+ }
}
}
@@ -87,6 +106,7 @@
bool probe,
bool notifyFramesProcessed) {
sp<IAfDeviceEffectProxy> effect;
+ std::vector<sp<IAfDeviceEffectProxy>> effectsForDevice = {};
sp<IAfEffectHandle> handle;
status_t lStatus;
@@ -100,12 +120,21 @@
audio_utils::lock_guard _l(mutex());
auto iter = mDeviceEffects.find(device);
if (iter != mDeviceEffects.end()) {
- effect = iter->second;
- } else {
+ effectsForDevice = iter->second;
+ for (const auto& iterEffect : effectsForDevice) {
+ if (memcmp(&iterEffect->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) ==
+ 0) {
+ effect = iterEffect;
+ break;
+ }
+ }
+ }
+ if (effect == nullptr) {
effect = IAfDeviceEffectProxy::create(device, mMyCallback,
descriptor,
mAfDeviceEffectManagerCallback->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT),
notifyFramesProcessed);
+ effectsForDevice.push_back(effect);
}
// create effect handle and connect it to effect module
handle = IAfEffectHandle::create(
@@ -119,7 +148,8 @@
lStatus = NO_ERROR;
}
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
- mDeviceEffects.emplace(device, effect);
+ mDeviceEffects.erase(device);
+ mDeviceEffects.emplace(device, effectsForDevice);
}
}
}
@@ -187,8 +217,10 @@
String8 outStr;
outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
::android::toString(iter.first.mType).c_str(), iter.first.getAddress());
- write(fd, outStr.c_str(), outStr.size());
- iter.second->dump2(fd, 4);
+ for (const auto& effect : iter.second) {
+ write(fd, outStr.c_str(), outStr.size());
+ effect->dump2(fd, 4);
+ }
}
if (locked) {
@@ -199,7 +231,20 @@
size_t DeviceEffectManager::removeEffect(const sp<IAfDeviceEffectProxy>& effect)
{
audio_utils::lock_guard _l(mutex());
- mDeviceEffects.erase(effect->device());
+ const auto& iter = mDeviceEffects.find(effect->device());
+ if (iter != mDeviceEffects.end()) {
+ const auto& iterEffect = std::find_if(
+ iter->second.begin(), iter->second.end(), [&effect](const auto& effectProxy) {
+ return memcmp(&effectProxy->desc().uuid, &effect->desc().uuid,
+ sizeof(effect_uuid_t)) == 0;
+ });
+ if (iterEffect != iter->second.end()) {
+ iter->second.erase(iterEffect);
+ if (iter->second.empty()) {
+ mDeviceEffects.erase(effect->device());
+ }
+ }
+ }
return mDeviceEffects.size();
}
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 6a5c889..faba806 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -75,6 +75,9 @@
EXCLUDES_DeviceEffectManager_Mutex;
void onReleaseAudioPatch(audio_patch_handle_t handle) final
EXCLUDES_DeviceEffectManager_Mutex;
+ void onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle, const IAfPatchPanel::Patch& patch) final
+ EXCLUDES_DeviceEffectManager_Mutex;
private:
static status_t checkEffectCompatibility(const effect_descriptor_t *desc);
@@ -85,7 +88,8 @@
mutable audio_utils::mutex mMutex;
const sp<IAfDeviceEffectManagerCallback> mAfDeviceEffectManagerCallback;
const sp<DeviceEffectManagerCallback> mMyCallback;
- std::map<AudioDeviceTypeAddr, sp<IAfDeviceEffectProxy>> mDeviceEffects GUARDED_BY(mutex());
+ std::map<AudioDeviceTypeAddr, std::vector<sp<IAfDeviceEffectProxy>>>
+ mDeviceEffects GUARDED_BY(mutex());
};
class DeviceEffectManagerCallback : public EffectCallbackInterface {
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 0f3e130..589050c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -514,11 +514,12 @@
if (!locked) {
result.append("\t\tCould not lock Fx mutex:\n");
}
-
- result.append("\t\tSession State Registered Enabled Suspended:\n");
- result.appendFormat("\t\t%05d %03d %s %s %s\n",
- mSessionId, mState, mPolicyRegistered ? "y" : "n",
- mPolicyEnabled ? "y" : "n", mSuspended ? "y" : "n");
+ bool isInternal = isInternal_l();
+ result.append("\t\tSession State Registered Internal Enabled Suspended:\n");
+ result.appendFormat("\t\t%05d %03d %s %s %s %s\n",
+ mSessionId, mState, mPolicyRegistered ? "y" : "n", isInternal ? "y" : "n",
+ ((isInternal && isEnabled()) || (!isInternal && mPolicyEnabled)) ? "y" : "n",
+ mSuspended ? "y" : "n");
result.append("\t\tDescriptor:\n");
char uuidStr[64];
@@ -1004,8 +1005,9 @@
// mConfig.outputCfg.buffer.frameCount cannot be zero.
mMaxDisableWaitCnt = (uint32_t)std::max(
(uint64_t)1, // mMaxDisableWaitCnt must be greater than zero.
- (uint64_t)MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate
- / ((uint64_t)1000 * mConfig.outputCfg.buffer.frameCount));
+ (uint64_t)mConfig.outputCfg.buffer.frameCount == 0 ? 1
+ : (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate
+ / ((uint64_t)1000 * mConfig.outputCfg.buffer.frameCount)));
exit:
// TODO: consider clearing mConfig on error.
@@ -3084,7 +3086,10 @@
return t->sampleRate();
}
-audio_channel_mask_t EffectChain::EffectCallback::inChannelMask(int id) const {
+audio_channel_mask_t EffectChain::EffectCallback::inChannelMask(int id) const
+NO_THREAD_SAFETY_ANALYSIS
+// calling function 'hasAudioSession_l' requires holding mutex 'ThreadBase_Mutex' exclusively
+{
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
return AUDIO_CHANNEL_NONE;
@@ -3120,7 +3125,10 @@
return audio_channel_count_from_out_mask(inChannelMask(id));
}
-audio_channel_mask_t EffectChain::EffectCallback::outChannelMask() const {
+audio_channel_mask_t EffectChain::EffectCallback::outChannelMask() const
+NO_THREAD_SAFETY_ANALYSIS
+// calling function 'hasAudioSession_l' requires holding mutex 'ThreadBase_Mutex' exclusively
+{
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
return AUDIO_CHANNEL_NONE;
@@ -3316,6 +3324,23 @@
return status;
}
+status_t DeviceEffectProxy::onUpdatePatch(audio_patch_handle_t oldPatchHandle,
+ audio_patch_handle_t newPatchHandle,
+ const IAfPatchPanel::Patch& /* patch */) {
+ status_t status = NAME_NOT_FOUND;
+ ALOGV("%s", __func__);
+ audio_utils::lock_guard _l(proxyMutex());
+ if (mEffectHandles.find(oldPatchHandle) != mEffectHandles.end()) {
+ ALOGV("%s replacing effect from handle %d to handle %d", __func__, oldPatchHandle,
+ newPatchHandle);
+ sp<IAfEffectHandle> effect = mEffectHandles.at(oldPatchHandle);
+ mEffectHandles.erase(oldPatchHandle);
+ mEffectHandles.emplace(newPatchHandle, effect);
+ status = NO_ERROR;
+ }
+ return status;
+}
+
status_t DeviceEffectProxy::onCreatePatch(
audio_patch_handle_t patchHandle, const IAfPatchPanel::Patch& patch) {
status_t status = NAME_NOT_FOUND;
@@ -3329,6 +3354,9 @@
}
if (status == NO_ERROR || status == ALREADY_EXISTS) {
audio_utils::lock_guard _l(proxyMutex());
+ size_t erasedHandle = mEffectHandles.erase(patchHandle);
+ ALOGV("%s %s effecthandle %p for patch %d",
+ __func__, (erasedHandle == 0 ? "adding" : "replacing"), handle.get(), patchHandle);
mEffectHandles.emplace(patchHandle, handle);
}
ALOGW_IF(status == BAD_VALUE,
@@ -3364,18 +3392,21 @@
if (mDescriptor.flags & EFFECT_FLAG_HW_ACC_TUNNEL) {
audio_utils::lock_guard _l(proxyMutex());
- mDevicePort = *port;
- mHalEffect = new EffectModule(mMyCallback,
+ if (mHalEffect != nullptr && mDevicePort.id == port->id) {
+ ALOGV("%s reusing HAL effect", __func__);
+ } else {
+ mDevicePort = *port;
+ mHalEffect = new EffectModule(mMyCallback,
const_cast<effect_descriptor_t *>(&mDescriptor),
mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE,
false /* pinned */, port->id);
- if (audio_is_input_device(mDevice.mType)) {
- mHalEffect->setInputDevice(mDevice);
- } else {
- mHalEffect->setDevices({mDevice});
+ if (audio_is_input_device(mDevice.mType)) {
+ mHalEffect->setInputDevice(mDevice);
+ } else {
+ mHalEffect->setDevices({mDevice});
+ }
+ mHalEffect->configure();
}
- mHalEffect->configure();
-
*handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/,
mNotifyFramesProcessed);
status = (*handle)->initCheck();
@@ -3463,6 +3494,23 @@
return mManagerCallback->removeEffectFromHal(&mDevicePort, effect);
}
+status_t DeviceEffectProxy::command(
+ int32_t cmdCode, const std::vector<uint8_t>& cmdData, int32_t maxReplySize,
+ std::vector<uint8_t>* reply) {
+ audio_utils::lock_guard _l(proxyMutex());
+ status_t status = EffectBase::command(cmdCode, cmdData, maxReplySize, reply);
+ if (status == NO_ERROR) {
+ for (auto& handle : mEffectHandles) {
+ sp<IAfEffectBase> effect = handle.second->effect().promote();
+ if (effect != nullptr) {
+ status = effect->command(cmdCode, cmdData, maxReplySize, reply);
+ }
+ }
+ }
+ ALOGV("%s status %d", __func__, status);
+ return status;
+}
+
bool DeviceEffectProxy::isOutput() const {
if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE) {
return mDevicePort.role == AUDIO_PORT_ROLE_SINK;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 6b1d9cf..2ece5dc 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -488,8 +488,10 @@
bool isBitPerfectCompatible() const final;
// isCompatibleWithThread_l() must be called with thread->mutex() held
- bool isCompatibleWithThread_l(const sp<IAfThreadBase>& thread) const final;
+ bool isCompatibleWithThread_l(const sp<IAfThreadBase>& thread) const final
+ REQUIRES(audio_utils::ThreadBase_Mutex);
+ // Requires either IAfThreadBase::mutex() or EffectChain::mutex() held
bool containsHapticGeneratingEffect_l() final;
void setHapticIntensity_l(int id, os::HapticScale intensity) final;
@@ -670,6 +672,9 @@
status_t onCreatePatch(audio_patch_handle_t patchHandle,
const IAfPatchPanel::Patch& patch) final;
+ status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle, audio_patch_handle_t newPatchHandle,
+ const IAfPatchPanel::Patch& patch) final;
+
void onReleasePatch(audio_patch_handle_t patchHandle) final;
size_t removeEffect(const sp<IAfEffectModule>& effect) final;
@@ -683,6 +688,11 @@
audio_channel_mask_t channelMask() const final;
uint32_t channelCount() const final;
+ status_t command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxReplySize,
+ std::vector<uint8_t>* reply) final;
+
void dump2(int fd, int spaces) const final;
private:
diff --git a/services/audioflinger/IAfEffect.h b/services/audioflinger/IAfEffect.h
index ea0c6d9..8c5bc4b 100644
--- a/services/audioflinger/IAfEffect.h
+++ b/services/audioflinger/IAfEffect.h
@@ -125,6 +125,11 @@
virtual sp<IAfEffectModule> asEffectModule() = 0;
virtual sp<IAfDeviceEffectProxy> asDeviceEffectProxy() = 0;
+ virtual status_t command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxReplySize,
+ std::vector<uint8_t>* reply) = 0;
+
virtual void dump(int fd, const Vector<String16>& args) const = 0;
private:
@@ -133,11 +138,6 @@
virtual void setSuspended(bool suspended) = 0;
virtual bool suspended() const = 0;
- virtual status_t command(int32_t cmdCode,
- const std::vector<uint8_t>& cmdData,
- int32_t maxReplySize,
- std::vector<uint8_t>* reply) = 0;
-
virtual ssize_t disconnectHandle(IAfEffectHandle *handle, bool unpinIfLast) = 0;
virtual ssize_t removeHandle_l(IAfEffectHandle *handle) = 0;
virtual IAfEffectHandle* controlHandle_l() = 0;
@@ -360,6 +360,9 @@
virtual status_t onCreatePatch(
audio_patch_handle_t patchHandle,
const IAfPatchPanel::Patch& patch) = 0;
+ virtual status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle,
+ audio_patch_handle_t newPatchHandle,
+ const IAfPatchPanel::Patch& patch) = 0;
virtual void onReleasePatch(audio_patch_handle_t patchHandle) = 0;
virtual void dump2(int fd, int spaces) const = 0; // TODO(b/291319101) naming?
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index fc2f805..7084be9 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -70,7 +70,7 @@
virtual audio_utils::mutex& mutex() const
RETURN_CAPABILITY(audio_utils::AudioFlinger_Mutex) = 0;
virtual bool isNonOffloadableGlobalEffectEnabled_l() const
- REQUIRES(mutex()) = 0; // Tracks
+ REQUIRES(mutex()) EXCLUDES_ThreadBase_Mutex = 0; // Tracks
virtual audio_unique_id_t nextUniqueId(audio_unique_id_use_t use) = 0;
virtual bool btNrecIsOff() const = 0;
virtual float masterVolume_l() const
@@ -110,7 +110,8 @@
const wp<IAfTrackBase>& cookie)
EXCLUDES_AudioFlinger_Mutex = 0;
- virtual void ioConfigChanged(audio_io_config_event_t event,
+ // Hold either AudioFlinger::mutex or ThreadBase::mutex
+ virtual void ioConfigChanged_l(audio_io_config_event_t event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0) EXCLUDES_AudioFlinger_ClientMutex = 0;
virtual void onNonOffloadableGlobalEffectEnable() EXCLUDES_AudioFlinger_Mutex = 0;
@@ -140,7 +141,7 @@
static bool isValidPcmSinkFormat(audio_format_t format);
virtual status_t readyToRun() = 0;
- virtual void clearPowerManager() = 0;
+ virtual void clearPowerManager() EXCLUDES_ThreadBase_Mutex = 0;
virtual status_t initCheck() const = 0;
virtual type_t type() const = 0;
virtual bool isDuplicating() const = 0;
@@ -156,21 +157,23 @@
virtual size_t frameCount() const = 0;
virtual audio_channel_mask_t hapticChannelMask() const = 0;
virtual uint32_t hapticChannelCount() const = 0;
- virtual uint32_t latency_l() const = 0;
- virtual void setVolumeForOutput_l(float left, float right) const = 0;
+ virtual uint32_t latency_l() const = 0; // NO_THREAD_SAFETY_ANALYSIS
+ virtual void setVolumeForOutput_l(float left, float right) const REQUIRES(mutex()) = 0;
// Return's the HAL's frame count i.e. fast mixer buffer size.
virtual size_t frameCountHAL() const = 0;
virtual size_t frameSize() const = 0;
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
- virtual void exit() = 0;
- virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status) = 0;
- virtual status_t setParameters(const String8& keyValuePairs) = 0;
- virtual String8 getParameters(const String8& keys) = 0;
- virtual void ioConfigChanged(
+ virtual void exit() EXCLUDES_ThreadBase_Mutex = 0;
+ virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status)
+ REQUIRES(mutex()) = 0;
+ virtual status_t setParameters(const String8& keyValuePairs) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual String8 getParameters(const String8& keys) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void ioConfigChanged_l(
audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) = 0;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE)
+ /* holds either AF::mutex or TB::mutex */ = 0;
// sendConfigEvent_l() must be called with ThreadBase::mLock held
// Can temporarily release the lock if waiting for a reply from
@@ -178,38 +181,53 @@
// status_t sendConfigEvent_l(sp<ConfigEvent>& event);
virtual void sendIoConfigEvent(
audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) = 0;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) EXCLUDES_ThreadBase_Mutex = 0;
virtual void sendIoConfigEvent_l(
audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) = 0;
- virtual void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio, bool forApp) = 0;
- virtual void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio, bool forApp) = 0;
- virtual status_t sendSetParameterConfigEvent_l(const String8& keyValuePair) = 0;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) REQUIRES(mutex()) = 0;
+ virtual void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio, bool forApp)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio, bool forApp)
+ REQUIRES(mutex()) = 0;
+ virtual status_t sendSetParameterConfigEvent_l(const String8& keyValuePair)
+ REQUIRES(mutex()) = 0;
virtual status_t sendCreateAudioPatchConfigEvent(
- const struct audio_patch* patch, audio_patch_handle_t* handle) = 0;
- virtual status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle) = 0;
+ const struct audio_patch* patch, audio_patch_handle_t* handle)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle)
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual status_t sendUpdateOutDeviceConfigEvent(
- const DeviceDescriptorBaseVector& outDevices) = 0;
- virtual void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs) = 0;
- virtual void sendCheckOutputStageEffectsEvent() = 0;
- virtual void sendCheckOutputStageEffectsEvent_l() = 0;
- virtual void sendHalLatencyModesChangedEvent_l() = 0;
+ const DeviceDescriptorBaseVector& outDevices) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs)
+ REQUIRES(mutex()) = 0;
+ virtual void sendCheckOutputStageEffectsEvent() EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void sendCheckOutputStageEffectsEvent_l()
+ REQUIRES(mutex()) = 0;
+ virtual void sendHalLatencyModesChangedEvent_l()
+ REQUIRES(mutex()) = 0;
- virtual void processConfigEvents_l() = 0;
- virtual void setCheckOutputStageEffects() = 0;
- virtual void cacheParameters_l() = 0;
+ virtual void processConfigEvents_l()
+ REQUIRES(mutex()) = 0;
+ virtual void setCheckOutputStageEffects() = 0; // no mutex needed
+ virtual void cacheParameters_l()
+ REQUIRES(mutex()) = 0;
virtual status_t createAudioPatch_l(
- const struct audio_patch* patch, audio_patch_handle_t* handle) = 0;
- virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) = 0;
- virtual void updateOutDevices(const DeviceDescriptorBaseVector& outDevices) = 0;
- virtual void toAudioPortConfig(struct audio_port_config* config) = 0;
- virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) = 0;
+ const struct audio_patch* patch, audio_patch_handle_t* handle)
+ REQUIRES(mutex()) = 0;
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle)
+ REQUIRES(mutex()) = 0;
+ virtual void updateOutDevices(const DeviceDescriptorBaseVector& outDevices)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void toAudioPortConfig(struct audio_port_config* config)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs)
+ REQUIRES(mutex()) = 0;
// see note at declaration of mStandby, mOutDevice and mInDevice
virtual bool inStandby() const = 0;
- virtual const DeviceTypeSet outDeviceTypes() const = 0;
- virtual audio_devices_t inDeviceType() const = 0;
- virtual DeviceTypeSet getDeviceTypes() const = 0;
+ virtual const DeviceTypeSet outDeviceTypes_l() const REQUIRES(mutex()) = 0;
+ virtual audio_devices_t inDeviceType_l() const REQUIRES(mutex()) = 0;
+ virtual DeviceTypeSet getDeviceTypes_l() const REQUIRES(mutex()) = 0;
virtual const AudioDeviceTypeAddrVector& outDeviceTypeAddrs() const = 0;
virtual const AudioDeviceTypeAddr& inDeviceTypeAddr() const = 0;
virtual bool isOutput() const = 0;
@@ -226,7 +244,7 @@
bool pinned,
bool probe,
bool notifyFramesProcessed)
- REQUIRES(audio_utils::AudioFlinger_Mutex) = 0;
+ REQUIRES(audio_utils::AudioFlinger_Mutex) EXCLUDES_ThreadBase_Mutex = 0;
// return values for hasAudioSession (bit field)
enum effect_state {
@@ -243,28 +261,39 @@
};
// get effect chain corresponding to session Id.
- virtual sp<IAfEffectChain> getEffectChain(audio_session_t sessionId) const = 0;
+ virtual sp<IAfEffectChain> getEffectChain(audio_session_t sessionId) const
+ EXCLUDES_ThreadBase_Mutex = 0;
// same as getEffectChain() but must be called with ThreadBase mutex locked
- virtual sp<IAfEffectChain> getEffectChain_l(audio_session_t sessionId) const = 0;
- virtual std::vector<int> getEffectIds_l(audio_session_t sessionId) const = 0;
+ virtual sp<IAfEffectChain> getEffectChain_l(audio_session_t sessionId) const
+ REQUIRES(mutex()) = 0;
+ virtual std::vector<int> getEffectIds_l(audio_session_t sessionId) const
+ REQUIRES(mutex()) = 0;
// add an effect chain to the chain list (mEffectChains)
- virtual status_t addEffectChain_l(const sp<IAfEffectChain>& chain) = 0;
+ virtual status_t addEffectChain_l(const sp<IAfEffectChain>& chain)
+ REQUIRES(mutex()) = 0;
// remove an effect chain from the chain list (mEffectChains)
- virtual size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) = 0;
+ virtual size_t removeEffectChain_l(const sp<IAfEffectChain>& chain)
+ REQUIRES(mutex()) = 0;
// lock all effect chains Mutexes. Must be called before releasing the
// ThreadBase mutex before processing the mixer and effects. This guarantees the
// integrity of the chains during the process.
// Also sets the parameter 'effectChains' to current value of mEffectChains.
- virtual void lockEffectChains_l(Vector<sp<IAfEffectChain>>& effectChains) = 0;
+ virtual void lockEffectChains_l(Vector<sp<IAfEffectChain>>& effectChains)
+ REQUIRES(mutex()) = 0;
// unlock effect chains after process
- virtual void unlockEffectChains(const Vector<sp<IAfEffectChain>>& effectChains) = 0;
+ virtual void unlockEffectChains(const Vector<sp<IAfEffectChain>>& effectChains)
+ EXCLUDES_ThreadBase_Mutex = 0;
// get a copy of mEffectChains vector
- virtual Vector<sp<IAfEffectChain>> getEffectChains_l() const = 0;
+ virtual Vector<sp<IAfEffectChain>> getEffectChains_l() const
+ REQUIRES(mutex()) = 0;
// set audio mode to all effect chains
- virtual void setMode(audio_mode_t mode) = 0;
+ virtual void setMode(audio_mode_t mode)
+ EXCLUDES_ThreadBase_Mutex = 0;
// get effect module with corresponding ID on specified audio session
- virtual sp<IAfEffectModule> getEffect(audio_session_t sessionId, int effectId) const = 0;
- virtual sp<IAfEffectModule> getEffect_l(audio_session_t sessionId, int effectId) const = 0;
+ virtual sp<IAfEffectModule> getEffect(audio_session_t sessionId, int effectId) const
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual sp<IAfEffectModule> getEffect_l(audio_session_t sessionId, int effectId) const
+ REQUIRES(mutex()) = 0;
// add and effect module. Also creates the effect chain is none exists for
// the effects audio session. Only called in a context of moving an effect
// from one thread to another
@@ -272,29 +301,36 @@
REQUIRES(audio_utils::AudioFlinger_Mutex, mutex()) = 0;
// remove and effect module. Also removes the effect chain is this was the last
// effect
- virtual void removeEffect_l(const sp<IAfEffectModule>& effect, bool release = false) = 0;
+ virtual void removeEffect_l(const sp<IAfEffectModule>& effect, bool release = false)
+ REQUIRES(mutex()) = 0;
// disconnect an effect handle from module and destroy module if last handle
- virtual void disconnectEffectHandle(IAfEffectHandle* handle, bool unpinIfLast) = 0;
+ virtual void disconnectEffectHandle(IAfEffectHandle* handle, bool unpinIfLast)
+ EXCLUDES_ThreadBase_Mutex = 0;
// detach all tracks connected to an auxiliary effect
- virtual void detachAuxEffect_l(int effectId) = 0;
+ virtual void detachAuxEffect_l(int effectId) REQUIRES(mutex()) = 0;
// returns a combination of:
// - EFFECT_SESSION if effects on this audio session exist in one chain
// - TRACK_SESSION if tracks on this audio session exist
// - FAST_SESSION if fast tracks on this audio session exist
// - SPATIALIZED_SESSION if spatialized tracks on this audio session exist
- virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const = 0;
- virtual uint32_t hasAudioSession(audio_session_t sessionId) const = 0;
+ virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const REQUIRES(mutex()) = 0;
+ virtual uint32_t hasAudioSession(audio_session_t sessionId) const
+ EXCLUDES_ThreadBase_Mutex = 0;
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
- virtual product_strategy_t getStrategyForSession_l(audio_session_t sessionId) const = 0;
+ virtual product_strategy_t getStrategyForSession_l(audio_session_t sessionId) const
+ REQUIRES(mutex()) = 0;
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
virtual void checkSuspendOnEffectEnabled(
- bool enabled, audio_session_t sessionId, bool threadLocked) = 0;
+ bool enabled, audio_session_t sessionId, bool threadLocked)
+ EXCLUDES_ThreadBase_Mutex = 0;
- virtual status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) = 0;
+ virtual status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ // internally static, perhaps make static member.
virtual bool isValidSyncEvent(const sp<audioflinger::SyncEvent>& event) const = 0;
// Return a reference to a per-thread heap which can be used to allocate IMemory
@@ -307,33 +343,35 @@
virtual sp<IMemory> pipeMemory() const = 0;
- virtual void systemReady() = 0;
+ virtual void systemReady() EXCLUDES_ThreadBase_Mutex = 0;
// checkEffectCompatibility_l() must be called with ThreadBase::mLock held
virtual status_t checkEffectCompatibility_l(
- const effect_descriptor_t* desc, audio_session_t sessionId) = 0;
+ const effect_descriptor_t* desc, audio_session_t sessionId) REQUIRES(mutex()) = 0;
- virtual void broadcast_l() = 0;
+ virtual void broadcast_l() REQUIRES(mutex()) = 0;
- virtual bool isTimestampCorrectionEnabled() const = 0;
+ virtual bool isTimestampCorrectionEnabled_l() const REQUIRES(mutex()) = 0;
virtual bool isMsdDevice() const = 0;
- virtual void dump(int fd, const Vector<String16>& args) = 0;
+ virtual void dump(int fd, const Vector<String16>& args) EXCLUDES_ThreadBase_Mutex = 0;
// deliver stats to mediametrics.
- virtual void sendStatistics(bool force) = 0;
+ virtual void sendStatistics(bool force) EXCLUDES_ThreadBase_Mutex = 0;
virtual audio_utils::mutex& mutex() const
RETURN_CAPABILITY(audio_utils::ThreadBase_Mutex) = 0;
- virtual void onEffectEnable(const sp<IAfEffectModule>& effect) = 0;
- virtual void onEffectDisable() = 0;
+ virtual void onEffectEnable(const sp<IAfEffectModule>& effect) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void onEffectDisable() EXCLUDES_ThreadBase_Mutex = 0;
// invalidateTracksForAudioSession_l must be called with holding mLock.
- virtual void invalidateTracksForAudioSession_l(audio_session_t sessionId) const = 0;
+ virtual void invalidateTracksForAudioSession_l(audio_session_t sessionId) const
+ REQUIRES(mutex()) = 0;
// Invalidate all the tracks with the given audio session.
- virtual void invalidateTracksForAudioSession(audio_session_t sessionId) const = 0;
+ virtual void invalidateTracksForAudioSession(audio_session_t sessionId) const
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual bool isStreamInitialized() const = 0;
virtual void startMelComputation_l(const sp<audio_utils::MelProcessor>& processor)
@@ -341,10 +379,12 @@
virtual void stopMelComputation_l()
REQUIRES(audio_utils::AudioFlinger_Mutex) = 0;
- virtual product_strategy_t getStrategyForStream(audio_stream_type_t stream) const = 0;
+ virtual product_strategy_t getStrategyForStream(audio_stream_type_t stream) const
+ EXCLUDES_AUDIO_ALL = 0;
virtual void setEffectSuspended_l(
- const effect_uuid_t* type, bool suspend, audio_session_t sessionId) = 0;
+ const effect_uuid_t* type, bool suspend, audio_session_t sessionId)
+ REQUIRES(mutex()) = 0;
// Dynamic cast to derived interface
virtual sp<IAfDirectOutputThread> asIAfDirectOutputThread() { return nullptr; }
@@ -392,7 +432,7 @@
// return estimated latency in milliseconds, as reported by HAL
virtual uint32_t latency() const = 0; // should be in IAfThreadBase?
- virtual uint32_t& fastTrackAvailMask_l() = 0;
+ virtual uint32_t& fastTrackAvailMask_l() REQUIRES(mutex()) = 0;
virtual sp<IAfTrack> createTrack_l(
const sp<Client>& client,
@@ -415,66 +455,74 @@
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
- bool isBitPerfect)
+ bool isBitPerfect,
+ audio_output_flags_t* afTrackFlags)
REQUIRES(audio_utils::AudioFlinger_Mutex) = 0;
- virtual status_t addTrack_l(const sp<IAfTrack>& track) = 0;
- virtual bool destroyTrack_l(const sp<IAfTrack>& track) = 0;
- virtual bool isTrackActive(const sp<IAfTrack>& track) const = 0;
- virtual void addOutputTrack_l(const sp<IAfTrack>& track) = 0;
+ virtual status_t addTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex()) = 0;
+ virtual bool destroyTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex()) = 0;
+ virtual bool isTrackActive(const sp<IAfTrack>& track) const REQUIRES(mutex()) = 0;
+ virtual void addOutputTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex()) = 0;
- virtual AudioStreamOut* getOutput_l() const = 0;
- virtual AudioStreamOut* getOutput() const = 0;
- virtual AudioStreamOut* clearOutput() = 0;
+ virtual AudioStreamOut* getOutput_l() const REQUIRES(mutex()) = 0;
+ virtual AudioStreamOut* getOutput() const EXCLUDES_ThreadBase_Mutex = 0;
+ virtual AudioStreamOut* clearOutput() EXCLUDES_ThreadBase_Mutex = 0;
// a very large number of suspend() will eventually wraparound, but unlikely
virtual void suspend() = 0;
virtual void restore() = 0;
virtual bool isSuspended() const = 0;
- virtual status_t getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames) const = 0;
+ virtual status_t getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames) const
+ EXCLUDES_ThreadBase_Mutex = 0;
// Consider also removing and passing an explicit mMainBuffer initialization
// parameter to AF::IAfTrack::Track().
virtual float* sinkBuffer() const = 0;
- virtual status_t attachAuxEffect(const sp<IAfTrack>& track, int EffectId) = 0;
- virtual status_t attachAuxEffect_l(const sp<IAfTrack>& track, int EffectId) = 0;
+ virtual status_t attachAuxEffect(const sp<IAfTrack>& track, int EffectId)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t attachAuxEffect_l(const sp<IAfTrack>& track, int EffectId)
+ REQUIRES(mutex()) = 0;
// called with AudioFlinger lock held
- virtual bool invalidateTracks_l(audio_stream_type_t streamType) = 0;
- virtual bool invalidateTracks_l(std::set<audio_port_handle_t>& portIds) = 0;
- virtual void invalidateTracks(audio_stream_type_t streamType) = 0;
+ virtual bool invalidateTracks_l(audio_stream_type_t streamType) REQUIRES(mutex()) = 0;
+ virtual bool invalidateTracks_l(std::set<audio_port_handle_t>& portIds) REQUIRES(mutex()) = 0;
+ virtual void invalidateTracks(audio_stream_type_t streamType)
+ EXCLUDES_ThreadBase_Mutex = 0;
// Invalidate tracks by a set of port ids. The port id will be removed from
// the given set if the corresponding track is found and invalidated.
- virtual void invalidateTracks(std::set<audio_port_handle_t>& portIds) = 0;
+ virtual void invalidateTracks(std::set<audio_port_handle_t>& portIds)
+ EXCLUDES_ThreadBase_Mutex = 0;
- virtual status_t getTimestamp_l(AudioTimestamp& timestamp) = 0;
- virtual void addPatchTrack(const sp<IAfPatchTrack>& track) = 0;
- virtual void deletePatchTrack(const sp<IAfPatchTrack>& track) = 0;
+ virtual status_t getTimestamp_l(AudioTimestamp& timestamp) REQUIRES(mutex()) = 0;
+ virtual void addPatchTrack(const sp<IAfPatchTrack>& track) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void deletePatchTrack(const sp<IAfPatchTrack>& track) EXCLUDES_ThreadBase_Mutex = 0;
// Return the asynchronous signal wait time.
- virtual int64_t computeWaitTimeNs_l() const = 0;
+ virtual int64_t computeWaitTimeNs_l() const REQUIRES(mutex()) = 0;
// returns true if the track is allowed to be added to the thread.
virtual bool isTrackAllowed_l(
audio_channel_mask_t channelMask, audio_format_t format, audio_session_t sessionId,
- uid_t uid) const = 0;
+ uid_t uid) const REQUIRES(mutex()) = 0;
virtual bool supportsHapticPlayback() const = 0;
- virtual void setDownStreamPatch(const struct audio_patch* patch) = 0;
+ virtual void setDownStreamPatch(const struct audio_patch* patch)
+ EXCLUDES_ThreadBase_Mutex = 0;
- virtual IAfTrack* getTrackById_l(audio_port_handle_t trackId) = 0;
+ virtual IAfTrack* getTrackById_l(audio_port_handle_t trackId) REQUIRES(mutex()) = 0;
virtual bool hasMixer() const = 0;
virtual status_t setRequestedLatencyMode(audio_latency_mode_t mode) = 0;
- virtual status_t getSupportedLatencyModes(std::vector<audio_latency_mode_t>* modes) = 0;
+ virtual status_t getSupportedLatencyModes(std::vector<audio_latency_mode_t>* modes)
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual status_t setBluetoothVariableLatencyEnabled(bool enabled) = 0;
- virtual void setStandby() = 0;
- virtual void setStandby_l() = 0;
- virtual bool waitForHalStart() = 0;
+ virtual void setStandby() EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void setStandby_l() REQUIRES(mutex()) = 0;
+ virtual bool waitForHalStart() EXCLUDES_ThreadBase_Mutex = 0;
virtual bool hasFastMixer() const = 0;
virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const = 0;
@@ -494,9 +542,9 @@
const sp<IAfThreadCallback>& afThreadCallback, IAfPlaybackThread* mainThread,
audio_io_handle_t id, bool systemReady);
- virtual void addOutputTrack(IAfPlaybackThread* thread) = 0;
+ virtual void addOutputTrack(IAfPlaybackThread* thread) EXCLUDES_ThreadBase_Mutex = 0;
virtual uint32_t waitTimeMs() const = 0;
- virtual void removeOutputTrack(IAfPlaybackThread* thread) = 0;
+ virtual void removeOutputTrack(IAfPlaybackThread* thread) EXCLUDES_ThreadBase_Mutex = 0;
};
class IAfRecordThread : public virtual IAfThreadBase {
@@ -521,42 +569,49 @@
status_t* status /*non-NULL*/,
audio_port_handle_t portId,
int32_t maxSharedAudioHistoryMs)
- REQUIRES(audio_utils::AudioFlinger_Mutex) = 0;
- virtual void destroyTrack_l(const sp<IAfRecordTrack>& track) = 0;
- virtual void removeTrack_l(const sp<IAfRecordTrack>& track) = 0;
+ REQUIRES(audio_utils::AudioFlinger_Mutex) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void destroyTrack_l(const sp<IAfRecordTrack>& track) REQUIRES(mutex()) = 0;
+ virtual void removeTrack_l(const sp<IAfRecordTrack>& track) REQUIRES(mutex()) = 0;
virtual status_t start(
IAfRecordTrack* recordTrack, AudioSystem::sync_event_t event,
- audio_session_t triggerSession) = 0;
+ audio_session_t triggerSession) EXCLUDES_ThreadBase_Mutex = 0;
// ask the thread to stop the specified track, and
// return true if the caller should then do it's part of the stopping process
- virtual bool stop(IAfRecordTrack* recordTrack) = 0;
+ virtual bool stop(IAfRecordTrack* recordTrack) EXCLUDES_ThreadBase_Mutex = 0;
+ // NO_THREAD_SAFETY_ANALYSIS: consider atomics
virtual AudioStreamIn* getInput() const = 0;
virtual AudioStreamIn* clearInput() = 0;
virtual status_t getActiveMicrophones(
- std::vector<media::MicrophoneInfoFw>* activeMicrophones) const = 0;
- virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) = 0;
- virtual status_t setPreferredMicrophoneFieldDimension(float zoom) = 0;
+ std::vector<media::MicrophoneInfoFw>* activeMicrophones)
+ const EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t setPreferredMicrophoneFieldDimension(float zoom)
+ EXCLUDES_ThreadBase_Mutex = 0;
- virtual void addPatchTrack(const sp<IAfPatchRecord>& record) = 0;
- virtual void deletePatchTrack(const sp<IAfPatchRecord>& record) = 0;
+ virtual void addPatchTrack(const sp<IAfPatchRecord>& record)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void deletePatchTrack(const sp<IAfPatchRecord>& record)
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual bool fastTrackAvailable() const = 0;
virtual void setFastTrackAvailable(bool available) = 0;
- virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced) = 0;
+ virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced)
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual bool hasFastCapture() const = 0;
- virtual void checkBtNrec() = 0;
- virtual uint32_t getInputFramesLost() const = 0;
+ virtual void checkBtNrec() EXCLUDES_ThreadBase_Mutex = 0;
+ virtual uint32_t getInputFramesLost() const EXCLUDES_ThreadBase_Mutex = 0;
virtual status_t shareAudioHistory(
const std::string& sharedAudioPackageName,
audio_session_t sharedSessionId = AUDIO_SESSION_NONE,
- int64_t sharedAudioStartMs = -1) = 0;
- virtual void resetAudioHistory_l() = 0;
+ int64_t sharedAudioStartMs = -1) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void resetAudioHistory_l() REQUIRES(mutex()) = 0;
};
class IAfMmapThread : public virtual IAfThreadBase {
@@ -575,26 +630,32 @@
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
audio_port_handle_t deviceId,
- audio_port_handle_t portId) = 0;
- virtual void disconnect() = 0;
+ audio_port_handle_t portId) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual void disconnect() EXCLUDES_ThreadBase_Mutex = 0;
// MmapStreamInterface handling (see adapter)
virtual status_t createMmapBuffer(
- int32_t minSizeFrames, struct audio_mmap_buffer_info* info) = 0;
- virtual status_t getMmapPosition(struct audio_mmap_position* position) const = 0;
+ int32_t minSizeFrames, struct audio_mmap_buffer_info* info)
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t getMmapPosition(struct audio_mmap_position* position) const
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual status_t start(
const AudioClient& client, const audio_attributes_t* attr,
- audio_port_handle_t* handle) = 0;
- virtual status_t stop(audio_port_handle_t handle) = 0;
- virtual status_t standby() = 0;
- virtual status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) const = 0;
- virtual status_t reportData(const void* buffer, size_t frameCount) = 0;
+ audio_port_handle_t* handle) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t stop(audio_port_handle_t handle) EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t standby() EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) const
+ EXCLUDES_ThreadBase_Mutex = 0;
+ virtual status_t reportData(const void* buffer, size_t frameCount)
+ EXCLUDES_ThreadBase_Mutex = 0;
// TODO(b/291317898) move to IAfThreadBase?
- virtual void invalidateTracks(std::set<audio_port_handle_t>& portIds) = 0;
+ virtual void invalidateTracks(std::set<audio_port_handle_t>& portIds)
+ EXCLUDES_ThreadBase_Mutex = 0;
// Sets the UID records silence - TODO(b/291317898) move to IAfMmapCaptureThread
- virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced) = 0;
+ virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced)
+ EXCLUDES_ThreadBase_Mutex = 0;
virtual sp<IAfMmapPlaybackThread> asIAfMmapPlaybackThread() { return nullptr; }
virtual sp<IAfMmapCaptureThread> asIAfMmapCaptureThread() { return nullptr; }
@@ -606,7 +667,7 @@
const sp<IAfThreadCallback>& afThreadCallback, audio_io_handle_t id,
AudioHwDevice* hwDev, AudioStreamOut* output, bool systemReady);
- virtual AudioStreamOut* clearOutput() = 0;
+ virtual AudioStreamOut* clearOutput() EXCLUDES_ThreadBase_Mutex = 0;
};
class IAfMmapCaptureThread : public virtual IAfMmapThread {
@@ -615,7 +676,7 @@
const sp<IAfThreadCallback>& afThreadCallback, audio_io_handle_t id,
AudioHwDevice* hwDev, AudioStreamIn* input, bool systemReady);
- virtual AudioStreamIn* clearInput() = 0;
+ virtual AudioStreamIn* clearInput() EXCLUDES_ThreadBase_Mutex = 0;
};
} // namespace android
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index ef932ec..a9c868e 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -233,7 +233,17 @@
audio_utils::lock_guard _afl(mAfMelReporterCallback->mutex()); // AudioFlinger_Mutex
audio_utils::lock_guard _l(mutex());
- stopMelComputationForPatch_l(melPatch);
+ if (melPatch.csdActive) {
+ // only need to stop if patch was active
+ melPatch.csdActive = false;
+ stopMelComputationForPatch_l(melPatch);
+ }
+}
+
+void MelReporter::onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle, const IAfPatchPanel::Patch& patch) {
+ onReleaseAudioPatch(oldHandle);
+ onCreateAudioPatch(newHandle, patch);
}
sp<media::ISoundDose> MelReporter::getSoundDoseInterface(
diff --git a/services/audioflinger/MelReporter.h b/services/audioflinger/MelReporter.h
index ce89b24..bf4f390 100644
--- a/services/audioflinger/MelReporter.h
+++ b/services/audioflinger/MelReporter.h
@@ -89,6 +89,9 @@
const IAfPatchPanel::Patch& patch) final
EXCLUDES_AudioFlinger_Mutex;
void onReleaseAudioPatch(audio_patch_handle_t handle) final EXCLUDES_AudioFlinger_Mutex;
+ void onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) final EXCLUDES_AudioFlinger_Mutex;
/**
* The new metadata can determine whether we should compute MEL for the given thread.
diff --git a/services/audioflinger/PatchCommandThread.cpp b/services/audioflinger/PatchCommandThread.cpp
index f4c76d6..2cfefa0 100644
--- a/services/audioflinger/PatchCommandThread.cpp
+++ b/services/audioflinger/PatchCommandThread.cpp
@@ -58,6 +58,16 @@
releaseAudioPatchCommand(handle);
}
+void PatchCommandThread::updateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle, const IAfPatchPanel::Patch& patch) {
+ ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
+ __func__, oldHandle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+
+ updateAudioPatchCommand(oldHandle, newHandle, patch);
+}
+
bool PatchCommandThread::threadLoop()
{
audio_utils::unique_lock _l(mutex());
@@ -103,6 +113,21 @@
}
}
break;
+ case UPDATE_AUDIO_PATCH: {
+ const auto data = (UpdateAudioPatchData*) command->mData.get();
+ ALOGV("%s processing update audio patch old handle %d new handle %d",
+ __func__,
+ data->mOldHandle, data->mNewHandle);
+
+ for (const auto& listener : listenersCopy) {
+ auto spListener = listener.promote();
+ if (spListener) {
+ spListener->onUpdateAudioPatch(data->mOldHandle,
+ data->mNewHandle, data->mPatch);
+ }
+ }
+ }
+ break;
default:
ALOGW("%s unknown command %d", __func__, command->mCommand);
break;
@@ -144,6 +169,16 @@
sendCommand(command);
}
+void PatchCommandThread::updateAudioPatchCommand(
+ audio_patch_handle_t oldHandle, audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) {
+ sp<Command> command = sp<Command>::make(UPDATE_AUDIO_PATCH,
+ new UpdateAudioPatchData(oldHandle, newHandle, patch));
+ ALOGV("%s adding update patch old handle %d new handle %d mHalHandle %d.",
+ __func__, oldHandle, newHandle, patch.mHalHandle);
+ sendCommand(command);
+}
+
void PatchCommandThread::exit() {
ALOGV("%s", __func__);
{
diff --git a/services/audioflinger/PatchCommandThread.h b/services/audioflinger/PatchCommandThread.h
index 8ca96f1..f491e8a 100644
--- a/services/audioflinger/PatchCommandThread.h
+++ b/services/audioflinger/PatchCommandThread.h
@@ -38,6 +38,7 @@
enum {
CREATE_AUDIO_PATCH,
RELEASE_AUDIO_PATCH,
+ UPDATE_AUDIO_PATCH,
};
class PatchCommandListener : public virtual RefBase {
@@ -45,6 +46,9 @@
virtual void onCreateAudioPatch(audio_patch_handle_t handle,
const IAfPatchPanel::Patch& patch) = 0;
virtual void onReleaseAudioPatch(audio_patch_handle_t handle) = 0;
+ virtual void onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) = 0;
};
PatchCommandThread() : Thread(false /* canCallJava */) {}
@@ -56,6 +60,8 @@
void createAudioPatch(audio_patch_handle_t handle, const IAfPatchPanel::Patch& patch)
EXCLUDES_PatchCommandThread_Mutex;
void releaseAudioPatch(audio_patch_handle_t handle) EXCLUDES_PatchCommandThread_Mutex;
+ void updateAudioPatch(audio_patch_handle_t oldHandle, audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) EXCLUDES_PatchCommandThread_Mutex;
// Thread virtuals
void onFirstRef() override;
@@ -66,6 +72,9 @@
void createAudioPatchCommand(audio_patch_handle_t handle,
const IAfPatchPanel::Patch& patch) EXCLUDES_PatchCommandThread_Mutex;
void releaseAudioPatchCommand(audio_patch_handle_t handle) EXCLUDES_PatchCommandThread_Mutex;
+ void updateAudioPatchCommand(audio_patch_handle_t oldHandle, audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) EXCLUDES_PatchCommandThread_Mutex;
+
private:
class CommandData;
@@ -99,6 +108,18 @@
audio_patch_handle_t mHandle;
};
+ class UpdateAudioPatchData : public CommandData {
+ public:
+ UpdateAudioPatchData(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch)
+ : mOldHandle(oldHandle), mNewHandle(newHandle), mPatch(patch) {}
+
+ const audio_patch_handle_t mOldHandle;
+ const audio_patch_handle_t mNewHandle;
+ const IAfPatchPanel::Patch mPatch;
+ };
+
void sendCommand(const sp<Command>& command) EXCLUDES_PatchCommandThread_Mutex;
audio_utils::mutex& mutex() const RETURN_CAPABILITY(audio_utils::PatchCommandThread_Mutex) {
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 7d3900b..5f238fb 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -134,7 +134,8 @@
if (patch->num_sources > 2) {
return INVALID_OPERATION;
}
-
+ bool reuseExistingHalPatch = false;
+ audio_patch_handle_t oldhandle = AUDIO_PATCH_HANDLE_NONE;
if (*handle != AUDIO_PATCH_HANDLE_NONE) {
auto iter = mPatches.find(*handle);
if (iter != mPatches.end()) {
@@ -152,6 +153,7 @@
if (removedPatch.mHalHandle != AUDIO_PATCH_HANDLE_NONE) {
audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
const struct audio_patch &oldPatch = removedPatch.mAudioPatch;
+ oldhandle = *handle;
if (oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
(patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE ||
oldPatch.sources[0].ext.device.hw_module !=
@@ -174,8 +176,12 @@
hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
halHandle = removedPatch.mHalHandle;
+ // Prevent to remove/add device effect when mix / device did not change, and
+ // hal patch has not been released
+ // Note that no patch leak at hal layer as halHandle is reused.
+ reuseExistingHalPatch = (hwDevice == 0) && patchesHaveSameRoute(*patch, oldPatch);
}
- erasePatch(*handle);
+ erasePatch(*handle, reuseExistingHalPatch);
}
}
@@ -406,7 +412,23 @@
mAfPatchPanelCallback->updateOutDevicesForRecordThreads_l(devices);
}
+ // For endpoint patches, we do not need to re-evaluate the device effect state
+ // if the same HAL patch is reused (see calls to mAfPatchPanelCallback below)
+ if (endpointPatch) {
+ for (auto& p : mPatches) {
+ // end point patches are skipped so we do not compare against this patch
+ if (!p.second.mIsEndpointPatch && patchesHaveSameRoute(
+ newPatch.mAudioPatch, p.second.mAudioPatch)) {
+ ALOGV("%s() Sw Bridge endpoint reusing halHandle=%d", __func__,
+ p.second.mHalHandle);
+ halHandle = p.second.mHalHandle;
+ reuseExistingHalPatch = true;
+ break;
+ }
+ }
+ }
mAfPatchPanelCallback->mutex().unlock();
+
status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
mAfPatchPanelCallback->mutex().lock();
if (status == NO_ERROR) {
@@ -436,7 +458,19 @@
*handle = static_cast<audio_patch_handle_t>(
mAfPatchPanelCallback->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH));
newPatch.mHalHandle = halHandle;
- mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(*handle, newPatch);
+ // Skip device effect:
+ // -for sw bridge as effect are likely held by endpoint patches
+ // -for endpoint reusing a HalPatch handle
+ if (!(newPatch.isSoftware()
+ || (endpointPatch && reuseExistingHalPatch))) {
+ if (reuseExistingHalPatch) {
+ mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
+ oldhandle, *handle, newPatch);
+ } else {
+ mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(
+ *handle, newPatch);
+ }
+ }
if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
addSoftwarePatchToInsertedModules_l(insertedModule, *handle, &newPatch.mAudioPatch);
}
@@ -723,12 +757,14 @@
{
ALOGV("%s handle %d", __func__, handle);
status_t status = NO_ERROR;
+ bool doReleasePatch = true;
auto iter = mPatches.find(handle);
if (iter == mPatches.end()) {
return BAD_VALUE;
}
Patch &removedPatch = iter->second;
+ const bool isSwBridge = removedPatch.isSoftware();
const struct audio_patch &patch = removedPatch.mAudioPatch;
const struct audio_port_config &src = patch.sources[0];
@@ -780,22 +816,40 @@
break;
}
}
- mAfPatchPanelCallback->mutex().unlock();
- status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
- mAfPatchPanelCallback->mutex().lock();
+ // Check whether the removed patch Hal Handle is used in another non-Endpoint patch.
+ // Since this is a non-Endpoint patch, the removed patch is not considered (it is
+ // removed later from mPatches).
+ if (removedPatch.mIsEndpointPatch) {
+ for (auto& p: mPatches) {
+ if (!p.second.mIsEndpointPatch
+ && p.second.mHalHandle == removedPatch.mHalHandle) {
+ ALOGV("%s() Sw Bridge endpoint used existing halHandle=%d, do not release",
+ __func__, p.second.mHalHandle);
+ doReleasePatch = false;
+ break;
+ }
+ }
+ }
+ if (doReleasePatch) {
+ mAfPatchPanelCallback->mutex().unlock();
+ status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
+ mAfPatchPanelCallback->mutex().lock();
+ }
} break;
default:
status = BAD_VALUE;
}
- erasePatch(handle);
+ erasePatch(handle, /* reuseExistingHalPatch= */ !doReleasePatch || isSwBridge);
return status;
}
-void PatchPanel::erasePatch(audio_patch_handle_t handle) {
+void PatchPanel::erasePatch(audio_patch_handle_t handle, bool reuseExistingHalPatch) {
mPatches.erase(handle);
removeSoftwarePatchFromInsertedModules(handle);
- mAfPatchPanelCallback->getPatchCommandThread()->releaseAudioPatch(handle);
+ if (!reuseExistingHalPatch) {
+ mAfPatchPanelCallback->getPatchCommandThread()->releaseAudioPatch(handle);
+ }
}
/* List connected audio ports and they attributes */
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 1ff8fff..cec554c 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -83,7 +83,38 @@
const struct audio_patch *patch)
REQUIRES(audio_utils::AudioFlinger_Mutex);
void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);
- void erasePatch(audio_patch_handle_t handle);
+ /**
+ * erase the patch referred by its handle.
+ * @param handle of the patch to be erased
+ * @param reuseExistingHalPatch if set, do not trig the callback of listeners, listener
+ * would receive instead a onUpdateAudioPatch when the patch will be recreated.
+ * It prevents for example DeviceEffectManager to spuriously remove / add a device on an already
+ * opened input / output mix.
+ */
+ void erasePatch(audio_patch_handle_t handle, bool reuseExistingHalPatch = false);
+
+ /**
+ * Returns true if the old and new patches passed as arguments describe the same
+ * connections between the first sink and the first source
+ * @param oldPatch previous patch
+ * @param newPatch new patch
+ * @return true if the route is unchanged between the old and new patch, false otherwise
+ */
+ inline bool patchesHaveSameRoute(
+ const struct audio_patch &newPatch, const struct audio_patch &oldPatch) const {
+ return (newPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
+ oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
+ newPatch.sources[0].id == oldPatch.sources[0].id &&
+ newPatch.sinks[0].type == AUDIO_PORT_TYPE_MIX &&
+ oldPatch.sinks[0].type == AUDIO_PORT_TYPE_MIX &&
+ newPatch.sinks[0].ext.mix.handle == oldPatch.sinks[0].ext.mix.handle) ||
+ (newPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
+ oldPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
+ newPatch.sinks[0].id == oldPatch.sinks[0].id &&
+ newPatch.sources[0].type == AUDIO_PORT_TYPE_MIX &&
+ oldPatch.sources[0].type == AUDIO_PORT_TYPE_MIX &&
+ newPatch.sources[0].ext.mix.handle == oldPatch.sources[0].ext.mix.handle);
+ }
const sp<IAfPatchPanelCallback> mAfPatchPanelCallback;
std::map<audio_patch_handle_t, Patch> mPatches;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index ae60ed0..078ae12 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -63,8 +63,8 @@
wp<IAfThreadBase> mThread;
std::atomic_bool mHasOpPlayAudio;
- const AttributionSourceState mAttributionSource;
- const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as int32_t
+ const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as
+ // int32_t
const int mId; // for logging purposes only
const uid_t mUid;
const String16 mPackageName;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 914b60d..e506ca6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -875,7 +875,7 @@
} break;
case CFG_EVENT_IO: {
IoConfigEventData *data = (IoConfigEventData *)event->mData.get();
- ioConfigChanged(data->mEvent, data->mPid, data->mPortId);
+ ioConfigChanged_l(data->mEvent, data->mPid, data->mPortId);
} break;
case CFG_EVENT_SET_PARAMETER: {
SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get();
@@ -886,22 +886,22 @@
}
} break;
case CFG_EVENT_CREATE_AUDIO_PATCH: {
- const DeviceTypeSet oldDevices = getDeviceTypes();
+ const DeviceTypeSet oldDevices = getDeviceTypes_l();
CreateAudioPatchConfigEventData *data =
(CreateAudioPatchConfigEventData *)event->mData.get();
event->mStatus = createAudioPatch_l(&data->mPatch, &data->mHandle);
- const DeviceTypeSet newDevices = getDeviceTypes();
+ const DeviceTypeSet newDevices = getDeviceTypes_l();
configChanged = oldDevices != newDevices;
mLocalLog.log("CFG_EVENT_CREATE_AUDIO_PATCH: old device %s (%s) new device %s (%s)",
dumpDeviceTypes(oldDevices).c_str(), toString(oldDevices).c_str(),
dumpDeviceTypes(newDevices).c_str(), toString(newDevices).c_str());
} break;
case CFG_EVENT_RELEASE_AUDIO_PATCH: {
- const DeviceTypeSet oldDevices = getDeviceTypes();
+ const DeviceTypeSet oldDevices = getDeviceTypes_l();
ReleaseAudioPatchConfigEventData *data =
(ReleaseAudioPatchConfigEventData *)event->mData.get();
event->mStatus = releaseAudioPatch_l(data->mHandle);
- const DeviceTypeSet newDevices = getDeviceTypes();
+ const DeviceTypeSet newDevices = getDeviceTypes_l();
configChanged = oldDevices != newDevices;
mLocalLog.log("CFG_EVENT_RELEASE_AUDIO_PATCH: old device %s (%s) new device %s (%s)",
dumpDeviceTypes(oldDevices).c_str(), toString(oldDevices).c_str(),
@@ -1089,9 +1089,9 @@
}
// Note: output device may be used by capture threads for effects such as AEC.
dprintf(fd, " Output devices: %s (%s)\n",
- dumpDeviceTypes(outDeviceTypes()).c_str(), toString(outDeviceTypes()).c_str());
+ dumpDeviceTypes(outDeviceTypes_l()).c_str(), toString(outDeviceTypes_l()).c_str());
dprintf(fd, " Input device: %#x (%s)\n",
- inDeviceType(), toString(inDeviceType()).c_str());
+ inDeviceType_l(), toString(inDeviceType_l()).c_str());
dprintf(fd, " Audio source: %d (%s)\n", mAudioSource, toString(mAudioSource).c_str());
// Dump timestamp statistics for the Thread types that support it.
@@ -1102,7 +1102,8 @@
|| mType == OFFLOAD
|| mType == SPATIALIZER) {
dprintf(fd, " Timestamp stats: %s\n", mTimestampVerifier.toString().c_str());
- dprintf(fd, " Timestamp corrected: %s\n", isTimestampCorrectionEnabled() ? "yes" : "no");
+ dprintf(fd, " Timestamp corrected: %s\n",
+ isTimestampCorrectionEnabled_l() ? "yes" : "no");
}
if (mLastIoBeginNs > 0) { // MMAP may not set this
@@ -1978,7 +1979,7 @@
}
template <typename T>
-void ThreadBase::ActiveTracks<T>::updatePowerState(
+void ThreadBase::ActiveTracks<T>::updatePowerState_l(
const sp<ThreadBase>& thread, bool force) {
// Updates ActiveTracks client uids to the thread wakelock.
if (mActiveTracksGeneration != mLastActiveTracksGeneration || force) {
@@ -2023,6 +2024,7 @@
// Call only from threadLoop() or when it is idle.
// Do not call from high performance code as this may do binder rpc to the MediaMetrics service.
void ThreadBase::sendStatistics(bool force)
+NO_THREAD_SAFETY_ANALYSIS
{
// Do not log if we have no stats.
// We choose the timestamp verifier because it is the most likely item to be present.
@@ -2054,8 +2056,8 @@
item->setInt64(MM_PREFIX "channelMask", (int64_t)mChannelMask);
item->setCString(MM_PREFIX "encoding", toString(mFormat).c_str());
item->setInt32(MM_PREFIX "frameCount", (int32_t)mFrameCount);
- item->setCString(MM_PREFIX "outDevice", toString(outDeviceTypes()).c_str());
- item->setCString(MM_PREFIX "inDevice", toString(inDeviceType()).c_str());
+ item->setCString(MM_PREFIX "outDevice", toString(outDeviceTypes_l()).c_str());
+ item->setCString(MM_PREFIX "inDevice", toString(inDeviceType_l()).c_str());
// thread statistics
if (mIoJitterMs.getN() > 0) {
@@ -2336,10 +2338,7 @@
dprintf(fd, " Total writes: %d\n", mNumWrites);
dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites);
dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no");
- dprintf(fd, " Suspend count: %d\n", mSuspended);
- dprintf(fd, " Sink buffer : %p\n", mSinkBuffer);
- dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer);
- dprintf(fd, " Effect buffer: %p\n", mEffectBuffer);
+ dprintf(fd, " Suspend count: %d\n", (int32_t)mSuspended);
dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask);
dprintf(fd, " Standby delay ns=%lld\n", (long long)mStandbyDelayNs);
AudioStreamOut *output = mOutput;
@@ -2379,7 +2378,8 @@
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
- bool isBitPerfect)
+ bool isBitPerfect,
+ audio_output_flags_t *afTrackFlags)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2412,6 +2412,7 @@
}
if (isBitPerfect) {
+ audio_utils::lock_guard _l(mutex());
sp<IAfEffectChain> chain = getEffectChain_l(sessionId);
if (chain.get() != nullptr) {
// Bit-perfect is required according to the configuration and preferred mixer
@@ -2697,6 +2698,7 @@
if (mType == DIRECT) {
trackFlags = static_cast<audio_output_flags_t>(trackFlags | AUDIO_OUTPUT_FLAG_DIRECT);
}
+ *afTrackFlags = trackFlags;
track = IAfTrack::create(this, client, streamType, attr, sampleRate, format,
channelMask, frameCount,
@@ -2769,6 +2771,8 @@
return latency_l();
}
uint32_t PlaybackThread::latency_l() const
+NO_THREAD_SAFETY_ANALYSIS
+// Fix later.
{
uint32_t latency;
if (initCheck() == NO_ERROR && mOutput->stream->getLatency(&latency) == OK) {
@@ -2836,7 +2840,6 @@
// addTrack_l() must be called with ThreadBase::mutex() held
status_t PlaybackThread::addTrack_l(const sp<IAfTrack>& track)
-NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex()
{
status_t status = ALREADY_EXISTS;
@@ -3004,7 +3007,7 @@
return mOutput->stream->selectPresentation(presentationId, programId);
}
-void PlaybackThread::ioConfigChanged(audio_io_config_event_t event, pid_t pid,
+void PlaybackThread::ioConfigChanged_l(audio_io_config_event_t event, pid_t pid,
audio_port_handle_t portId) {
ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event);
sp<AudioIoDescriptor> desc;
@@ -3026,7 +3029,7 @@
desc = sp<AudioIoDescriptor>::make(mId);
break;
}
- mAfThreadCallback->ioConfigChanged(event, desc, pid);
+ mAfThreadCallback->ioConfigChanged_l(event, desc, pid);
}
void PlaybackThread::onWriteReady()
@@ -3212,8 +3215,8 @@
if (hasMixer()) {
mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
}
- ALOGI("HAL output buffer size %zu frames, normal sink buffer size %zu frames", mFrameCount,
- mNormalFrameCount);
+ ALOGI("HAL output buffer size %zu frames, normal sink buffer size %zu frames",
+ (size_t)mFrameCount, mNormalFrameCount);
// Check if we want to throttle the processing to no more than 2x normal rate
mThreadThrottle = property_get_bool("af.thread.throttle", true /* default_value */);
@@ -3450,7 +3453,7 @@
ALOGD("ro.audio.silent is ignored since no output device is set");
return;
}
- if (isSingleDeviceType(outDeviceTypes(), AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) {
+ if (isSingleDeviceType(outDeviceTypes_l(), AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) {
ALOGD("ro.audio.silent will be ignored for threads on AUDIO_DEVICE_OUT_REMOTE_SUBMIX");
return;
}
@@ -3620,7 +3623,7 @@
// make sure standby delay is not too short when connected to an A2DP sink to avoid
// truncating audio when going to standby.
- if (!Intersection(outDeviceTypes(), getAudioDeviceOutAllA2dpSet()).empty()) {
+ if (!Intersection(outDeviceTypes_l(), getAudioDeviceOutAllA2dpSet()).empty()) {
if (mStandbyDelayNs < kDefaultStandbyTimeInNsecs) {
mStandbyDelayNs = kDefaultStandbyTimeInNsecs;
}
@@ -3956,7 +3959,7 @@
// If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency.
//
// Note: we access outDeviceTypes() outside of mutex().
- if (isMsdDevice() && outDeviceTypes().count(AUDIO_DEVICE_OUT_BUS) != 0) {
+ if (isMsdDevice() && outDeviceTypes_l().count(AUDIO_DEVICE_OUT_BUS) != 0) {
// Here, we try for the AF lock, but do not block on it as the latency
// is more informational.
if (mAfThreadCallback->mutex().try_lock()) {
@@ -4101,7 +4104,7 @@
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
- mActiveTracks.updatePowerState(this);
+ mActiveTracks.updatePowerState_l(this);
metadataUpdate = updateMetadata_l();
@@ -4474,9 +4477,10 @@
// notify of throttle end on debug log
// but prevent spamming for bluetooth
ALOGD_IF(!isSingleDeviceType(
- outDeviceTypes(), audio_is_a2dp_out_device) &&
+ outDeviceTypes_l(), audio_is_a2dp_out_device) &&
!isSingleDeviceType(
- outDeviceTypes(), audio_is_hearing_aid_out_device),
+ outDeviceTypes_l(),
+ audio_is_hearing_aid_out_device),
"mixer(%p) throttle end: throttle time(%u)", this, diff);
mThreadThrottleEndMs = mThreadThrottleTimeMs;
}
@@ -4572,7 +4576,7 @@
timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
mSampleRate);
- if (isTimestampCorrectionEnabled()) {
+ if (isTimestampCorrectionEnabled_l()) {
ALOGVV("TS_BEFORE: %d %lld %lld", id(),
(long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
(long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
@@ -4651,7 +4655,7 @@
// and we use systemTime().
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = mFramesWritten;
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastIoBeginNs == -1
- ? systemTime() : mLastIoBeginNs;
+ ? systemTime() : (int64_t)mLastIoBeginNs;
}
for (const sp<IAfTrack>& t : mActiveTracks) {
@@ -5292,7 +5296,8 @@
// shared by MIXER and DIRECT, overridden by DUPLICATING
void PlaybackThread::threadLoop_standby()
{
- ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
+ ALOGV("%s: audio hardware entering standby, mixer %p, suspend count %d",
+ __func__, this, (int32_t)mSuspended);
mOutput->standby();
if (mUseAsyncWrite != 0) {
// discard any pending drain or write ack by incrementing sequence
@@ -6333,7 +6338,7 @@
void MixerThread::dumpInternals_l(int fd, const Vector<String16>& args)
{
PlaybackThread::dumpInternals_l(fd, args);
- dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs);
+ dprintf(fd, " Thread throttle time (msecs): %u\n", (uint32_t)mThreadThrottleTimeMs);
dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str());
dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off");
dprintf(fd, " Master balance: %f (%s)\n", mMasterBalance.load(),
@@ -7184,6 +7189,7 @@
{
if (mFlushPending || mHwPaused) {
// If a flush is pending or track was paused, just discard buffered data
+ audio_utils::lock_guard l(mutex());
flushHw_l();
} else {
mMixerStatus = MIXER_DRAIN_ALL;
@@ -7670,8 +7676,13 @@
mOutputTracks[i]->destroy();
mOutputTracks.removeAt(i);
updateWaitTime_l();
- if (thread->getOutput() == mOutput) {
- mOutput = NULL;
+ // NO_THREAD_SAFETY_ANALYSIS
+ // Lambda workaround: as thread != this
+ // we can safely call the remote thread getOutput.
+ const bool equalOutput =
+ [&](){ return thread->getOutput() == mOutput; }();
+ if (equalOutput) {
+ mOutput = nullptr;
}
return;
}
@@ -8242,7 +8253,7 @@
}
- mActiveTracks.updatePowerState(this);
+ mActiveTracks.updatePowerState_l(this);
updateMetadata_l();
@@ -8420,7 +8431,12 @@
mTimestampVerifier.add(position, time, mSampleRate);
// Correct timestamps
- if (isTimestampCorrectionEnabled()) {
+ bool timestampCorrectionEnabled = false;
+ {
+ audio_utils::lock_guard l(mutex());
+ timestampCorrectionEnabled = isTimestampCorrectionEnabled_l();
+ }
+ if (timestampCorrectionEnabled) {
ALOGVV("TS_BEFORE: %d %lld %lld",
id(), (long long)time, (long long)position);
auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp();
@@ -9399,7 +9415,7 @@
{
// disable AEC and NS if the device is a BT SCO headset supporting those
// pre processings
- bool suspend = audio_is_bluetooth_sco_device(inDeviceType()) &&
+ bool suspend = audio_is_bluetooth_sco_device(inDeviceType_l()) &&
mAfThreadCallback->btNrecIsOff();
if (mBtNrecSuspended.exchange(suspend) != suspend) {
for (size_t i = 0; i < mEffectChains.size(); i++) {
@@ -9510,7 +9526,7 @@
return {};
}
-void RecordThread::ioConfigChanged(audio_io_config_event_t event, pid_t pid,
+void RecordThread::ioConfigChanged_l(audio_io_config_event_t event, pid_t pid,
audio_port_handle_t portId) {
sp<AudioIoDescriptor> desc;
switch (event) {
@@ -9528,7 +9544,7 @@
desc = sp<AudioIoDescriptor>::make(mId);
break;
}
- mAfThreadCallback->ioConfigChanged(event, desc, pid);
+ mAfThreadCallback->ioConfigChanged_l(event, desc, pid);
}
void RecordThread::readInputParameters_l()
@@ -9995,25 +10011,27 @@
void MmapThread::disconnect()
{
ActiveTracks<IAfMmapTrack> activeTracks;
+ audio_port_handle_t localPortId;
{
audio_utils::lock_guard _l(mutex());
for (const sp<IAfMmapTrack>& t : mActiveTracks) {
activeTracks.add(t);
}
+ localPortId = mPortId;
}
for (const sp<IAfMmapTrack>& t : activeTracks) {
stop(t->portId());
}
// This will decrement references and may cause the destruction of this thread.
if (isOutput()) {
- AudioSystem::releaseOutput(mPortId);
+ AudioSystem::releaseOutput(localPortId);
} else {
- AudioSystem::releaseInput(mPortId);
+ AudioSystem::releaseInput(localPortId);
}
}
-void MmapThread::configure(const audio_attributes_t* attr,
+void MmapThread::configure_l(const audio_attributes_t* attr,
audio_stream_type_t streamType __unused,
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
@@ -10030,6 +10048,7 @@
status_t MmapThread::createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info)
{
+ audio_utils::lock_guard l(mutex());
if (mHalStream == 0) {
return NO_INIT;
}
@@ -10039,6 +10058,7 @@
status_t MmapThread::getMmapPosition(struct audio_mmap_position* position) const
{
+ audio_utils::lock_guard l(mutex());
if (mHalStream == 0) {
return NO_INIT;
}
@@ -10066,6 +10086,7 @@
const audio_attributes_t *attr,
audio_port_handle_t *handle)
{
+ audio_utils::lock_guard l(mutex());
ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
client.attributionSource.uid, mStandby, mPortId, *handle);
if (mHalStream == 0) {
@@ -10076,7 +10097,7 @@
// For the first track, reuse portId and session allocated when the stream was opened.
if (*handle == mPortId) {
- acquireWakeLock();
+ acquireWakeLock_l();
return NO_ERROR;
}
@@ -10086,20 +10107,23 @@
const AttributionSourceState adjAttributionSource = afutils::checkAttributionSourcePackage(
client.attributionSource);
+ const auto localSessionId = mSessionId;
+ auto localAttr = mAttr;
if (isOutput()) {
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = mSampleRate;
config.channel_mask = mChannelMask;
config.format = mFormat;
- audio_stream_type_t stream = streamType();
+ audio_stream_type_t stream = streamType_l();
audio_output_flags_t flags =
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId = mDeviceId;
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized;
bool isBitPerfect;
- ret = AudioSystem::getOutputForAttr(&mAttr, &io,
- mSessionId,
+ mutex().unlock();
+ ret = AudioSystem::getOutputForAttr(&localAttr, &io,
+ localSessionId,
&stream,
adjAttributionSource,
&config,
@@ -10109,6 +10133,8 @@
&secondaryOutputs,
&isSpatialized,
&isBitPerfect);
+ mutex().lock();
+ mAttr = localAttr;
ALOGD_IF(!secondaryOutputs.empty(),
"MmapThread::start does not support secondary outputs, ignoring them");
} else {
@@ -10117,14 +10143,17 @@
config.channel_mask = mChannelMask;
config.format = mFormat;
audio_port_handle_t deviceId = mDeviceId;
- ret = AudioSystem::getInputForAttr(&mAttr, &io,
+ mutex().unlock();
+ ret = AudioSystem::getInputForAttr(&localAttr, &io,
RECORD_RIID_INVALID,
- mSessionId,
+ localSessionId,
adjAttributionSource,
&config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ,
&deviceId,
&portId);
+ mutex().lock();
+ // localAttr is const for getInputForAttr.
}
// APM should not chose a different input or output stream for the same set of attributes
// and audo configuration
@@ -10135,18 +10164,20 @@
}
if (isOutput()) {
+ mutex().unlock();
ret = AudioSystem::startOutput(portId);
+ mutex().lock();
} else {
{
// Add the track record before starting input so that the silent status for the
// client can be cached.
- audio_utils::lock_guard _l(mutex());
setClientSilencedState_l(portId, false /*silenced*/);
}
+ mutex().unlock();
ret = AudioSystem::startInput(portId);
+ mutex().lock();
}
- audio_utils::lock_guard _l(mutex());
// abort if start is rejected by audio policy manager
if (ret != NO_ERROR) {
ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
@@ -10190,7 +10221,7 @@
mActiveTracks.add(track);
sp<IAfEffectChain> chain = getEffectChain_l(mSessionId);
if (chain != 0) {
- chain->setStrategy(getStrategyForStream(streamType()));
+ chain->setStrategy(getStrategyForStream(streamType_l()));
chain->incTrackCnt();
chain->incActiveTrackCnt();
}
@@ -10212,18 +10243,17 @@
status_t MmapThread::stop(audio_port_handle_t handle)
{
ALOGV("%s handle %d", __FUNCTION__, handle);
+ audio_utils::lock_guard l(mutex());
if (mHalStream == 0) {
return NO_INIT;
}
if (handle == mPortId) {
- releaseWakeLock();
+ releaseWakeLock_l();
return NO_ERROR;
}
- audio_utils::lock_guard _l(mutex());
-
sp<IAfMmapTrack> track;
for (const sp<IAfMmapTrack>& t : mActiveTracks) {
if (handle == t->portId()) {
@@ -10264,8 +10294,10 @@
}
status_t MmapThread::standby()
+NO_THREAD_SAFETY_ANALYSIS // clang bug
{
ALOGV("%s", __FUNCTION__);
+ audio_utils::lock_guard(mutex());
if (mHalStream == 0) {
return NO_INIT;
@@ -10279,7 +10311,7 @@
mThreadSnapshot.onEnd();
mStandby = true;
}
- releaseWakeLock();
+ releaseWakeLock_l();
return NO_ERROR;
}
@@ -10326,7 +10358,10 @@
bool MmapThread::threadLoop()
{
- checkSilentMode_l();
+ {
+ audio_utils::unique_lock _l(mutex());
+ checkSilentMode_l();
+ }
const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid()));
@@ -10366,7 +10401,7 @@
checkInvalidTracks_l();
- mActiveTracks.updatePowerState(this);
+ mActiveTracks.updatePowerState_l(this);
updateMetadata_l();
@@ -10423,7 +10458,7 @@
return {};
}
-void MmapThread::ioConfigChanged(audio_io_config_event_t event, pid_t pid,
+void MmapThread::ioConfigChanged_l(audio_io_config_event_t event, pid_t pid,
audio_port_handle_t portId __unused) {
sp<AudioIoDescriptor> desc;
bool isInput = false;
@@ -10445,7 +10480,7 @@
desc = sp<AudioIoDescriptor>::make(mId);
break;
}
- mAfThreadCallback->ioConfigChanged(event, desc, pid);
+ mAfThreadCallback->ioConfigChanged_l(event, desc, pid);
}
status_t MmapThread::createAudioPatch_l(const struct audio_patch* patch,
@@ -10562,6 +10597,7 @@
}
void MmapThread::toAudioPortConfig(struct audio_port_config* config)
+NO_THREAD_SAFETY_ANALYSIS // mAudioHwDev handle access
{
ThreadBase::toAudioPortConfig(config);
if (isOutput()) {
@@ -10679,7 +10715,6 @@
}
void MmapThread::checkInvalidTracks_l()
-NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex()
{
sp<MmapStreamCallback> callback;
for (const sp<IAfMmapTrack>& track : mActiveTracks) {
@@ -10741,14 +10776,24 @@
AudioHwDevice *hwDev, AudioStreamOut *output, bool systemReady)
: MmapThread(afThreadCallback, id, hwDev, output->stream, systemReady, true /* isOut */),
mStreamType(AUDIO_STREAM_MUSIC),
- mStreamVolume(1.0),
- mStreamMute(false),
mOutput(output)
{
snprintf(mThreadName, kThreadNameLength, "AudioMmapOut_%X", id);
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);
+ }
+ // 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;
@@ -10767,7 +10812,8 @@
audio_port_handle_t deviceId,
audio_port_handle_t portId)
{
- MmapThread::configure(attr, streamType, sessionId, callback, deviceId, portId);
+ audio_utils::lock_guard l(mutex());
+ MmapThread::configure_l(attr, streamType, sessionId, callback, deviceId, portId);
mStreamType = streamType;
}
@@ -10805,8 +10851,8 @@
void MmapPlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
audio_utils::lock_guard _l(mutex());
+ mStreamTypes[stream].volume = value;
if (stream == mStreamType) {
- mStreamVolume = value;
broadcast_l();
}
}
@@ -10814,17 +10860,14 @@
float MmapPlaybackThread::streamVolume(audio_stream_type_t stream) const
{
audio_utils::lock_guard _l(mutex());
- if (stream == mStreamType) {
- return mStreamVolume;
- }
- return 0.0f;
+ return mStreamTypes[stream].volume;
}
void MmapPlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
audio_utils::lock_guard _l(mutex());
+ mStreamTypes[stream].mute = muted;
if (stream == mStreamType) {
- mStreamMute= muted;
broadcast_l();
}
}
@@ -10864,14 +10907,13 @@
{
float volume;
- if (mMasterMute || mStreamMute) {
+ if (mMasterMute || streamMuted_l()) {
volume = 0;
} else {
- volume = mMasterVolume * mStreamVolume;
+ volume = mMasterVolume * streamVolume_l();
}
if (volume != mHalVolFloat) {
-
// Convert volumes from float to 8.24
uint32_t vol = (uint32_t)(volume * (1 << 24));
@@ -10905,8 +10947,8 @@
track->setMetadataHasChanged();
track->processMuteEvent_l(mAfThreadCallback->getOrCreateAudioManager(),
/*muteState=*/{mMasterMute,
- mStreamVolume == 0.f,
- mStreamMute,
+ streamVolume_l() == 0.f,
+ streamMuted_l(),
// TODO(b/241533526): adjust logic to include mute from AppOps
false /*muteFromPlaybackRestricted*/,
false /*muteFromClientVolume*/,
@@ -11019,7 +11061,7 @@
MmapThread::dumpInternals_l(fd, args);
dprintf(fd, " Stream type: %d Stream volume: %f HAL volume: %f Stream mute %d\n",
- mStreamType, mStreamVolume, mHalVolFloat, mStreamMute);
+ mStreamType, streamVolume_l(), mHalVolFloat, streamMuted_l());
dprintf(fd, " Master volume: %f Master mute %d\n", mMasterVolume, mMasterMute);
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 01a8a22..724a46d 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -44,6 +44,13 @@
public:
static const char *threadTypeToString(type_t type);
+ // ThreadBase_ThreadLoop is a virtual mutex (always nullptr) that
+ // guards methods and variables that ONLY run and are accessed
+ // on the single threaded threadLoop().
+ //
+ // As access is by a single thread, the variables are thread safe.
+ static audio_utils::mutex* ThreadBase_ThreadLoop;
+
IAfThreadCallback* afThreadCallback() const final { return mAfThreadCallback.get(); }
ThreadBase(const sp<IAfThreadCallback>& afThreadCallback, audio_io_handle_t id,
@@ -51,7 +58,7 @@
~ThreadBase() override;
status_t readyToRun() final;
- void clearPowerManager() final;
+ void clearPowerManager() final EXCLUDES_ThreadBase_Mutex;
// base for record and playback
enum {
@@ -68,11 +75,9 @@
class ConfigEventData: public RefBase {
public:
- virtual ~ConfigEventData() {}
-
virtual void dump(char *buffer, size_t size) = 0;
protected:
- ConfigEventData() {}
+ ConfigEventData() = default;
};
// Config event sequence by client if status needed (e.g binder thread calling setParameters()):
@@ -103,14 +108,22 @@
}
}
- audio_utils::mutex& mutex() const { return mMutex; }
+ audio_utils::mutex& mutex() const RETURN_CAPABILITY(audio_utils::ConfigEvent_Mutex) {
+ return mMutex;
+ }
const int mType; // event type e.g. CFG_EVENT_IO
mutable audio_utils::mutex mMutex; // mutex associated with mCondition
audio_utils::condition_variable mCondition; // condition for status return
+
+ // NO_THREAD_SAFETY_ANALYSIS Can we add GUARDED_BY?
status_t mStatus; // status communicated to sender
- bool mWaitStatus; // true if sender is waiting for status
- bool mRequiresSystemReady; // true if must wait for system ready to enter event queue
- sp<ConfigEventData> mData; // event specific parameter data
+
+ bool mWaitStatus GUARDED_BY(mutex()); // true if sender is waiting for status
+ // true if must wait for system ready to enter event queue
+ bool mRequiresSystemReady GUARDED_BY(mutex());
+
+ // NO_THREAD_SAFETY_ANALYSIS Can we add GUARDED_BY?
+ sp<ConfigEventData> mData; // event specific parameter data
protected:
explicit ConfigEvent(int type, bool requiresSystemReady = false) :
@@ -197,7 +210,7 @@
}
const struct audio_patch mPatch;
- audio_patch_handle_t mHandle;
+ audio_patch_handle_t mHandle; // cannot be const
};
class CreateAudioPatchConfigEvent : public ConfigEvent {
@@ -219,7 +232,7 @@
snprintf(buffer, size, "- Patch handle: %u\n", mHandle);
}
- audio_patch_handle_t mHandle;
+ const audio_patch_handle_t mHandle;
};
class ReleaseAudioPatchConfigEvent : public ConfigEvent {
@@ -240,7 +253,7 @@
snprintf(buffer, size, "- Devices: %s", android::toString(mOutDevices).c_str());
}
- DeviceDescriptorBaseVector mOutDevices;
+ const DeviceDescriptorBaseVector mOutDevices;
};
class UpdateOutDevicesConfigEvent : public ConfigEvent {
@@ -260,7 +273,7 @@
snprintf(buffer, size, "- mMaxSharedAudioHistoryMs: %d", mMaxSharedAudioHistoryMs);
}
- int32_t mMaxSharedAudioHistoryMs;
+ const int32_t mMaxSharedAudioHistoryMs;
};
class ResizeBufferConfigEvent : public ConfigEvent {
@@ -289,15 +302,14 @@
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
explicit PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {}
- virtual ~PMDeathRecipient() {}
// IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who);
+ void binderDied(const wp<IBinder>& who) final;
private:
DISALLOW_COPY_AND_ASSIGN(PMDeathRecipient);
- wp<ThreadBase> mThread;
+ const wp<ThreadBase> mThread;
};
type_t type() const final { return mType; }
@@ -311,8 +323,9 @@
uint32_t channelCount() const final { return mChannelCount; }
audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
uint32_t hapticChannelCount() const override { return 0; }
- uint32_t latency_l() const override { return 0; }
- void setVolumeForOutput_l(float /* left */, float /* right */) const override {}
+ uint32_t latency_l() const override { return 0; } // NO_THREAD_SAFETY_ANALYSIS
+ void setVolumeForOutput_l(float /* left */, float /* right */) const override
+ REQUIRES(mutex()) {}
// Return's the HAL's frame count i.e. fast mixer buffer size.
size_t frameCountHAL() const final { return mFrameCount; }
@@ -320,44 +333,49 @@
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
- void exit() final;
- status_t setParameters(const String8& keyValuePairs) final;
+ void exit() final EXCLUDES_ThreadBase_Mutex;
+ status_t setParameters(const String8& keyValuePairs) final EXCLUDES_ThreadBase_Mutex;
// sendConfigEvent_l() must be called with ThreadBase::mutex() held
// Can temporarily release the lock if waiting for a reply from
// processConfigEvents_l().
- status_t sendConfigEvent_l(sp<ConfigEvent>& event);
+ status_t sendConfigEvent_l(sp<ConfigEvent>& event) REQUIRES(mutex());
void sendIoConfigEvent(audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final EXCLUDES_ThreadBase_Mutex;
void sendIoConfigEvent_l(audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final;
- void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio, bool forApp) final;
- void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio, bool forApp) final;
- status_t sendSetParameterConfigEvent_l(const String8& keyValuePair) final;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final REQUIRES(mutex());
+ void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio, bool forApp) final
+ EXCLUDES_ThreadBase_Mutex;
+ void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio, bool forApp) final
+ REQUIRES(mutex());
+ status_t sendSetParameterConfigEvent_l(const String8& keyValuePair) final REQUIRES(mutex());
status_t sendCreateAudioPatchConfigEvent(const struct audio_patch* patch,
- audio_patch_handle_t* handle) final;
- status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle) final;
+ audio_patch_handle_t* handle) final EXCLUDES_ThreadBase_Mutex;
+ status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle) final
+ EXCLUDES_ThreadBase_Mutex;
status_t sendUpdateOutDeviceConfigEvent(
- const DeviceDescriptorBaseVector& outDevices) final;
- void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs) final;
- void sendCheckOutputStageEffectsEvent() final;
- void sendCheckOutputStageEffectsEvent_l() final;
- void sendHalLatencyModesChangedEvent_l() final;
+ const DeviceDescriptorBaseVector& outDevices) final EXCLUDES_ThreadBase_Mutex;
+ void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs) final REQUIRES(mutex());
+ void sendCheckOutputStageEffectsEvent() final EXCLUDES_ThreadBase_Mutex;
+ void sendCheckOutputStageEffectsEvent_l() final REQUIRES(mutex());
+ void sendHalLatencyModesChangedEvent_l() final REQUIRES(mutex());
- void processConfigEvents_l() final;
+ void processConfigEvents_l() final REQUIRES(mutex());
void setCheckOutputStageEffects() override {}
void updateOutDevices(const DeviceDescriptorBaseVector& outDevices) override;
void toAudioPortConfig(struct audio_port_config* config) override;
- void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) override;
+ void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) override REQUIRES(mutex());
// see note at declaration of mStandby, mOutDevice and mInDevice
bool inStandby() const override { return mStandby; }
- const DeviceTypeSet outDeviceTypes() const final {
+ const DeviceTypeSet outDeviceTypes_l() const final REQUIRES(mutex()) {
return getAudioDeviceTypes(mOutDeviceTypeAddrs);
}
- audio_devices_t inDeviceType() const final { return mInDeviceTypeAddr.mType; }
- DeviceTypeSet getDeviceTypes() const final {
- return isOutput() ? outDeviceTypes() : DeviceTypeSet({inDeviceType()});
+ audio_devices_t inDeviceType_l() const final REQUIRES(mutex()) {
+ return mInDeviceTypeAddr.mType;
+ }
+ DeviceTypeSet getDeviceTypes_l() const final REQUIRES(mutex()) {
+ return isOutput() ? outDeviceTypes_l() : DeviceTypeSet({inDeviceType_l()});
}
const AudioDeviceTypeAddrVector& outDeviceTypeAddrs() const final {
@@ -410,23 +428,26 @@
// get effect chain corresponding to session Id.
sp<IAfEffectChain> getEffectChain(audio_session_t sessionId) const final;
// same as getEffectChain() but must be called with ThreadBase mutex locked
- sp<IAfEffectChain> getEffectChain_l(audio_session_t sessionId) const final;
- std::vector<int> getEffectIds_l(audio_session_t sessionId) const final;
+ sp<IAfEffectChain> getEffectChain_l(audio_session_t sessionId) const final REQUIRES(mutex());
+ std::vector<int> getEffectIds_l(audio_session_t sessionId) const final REQUIRES(mutex());
// lock all effect chains Mutexes. Must be called before releasing the
// ThreadBase mutex before processing the mixer and effects. This guarantees the
// integrity of the chains during the process.
// Also sets the parameter 'effectChains' to current value of mEffectChains.
- void lockEffectChains_l(Vector<sp<IAfEffectChain>>& effectChains) final;
+ void lockEffectChains_l(Vector<sp<IAfEffectChain>>& effectChains) final REQUIRES(mutex());
// unlock effect chains after process
void unlockEffectChains(const Vector<sp<IAfEffectChain>>& effectChains) final;
// get a copy of mEffectChains vector
- Vector<sp<IAfEffectChain>> getEffectChains_l() const final { return mEffectChains; };
+ Vector<sp<IAfEffectChain>> getEffectChains_l() const final REQUIRES(mutex()) {
+ return mEffectChains;
+ }
// set audio mode to all effect chains
void setMode(audio_mode_t mode) final;
// get effect module with corresponding ID on specified audio session
sp<IAfEffectModule> getEffect(audio_session_t sessionId, int effectId) const final;
- sp<IAfEffectModule> getEffect_l(audio_session_t sessionId, int effectId) const final;
+ sp<IAfEffectModule> getEffect_l(audio_session_t sessionId, int effectId) const final
+ REQUIRES(mutex());
// add and effect module. Also creates the effect chain is none exists for
// the effects audio session. Only called in a context of moving an effect
// from one thread to another
@@ -434,20 +455,22 @@
REQUIRES(audio_utils::AudioFlinger_Mutex, mutex());
// remove and effect module. Also removes the effect chain is this was the last
// effect
- void removeEffect_l(const sp<IAfEffectModule>& effect, bool release = false) final;
+ void removeEffect_l(const sp<IAfEffectModule>& effect, bool release = false) final
+ REQUIRES(mutex());
// disconnect an effect handle from module and destroy module if last handle
void disconnectEffectHandle(IAfEffectHandle* handle, bool unpinIfLast) final;
// detach all tracks connected to an auxiliary effect
- void detachAuxEffect_l(int /* effectId */) override {}
+ void detachAuxEffect_l(int /* effectId */) override REQUIRES(mutex()) {}
// TODO(b/291317898) - remove hasAudioSession_l below.
- uint32_t hasAudioSession_l(audio_session_t sessionId) const override = 0;
- uint32_t hasAudioSession(audio_session_t sessionId) const final {
- std::lock_guard _l(mutex());
- return hasAudioSession_l(sessionId);
- }
+ uint32_t hasAudioSession_l(audio_session_t sessionId) const override REQUIRES(mutex()) = 0;
+ uint32_t hasAudioSession(audio_session_t sessionId) const final EXCLUDES_ThreadBase_Mutex {
+ audio_utils::lock_guard _l(mutex());
+ return hasAudioSession_l(sessionId);
+ }
template <typename T>
- uint32_t hasAudioSession_l(audio_session_t sessionId, const T& tracks) const {
+ uint32_t hasAudioSession_l(audio_session_t sessionId, const T& tracks) const
+ REQUIRES(mutex()) {
uint32_t result = 0;
if (getEffectChain_l(sessionId) != 0) {
result = EFFECT_SESSION;
@@ -476,9 +499,9 @@
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
product_strategy_t getStrategyForSession_l(
- audio_session_t /* sessionId */) const override {
- return static_cast<product_strategy_t>(0);
- }
+ audio_session_t /* sessionId */) const override REQUIRES(mutex()){
+ return static_cast<product_strategy_t>(0);
+ }
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
@@ -497,38 +520,41 @@
sp<IMemory> pipeMemory() const override { return nullptr; }
- void systemReady() final;
+ void systemReady() final EXCLUDES_ThreadBase_Mutex;
- void broadcast_l() final;
+ void broadcast_l() final REQUIRES(mutex());
- bool isTimestampCorrectionEnabled() const override { return false; }
+ bool isTimestampCorrectionEnabled_l() const override REQUIRES(mutex()) { return false; }
bool isMsdDevice() const final { return mIsMsdDevice; }
void dump(int fd, const Vector<String16>& args) override;
// deliver stats to mediametrics.
- void sendStatistics(bool force) final;
+ void sendStatistics(bool force) final
+ REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
audio_utils::mutex& mutex() const final RETURN_CAPABILITY(audio_utils::ThreadBase_Mutex) {
return mMutex;
}
mutable audio_utils::mutex mMutex;
- void onEffectEnable(const sp<IAfEffectModule>& effect) final;
- void onEffectDisable() final;
+ void onEffectEnable(const sp<IAfEffectModule>& effect) final EXCLUDES_ThreadBase_Mutex;
+ void onEffectDisable() final EXCLUDES_ThreadBase_Mutex;
// invalidateTracksForAudioSession_l must be called with holding mutex().
- void invalidateTracksForAudioSession_l(audio_session_t /* sessionId */) const override {}
+ void invalidateTracksForAudioSession_l(audio_session_t /* sessionId */) const override
+ REQUIRES(mutex()) {}
// Invalidate all the tracks with the given audio session.
- void invalidateTracksForAudioSession(audio_session_t sessionId) const final {
- std::lock_guard _l(mutex());
+ void invalidateTracksForAudioSession(audio_session_t sessionId) const final
+ EXCLUDES_ThreadBase_Mutex {
+ audio_utils::lock_guard _l(mutex());
invalidateTracksForAudioSession_l(sessionId);
}
template <typename T>
- void invalidateTracksForAudioSession_l(audio_session_t sessionId,
- const T& tracks) const {
+ void invalidateTracksForAudioSession_l(audio_session_t sessionId,
+ const T& tracks) const REQUIRES(mutex()) {
for (size_t i = 0; i < tracks.size(); ++i) {
const sp<IAfTrackBase>& track = tracks[i];
if (sessionId == track->sessionId()) {
@@ -553,41 +579,44 @@
effect_uuid_t mType; // effect type UUID
};
- void acquireWakeLock();
- virtual void acquireWakeLock_l();
- void releaseWakeLock();
- void releaseWakeLock_l();
- void updateWakeLockUids_l(const SortedVector<uid_t> &uids);
- void getPowerManager_l();
+ void acquireWakeLock() EXCLUDES_ThreadBase_Mutex;
+ virtual void acquireWakeLock_l() REQUIRES(mutex());
+ void releaseWakeLock() EXCLUDES_ThreadBase_Mutex;
+ void releaseWakeLock_l() REQUIRES(mutex());
+ void updateWakeLockUids_l(const SortedVector<uid_t> &uids) REQUIRES(mutex());
+ void getPowerManager_l() REQUIRES(mutex());
// suspend or restore effects of the specified type (or all if type is NULL)
// on a given session. The number of suspend requests is counted and restore
// occurs when all suspend requests are cancelled.
- void setEffectSuspended_l(const effect_uuid_t *type,
+ void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
- audio_session_t sessionId) final;
+ audio_session_t sessionId) final REQUIRES(mutex());
// updated mSuspendedSessions when an effect is suspended or restored
- void updateSuspendedSessions_l(const effect_uuid_t *type,
+ void updateSuspendedSessions_l(const effect_uuid_t *type,
bool suspend,
- audio_session_t sessionId);
+ audio_session_t sessionId) REQUIRES(mutex());
// check if some effects must be suspended when an effect chain is added
- void checkSuspendOnAddEffectChain_l(const sp<IAfEffectChain>& chain);
+ void checkSuspendOnAddEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex());
// sends the metadata of the active tracks to the HAL
struct MetadataUpdate {
std::vector<playback_track_metadata_v7_t> playbackMetadataUpdate;
std::vector<record_track_metadata_v7_t> recordMetadataUpdate;
};
- virtual MetadataUpdate updateMetadata_l() = 0;
+ // NO_THREAD_SAFETY_ANALYSIS, updateMetadata_l() should include ThreadBase_ThreadLoop
+ // but MmapThread::start() -> exitStandby_l() -> updateMetadata_l() prevents this.
+ virtual MetadataUpdate updateMetadata_l() REQUIRES(mutex()) = 0;
String16 getWakeLockTag();
- virtual void preExit() { }
- virtual void setMasterMono_l(bool mono __unused) { }
+ virtual void preExit() EXCLUDES_ThreadBase_Mutex {}
+ virtual void setMasterMono_l(bool mono __unused) REQUIRES(mutex()) {}
virtual bool requireMonoBlend() { return false; }
// called within the threadLoop to obtain timestamp from the HAL.
- virtual status_t threadloop_getHalTimestamp_l(
- ExtendedTimestamp *timestamp __unused) const {
+ virtual status_t threadloop_getHalTimestamp_l(
+ ExtendedTimestamp *timestamp __unused) const
+ REQUIRES(mutex(), ThreadBase_ThreadLoop) {
return INVALID_OPERATION;
}
public:
@@ -595,11 +624,12 @@
product_strategy_t getStrategyForStream(audio_stream_type_t stream) const;
protected:
- virtual void onHalLatencyModesChanged_l() {}
+ virtual void onHalLatencyModesChanged_l() REQUIRES(mutex()) {}
- virtual void dumpInternals_l(int fd __unused, const Vector<String16>& args __unused)
- { }
- virtual void dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
+ virtual void dumpInternals_l(int fd __unused, const Vector<String16>& args __unused)
+ REQUIRES(mutex()) {}
+ virtual void dumpTracks_l(int fd __unused, const Vector<String16>& args __unused)
+ REQUIRES(mutex()) {}
const type_t mType;
@@ -624,10 +654,14 @@
// HAL format if Fastmixer is used.
audio_format_t mHALFormat;
size_t mBufferSize; // HAL buffer size for read() or write()
- AudioDeviceTypeAddrVector mOutDeviceTypeAddrs; // output device types and addresses
- AudioDeviceTypeAddr mInDeviceTypeAddr; // input device type and address
- Vector< sp<ConfigEvent> > mConfigEvents;
- Vector< sp<ConfigEvent> > mPendingConfigEvents; // events awaiting system ready
+
+ // output device types and addresses
+ AudioDeviceTypeAddrVector mOutDeviceTypeAddrs GUARDED_BY(mutex());
+ AudioDeviceTypeAddr mInDeviceTypeAddr GUARDED_BY(mutex()); // input device type and address
+ Vector<sp<ConfigEvent>> mConfigEvents GUARDED_BY(mutex());
+
+ // events awaiting system ready
+ Vector<sp<ConfigEvent>> mPendingConfigEvents GUARDED_BY(mutex());
// These fields are written and read by thread itself without lock or barrier,
// and read by other threads without lock or barrier via standby(), outDeviceTypes()
@@ -637,17 +671,17 @@
// with possibility that it might be inconsistent with other information.
bool mStandby; // Whether thread is currently in standby.
+ // NO_THREAD_SAFETY_ANALYSIS - mPatch and mAudioSource should be guarded by mutex().
struct audio_patch mPatch;
-
audio_source_t mAudioSource;
const audio_io_handle_t mId;
- Vector<sp<IAfEffectChain>> mEffectChains;
+ Vector<sp<IAfEffectChain>> mEffectChains GUARDED_BY(mutex());
static const int kThreadNameLength = 16; // prctl(PR_SET_NAME) limit
char mThreadName[kThreadNameLength]; // guaranteed NUL-terminated
- sp<os::IPowerManager> mPowerManager;
- sp<IBinder> mWakeLockToken;
+ sp<os::IPowerManager> mPowerManager GUARDED_BY(mutex());
+ sp<IBinder> mWakeLockToken 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
@@ -658,27 +692,31 @@
static const size_t kLogSize = 4 * 1024;
sp<NBLog::Writer> mNBLogWriter;
bool mSystemReady;
- ExtendedTimestamp mTimestamp;
- TimestampVerifier< // For timestamp statistics.
- int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier;
+
+ // NO_THREAD_SAFETY_ANALYSIS - mTimestamp and mTimestampVerifier should be
+ // accessed under mutex for the RecordThread.
+ ExtendedTimestamp mTimestamp;
+ TimestampVerifier<int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier;
// DIRECT and OFFLOAD threads should reset frame count to zero on stop/flush
// TODO: add confirmation checks:
// 1) DIRECT threads and linear PCM format really resets to 0?
// 2) Is frame count really valid if not linear pcm?
// 3) Are all 64 bits of position returned, not just lowest 32 bits?
// Timestamp corrected device should be a single device.
- audio_devices_t mTimestampCorrectedDevice = AUDIO_DEVICE_NONE;
+
+ audio_devices_t mTimestampCorrectedDevice = AUDIO_DEVICE_NONE; // CONST set in ctor
// ThreadLoop statistics per iteration.
- int64_t mLastIoBeginNs = -1;
- int64_t mLastIoEndNs = -1;
+ std::atomic<int64_t> mLastIoBeginNs = -1; // set in threadLoop, read by dump()
+ int64_t mLastIoEndNs GUARDED_BY(ThreadBase_ThreadLoop) = -1;
// ThreadSnapshot is thread-safe (internally locked)
mediautils::ThreadSnapshot mThreadSnapshot;
- // This should be read under ThreadBase lock (if not on the threadLoop thread).
- audio_utils::Statistics<double> mIoJitterMs{0.995 /* alpha */};
- audio_utils::Statistics<double> mProcessTimeMs{0.995 /* alpha */};
+ audio_utils::Statistics<double> mIoJitterMs GUARDED_BY(mutex()) {0.995 /* alpha */};
+ audio_utils::Statistics<double> mProcessTimeMs GUARDED_BY(mutex()) {0.995 /* alpha */};
+
+ // NO_THREAD_SAFETY_ANALYSIS GUARDED_BY(mutex())
audio_utils::Statistics<double> mLatencyMs{0.995 /* alpha */};
audio_utils::Statistics<double> mMonopipePipeDepthStats{0.999 /* alpha */};
@@ -764,7 +802,8 @@
// ThreadBase thread.
void clear();
// periodically called in the threadLoop() to update power state uids.
- void updatePowerState(const sp<ThreadBase>& thread, bool force = false);
+ void updatePowerState_l(const sp<ThreadBase>& thread, bool force = false)
+ REQUIRES(audio_utils::ThreadBase_Mutex);
/** @return true if one or move active tracks was added or removed since the
* last time this function was called or the vector was created.
@@ -797,11 +836,11 @@
bool mHasChanged = false;
};
- SimpleLog mLocalLog;
+ SimpleLog mLocalLog; // locked internally
private:
- void dumpBase_l(int fd, const Vector<String16>& args);
- void dumpEffectChains_l(int fd, const Vector<String16>& args);
+ void dumpBase_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
+ void dumpEffectChains_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
};
// --- PlaybackThread ---
@@ -834,35 +873,38 @@
~PlaybackThread() override;
// Thread virtuals
- bool threadLoop() final;
+ bool threadLoop() final REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
// RefBase
void onFirstRef() override;
status_t checkEffectCompatibility_l(
- const effect_descriptor_t* desc, audio_session_t sessionId) final;
+ const effect_descriptor_t* desc, audio_session_t sessionId) final REQUIRES(mutex());
- void addOutputTrack_l(const sp<IAfTrack>& track) final {
+ void addOutputTrack_l(const sp<IAfTrack>& track) final REQUIRES(mutex()) {
mTracks.add(track);
}
protected:
// Code snippets that were lifted up out of threadLoop()
- virtual void threadLoop_mix() = 0;
- virtual void threadLoop_sleepTime() = 0;
- virtual ssize_t threadLoop_write();
- virtual void threadLoop_drain();
- virtual void threadLoop_standby();
- virtual void threadLoop_exit();
- virtual void threadLoop_removeTracks(const Vector<sp<IAfTrack>>& tracksToRemove);
+ virtual void threadLoop_mix() REQUIRES(ThreadBase_ThreadLoop) = 0;
+ virtual void threadLoop_sleepTime() REQUIRES(ThreadBase_ThreadLoop) = 0;
+ virtual ssize_t threadLoop_write() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_drain() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_standby() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_exit() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_removeTracks(const Vector<sp<IAfTrack>>& tracksToRemove)
+ REQUIRES(ThreadBase_ThreadLoop);
// prepareTracks_l reads and writes mActiveTracks, and returns
// the pending set of tracks to remove via Vector 'tracksToRemove'. The caller
// is responsible for clearing or destroying this Vector later on, when it
// is safe to do so. That will drop the final ref count and destroy the tracks.
- virtual mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) = 0;
- void removeTracks_l(const Vector<sp<IAfTrack>>& tracksToRemove);
- status_t handleVoipVolume_l(float *volume);
+ virtual mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove)
+ REQUIRES(mutex(), ThreadBase_ThreadLoop) = 0;
+
+ void removeTracks_l(const Vector<sp<IAfTrack>>& tracksToRemove) REQUIRES(mutex());
+ status_t handleVoipVolume_l(float *volume) REQUIRES(mutex());
// StreamOutHalInterfaceCallback implementation
virtual void onWriteReady();
@@ -875,9 +917,9 @@
protected:
virtual bool waitingAsyncCallback();
- virtual bool waitingAsyncCallback_l();
- virtual bool shouldStandby_l();
- virtual void onAddNewTrack_l();
+ virtual bool waitingAsyncCallback_l() REQUIRES(mutex());
+ virtual bool shouldStandby_l() REQUIRES(mutex(), ThreadBase_ThreadLoop);
+ virtual void onAddNewTrack_l() REQUIRES(mutex());
public: // AsyncCallbackThread
void onAsyncError(); // error reported by AsyncCallbackThread
protected:
@@ -886,20 +928,21 @@
const std::basic_string<uint8_t>& metadataBs) final;
// ThreadBase virtuals
- virtual void preExit();
+ void preExit() final EXCLUDES_ThreadBase_Mutex;
virtual bool keepWakeLock() const { return true; }
- virtual void acquireWakeLock_l() {
+ virtual void acquireWakeLock_l() REQUIRES(mutex()) {
ThreadBase::acquireWakeLock_l();
- mActiveTracks.updatePowerState(this, true /* force */);
+ mActiveTracks.updatePowerState_l(this, true /* force */);
}
- virtual void checkOutputStageEffects() {}
+ virtual void checkOutputStageEffects()
+ REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex {}
virtual void setHalLatencyMode_l() {}
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
- void dumpTracks_l(int fd, const Vector<String16>& args) final;
+ void dumpInternals_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
+ void dumpTracks_l(int fd, const Vector<String16>& args) final REQUIRES(mutex());
public:
@@ -908,15 +951,15 @@
// return estimated latency in milliseconds, as reported by HAL
uint32_t latency() const final;
// same, but lock must already be held
- uint32_t latency_l() const final;
+ uint32_t latency_l() const final /* REQUIRES(mutex()) */; // NO_THREAD_SAFETY_ANALYSIS
// VolumeInterface
void setMasterVolume(float value) final;
- void setMasterBalance(float balance) override;
+ void setMasterBalance(float balance) override EXCLUDES_ThreadBase_Mutex;
void setMasterMute(bool muted) final;
- void setStreamVolume(audio_stream_type_t stream, float value) final;
- void setStreamMute(audio_stream_type_t stream, bool muted) final;
- float streamVolume(audio_stream_type_t stream) const final;
+ 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;
void setVolumeForOutput_l(float left, float right) const final;
sp<IAfTrack> createTrack_l(
@@ -940,63 +983,80 @@
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
- bool isBitPerfect) final
+ bool isBitPerfect,
+ audio_output_flags_t* afTrackFlags) final
REQUIRES(audio_utils::AudioFlinger_Mutex);
bool isTrackActive(const sp<IAfTrack>& track) const final {
return mActiveTracks.indexOf(track) >= 0;
}
- AudioStreamOut* getOutput_l() const final { return mOutput; }
- AudioStreamOut* getOutput() const final;
- AudioStreamOut* clearOutput() final;
+ AudioStreamOut* getOutput_l() const final REQUIRES(mutex()) { return mOutput; }
+ AudioStreamOut* getOutput() const final EXCLUDES_ThreadBase_Mutex;
+ AudioStreamOut* clearOutput() final EXCLUDES_ThreadBase_Mutex;
+
+ // NO_THREAD_SAFETY_ANALYSIS -- probably needs a lock.
sp<StreamHalInterface> stream() const final;
- // a very large number of suspend() will eventually wraparound, but unlikely
- void suspend() final { (void) android_atomic_inc(&mSuspended); }
- void restore() final
- {
- // if restore() is done without suspend(), get back into
- // range so that the next suspend() will operate correctly
- if (android_atomic_dec(&mSuspended) <= 0) {
- android_atomic_release_store(0, &mSuspended);
- }
- }
- bool isSuspended() const final
- { return android_atomic_acquire_load(&mSuspended) > 0; }
+ // suspend(), restore(), and isSuspended() are implemented atomically.
+ void suspend() final { ++mSuspended; }
+ void restore() final {
+ // if restore() is done without suspend(), get back into
+ // range so that the next suspend() will operate correctly
+ while (true) {
+ int32_t suspended = mSuspended;
+ if (suspended <= 0) {
+ ALOGW("%s: invalid mSuspended %d <= 0", __func__, suspended);
+ return;
+ }
+ const int32_t desired = suspended - 1;
+ if (mSuspended.compare_exchange_weak(suspended, desired)) return;
+ }
+ }
+ bool isSuspended() const final { return mSuspended > 0; }
- String8 getParameters(const String8& keys);
- void ioConfigChanged(audio_io_config_event_t event, pid_t pid = 0,
+ String8 getParameters(const String8& keys) EXCLUDES_ThreadBase_Mutex;
+
+ // Hold either the AudioFlinger::mutex or the ThreadBase::mutex
+ void ioConfigChanged_l(audio_io_config_event_t event, pid_t pid = 0,
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final;
- status_t getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames) const final;
+ status_t getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames) const final
+ EXCLUDES_ThreadBase_Mutex;
// Consider also removing and passing an explicit mMainBuffer initialization
// parameter to AF::IAfTrack::Track().
float* sinkBuffer() const final {
return reinterpret_cast<float *>(mSinkBuffer); };
- void detachAuxEffect_l(int effectId) final;
+ void detachAuxEffect_l(int effectId) final REQUIRES(mutex());
- status_t attachAuxEffect(const sp<IAfTrack>& track, int EffectId) final;
- status_t attachAuxEffect_l(const sp<IAfTrack>& track, int EffectId) final;
+ status_t attachAuxEffect(const sp<IAfTrack>& track, int EffectId) final
+ EXCLUDES_ThreadBase_Mutex;
+ status_t attachAuxEffect_l(const sp<IAfTrack>& track, int EffectId) final REQUIRES(mutex());
- status_t addEffectChain_l(const sp<IAfEffectChain>& chain) final;
- size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) final;
- uint32_t hasAudioSession_l(audio_session_t sessionId) const final {
+ status_t addEffectChain_l(const sp<IAfEffectChain>& chain) final REQUIRES(mutex());
+ size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) final REQUIRES(mutex());
+ uint32_t hasAudioSession_l(audio_session_t sessionId) const final REQUIRES(mutex()) {
return ThreadBase::hasAudioSession_l(sessionId, mTracks);
}
- product_strategy_t getStrategyForSession_l(audio_session_t sessionId) const final;
+ product_strategy_t getStrategyForSession_l(audio_session_t sessionId) const final
+ REQUIRES(mutex());
- status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) final;
+ status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) final
+ EXCLUDES_ThreadBase_Mutex;
+ // could be static.
bool isValidSyncEvent(const sp<audioflinger::SyncEvent>& event) const final;
- // called with AudioFlinger lock held
- bool invalidateTracks_l(audio_stream_type_t streamType) final;
- bool invalidateTracks_l(std::set<audio_port_handle_t>& portIds) final;
+ // Does this require the AudioFlinger mutex as well?
+ bool invalidateTracks_l(audio_stream_type_t streamType) final
+ REQUIRES(mutex());
+ bool invalidateTracks_l(std::set<audio_port_handle_t>& portIds) final
+ REQUIRES(mutex());
void invalidateTracks(audio_stream_type_t streamType) override;
// Invalidate tracks by a set of port ids. The port id will be removed from
// the given set if the corresponding track is found and invalidated.
- void invalidateTracks(std::set<audio_port_handle_t>& portIds) override;
+ void invalidateTracks(std::set<audio_port_handle_t>& portIds) override
+ EXCLUDES_ThreadBase_Mutex;
size_t frameCount() const final { return mNormalFrameCount; }
@@ -1004,30 +1064,33 @@
return mMixerChannelMask;
}
- status_t getTimestamp_l(AudioTimestamp& timestamp) final;
+ status_t getTimestamp_l(AudioTimestamp& timestamp) final
+ REQUIRES(mutex(), ThreadBase_ThreadLoop);
- void addPatchTrack(const sp<IAfPatchTrack>& track) final;
- void deletePatchTrack(const sp<IAfPatchTrack>& track) final;
+ void addPatchTrack(const sp<IAfPatchTrack>& track) final EXCLUDES_ThreadBase_Mutex;
+ void deletePatchTrack(const sp<IAfPatchTrack>& track) final EXCLUDES_ThreadBase_Mutex;
+ // NO_THREAD_SAFETY_ANALYSIS - fix this to use atomics.
void toAudioPortConfig(struct audio_port_config* config) final;
// Return the asynchronous signal wait time.
- int64_t computeWaitTimeNs_l() const override { return INT64_MAX; }
+ int64_t computeWaitTimeNs_l() const override REQUIRES(mutex()) { return INT64_MAX; }
// returns true if the track is allowed to be added to the thread.
bool isTrackAllowed_l(
audio_channel_mask_t channelMask __unused,
audio_format_t format __unused,
audio_session_t sessionId __unused,
- uid_t uid) const override {
+ uid_t uid) const override REQUIRES(mutex()) {
return trackCountForUid_l(uid) < PlaybackThread::kMaxTracksPerUid
&& mTracks.size() < PlaybackThread::kMaxTracks;
}
- bool isTimestampCorrectionEnabled() const final {
- return audio_is_output_devices(mTimestampCorrectedDevice)
- && outDeviceTypes().count(mTimestampCorrectedDevice) != 0;
+ bool isTimestampCorrectionEnabled_l() const final REQUIRES(mutex()) {
+ return audio_is_output_devices(mTimestampCorrectedDevice)
+ && outDeviceTypes_l().count(mTimestampCorrectedDevice) != 0;
}
+ // NO_THREAD_SAFETY_ANALYSIS - fix this to be atomic.
bool isStreamInitialized() const final {
return !(mOutput == nullptr || mOutput->stream == nullptr);
}
@@ -1044,12 +1107,12 @@
return (mHapticChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE;
}
- void setDownStreamPatch(const struct audio_patch* patch) final {
- std::lock_guard _l(mutex());
+ void setDownStreamPatch(const struct audio_patch* patch) final EXCLUDES_ThreadBase_Mutex {
+ audio_utils::lock_guard _l(mutex());
mDownStreamPatch = *patch;
}
- IAfTrack* getTrackById_l(audio_port_handle_t trackId) final;
+ IAfTrack* getTrackById_l(audio_port_handle_t trackId) final REQUIRES(mutex());
bool hasMixer() const final {
return mType == MIXER || mType == DUPLICATING || mType == SPATIALIZER;
@@ -1072,19 +1135,19 @@
void stopMelComputation_l() override
REQUIRES(audio_utils::AudioFlinger_Mutex);
- void setStandby() final {
- std::lock_guard _l(mutex());
+ void setStandby() final EXCLUDES_ThreadBase_Mutex {
+ audio_utils::lock_guard _l(mutex());
setStandby_l();
}
- void setStandby_l() final {
+ void setStandby_l() final REQUIRES(mutex()) {
mStandby = true;
mHalStarted = false;
mKernelPositionOnStandby =
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
}
- bool waitForHalStart() final {
+ bool waitForHalStart() final EXCLUDES_ThreadBase_Mutex {
audio_utils::unique_lock _l(mutex());
static const nsecs_t kWaitHalTimeoutNs = seconds(2);
nsecs_t endWaitTimetNs = systemTime() + kWaitHalTimeoutNs;
@@ -1102,10 +1165,17 @@
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
- bool mThreadThrottle; // throttle the thread processing
- uint32_t mThreadThrottleTimeMs; // throttle time for MIXER threads
- uint32_t mThreadThrottleEndMs; // notify once per throttling
- uint32_t mHalfBufferMs; // half the buffer size in milliseconds
+ // throttle the thread processing
+ bool mThreadThrottle GUARDED_BY(ThreadBase_ThreadLoop);
+
+ // throttle time for MIXER threads - atomic as read by dump()
+ std::atomic<uint32_t> mThreadThrottleTimeMs;
+
+ // notify once per throttling
+ uint32_t mThreadThrottleEndMs GUARDED_BY(ThreadBase_ThreadLoop);
+
+ // half the buffer size in milliseconds
+ uint32_t mHalfBufferMs GUARDED_BY(ThreadBase_ThreadLoop);
void* mSinkBuffer; // frame size aligned sink buffer
@@ -1125,21 +1195,21 @@
// buffer before downmixing or data conversion to the sink buffer.
// Set to "true" to enable the Mixer Buffer otherwise mixer output goes to sink buffer.
- bool mMixerBufferEnabled;
+ bool mMixerBufferEnabled GUARDED_BY(ThreadBase_ThreadLoop);
// Storage, 32 byte aligned (may make this alignment a requirement later).
// Due to constraints on mNormalFrameCount, the buffer size is a multiple of 16 frames.
- void* mMixerBuffer;
+ void* mMixerBuffer GUARDED_BY(ThreadBase_ThreadLoop);
// Size of mMixerBuffer in bytes: mNormalFrameCount * #channels * sampsize.
- size_t mMixerBufferSize;
+ size_t mMixerBufferSize GUARDED_BY(ThreadBase_ThreadLoop);
// The audio format of mMixerBuffer. Set to AUDIO_FORMAT_PCM_(FLOAT|16_BIT) only.
- audio_format_t mMixerBufferFormat;
+ audio_format_t mMixerBufferFormat GUARDED_BY(ThreadBase_ThreadLoop);
// An internal flag set to true by MixerThread::prepareTracks_l()
// when mMixerBuffer contains valid data after mixing.
- bool mMixerBufferValid;
+ bool mMixerBufferValid GUARDED_BY(ThreadBase_ThreadLoop);
// Effects Buffer (mEffectsBuffer*)
//
@@ -1148,46 +1218,49 @@
// to the sink buffer.
// Set to "true" to enable the Effects Buffer otherwise effects output goes to sink buffer.
- bool mEffectBufferEnabled;
+ bool mEffectBufferEnabled;
+ // NO_THREAD_SAFETY_ANALYSIS: Spatializer access this in addEffectChain_l()
// Storage, 32 byte aligned (may make this alignment a requirement later).
// Due to constraints on mNormalFrameCount, the buffer size is a multiple of 16 frames.
- void* mEffectBuffer;
+ void* mEffectBuffer;
+ // NO_THREAD_SAFETY_ANALYSIS: Spatializer access this in addEffectChain_l()
// Size of mEffectsBuffer in bytes: mNormalFrameCount * #channels * sampsize.
- size_t mEffectBufferSize;
+ size_t mEffectBufferSize;
+ // NO_THREAD_SAFETY_ANALYSIS: Spatializer access this in addEffectChain_l()
// The audio format of mEffectsBuffer. Set to AUDIO_FORMAT_PCM_16_BIT only.
- audio_format_t mEffectBufferFormat;
+ // NO_THREAD_SAFETY_ANALYSIS: Spatializer access this in addEffectChain_l()
+ audio_format_t mEffectBufferFormat;
// An internal flag set to true by MixerThread::prepareTracks_l()
// when mEffectsBuffer contains valid data after mixing.
//
// When this is set, all mixer data is routed into the effects buffer
// for any processing (including output processing).
- bool mEffectBufferValid;
+ bool mEffectBufferValid GUARDED_BY(ThreadBase_ThreadLoop);
// Set to "true" to enable when data has already copied to sink
- bool mHasDataCopiedToSinkBuffer = false;
+ bool mHasDataCopiedToSinkBuffer GUARDED_BY(ThreadBase_ThreadLoop) = false;
// Frame size aligned buffer used as input and output to all post processing effects
// except the Spatializer in a SPATIALIZER thread. Non spatialized tracks are mixed into
// this buffer so that post processing effects can be applied.
- void* mPostSpatializerBuffer = nullptr;
+ void* mPostSpatializerBuffer GUARDED_BY(mutex()) = nullptr;
// Size of mPostSpatializerBuffer in bytes
- size_t mPostSpatializerBufferSize;
-
+ size_t mPostSpatializerBufferSize GUARDED_BY(mutex());
// suspend count, > 0 means suspended. While suspended, the thread continues to pull from
// tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
// concurrent use of both of them, so Audio Policy Service suspends one of the threads to
// workaround that restriction.
// 'volatile' means accessed via atomic operations and no lock.
- volatile int32_t mSuspended;
+ std::atomic<int32_t> mSuspended;
int64_t mBytesWritten;
- std::atomic<int64_t> mFramesWritten; // not reset on standby
+ std::atomic<int64_t> mFramesWritten; // not reset on standby
int64_t mLastFramesWritten = -1; // track changes in timestamp
// server frames written.
int64_t mSuspendedFrames; // not reset on standby
@@ -1202,8 +1275,8 @@
// mMasterMute is in both PlaybackThread and in AudioFlinger. When a
// PlaybackThread needs to find out if master-muted, it checks it's local
// copy rather than the one in AudioFlinger. This optimization saves a lock.
- bool mMasterMute;
- void setMasterMute_l(bool muted) { mMasterMute = muted; }
+ bool mMasterMute GUARDED_BY(mutex());
+ void setMasterMute_l(bool muted) REQUIRES(mutex()) { mMasterMute = muted; }
auto discontinuityForStandbyOrFlush() const { // call on threadLoop or with lock.
return ((mType == DIRECT && !audio_is_linear_pcm(mFormat))
@@ -1222,24 +1295,28 @@
// No sleep in standby mode; waits on a condition
// Code snippets that are temporarily lifted up out of threadLoop() until the merge
- virtual void checkSilentMode_l() final; // consider unification with MMapThread
+
+ // consider unification with MMapThread
+ virtual void checkSilentMode_l() final REQUIRES(mutex());
// Non-trivial for DUPLICATING only
- virtual void saveOutputTracks() { }
- virtual void clearOutputTracks() { }
+ virtual void saveOutputTracks() REQUIRES(ThreadBase_ThreadLoop) {}
+ virtual void clearOutputTracks() REQUIRES(ThreadBase_ThreadLoop) {}
// Cache various calculated values, at threadLoop() entry and after a parameter change
- virtual void cacheParameters_l();
+ virtual void cacheParameters_l() REQUIRES(mutex(), ThreadBase_ThreadLoop);
void setCheckOutputStageEffects() override {
mCheckOutputStageEffects.store(true);
}
- virtual uint32_t correctLatency_l(uint32_t latency) const;
+ virtual uint32_t correctLatency_l(uint32_t latency) const REQUIRES(mutex());
virtual status_t createAudioPatch_l(const struct audio_patch *patch,
- audio_patch_handle_t *handle);
- virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
+ audio_patch_handle_t *handle) REQUIRES(mutex());
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle)
+ REQUIRES(mutex());
+ // NO_THREAD_SAFETY_ANALYSIS - fix this to use atomics
bool usesHwAvSync() const final { return mType == DIRECT && mOutput != nullptr
&& mHwSupportsPause
&& (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
@@ -1247,22 +1324,23 @@
uint32_t trackCountForUid_l(uid_t uid) const;
void invalidateTracksForAudioSession_l(
- audio_session_t sessionId) const override {
+ audio_session_t sessionId) const override REQUIRES(mutex()) {
ThreadBase::invalidateTracksForAudioSession_l(sessionId, mTracks);
}
DISALLOW_COPY_AND_ASSIGN(PlaybackThread);
- status_t addTrack_l(const sp<IAfTrack>& track) final;
- bool destroyTrack_l(const sp<IAfTrack>& track) final;
+ status_t addTrack_l(const sp<IAfTrack>& track) final REQUIRES(mutex());
+ bool destroyTrack_l(const sp<IAfTrack>& track) final REQUIRES(mutex());
- void removeTrack_l(const sp<IAfTrack>& track);
+ void removeTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex());
- void readOutputParameters_l();
- MetadataUpdate updateMetadata_l() final;
- virtual void sendMetadataToBackend_l(const StreamOutHalInterface::SourceMetadata& metadata);
+ void readOutputParameters_l() REQUIRES(mutex());
+ MetadataUpdate updateMetadata_l() final REQUIRES(mutex());
+ virtual void sendMetadataToBackend_l(const StreamOutHalInterface::SourceMetadata& metadata)
+ REQUIRES(mutex()) ;
- void collectTimestamps_l();
+ void collectTimestamps_l() REQUIRES(mutex(), ThreadBase_ThreadLoop);
// The Tracks class manages tracks added and removed from the Thread.
template <typename T>
@@ -1338,17 +1416,17 @@
uint32_t mSleepTimeUs;
// mixer status returned by prepareTracks_l()
- mixer_state mMixerStatus; // current cycle
+ mixer_state mMixerStatus GUARDED_BY(ThreadBase_ThreadLoop); // current cycle
// previous cycle when in prepareTracks_l()
- mixer_state mMixerStatusIgnoringFastTracks;
+ mixer_state mMixerStatusIgnoringFastTracks GUARDED_BY(ThreadBase_ThreadLoop);
// FIXME or a separate ready state per track
// FIXME move these declarations into the specific sub-class that needs them
// MIXER only
- uint32_t sleepTimeShift;
+ uint32_t sleepTimeShift GUARDED_BY(ThreadBase_ThreadLoop);
// same as AudioFlinger::mStandbyTimeInNsecs except for DIRECT which uses a shorter value
- nsecs_t mStandbyDelayNs;
+ nsecs_t mStandbyDelayNs; // GUARDED_BY(mutex());
// MIXER only
nsecs_t maxPeriod;
@@ -1356,8 +1434,8 @@
// DUPLICATING only
uint32_t writeFrames;
- size_t mBytesRemaining;
- size_t mCurrentWriteLength;
+ size_t mBytesRemaining GUARDED_BY(ThreadBase_ThreadLoop);
+ size_t mCurrentWriteLength GUARDED_BY(ThreadBase_ThreadLoop);
bool mUseAsyncWrite;
// mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
// incremented each time a write(), a flush() or a standby() occurs.
@@ -1410,7 +1488,7 @@
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
- uint32_t& fastTrackAvailMask_l() final { return mFastTrackAvailMask; }
+ uint32_t& fastTrackAvailMask_l() final REQUIRES(mutex()) { return mFastTrackAvailMask; }
uint32_t mFastTrackAvailMask; // bit i set if fast track [i] is available
bool mHwSupportsPause;
bool mHwPaused;
@@ -1479,18 +1557,20 @@
// Thread virtuals
- bool checkForNewParameter_l(const String8& keyValuePair, status_t& status) final;
+ bool checkForNewParameter_l(const String8& keyValuePair, status_t& status) final
+ REQUIRES(mutex());
bool isTrackAllowed_l(
audio_channel_mask_t channelMask, audio_format_t format,
- audio_session_t sessionId, uid_t uid) const final;
+ audio_session_t sessionId, uid_t uid) const final REQUIRES(mutex());
protected:
- mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) override;
+ mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) override
+ REQUIRES(mutex(), ThreadBase_ThreadLoop);
uint32_t idleSleepTimeUs() const final;
uint32_t suspendSleepTimeUs() const final;
- void cacheParameters_l() override;
+ void cacheParameters_l() override REQUIRES(mutex(), ThreadBase_ThreadLoop);
- void acquireWakeLock_l() final {
+ void acquireWakeLock_l() final REQUIRES(mutex()) {
PlaybackThread::acquireWakeLock_l();
if (hasFastMixer()) {
mFastMixer->setBoottimeOffset(
@@ -1498,18 +1578,19 @@
}
}
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
+ void dumpInternals_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
// threadLoop snippets
- ssize_t threadLoop_write() override;
- void threadLoop_standby() override;
- void threadLoop_mix() override;
- void threadLoop_sleepTime() override;
- uint32_t correctLatency_l(uint32_t latency) const final;
+ ssize_t threadLoop_write() override REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_standby() override REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_mix() override REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_sleepTime() override REQUIRES(ThreadBase_ThreadLoop);
+ uint32_t correctLatency_l(uint32_t latency) const final REQUIRES(mutex());
status_t createAudioPatch_l(
- const struct audio_patch* patch, audio_patch_handle_t* handle) final;
- status_t releaseAudioPatch_l(const audio_patch_handle_t handle) final;
+ const struct audio_patch* patch, audio_patch_handle_t* handle)
+ final REQUIRES(mutex());
+ status_t releaseAudioPatch_l(const audio_patch_handle_t handle) final REQUIRES(mutex());
AudioMixer* mAudioMixer; // normal mixer
@@ -1538,7 +1619,7 @@
// accessible only within the threadLoop(), no locks required
// mFastMixer->sq() // for mutating and pushing state
- int32_t mFastMixerFutex; // for cold idle
+ int32_t mFastMixerFutex GUARDED_BY(ThreadBase_ThreadLoop); // for cold idle
std::atomic_bool mMasterMono;
public:
@@ -1548,8 +1629,9 @@
return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
}
- status_t threadloop_getHalTimestamp_l(
- ExtendedTimestamp *timestamp) const override {
+ status_t threadloop_getHalTimestamp_l(
+ ExtendedTimestamp *timestamp) const override
+ REQUIRES(mutex(), ThreadBase_ThreadLoop) {
if (mNormalSink.get() != nullptr) {
return mNormalSink->getTimestamp(*timestamp);
}
@@ -1573,16 +1655,16 @@
// and blending without limiter is idempotent but inefficient to do twice.
virtual bool requireMonoBlend() { return mMasterMono.load() && !hasFastMixer(); }
- void setMasterBalance(float balance) override {
+ void setMasterBalance(float balance) override EXCLUDES_ThreadBase_Mutex {
mMasterBalance.store(balance);
if (hasFastMixer()) {
mFastMixer->setMasterBalance(balance);
}
}
- void updateHalSupportedLatencyModes_l();
- void onHalLatencyModesChanged_l() override;
- void setHalLatencyMode_l() override;
+ void updateHalSupportedLatencyModes_l() REQUIRES(mutex());
+ void onHalLatencyModesChanged_l() override REQUIRES(mutex());
+ void setHalLatencyMode_l() override REQUIRES(mutex());
};
class DirectOutputThread : public PlaybackThread, public virtual IAfDirectOutputThread {
@@ -1597,35 +1679,36 @@
const audio_offload_info_t& offloadInfo)
: DirectOutputThread(afThreadCallback, output, id, DIRECT, systemReady, offloadInfo) { }
- virtual ~DirectOutputThread();
+ ~DirectOutputThread() override;
status_t selectPresentation(int presentationId, int programId) final;
// Thread virtuals
virtual bool checkForNewParameter_l(const String8& keyValuePair,
- status_t& status);
+ status_t& status) REQUIRES(mutex());
- void flushHw_l() override;
+ void flushHw_l() override REQUIRES(mutex(), ThreadBase_ThreadLoop);
- void setMasterBalance(float balance) override;
+ void setMasterBalance(float balance) override EXCLUDES_ThreadBase_Mutex;
protected:
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
- virtual void cacheParameters_l();
+ virtual void cacheParameters_l() REQUIRES(mutex(), ThreadBase_ThreadLoop);
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
+ void dumpInternals_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
// threadLoop snippets
- virtual mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove);
- virtual void threadLoop_mix();
- virtual void threadLoop_sleepTime();
- virtual void threadLoop_exit();
- virtual bool shouldStandby_l();
+ virtual mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove)
+ REQUIRES(mutex(), ThreadBase_ThreadLoop);
+ virtual void threadLoop_mix() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_sleepTime() REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_exit() REQUIRES(ThreadBase_ThreadLoop);
+ virtual bool shouldStandby_l() REQUIRES(mutex());
- virtual void onAddNewTrack_l();
+ virtual void onAddNewTrack_l() REQUIRES(mutex());
const audio_offload_info_t mOffloadInfo;
@@ -1635,7 +1718,7 @@
DirectOutputThread(const sp<IAfThreadCallback>& afThreadCallback, AudioStreamOut* output,
audio_io_handle_t id, ThreadBase::type_t type, bool systemReady,
const audio_offload_info_t& offloadInfo);
- void processVolume_l(IAfTrack *track, bool lastTrack);
+ void processVolume_l(IAfTrack *track, bool lastTrack) REQUIRES(mutex());
bool isTunerStream() const { return (mOffloadInfo.content_id > 0); }
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
@@ -1650,7 +1733,7 @@
public:
virtual bool hasFastMixer() const { return false; }
- virtual int64_t computeWaitTimeNs_l() const override;
+ virtual int64_t computeWaitTimeNs_l() const override REQUIRES(mutex());
status_t threadloop_getHalTimestamp_l(ExtendedTimestamp *timestamp) const override {
// For DIRECT and OFFLOAD threads, query the output sink directly.
@@ -1677,19 +1760,20 @@
audio_io_handle_t id, bool systemReady,
const audio_offload_info_t& offloadInfo);
virtual ~OffloadThread() {};
- void flushHw_l() override;
+ void flushHw_l() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
protected:
// threadLoop snippets
- virtual mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove);
- virtual void threadLoop_exit();
+ mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) final
+ REQUIRES(mutex(), ThreadBase_ThreadLoop);
+ void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
- virtual bool waitingAsyncCallback();
- virtual bool waitingAsyncCallback_l();
- virtual void invalidateTracks(audio_stream_type_t streamType);
- void invalidateTracks(std::set<audio_port_handle_t>& portIds) override;
+ bool waitingAsyncCallback() final;
+ bool waitingAsyncCallback_l() final REQUIRES(mutex());
+ void invalidateTracks(audio_stream_type_t streamType) final EXCLUDES_ThreadBase_Mutex;
+ void invalidateTracks(std::set<audio_port_handle_t>& portIds) final EXCLUDES_ThreadBase_Mutex;
- virtual bool keepWakeLock() const { return (mKeepWakeLock || (mDrainSequence & 1)); }
+ bool keepWakeLock() const final { return (mKeepWakeLock || (mDrainSequence & 1)); }
private:
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
@@ -1702,10 +1786,10 @@
explicit AsyncCallbackThread(const wp<PlaybackThread>& playbackThread);
// Thread virtuals
- virtual bool threadLoop();
+ bool threadLoop() final;
// RefBase
- virtual void onFirstRef();
+ void onFirstRef() final;
void exit();
void setWriteBlocked(uint32_t sequence);
@@ -1728,7 +1812,9 @@
mutable audio_utils::mutex mMutex;
bool mAsyncError;
- audio_utils::mutex& mutex() const { return mMutex; }
+ audio_utils::mutex& mutex() const RETURN_CAPABILITY(audio_utils::AsyncCallbackThread_Mutex) {
+ return mMutex;
+ }
};
class DuplicatingThread : public MixerThread, public IAfDuplicatingThread {
@@ -1743,41 +1829,42 @@
}
// Thread virtuals
- void addOutputTrack(IAfPlaybackThread* thread) final;
- void removeOutputTrack(IAfPlaybackThread* thread) final;
+ void addOutputTrack(IAfPlaybackThread* thread) final EXCLUDES_ThreadBase_Mutex;
+ void removeOutputTrack(IAfPlaybackThread* thread) final EXCLUDES_ThreadBase_Mutex;
uint32_t waitTimeMs() const final { return mWaitTimeMs; }
void sendMetadataToBackend_l(
- const StreamOutHalInterface::SourceMetadata& metadata) override;
+ const StreamOutHalInterface::SourceMetadata& metadata) final REQUIRES(mutex());
protected:
virtual uint32_t activeSleepTimeUs() const;
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
+ void dumpInternals_l(int fd, const Vector<String16>& args) final REQUIRES(mutex());
private:
- bool outputsReady();
+ bool outputsReady() REQUIRES(ThreadBase_ThreadLoop);
protected:
// threadLoop snippets
- virtual void threadLoop_mix();
- virtual void threadLoop_sleepTime();
- virtual ssize_t threadLoop_write();
- virtual void threadLoop_standby();
- virtual void cacheParameters_l();
+ void threadLoop_mix() final REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_sleepTime() final REQUIRES(ThreadBase_ThreadLoop);
+ ssize_t threadLoop_write() final REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_standby() final REQUIRES(ThreadBase_ThreadLoop);
+ void cacheParameters_l() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
private:
// called from threadLoop, addOutputTrack, removeOutputTrack
- virtual void updateWaitTime_l();
+ void updateWaitTime_l() REQUIRES(mutex());
protected:
- virtual void saveOutputTracks();
- virtual void clearOutputTracks();
+ void saveOutputTracks() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
+ void clearOutputTracks() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
private:
uint32_t mWaitTimeMs;
- SortedVector <sp<IAfOutputTrack>> outputTracks;
- SortedVector <sp<IAfOutputTrack>> mOutputTracks;
+ // NO_THREAD_SAFETY_ANALYSIS GUARDED_BY(ThreadBase_ThreadLoop)
+ SortedVector <sp<IAfOutputTrack>> outputTracks;
+ SortedVector <sp<IAfOutputTrack>> mOutputTracks GUARDED_BY(mutex());
public:
virtual bool hasFastMixer() const { return false; }
status_t threadloop_getHalTimestamp_l(
- ExtendedTimestamp *timestamp) const override {
+ ExtendedTimestamp *timestamp) const override REQUIRES(mutex()) {
if (mOutputTracks.size() > 0) {
// forward the first OutputTrack's kernel information for timestamp.
const ExtendedTimestamp trackTimestamp =
@@ -1807,11 +1894,12 @@
// RefBase
void onFirstRef() final;
- status_t setRequestedLatencyMode(audio_latency_mode_t mode) final;
+ status_t setRequestedLatencyMode(audio_latency_mode_t mode) final EXCLUDES_ThreadBase_Mutex;
protected:
- void checkOutputStageEffects() final;
- void setHalLatencyMode_l() final;
+ void checkOutputStageEffects() final
+ REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
+ void setHalLatencyMode_l() final REQUIRES(mutex());
private:
// Do not request a specific mode by default
@@ -1837,15 +1925,15 @@
~RecordThread() override;
// no addTrack_l ?
- void destroyTrack_l(const sp<IAfRecordTrack>& track) final;
- void removeTrack_l(const sp<IAfRecordTrack>& track) final;
+ void destroyTrack_l(const sp<IAfRecordTrack>& track) final REQUIRES(mutex());
+ void removeTrack_l(const sp<IAfRecordTrack>& track) final REQUIRES(mutex());
// Thread virtuals
- bool threadLoop() final;
- void preExit() final;
+ bool threadLoop() final REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
+ void preExit() final EXCLUDES_ThreadBase_Mutex;
// RefBase
- void onFirstRef() final;
+ void onFirstRef() final EXCLUDES_ThreadBase_Mutex;
status_t initCheck() const final { return mInput == nullptr ? NO_INIT : NO_ERROR; }
@@ -1869,15 +1957,15 @@
status_t *status /*non-NULL*/,
audio_port_handle_t portId,
int32_t maxSharedAudioHistoryMs) final
- REQUIRES(audio_utils::AudioFlinger_Mutex);
+ REQUIRES(audio_utils::AudioFlinger_Mutex) EXCLUDES_ThreadBase_Mutex;
status_t start(IAfRecordTrack* recordTrack,
AudioSystem::sync_event_t event,
- audio_session_t triggerSession) final;
+ audio_session_t triggerSession) final EXCLUDES_ThreadBase_Mutex;
// ask the thread to stop the specified track, and
// return true if the caller should then do it's part of the stopping process
- bool stop(IAfRecordTrack* recordTrack) final;
+ bool stop(IAfRecordTrack* recordTrack) final EXCLUDES_ThreadBase_Mutex;
AudioStreamIn* getInput() const final { return mInput; }
AudioStreamIn* clearInput() final;
@@ -1885,27 +1973,30 @@
virtual sp<StreamHalInterface> stream() const;
- virtual bool checkForNewParameter_l(const String8& keyValuePair,
- status_t& status);
- virtual void cacheParameters_l() {}
- virtual String8 getParameters(const String8& keys);
- void ioConfigChanged(audio_io_config_event_t event, pid_t pid = 0,
+ virtual bool checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status) REQUIRES(mutex());
+ virtual void cacheParameters_l() REQUIRES(mutex(), ThreadBase_ThreadLoop) {}
+ virtual String8 getParameters(const String8& keys) EXCLUDES_ThreadBase_Mutex;
+
+ // Hold either the AudioFlinger::mutex or the ThreadBase::mutex
+ void ioConfigChanged_l(audio_io_config_event_t event, pid_t pid = 0,
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final;
virtual status_t createAudioPatch_l(const struct audio_patch *patch,
- audio_patch_handle_t *handle);
- virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
- void updateOutDevices(const DeviceDescriptorBaseVector& outDevices) override;
- void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) override;
+ audio_patch_handle_t *handle) REQUIRES(mutex());
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) REQUIRES(mutex());
+ void updateOutDevices(const DeviceDescriptorBaseVector& outDevices) override
+ EXCLUDES_ThreadBase_Mutex;
+ void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) override REQUIRES(mutex());
- void addPatchTrack(const sp<IAfPatchRecord>& record) final;
- void deletePatchTrack(const sp<IAfPatchRecord>& record) final;
+ void addPatchTrack(const sp<IAfPatchRecord>& record) final EXCLUDES_ThreadBase_Mutex;
+ void deletePatchTrack(const sp<IAfPatchRecord>& record) final EXCLUDES_ThreadBase_Mutex;
- void readInputParameters_l();
- uint32_t getInputFramesLost() const final;
+ void readInputParameters_l() REQUIRES(mutex());
+ uint32_t getInputFramesLost() const final EXCLUDES_ThreadBase_Mutex;
- virtual status_t addEffectChain_l(const sp<IAfEffectChain>& chain);
- virtual size_t removeEffectChain_l(const sp<IAfEffectChain>& chain);
- uint32_t hasAudioSession_l(audio_session_t sessionId) const override {
+ virtual status_t addEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex());
+ virtual size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex());
+ uint32_t hasAudioSession_l(audio_session_t sessionId) const override REQUIRES(mutex()) {
return ThreadBase::hasAudioSession_l(sessionId, mTracks);
}
@@ -1914,7 +2005,8 @@
// FIXME replace by Set [and implement Bag/Multiset for other uses].
KeyedVector<audio_session_t, bool> sessionIds() const;
- status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) override;
+ status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) override
+ EXCLUDES_ThreadBase_Mutex;
bool isValidSyncEvent(const sp<audioflinger::SyncEvent>& event) const override;
static void syncStartEventCallback(const wp<audioflinger::SyncEvent>& event);
@@ -1923,52 +2015,55 @@
bool hasFastCapture() const final { return mFastCapture != 0; }
virtual void toAudioPortConfig(struct audio_port_config *config);
- virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
- audio_session_t sessionId);
+ virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
+ audio_session_t sessionId) REQUIRES(mutex());
- virtual void acquireWakeLock_l() {
+ virtual void acquireWakeLock_l() REQUIRES(mutex()) {
ThreadBase::acquireWakeLock_l();
- mActiveTracks.updatePowerState(this, true /* force */);
+ mActiveTracks.updatePowerState_l(this, true /* force */);
}
- void checkBtNrec() final;
+ void checkBtNrec() final EXCLUDES_ThreadBase_Mutex;
// Sets the UID records silence
- void setRecordSilenced(audio_port_handle_t portId, bool silenced) final;
+ void setRecordSilenced(audio_port_handle_t portId, bool silenced) final
+ EXCLUDES_ThreadBase_Mutex;
status_t getActiveMicrophones(
- std::vector<media::MicrophoneInfoFw>* activeMicrophones) const final;
- status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) final;
- status_t setPreferredMicrophoneFieldDimension(float zoom) final;
+ std::vector<media::MicrophoneInfoFw>* activeMicrophones) const final
+ EXCLUDES_ThreadBase_Mutex;
+ status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) final
+ EXCLUDES_ThreadBase_Mutex;
+ status_t setPreferredMicrophoneFieldDimension(float zoom) final EXCLUDES_ThreadBase_Mutex;
- MetadataUpdate updateMetadata_l() override;
+ MetadataUpdate updateMetadata_l() override REQUIRES(mutex());
bool fastTrackAvailable() const final { return mFastTrackAvail; }
void setFastTrackAvailable(bool available) final { mFastTrackAvail = available; }
- bool isTimestampCorrectionEnabled() const override {
+ bool isTimestampCorrectionEnabled_l() const override REQUIRES(mutex()) {
// checks popcount for exactly one device.
// Is currently disabled. Before enabling,
// verify compressed record timestamps.
return audio_is_input_device(mTimestampCorrectedDevice)
- && inDeviceType() == mTimestampCorrectedDevice;
+ && inDeviceType_l() == mTimestampCorrectedDevice;
}
status_t shareAudioHistory(const std::string& sharedAudioPackageName,
audio_session_t sharedSessionId = AUDIO_SESSION_NONE,
- int64_t sharedAudioStartMs = -1) final;
+ int64_t sharedAudioStartMs = -1) final EXCLUDES_ThreadBase_Mutex;
status_t shareAudioHistory_l(const std::string& sharedAudioPackageName,
audio_session_t sharedSessionId = AUDIO_SESSION_NONE,
- int64_t sharedAudioStartMs = -1);
- void resetAudioHistory_l() final;
+ int64_t sharedAudioStartMs = -1) REQUIRES(mutex());
+ void resetAudioHistory_l() final REQUIRES(mutex());
bool isStreamInitialized() const final {
return !(mInput == nullptr || mInput->stream == nullptr);
}
protected:
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
- void dumpTracks_l(int fd, const Vector<String16>& args) override;
+ void dumpInternals_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
+ void dumpTracks_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
private:
// Enter standby if not already in standby, and set mStandby flag
@@ -1977,10 +2072,10 @@
// Call the HAL standby method unconditionally, and don't change mStandby flag
void inputStandBy();
- void checkBtNrec_l();
+ void checkBtNrec_l() REQUIRES(mutex());
- int32_t getOldestFront_l();
- void updateFronts_l(int32_t offset);
+ int32_t getOldestFront_l() REQUIRES(mutex());
+ void updateFronts_l(int32_t offset) REQUIRES(mutex());
AudioStreamIn *mInput;
Source *mSource;
@@ -2066,88 +2161,112 @@
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
audio_port_handle_t deviceId,
- audio_port_handle_t portId) override;
+ audio_port_handle_t portId) override EXCLUDES_ThreadBase_Mutex {
+ audio_utils::lock_guard l(mutex());
+ configure_l(attr, streamType, sessionId, callback, deviceId, portId);
+ }
- void disconnect() final;
+ void configure_l(const audio_attributes_t* attr,
+ audio_stream_type_t streamType,
+ audio_session_t sessionId,
+ const sp<MmapStreamCallback>& callback,
+ audio_port_handle_t deviceId,
+ audio_port_handle_t portId) REQUIRES(mutex());
+
+ void disconnect() final EXCLUDES_ThreadBase_Mutex;
// MmapStreamInterface for adapter.
- status_t createMmapBuffer(int32_t minSizeFrames, struct audio_mmap_buffer_info* info) final;
- status_t getMmapPosition(struct audio_mmap_position* position) const override;
+ status_t createMmapBuffer(int32_t minSizeFrames, struct audio_mmap_buffer_info* info) final
+ EXCLUDES_ThreadBase_Mutex;
+ status_t getMmapPosition(struct audio_mmap_position* position) const override
+ EXCLUDES_ThreadBase_Mutex;
status_t start(const AudioClient& client,
const audio_attributes_t *attr,
- audio_port_handle_t* handle) final;
- status_t stop(audio_port_handle_t handle) final;
- status_t standby() final;
- status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) const = 0;
- status_t reportData(const void* buffer, size_t frameCount) override;
+ audio_port_handle_t* handle) final EXCLUDES_ThreadBase_Mutex;
+ status_t stop(audio_port_handle_t handle) final EXCLUDES_ThreadBase_Mutex;
+ status_t standby() final EXCLUDES_ThreadBase_Mutex;
+ status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) const
+ EXCLUDES_ThreadBase_Mutex = 0;
+ status_t reportData(const void* buffer, size_t frameCount) override EXCLUDES_ThreadBase_Mutex;
// RefBase
void onFirstRef() final;
// Thread virtuals
- bool threadLoop() final;
+ bool threadLoop() final REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
// Not in ThreadBase
- virtual void threadLoop_exit() final;
- virtual void threadLoop_standby() final;
- virtual bool shouldStandby_l() final { return false; }
+ virtual void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
+ virtual void threadLoop_standby() final REQUIRES(ThreadBase_ThreadLoop);
+ virtual bool shouldStandby_l() final REQUIRES(mutex()){ return false; }
virtual status_t exitStandby_l() REQUIRES(mutex());
status_t initCheck() const final { return mHalStream == nullptr ? NO_INIT : NO_ERROR; }
size_t frameCount() const final { return mFrameCount; }
- bool checkForNewParameter_l(const String8& keyValuePair, status_t& status) final;
- String8 getParameters(const String8& keys) final;
- void ioConfigChanged(audio_io_config_event_t event, pid_t pid = 0,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final;
- void readHalParameters_l();
- void cacheParameters_l() final {}
+ bool checkForNewParameter_l(const String8& keyValuePair, status_t& status)
+ final REQUIRES(mutex());
+ String8 getParameters(const String8& keys) final EXCLUDES_ThreadBase_Mutex;
+ void ioConfigChanged_l(audio_io_config_event_t event, pid_t pid = 0,
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) final
+ /* holds either AF::mutex or TB::mutex */;
+ void readHalParameters_l() REQUIRES(mutex());
+ void cacheParameters_l() final REQUIRES(mutex(), ThreadBase_ThreadLoop) {}
status_t createAudioPatch_l(
- const struct audio_patch* patch, audio_patch_handle_t* handle) final;
- status_t releaseAudioPatch_l(const audio_patch_handle_t handle) final;
+ const struct audio_patch* patch, audio_patch_handle_t* handle) final
+ REQUIRES(mutex());
+ status_t releaseAudioPatch_l(const audio_patch_handle_t handle) final
+ REQUIRES(mutex());
+ // NO_THREAD_SAFETY_ANALYSIS
void toAudioPortConfig(struct audio_port_config* config) override;
sp<StreamHalInterface> stream() const final { return mHalStream; }
- status_t addEffectChain_l(const sp<IAfEffectChain>& chain) final;
- size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) final;
+ status_t addEffectChain_l(const sp<IAfEffectChain>& chain) final REQUIRES(mutex());
+ size_t removeEffectChain_l(const sp<IAfEffectChain>& chain) final REQUIRES(mutex());
status_t checkEffectCompatibility_l(
- const effect_descriptor_t *desc, audio_session_t sessionId) final;
+ const effect_descriptor_t *desc, audio_session_t sessionId) final REQUIRES(mutex());
- uint32_t hasAudioSession_l(audio_session_t sessionId) const override {
+ uint32_t hasAudioSession_l(audio_session_t sessionId) const override REQUIRES(mutex()) {
// Note: using mActiveTracks as no mTracks here.
return ThreadBase::hasAudioSession_l(sessionId, mActiveTracks);
}
status_t setSyncEvent(const sp<audioflinger::SyncEvent>& event) final;
bool isValidSyncEvent(const sp<audioflinger::SyncEvent>& event) const final;
- virtual void checkSilentMode_l() {} // cannot be const (RecordThread)
- virtual void processVolume_l() {}
- void checkInvalidTracks_l();
+ virtual void checkSilentMode_l() REQUIRES(mutex()) {} // cannot be const (RecordThread)
+ virtual void processVolume_l() REQUIRES(mutex()) {}
+ void checkInvalidTracks_l() REQUIRES(mutex());
// Not in ThreadBase
- virtual audio_stream_type_t streamType() const { return AUDIO_STREAM_DEFAULT; }
- virtual void invalidateTracks(audio_stream_type_t /* streamType */) {}
- void invalidateTracks(std::set<audio_port_handle_t>& /* portIds */) override {}
+ virtual audio_stream_type_t streamType_l() const REQUIRES(mutex()) {
+ return AUDIO_STREAM_DEFAULT;
+ }
+ virtual void invalidateTracks(audio_stream_type_t /* streamType */)
+ EXCLUDES_ThreadBase_Mutex {}
+ void invalidateTracks(std::set<audio_port_handle_t>& /* portIds */) override
+ EXCLUDES_ThreadBase_Mutex {}
// Sets the UID records silence
void setRecordSilenced(
- audio_port_handle_t /* portId */, bool /* silenced */) override {}
+ audio_port_handle_t /* portId */, bool /* silenced */) override
+ EXCLUDES_ThreadBase_Mutex {}
bool isStreamInitialized() const override { return false; }
- void setClientSilencedState_l(audio_port_handle_t portId, bool silenced) {
+ void setClientSilencedState_l(audio_port_handle_t portId, bool silenced) REQUIRES(mutex()) {
mClientSilencedStates[portId] = silenced;
}
- size_t eraseClientSilencedState_l(audio_port_handle_t portId) {
+ size_t eraseClientSilencedState_l(audio_port_handle_t portId) REQUIRES(mutex()) {
return mClientSilencedStates.erase(portId);
}
- bool isClientSilenced_l(audio_port_handle_t portId) const {
+ bool isClientSilenced_l(audio_port_handle_t portId) const REQUIRES(mutex()) {
const auto it = mClientSilencedStates.find(portId);
return it != mClientSilencedStates.end() ? it->second : false;
}
- void setClientSilencedIfExists_l(audio_port_handle_t portId, bool silenced) {
+ void setClientSilencedIfExists_l(audio_port_handle_t portId, bool silenced)
+ REQUIRES(mutex()) {
const auto it = mClientSilencedStates.find(portId);
if (it != mClientSilencedStates.end()) {
it->second = silenced;
@@ -2155,28 +2274,28 @@
}
protected:
- void dumpInternals_l(int fd, const Vector<String16>& args) override;
- void dumpTracks_l(int fd, const Vector<String16>& args) final;
+ void dumpInternals_l(int fd, const Vector<String16>& args) override REQUIRES(mutex());
+ void dumpTracks_l(int fd, const Vector<String16>& args) final REQUIRES(mutex());
/**
* @brief mDeviceId current device port unique identifier
*/
- audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_port_handle_t mDeviceId GUARDED_BY(mutex()) = AUDIO_PORT_HANDLE_NONE;
- audio_attributes_t mAttr;
- audio_session_t mSessionId;
- audio_port_handle_t mPortId;
+ audio_attributes_t mAttr GUARDED_BY(mutex());
+ audio_session_t mSessionId GUARDED_BY(mutex());
+ audio_port_handle_t mPortId GUARDED_BY(mutex());
- wp<MmapStreamCallback> mCallback;
- sp<StreamHalInterface> mHalStream;
- sp<DeviceHalInterface> mHalDevice;
- AudioHwDevice* const mAudioHwDev;
- ActiveTracks<IAfMmapTrack> mActiveTracks;
- float mHalVolFloat;
- std::map<audio_port_handle_t, bool> mClientSilencedStates;
+ wp<MmapStreamCallback> mCallback GUARDED_BY(mutex());
+ sp<StreamHalInterface> mHalStream; // NO_THREAD_SAFETY_ANALYSIS
+ sp<DeviceHalInterface> mHalDevice GUARDED_BY(mutex());
+ AudioHwDevice* const mAudioHwDev GUARDED_BY(mutex());
+ ActiveTracks<IAfMmapTrack> mActiveTracks GUARDED_BY(mutex());
+ float mHalVolFloat GUARDED_BY(mutex());
+ std::map<audio_port_handle_t, bool> mClientSilencedStates GUARDED_BY(mutex());
- int32_t mNoCallbackWarningCount;
- static constexpr int32_t kMaxNoCallbackWarnings = 5;
+ int32_t mNoCallbackWarningCount GUARDED_BY(mutex());
+ static constexpr int32_t kMaxNoCallbackWarnings = 5;
};
class MmapPlaybackThread : public MmapThread, public IAfMmapPlaybackThread,
@@ -2194,28 +2313,31 @@
audio_session_t sessionId,
const sp<MmapStreamCallback>& callback,
audio_port_handle_t deviceId,
- audio_port_handle_t portId) final;
+ audio_port_handle_t portId) final EXCLUDES_ThreadBase_Mutex;
- AudioStreamOut* clearOutput() final;
+ AudioStreamOut* clearOutput() final EXCLUDES_ThreadBase_Mutex;
// VolumeInterface
void setMasterVolume(float value) final;
- void setMasterBalance(float /* value */) final {} // Needs implementation?
- void setMasterMute(bool muted) final;
- void setStreamVolume(audio_stream_type_t stream, float value) final;
- void setStreamMute(audio_stream_type_t stream, bool muted) final;
- float streamVolume(audio_stream_type_t stream) const final;
+ // Needs implementation?
+ void setMasterBalance(float /* value */) final EXCLUDES_ThreadBase_Mutex {}
+ void setMasterMute(bool muted) final EXCLUDES_ThreadBase_Mutex;
+ 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;
- void setMasterMute_l(bool muted) { mMasterMute = muted; }
+ void setMasterMute_l(bool muted) REQUIRES(mutex()) { mMasterMute = muted; }
- void invalidateTracks(audio_stream_type_t streamType) final;
- void invalidateTracks(std::set<audio_port_handle_t>& portIds) final;
+ void invalidateTracks(audio_stream_type_t streamType) final EXCLUDES_ThreadBase_Mutex;
+ void invalidateTracks(std::set<audio_port_handle_t>& portIds) final EXCLUDES_ThreadBase_Mutex;
- audio_stream_type_t streamType() const final { return mStreamType; }
- void checkSilentMode_l() final;
- void processVolume_l() final;
+ audio_stream_type_t streamType_l() const final REQUIRES(mutex()) {
+ return mStreamType;
+ }
+ void checkSilentMode_l() final REQUIRES(mutex());
+ void processVolume_l() final REQUIRES(mutex());
- MetadataUpdate updateMetadata_l() final;
+ MetadataUpdate updateMetadata_l() final REQUIRES(mutex());
void toAudioPortConfig(struct audio_port_config* config) final;
@@ -2233,16 +2355,21 @@
REQUIRES(audio_utils::AudioFlinger_Mutex);
protected:
- void dumpInternals_l(int fd, const Vector<String16>& args) final;
+ void dumpInternals_l(int fd, const Vector<String16>& args) final REQUIRES(mutex());
+ float streamVolume_l() const REQUIRES(mutex()) {
+ return mStreamTypes[mStreamType].volume;
+ }
+ bool streamMuted_l() const REQUIRES(mutex()) {
+ return mStreamTypes[mStreamType].mute;
+ }
- audio_stream_type_t mStreamType;
- float mMasterVolume;
- float mStreamVolume;
- bool mMasterMute;
- bool mStreamMute;
- AudioStreamOut* mOutput;
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT] GUARDED_BY(mutex());
+ audio_stream_type_t mStreamType GUARDED_BY(mutex());
+ float mMasterVolume GUARDED_BY(mutex());
+ bool mMasterMute GUARDED_BY(mutex());
+ AudioStreamOut* mOutput; // NO_THREAD_SAFETY_ANALYSIS
- mediautils::atomic_sp<audio_utils::MelProcessor> mMelProcessor;
+ mediautils::atomic_sp<audio_utils::MelProcessor> mMelProcessor; // locked internally
};
class MmapCaptureThread : public MmapThread, public IAfMmapCaptureThread
@@ -2255,13 +2382,14 @@
return sp<IAfMmapCaptureThread>::fromExisting(this);
}
- AudioStreamIn* clearInput() final;
+ AudioStreamIn* clearInput() final EXCLUDES_ThreadBase_Mutex;
status_t exitStandby_l() REQUIRES(mutex()) final;
- MetadataUpdate updateMetadata_l() final;
- void processVolume_l() final;
- void setRecordSilenced(audio_port_handle_t portId, bool silenced) final;
+ MetadataUpdate updateMetadata_l() final REQUIRES(mutex());
+ void processVolume_l() final REQUIRES(mutex());
+ void setRecordSilenced(audio_port_handle_t portId, bool silenced) final
+ EXCLUDES_ThreadBase_Mutex;
void toAudioPortConfig(struct audio_port_config* config) final;
@@ -2273,7 +2401,7 @@
protected:
- AudioStreamIn* mInput;
+ AudioStreamIn* mInput; // NO_THREAD_SAFETY_ANALYSIS
};
class BitPerfectThread : public MixerThread {
@@ -2282,13 +2410,15 @@
audio_io_handle_t id, bool systemReady);
protected:
- mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) final;
- void threadLoop_mix() final;
+ mixer_state prepareTracks_l(Vector<sp<IAfTrack>>* tracksToRemove) final
+ REQUIRES(mutex(), ThreadBase_ThreadLoop);
+ void threadLoop_mix() final REQUIRES(ThreadBase_ThreadLoop);
private:
- bool mIsBitPerfect;
- float mVolumeLeft = 0.f;
- float mVolumeRight = 0.f;
+ // These variables are only accessed on the threadLoop; hence need no mutex.
+ bool mIsBitPerfect GUARDED_BY(ThreadBase_ThreadLoop) = false;
+ float mVolumeLeft GUARDED_BY(ThreadBase_ThreadLoop) = 0.f;
+ float mVolumeRight GUARDED_BY(ThreadBase_ThreadLoop) = 0.f;
};
} // namespace android
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 47f95ac..0dbb502 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -568,9 +568,7 @@
getPackagesForUid(uid, packages);
if (isServiceUid(uid)) {
if (packages.isEmpty()) {
- ALOGD("OpPlayAudio: not muting track:%d usage:%d for service UID %d",
- id,
- attr.usage,
+ ALOGW("OpPlayAudio: not muting track:%d usage:%d for service UID %d", id, attr.usage,
uid);
return nullptr;
}
@@ -594,7 +592,6 @@
audio_usage_t usage, int id, uid_t uid)
: mThread(wp<IAfThreadBase>::fromExisting(thread)),
mHasOpPlayAudio(true),
- mAttributionSource(attributionSource),
mUsage((int32_t)usage),
mId(id),
mUid(uid),
@@ -614,10 +611,11 @@
// make sure not to broadcast the initial state since it is not needed and could
// cause a deadlock since this method can be called with the mThread->mLock held
checkPlayAudioForUsage(/*doBroadcast=*/false);
- if (mAttributionSource.packageName.has_value()) {
+ if (mPackageName.size()) {
mOpCallback = new PlayAudioOpCallback(this);
- mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO,
- mPackageName, mOpCallback);
+ mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mPackageName, mOpCallback);
+ } else {
+ ALOGW("Skipping OpPlayAudioMonitor due to null package name");
}
}
@@ -628,16 +626,16 @@
// Note this method is never called (and never to be) for audio server / patch record track
// - not called from constructor due to check on UID,
// - not called from PlayAudioOpCallback because the callback is not installed in this case
-void OpPlayAudioMonitor::checkPlayAudioForUsage(bool doBroadcast)
-{
- const bool hasAppOps = mAttributionSource.packageName.has_value()
- && mAppOpsManager.checkAudioOpNoThrow(
- AppOpsManager::OP_PLAY_AUDIO, mUsage, mUid, mPackageName) ==
- AppOpsManager::MODE_ALLOWED;
+void OpPlayAudioMonitor::checkPlayAudioForUsage(bool doBroadcast) {
+ const bool hasAppOps =
+ mPackageName.size() &&
+ mAppOpsManager.checkAudioOpNoThrow(AppOpsManager::OP_PLAY_AUDIO, mUsage, mUid,
+ mPackageName) == AppOpsManager::MODE_ALLOWED;
bool shouldChange = !hasAppOps; // check if we need to update.
if (mHasOpPlayAudio.compare_exchange_strong(shouldChange, hasAppOps)) {
- ALOGD("OpPlayAudio: track:%d usage:%d %smuted", mId, mUsage, hasAppOps ? "not " : "");
+ ALOGI("OpPlayAudio: track:%d package:%s usage:%d %smuted", mId,
+ String8(mPackageName).c_str(), mUsage, hasAppOps ? "not " : "");
if (doBroadcast) {
auto thread = mThread.promote();
if (thread != nullptr && thread->type() == IAfThreadBase::OFFLOAD) {
@@ -655,11 +653,11 @@
void OpPlayAudioMonitor::PlayAudioOpCallback::opChanged(int32_t op,
const String16& packageName) {
- // we only have uid, so we need to check all package names anyway
- UNUSED(packageName);
if (op != AppOpsManager::OP_PLAY_AUDIO) {
return;
}
+
+ ALOGI("%s OP_PLAY_AUDIO callback received for %s", __func__, String8(packageName).c_str());
sp<OpPlayAudioMonitor> monitor = mMonitor.promote();
if (monitor != NULL) {
monitor->checkPlayAudioForUsage(/*doBroadcast=*/true);
@@ -1170,9 +1168,11 @@
if (thread != 0) {
if (isOffloaded()) {
audio_utils::lock_guard _laf(thread->afThreadCallback()->mutex());
+ const bool nonOffloadableGlobalEffectEnabled =
+ thread->afThreadCallback()->isNonOffloadableGlobalEffectEnabled_l();
audio_utils::lock_guard _lth(thread->mutex());
sp<IAfEffectChain> ec = thread->getEffectChain_l(mSessionId);
- if (thread->afThreadCallback()->isNonOffloadableGlobalEffectEnabled_l() ||
+ if (nonOffloadableGlobalEffectEnabled ||
(ec != 0 && ec->isNonOffloadableEnabled())) {
invalidate();
return PERMISSION_DENIED;
@@ -1640,22 +1640,18 @@
if (mMuteEventExtras == nullptr) {
mMuteEventExtras = std::make_unique<os::PersistableBundle>();
}
- mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey),
- static_cast<int>(muteState));
+ mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey), static_cast<int>(muteState));
- result = audioManager->portEvent(mPortId,
- PLAYER_UPDATE_MUTED,
- mMuteEventExtras);
+ result = audioManager->portEvent(mPortId, PLAYER_UPDATE_MUTED, mMuteEventExtras);
}
if (result == OK) {
+ ALOGI("%s(%d): processed mute state for port ID %d from %d to %d", __func__, id(), mPortId,
+ int(muteState), int(mMuteState));
mMuteState = muteState;
} else {
- ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d",
- __func__,
- id(),
- mPortId,
- result);
+ ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d", __func__, id(),
+ mPortId, result);
}
}
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index da0df5f..b954bfc 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -270,6 +270,10 @@
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes) = 0;
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes) = 0;
+ virtual status_t updatePolicyMix(
+ const AudioMix& mix,
+ const std::vector<AudioMixMatchCriterion>& updatedCriteria) = 0;
+
virtual status_t setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices)
= 0;
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
diff --git a/services/audiopolicy/TEST_MAPPING b/services/audiopolicy/TEST_MAPPING
index fa3a5d3..a2ebb8d 100644
--- a/services/audiopolicy/TEST_MAPPING
+++ b/services/audiopolicy/TEST_MAPPING
@@ -34,5 +34,18 @@
{
"name": "audiopolicy_tests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "GtsGmscoreHostTestCases",
+ "options" : [
+ {
+ "include-filter": "com.google.android.gts.audio.AudioHostTest"
+ },
+ {
+ "include-filter": "com.google.android.gts.audio.AudioPolicyHostTest"
+ }
+ ]
+ }
]
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 1e57edd..13b70e5 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -328,6 +328,13 @@
wp<AudioPolicyMix> mPolicyMix; // non NULL when used by a dynamic policy
virtual uint32_t getRecommendedMuteDurationMs() const { return 0; }
+ virtual std::string info() const {
+ std::string result;
+ result.append("[portId:" );
+ result.append(android::internal::ToString(getId()));
+ result.append("]");
+ return result;
+ }
protected:
const sp<PolicyAudioPort> mPolicyAudioPort;
@@ -471,6 +478,8 @@
PortHandleVector getClientsForStream(audio_stream_type_t streamType) const;
+ virtual std::string info() const override;
+
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/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 7e29e10..b560bc4 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -59,6 +59,8 @@
status_t unregisterMix(const AudioMix& mix);
+ status_t updateMix(const AudioMix& mix, const std::vector<AudioMixMatchCriterion>& newCriteria);
+
void closeOutput(sp<SwAudioOutputDescriptor> &desc);
/**
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index 59eee52..c2e4b11 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -25,6 +25,10 @@
namespace android {
+class AudioInputCollection;
+class AudioInputDescriptor;
+class AudioPolicyClientInterface;
+
class EffectDescriptor : public RefBase
{
public:
@@ -40,6 +44,8 @@
int mId; // effect unique ID
audio_io_handle_t mIo; // io the effect is attached to
+ bool mIsOrphan = false; // on creation, effect is not yet attached but not yet orphan
+ bool mEnabledWhenMoved = false; // Backup enabled state before being moved
audio_session_t mSession; // audio session the effect is on
effect_descriptor_t mDesc; // effect descriptor
bool mEnabled; // enabled state: CPU load being used or not
@@ -69,12 +75,29 @@
void moveEffects(audio_session_t session,
audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput);
+ audio_io_handle_t dstOutput,
+ AudioPolicyClientInterface *clientInterface);
void moveEffects(const std::vector<int>& ids, audio_io_handle_t dstOutput);
+ void moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo, audio_io_handle_t dstIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *clientInterface);
+ void moveEffectsForIo(audio_session_t sessionId, audio_io_handle_t dstIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *mClientInterface);
+ void putOrphanEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *clientInterface);
+ void putOrphanEffectsForIo(audio_io_handle_t srcIo);
+ /**
+ * @brief Checks if an effect session was already attached to an io handle and return it if
+ * found. Check only for a given effect type if effectType is not null or for any effect
+ * otherwise.
+ * @param sessionId to consider.
+ * @param effectType to consider.
+ * @return ioHandle if found, AUDIO_IO_HANDLE_NONE otherwise.
+ */
audio_io_handle_t getIoForSession(audio_session_t sessionId,
const effect_uuid_t *effectType = nullptr);
-
+ bool hasOrphansForSession(audio_session_t sessionId);
+ EffectDescriptorCollection getOrphanEffectsForSession(audio_session_t sessionId) const;
void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
private:
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 2f424b8..8bd7575 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -987,6 +987,18 @@
return clientsForStream;
}
+std::string SwAudioOutputDescriptor::info() const {
+ std::string result;
+ result.append("[" );
+ result.append(AudioOutputDescriptor::info());
+ result.append("[io:" );
+ result.append(android::internal::ToString(mIoHandle));
+ result.append(", " );
+ result.append(isDuplicated() ? "duplicating" : mProfile->getTagName());
+ result.append("]]");
+ return result;
+}
+
void SwAudioOutputCollection::dump(String8 *dst) const
{
dst->appendFormat("\n Outputs (%zu):\n", size());
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index f870b4f..7ee75c7 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -21,6 +21,7 @@
#include <iterator>
#include <optional>
#include <regex>
+#include <vector>
#include "AudioPolicyMix.h"
#include "TypeConverter.h"
#include "HwModule.h"
@@ -225,6 +226,31 @@
return BAD_VALUE;
}
+status_t AudioPolicyMixCollection::updateMix(
+ const AudioMix& mix, const std::vector<AudioMixMatchCriterion>& updatedCriteria) {
+ if (!areMixCriteriaConsistent(mix.mCriteria)) {
+ ALOGE("updateMix(): updated criteria are not consistent "
+ "(MATCH & EXCLUDE criteria of the same type)");
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < size(); i++) {
+ const sp<AudioPolicyMix>& registeredMix = itemAt(i);
+ if (mix.mDeviceType == registeredMix->mDeviceType &&
+ mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0 &&
+ mix.mRouteFlags == registeredMix->mRouteFlags) {
+ registeredMix->mCriteria = updatedCriteria;
+ ALOGV("updateMix(): updated mix for dev=0x%x addr=%s", mix.mDeviceType,
+ mix.mDeviceAddress.c_str());
+ return NO_ERROR;
+ }
+ }
+
+ ALOGE("updateMix(): mix not registered for dev=0x%x addr=%s", mix.mDeviceType,
+ mix.mDeviceAddress.c_str());
+ return BAD_VALUE;
+}
+
status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType,
const String8& address, sp<AudioPolicyMix> &policyMix) const
{
@@ -269,6 +295,7 @@
ALOGV("getOutputForAttr() querying %zu mixes:", size());
primaryMix.clear();
bool mixesDisallowsRequestedDevice = false;
+ const bool isMmapRequested = (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = itemAt(i);
const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
@@ -279,6 +306,17 @@
mixesDisallowsRequestedDevice = true;
}
+ if (!primaryOutputMix && isMmapRequested) {
+ // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
+ // the current MmapStreamInterface::start to reject a specific client added to a shared
+ // mmap stream.
+ // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render
+ // policy is present. That ensures no shared mmap stream is used when an loopback
+ // render policy is registered.
+ ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__);
+ return INVALID_OPERATION;
+ }
+
if (primaryOutputMix && primaryMix != nullptr) {
ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
continue; // Primary output already found
@@ -289,12 +327,21 @@
continue; // skip the mix
}
- if ((flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) && is_mix_loopback(policyMix->mRouteFlags)) {
- // AAudio MMAP_NOIRQ streams cannot be routed to loopback/loopback+render
- // using dynamic audio policy.
- ALOGD("%s: Rejecting MMAP_NOIRQ request matched to loopback dynamic audio policy mix.",
- __func__);
- return INVALID_OPERATION;
+ if (isMmapRequested) {
+ if (is_mix_loopback(policyMix->mRouteFlags)) {
+ // AAudio MMAP_NOIRQ streams cannot be routed to loopback/loopback+render
+ // using dynamic audio policy.
+ ALOGD("%s: Rejecting MMAP_NOIRQ request matched to loopback dynamic "
+ "audio policy mix.", __func__);
+ return INVALID_OPERATION;
+ }
+ if (mixDevice != nullptr) {
+ // TODO(b/301619865): Only disallow the device that doesn't support MMAP.
+ ALOGD("%s: Rejecting MMAP_NOIRQ request matched to dynamic audio policy "
+ "mix pointing to device %s which the mmap support is unknown at this moment",
+ __func__, mixDevice->toString(false).c_str());
+ return INVALID_OPERATION;
+ }
}
if (mixDevice != nullptr && mixDevice->equals(requestedDevice)) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 3f9c8b0..c85df0f 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -18,9 +18,15 @@
//#define LOG_NDEBUG 0
#include <android-base/stringprintf.h>
+
+#include "AudioInputDescriptor.h"
#include "EffectDescriptor.h"
#include <utils/String8.h>
+#include <AudioPolicyInterface.h>
+#include "AudioPolicyMix.h"
+#include "HwModule.h"
+
namespace android {
void EffectDescriptor::dump(String8 *dst, int spaces) const
@@ -175,30 +181,57 @@
return MAX_EFFECTS_MEMORY;
}
-void EffectDescriptorCollection::moveEffects(audio_session_t session,
- audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
+void EffectDescriptorCollection::moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ audio_io_handle_t dstIo,
+ AudioPolicyClientInterface *clientInterface)
{
- ALOGV("%s session %d srcOutput %d dstOutput %d", __func__, session, srcOutput, dstOutput);
+ ALOGV("%s session %d srcIo %d dstIo %d", __func__, sessionId, srcIo, dstIo);
for (size_t i = 0; i < size(); i++) {
sp<EffectDescriptor> effect = valueAt(i);
- if (effect->mSession == session && effect->mIo == srcOutput) {
- effect->mIo = dstOutput;
+ if (effect->mSession == sessionId && effect->mIo == srcIo) {
+ effect->mIo = dstIo;
+ // Backup enable state before any updatePolicyState call
+ effect->mIsOrphan = (dstIo == AUDIO_IO_HANDLE_NONE);
+ }
+ }
+ clientInterface->moveEffects(sessionId, srcIo, dstIo);
+}
+
+void EffectDescriptorCollection::moveEffects(const std::vector<int>& ids, audio_io_handle_t dstIo)
+{
+ ALOGV("%s num effects %zu, first ID %d, dstIo %d",
+ __func__, ids.size(), ids.size() ? ids[0] : 0, dstIo);
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (std::find(begin(ids), end(ids), effect->mId) != end(ids)) {
+ effect->mIo = dstIo;
+ effect->mIsOrphan = (dstIo == AUDIO_IO_HANDLE_NONE);
}
}
}
-void EffectDescriptorCollection::moveEffects(const std::vector<int>& ids,
- audio_io_handle_t dstOutput)
+bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId)
{
- ALOGV("%s num effects %zu, first ID %d, dstOutput %d",
- __func__, ids.size(), ids.size() ? ids[0] : 0, dstOutput);
- for (size_t i = 0; i < size(); i++) {
+ for (size_t i = 0; i < size(); ++i) {
sp<EffectDescriptor> effect = valueAt(i);
- if (std::find(begin(ids), end(ids), effect->mId) != end(ids)) {
- effect->mIo = dstOutput;
+ if (effect->mSession == sessionId && effect->mIsOrphan) {
+ return true;
}
}
+ return false;
+}
+
+EffectDescriptorCollection EffectDescriptorCollection::getOrphanEffectsForSession(
+ audio_session_t sessionId) const
+{
+ EffectDescriptorCollection effects;
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (effect->mSession == sessionId && effect->mIsOrphan) {
+ effects.add(keyAt(i), effect);
+ }
+ }
+ return effects;
}
audio_io_handle_t EffectDescriptorCollection::getIoForSession(audio_session_t sessionId,
@@ -214,6 +247,84 @@
return AUDIO_IO_HANDLE_NONE;
}
+void EffectDescriptorCollection::moveEffectsForIo(audio_session_t session,
+ audio_io_handle_t dstIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ // No src io: try to find from effect session the src Io to move from
+ audio_io_handle_t srcIo = getIoForSession(session);
+ if (hasOrphansForSession(session) || (srcIo != AUDIO_IO_HANDLE_NONE && srcIo != dstIo)) {
+ moveEffects(session, srcIo, dstIo, inputs, clientInterface);
+ }
+}
+
+void EffectDescriptorCollection::moveEffects(audio_session_t session,
+ audio_io_handle_t srcIo, audio_io_handle_t dstIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ if ((srcIo != AUDIO_IO_HANDLE_NONE && srcIo == dstIo)
+ || (srcIo == AUDIO_IO_HANDLE_NONE && !hasOrphansForSession(session))) {
+ return;
+ }
+ // Either we may find orphan effects for given session or effects for this session might have
+ // been assigned first to another input (it may happen when an input is released or recreated
+ // after client sets its preferred device)
+ EffectDescriptorCollection effectsToMove;
+ if (srcIo == AUDIO_IO_HANDLE_NONE) {
+ ALOGV("%s: restoring effects for session %d from orphan park to io=%d", __func__,
+ session, dstIo);
+ effectsToMove = getOrphanEffectsForSession(session);
+ } else {
+ ALOGV("%s: moving effects for session %d from io=%d to io=%d", __func__, session, srcIo,
+ dstIo);
+ if (const sp<AudioInputDescriptor>& previousInputDesc = inputs->valueFor(srcIo)) {
+ effectsToMove = getEffectsForIo(srcIo);
+ for (size_t i = 0; i < effectsToMove.size(); ++i) {
+ const sp<EffectDescriptor>& effect = effectsToMove.valueAt(i);
+ effect->mEnabledWhenMoved = effect->mEnabled;
+ previousInputDesc->trackEffectEnabled(effect, false);
+ }
+ } else {
+ ALOGW("%s: no effect descriptor for srcIo %d", __func__, srcIo);
+ }
+ }
+ moveEffects(session, srcIo, dstIo, clientInterface);
+
+ if (dstIo != AUDIO_IO_HANDLE_NONE) {
+ if (const sp<AudioInputDescriptor>& inputDesc = inputs->valueFor(dstIo)) {
+ for (size_t i = 0; i < effectsToMove.size(); ++i) {
+ const sp<EffectDescriptor>& effect = effectsToMove.valueAt(i);
+ inputDesc->trackEffectEnabled(effect, effect->mEnabledWhenMoved);
+ }
+ } else {
+ ALOGW("%s: no effect descriptor for dstIo %d", __func__, dstIo);
+ }
+ }
+}
+
+void EffectDescriptorCollection::putOrphanEffectsForIo(audio_io_handle_t srcIo)
+{
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (effect->mIo == srcIo) {
+ effect->mIo = AUDIO_IO_HANDLE_NONE;
+ effect->mIsOrphan = true;
+ }
+ }
+}
+
+void EffectDescriptorCollection::putOrphanEffects(audio_session_t session,
+ audio_io_handle_t srcIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ if (getIoForSession(session) != srcIo) {
+ // Effect session not held by this client io handle
+ return;
+ }
+ ALOGV("%s: park effects for session %d and io=%d to orphans", __func__, session, srcIo);
+ moveEffects(session, srcIo, AUDIO_IO_HANDLE_NONE, inputs, clientInterface);
+}
+
EffectDescriptorCollection EffectDescriptorCollection::getEffectsForIo(audio_io_handle_t io) const
{
EffectDescriptorCollection effects;
diff --git a/services/audiopolicy/config/Android.bp b/services/audiopolicy/config/Android.bp
index 671b30a..86600f4 100644
--- a/services/audiopolicy/config/Android.bp
+++ b/services/audiopolicy/config/Android.bp
@@ -112,3 +112,15 @@
name: "r_submix_audio_policy_configuration",
srcs: ["r_submix_audio_policy_configuration.xml"],
}
+filegroup {
+ name: "bluetooth_audio_policy_configuration_7_0",
+ srcs: ["bluetooth_audio_policy_configuration_7_0.xml"],
+}
+filegroup {
+ name: "bluetooth_with_le_audio_policy_configuration_7_0",
+ srcs: ["bluetooth_with_le_audio_policy_configuration_7_0.xml"],
+}
+filegroup {
+ name: "hearing_aid_audio_policy_configuration_7_0",
+ srcs: ["hearing_aid_audio_policy_configuration_7_0.xml"],
+}
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 15f7842..fa6be39 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -175,34 +175,37 @@
// - cannot route from voice call RX OR
// - audio HAL version is < 3.0 and TX device is on the primary HW module
if (getPhoneState() == AUDIO_MODE_IN_CALL) {
- audio_devices_t txDevice = AUDIO_DEVICE_NONE;
- sp<DeviceDescriptor> txDeviceDesc =
- getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION);
- if (txDeviceDesc != nullptr) {
- txDevice = txDeviceDesc->type();
- }
sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
- LOG_ALWAYS_FATAL_IF(primaryOutput == nullptr, "Primary output not found");
- DeviceVector availPrimaryInputDevices =
- availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle());
+ if (primaryOutput != nullptr) {
+ audio_devices_t txDevice = AUDIO_DEVICE_NONE;
+ sp<DeviceDescriptor> txDeviceDesc =
+ getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION);
+ if (txDeviceDesc != nullptr) {
+ txDevice = txDeviceDesc->type();
+ }
+ DeviceVector availPrimaryInputDevices =
+ availableInputDevices.getDevicesFromHwModule(
+ primaryOutput->getModuleHandle());
- // TODO: getPrimaryOutput return only devices from first module in
- // audio_policy_configuration.xml, hearing aid is not there, but it's
- // a primary device
- // FIXME: this is not the right way of solving this problem
- DeviceVector availPrimaryOutputDevices = availableOutputDevices.getDevicesFromTypes(
- primaryOutput->supportedDevices().types());
- availPrimaryOutputDevices.add(
- availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID));
+ // TODO: getPrimaryOutput return only devices from first module in
+ // audio_policy_configuration.xml, hearing aid is not there, but it's
+ // a primary device
+ // FIXME: this is not the right way of solving this problem
+ DeviceVector availPrimaryOutputDevices = availableOutputDevices.getDevicesFromTypes(
+ primaryOutput->supportedDevices().types());
+ availPrimaryOutputDevices.add(
+ availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID));
- if ((availableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX,
- String8(""), AUDIO_FORMAT_DEFAULT) == nullptr) ||
- ((availPrimaryInputDevices.getDevice(
- txDevice, String8(""), AUDIO_FORMAT_DEFAULT) != nullptr) &&
- (primaryOutput->getPolicyAudioPort()->getModuleVersionMajor() < 3))) {
- availableOutputDevices = availPrimaryOutputDevices;
+ if ((availableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX,
+ String8(""), AUDIO_FORMAT_DEFAULT) == nullptr)
+ || ((availPrimaryInputDevices.getDevice(
+ txDevice, String8(""), AUDIO_FORMAT_DEFAULT) != nullptr) &&
+ (primaryOutput->getPolicyAudioPort()->getModuleVersionMajor() < 3))) {
+ availableOutputDevices = availPrimaryOutputDevices;
+ }
+ } else {
+ ALOGE("%s, STRATEGY_PHONE: Primary output not found", __func__);
}
-
}
// Do not use A2DP devices when in call but use them when not in call
// (e.g for voice mail playback)
@@ -595,8 +598,11 @@
if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
(availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX,
String8(""), AUDIO_FORMAT_DEFAULT)) == nullptr) {
- LOG_ALWAYS_FATAL_IF(availablePrimaryDevices.isEmpty(), "Primary devices not found");
- availableDevices = availablePrimaryDevices;
+ if (!availablePrimaryDevices.isEmpty()) {
+ availableDevices = availablePrimaryDevices;
+ } else {
+ ALOGE("%s, AUDIO_SOURCE_VOICE_COMMUNICATION: Primary devices not found", __func__);
+ }
}
if (audio_is_bluetooth_out_sco_device(commDeviceType)) {
@@ -650,8 +656,11 @@
case AUDIO_SOURCE_HOTWORD:
// We should not use primary output criteria for Hotword but rather limit
// to devices attached to the same HW module as the build in mic
- LOG_ALWAYS_FATAL_IF(availablePrimaryDevices.isEmpty(), "Primary devices not found");
- availableDevices = availablePrimaryDevices;
+ if (!availablePrimaryDevices.isEmpty()) {
+ availableDevices = availablePrimaryDevices;
+ } else {
+ ALOGE("%s, AUDIO_SOURCE_HOTWORD: Primary devices not found", __func__);
+ }
if (audio_is_bluetooth_out_sco_device(commDeviceType)) {
device = availableDevices.getDevice(
AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
diff --git a/services/audiopolicy/fuzzer/aidl/Android.bp b/services/audiopolicy/fuzzer/aidl/Android.bp
new file mode 100644
index 0000000..38a2cde
--- /dev/null
+++ b/services/audiopolicy/fuzzer/aidl/Android.bp
@@ -0,0 +1,74 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+cc_defaults {
+ name: "audiopolicy_aidl_fuzzer_defaults",
+ shared_libs: [
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ "libaudiopolicy",
+ "libaudiopolicymanagerdefault",
+ "libactivitymanager_aidl",
+ "libaudiohal",
+ "libaudiopolicyservice",
+ "libaudioflinger",
+ "libaudioclient",
+ "libaudioprocessing",
+ "libhidlbase",
+ "liblog",
+ "libmediautils",
+ "libnblog",
+ "libnbaio",
+ "libpowermanager",
+ "libvibrator",
+ "packagemanager_aidl-cpp",
+ ],
+ static_libs: [
+ "libfakeservicemanager",
+ "libmediaplayerservice",
+ ],
+ header_libs: [
+ "libaudiohal_headers",
+ "libaudioflinger_headers",
+ "libaudiopolicymanager_interface_headers",
+ "libbinder_headers",
+ "libmedia_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ hotlists: ["4593311"],
+ description: "The fuzzer targets the APIs of libaudiopolicy",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
+ },
+}
+
+cc_fuzz {
+ name: "audiopolicy_aidl_fuzzer",
+ srcs: ["audiopolicy_aidl_fuzzer.cpp"],
+ defaults: [
+ "audiopolicy_aidl_fuzzer_defaults",
+ "service_fuzzer_defaults",
+ ],
+}
diff --git a/services/audiopolicy/fuzzer/aidl/audiopolicy_aidl_fuzzer.cpp b/services/audiopolicy/fuzzer/aidl/audiopolicy_aidl_fuzzer.cpp
new file mode 100644
index 0000000..ca79c49
--- /dev/null
+++ b/services/audiopolicy/fuzzer/aidl/audiopolicy_aidl_fuzzer.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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 <AudioFlinger.h>
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/media/IAudioPolicyService.h>
+#include <fakeservicemanager/FakeServiceManager.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/IAudioFlinger.h>
+#include <service/AudioPolicyService.h>
+
+using namespace android;
+using namespace android::binder;
+using namespace android::hardware;
+using android::fuzzService;
+
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+sp<FakeServiceManager> gFakeServiceManager;
+
+bool addService(const String16& serviceName, const sp<FakeServiceManager>& fakeServiceManager,
+ FuzzedDataProvider& fdp) {
+ sp<IBinder> binder = getRandomBinder(&fdp);
+ if (binder == nullptr) {
+ return false;
+ }
+ CHECK_EQ(NO_ERROR, fakeServiceManager->addService(serviceName, binder));
+ return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ std::call_once(gSmOnce, [&] {
+ /* Create a FakeServiceManager instance and add required services */
+ gFakeServiceManager = sp<FakeServiceManager>::make();
+ setDefaultServiceManager(gFakeServiceManager);
+ });
+ gFakeServiceManager->clear();
+
+ for (const char* service :
+ {"activity", "sensor_privacy", "permission", "scheduling_policy",
+ "android.hardware.audio.core.IConfig", "batterystats", "media.metrics"}) {
+ if (!addService(String16(service), gFakeServiceManager, fdp)) {
+ return 0;
+ }
+ }
+
+ const auto audioFlinger = sp<AudioFlinger>::make();
+ const auto afAdapter = sp<AudioFlingerServerAdapter>::make(audioFlinger);
+
+ CHECK_EQ(NO_ERROR,
+ gFakeServiceManager->addService(
+ String16(IAudioFlinger::DEFAULT_SERVICE_NAME), IInterface::asBinder(afAdapter),
+ false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT));
+
+ AudioSystem::get_audio_flinger_for_fuzzer();
+ const auto audioPolicyService = sp<AudioPolicyService>::make();
+
+ CHECK_EQ(NO_ERROR,
+ gFakeServiceManager->addService(String16("media.audio_policy"), audioPolicyService,
+ false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT));
+
+ fuzzService(media::IAudioPolicyService::asBinder(audioPolicyService),
+ FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 8793085..58fcb5c 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -661,7 +661,9 @@
}
AudioPolicyManagerFuzzerDPPlaybackReRouting::~AudioPolicyManagerFuzzerDPPlaybackReRouting() {
- mManager->stopInput(mPortId);
+ if (mManager) {
+ mManager->stopInput(mPortId);
+ }
}
bool AudioPolicyManagerFuzzerDPPlaybackReRouting::initialize() {
@@ -773,7 +775,9 @@
}
AudioPolicyManagerFuzzerDPMixRecordInjection::~AudioPolicyManagerFuzzerDPMixRecordInjection() {
- mManager->stopOutput(mPortId);
+ if (mManager) {
+ mManager->stopOutput(mPortId);
+ }
}
bool AudioPolicyManagerFuzzerDPMixRecordInjection::initialize() {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 878c0cd..99042af 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "utils/Errors.h"
#define LOG_TAG "APM_AudioPolicyManager"
// Need to keep the log statements even in production builds
@@ -337,7 +338,7 @@
outputsToReopenWithDevices.emplace(mOutputs.keyAt(i), newDevices);
continue;
}
- setOutputDevices(desc, newDevices, force, 0);
+ setOutputDevices(__func__, desc, newDevices, force, 0);
}
if (!desc->isDuplicated() && desc->mProfile->hasDynamicAudioProfile() &&
!activeMediaDevices.empty() && desc->devices() != activeMediaDevices &&
@@ -657,7 +658,7 @@
status_t AudioPolicyManager::updateCallRouting(bool fromCache, uint32_t delayMs, uint32_t *waitMs)
{
- if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
+ if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) {
DeviceVector rxDevices = selectBestRxSinkDevicesForCall(fromCache);
return updateCallRoutingInternal(rxDevices, delayMs, waitMs);
}
@@ -670,14 +671,21 @@
bool createTxPatch = false;
bool createRxPatch = false;
uint32_t muteWaitMs = 0;
- if(!hasPrimaryOutput() ||
+ if (hasPrimaryOutput() &&
mPrimaryOutput->devices().onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_STUB)) {
return INVALID_OPERATION;
}
- ALOG_ASSERT(!rxDevices.isEmpty(), "%s() no selected output device", __func__);
audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
+
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+
+ if (rxDevices.isEmpty()) {
+ ALOGW("%s() no selected output device", __func__);
+ return INVALID_OPERATION;
+ }
if (txSourceDevice == nullptr) {
ALOGE("%s() selected input device not available", __func__);
return INVALID_OPERATION;
@@ -686,9 +694,6 @@
ALOGV("%s device rxDevice %s txDevice %s", __func__,
rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str());
- disconnectTelephonyAudioSource(mCallRxSourceClient);
- disconnectTelephonyAudioSource(mCallTxSourceClient);
-
auto telephonyRxModule =
mHwModules.getModuleForDeviceType(AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT);
auto telephonyTxModule =
@@ -728,7 +733,11 @@
// Use legacy routing method for voice calls via setOutputDevice() on primary output.
// Otherwise, create two audio patches for TX and RX path.
if (!createRxPatch) {
- muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs);
+ if (!hasPrimaryOutput()) {
+ ALOGW("%s() no primary output available", __func__);
+ return INVALID_OPERATION;
+ }
+ muteWaitMs = setOutputDevices(__func__, mPrimaryOutput, rxDevices, true, delayMs);
} else { // create RX path audio patch
connectTelephonyRxAudioSource();
// If the TX device is on the primary HW module but RX device is
@@ -874,21 +883,21 @@
}
}
- if (hasPrimaryOutput()) {
- if (state == AUDIO_MODE_IN_CALL) {
- (void)updateCallRouting(false /*fromCache*/, delayMs);
- } else {
+ if (state == AUDIO_MODE_IN_CALL) {
+ (void)updateCallRouting(false /*fromCache*/, delayMs);
+ } else {
+ if (oldState == AUDIO_MODE_IN_CALL) {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
+ if (hasPrimaryOutput()) {
DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
// force routing command to audio hardware when ending call
// even if no device change is needed
if (isStateInCall(oldState) && rxDevices.isEmpty()) {
rxDevices = mPrimaryOutput->devices();
}
- if (oldState == AUDIO_MODE_IN_CALL) {
- disconnectTelephonyAudioSource(mCallRxSourceClient);
- disconnectTelephonyAudioSource(mCallTxSourceClient);
- }
- setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
+ setOutputDevices(__func__, mPrimaryOutput, rxDevices, force, 0);
}
}
@@ -906,7 +915,7 @@
outputsToReopen.emplace(mOutputs.keyAt(i), newDevices);
continue;
}
- setOutputDevices(desc, newDevices, forceRouting, 0 /*delayMs*/, nullptr,
+ setOutputDevices(__func__, desc, newDevices, forceRouting, 0 /*delayMs*/, nullptr,
true /*requiresMuteCheck*/, !forceRouting /*requiresVolumeCheck*/);
}
}
@@ -1325,10 +1334,15 @@
AudioProfileVector profiles;
status_t ret = getProfilesForDevices(outputDevices, profiles, *flags, false /*isInput*/);
if (ret == NO_ERROR && !profiles.empty()) {
- config->channel_mask = profiles[0]->getChannels().empty() ? config->channel_mask
- : *profiles[0]->getChannels().begin();
- config->sample_rate = profiles[0]->getSampleRates().empty() ? config->sample_rate
- : *profiles[0]->getSampleRates().begin();
+ const auto channels = profiles[0]->getChannels();
+ if (!channels.empty() && (channels.find(config->channel_mask) == channels.end())) {
+ config->channel_mask = *channels.begin();
+ }
+ const auto sampleRates = profiles[0]->getSampleRates();
+ if (!sampleRates.empty() &&
+ (sampleRates.find(config->sample_rate) == sampleRates.end())) {
+ config->sample_rate = *sampleRates.begin();
+ }
config->format = profiles[0]->getFormat();
}
return INVALID_OPERATION;
@@ -2334,7 +2348,8 @@
return DEAD_OBJECT;
}
const uint32_t muteWaitMs =
- setOutputDevices(outputDesc, devices, force, 0, nullptr, requiresMuteCheck);
+ setOutputDevices(__func__, outputDesc, devices, force, 0, nullptr,
+ requiresMuteCheck);
// apply volume rules for current stream and device if necessary
auto &curves = getVolumeCurves(client->attributes());
@@ -2417,7 +2432,7 @@
outputsToReopen.emplace(mOutputs.keyAt(i), newDevices);
continue;
}
- setOutputDevices(desc, newDevices, force, delayMs);
+ setOutputDevices(__func__, desc, newDevices, force, delayMs);
// re-apply device specific volume if not done by setOutputDevice()
if (!force) {
applyStreamVolumes(desc, newDevices.types(), delayMs);
@@ -2519,7 +2534,7 @@
// still contain data that needs to be drained. The latency only covers the audio HAL
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
- setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2,
+ setOutputDevices(__func__, outputDesc, newDevices, false, outputDesc->latency()*2,
nullptr, true /*requiresMuteCheck*/, requiresVolumeCheck);
// force restoring the device selection on other active outputs if it differs from the
@@ -2542,7 +2557,7 @@
outputsToReopen.emplace(mOutputs.keyAt(i), newDevices2);
continue;
}
- setOutputDevices(desc, newDevices2, force, delayMs);
+ setOutputDevices(__func__, desc, newDevices2, force, delayMs);
// re-apply device specific volume if not done by setOutputDevice()
if (!force) {
@@ -2645,6 +2660,7 @@
sp<AudioPolicyMix> policyMix;
sp<DeviceDescriptor> device;
sp<AudioInputDescriptor> inputDesc;
+ sp<AudioInputDescriptor> previousInputDesc;
sp<RecordClientDescriptor> clientDesc;
audio_port_handle_t requestedDeviceId = *selectedDeviceId;
uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(attributionSource.uid));
@@ -2774,10 +2790,15 @@
status_t ret = getProfilesForDevices(
DeviceVector(device), profiles, flags, true /*isInput*/);
if (ret == NO_ERROR && !profiles.empty()) {
- config->channel_mask = profiles[0]->getChannels().empty() ? config->channel_mask
- : *profiles[0]->getChannels().begin();
- config->sample_rate = profiles[0]->getSampleRates().empty() ? config->sample_rate
- : *profiles[0]->getSampleRates().begin();
+ const auto channels = profiles[0]->getChannels();
+ if (!channels.empty() && (channels.find(config->channel_mask) == channels.end())) {
+ config->channel_mask = *channels.begin();
+ }
+ const auto sampleRates = profiles[0]->getSampleRates();
+ if (!sampleRates.empty() &&
+ (sampleRates.find(config->sample_rate) == sampleRates.end())) {
+ config->sample_rate = *sampleRates.begin();
+ }
config->format = profiles[0]->getFormat();
}
goto error;
@@ -2796,6 +2817,8 @@
requestedDeviceId, attributes.source, flags,
isSoundTrigger);
inputDesc = mInputs.valueFor(*input);
+ // Move (if found) effect for the client session to its input
+ mEffects.moveEffectsForIo(session, *input, &mInputs, mpClientInterface);
inputDesc->addClient(clientDesc);
ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d",
@@ -3105,7 +3128,7 @@
ALOGV("%s %d", __FUNCTION__, input);
inputDesc->removeClient(portId);
-
+ mEffects.putOrphanEffects(client->session(), input, &mInputs, mpClientInterface);
if (inputDesc->getClientCount() > 0) {
ALOGV("%s(%d) %zu clients remaining", __func__, portId, inputDesc->getClientCount());
return;
@@ -3467,8 +3490,8 @@
}
if (output != mMusicEffectOutput) {
- mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output);
- mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output);
+ mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output,
+ mpClientInterface);
mMusicEffectOutput = output;
}
@@ -3776,6 +3799,17 @@
return res;
}
+status_t AudioPolicyManager::updatePolicyMix(
+ const AudioMix& mix,
+ const std::vector<AudioMixMatchCriterion>& updatedCriteria) {
+ status_t res = mPolicyMixes.updateMix(mix, updatedCriteria);
+ if (res == NO_ERROR) {
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+ }
+ return res;
+}
+
void AudioPolicyManager::dumpManualSurroundFormats(String8 *dst) const
{
size_t i = 0;
@@ -3938,8 +3972,9 @@
outputsToReopen.emplace(mOutputs.keyAt(i), newDevices);
continue;
}
- waitMs = setOutputDevices(outputDesc, newDevices, forceRouting, delayMs, nullptr,
- !skipDelays /*requiresMuteCheck*/,
+
+ waitMs = setOutputDevices(__func__, outputDesc, newDevices, forceRouting, delayMs,
+ nullptr, !skipDelays /*requiresMuteCheck*/,
!forceRouting /*requiresVolumeCheck*/, skipDelays);
// Only apply special touch sound delay once
delayMs = 0;
@@ -4926,7 +4961,7 @@
// TODO: reconfigure output format and channels here
ALOGV("%s setting device %s on output %d",
__func__, dumpDeviceTypes(devices.types()).c_str(), outputDesc->mIoHandle);
- setOutputDevices(outputDesc, devices, true, 0, handle);
+ setOutputDevices(__func__, outputDesc, devices, true, 0, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -5185,7 +5220,7 @@
return BAD_VALUE;
}
- setOutputDevices(outputDesc,
+ setOutputDevices(__func__, outputDesc,
getNewOutputDevices(outputDesc, true /*fromCache*/),
true,
0,
@@ -5223,14 +5258,9 @@
return NO_ERROR;
}
patchHandle = outputDesc->getPatchHandle();
- // When a Sw bridge is released, the mixer used by this bridge will release its
- // patch at AudioFlinger side. Hence, the mixer audio patch must be recreated
- // Reuse patch handle to force audio flinger removing initial mixer patch removal
- // updating hal patch handle (prevent leaks).
// While using a HwBridge, force reconsidering device only if not reusing an existing
// output and no more activity on output (will force to close).
- bool force = sourceDesc->useSwBridge() ||
- (sourceDesc->canCloseOutput() && !outputDesc->isActive());
+ const bool force = sourceDesc->canCloseOutput() && !outputDesc->isActive();
// APM pattern is to have always outputs opened / patch realized for reachable devices.
// Update device may result to NONE (empty), coupled with force, it releases the patch.
// Reconsider device only for cases:
@@ -5239,7 +5269,7 @@
// 3 / Inactive Output previously hosting SwBridge that can be closed.
bool updateDevice = outputDesc->isActive() || !sourceDesc->useSwBridge() ||
sourceDesc->canCloseOutput();
- setOutputDevices(outputDesc,
+ setOutputDevices(__func__, outputDesc,
updateDevice ? getNewOutputDevices(outputDesc, true /*fromCache*/) :
outputDesc->devices(),
force,
@@ -5376,7 +5406,7 @@
outputsToReopen.emplace(mOutputs.keyAt(j), newDevices);
continue;
}
- setOutputDevices(outputDesc, newDevices, false);
+ setOutputDevices(__func__, outputDesc, newDevices, false);
}
}
reopenOutputsWithDevices(outputsToReopen);
@@ -6253,12 +6283,13 @@
if (mPrimaryOutput == nullptr &&
outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
mPrimaryOutput = outputDesc;
+ mPrimaryModuleHandle = mPrimaryOutput->getModuleHandle();
}
if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
outputDesc->close();
} else {
addOutput(output, outputDesc);
- setOutputDevices(outputDesc,
+ setOutputDevices(__func__, outputDesc,
DeviceVector(supportedDevice),
true,
0,
@@ -6458,8 +6489,8 @@
if (device_distinguishes_on_address(deviceType)) {
ALOGV("checkOutputsForDevice(): setOutputDevices %s",
device->toString().c_str());
- setOutputDevices(desc, DeviceVector(device), true/*force*/, 0/*delay*/,
- NULL/*patch handle*/);
+ setOutputDevices(__func__, desc, DeviceVector(device), true/*force*/,
+ 0/*delay*/, NULL/*patch handle*/);
}
ALOGV("checkOutputsForDevice(): adding output %d", output);
}
@@ -6754,6 +6785,7 @@
mpClientInterface->onAudioPatchListUpdate();
}
+ mEffects.putOrphanEffectsForIo(input);
inputDesc->close();
mInputs.removeItem(input);
@@ -7086,7 +7118,9 @@
DeviceVector AudioPolicyManager::getNewOutputDevices(const sp<SwAudioOutputDescriptor>& outputDesc,
bool fromCache)
{
- DeviceVector devices;
+ if (outputDesc == nullptr) {
+ return DeviceVector{};
+ }
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
@@ -7120,6 +7154,7 @@
return DeviceVector(device);
}
+ DeviceVector devices;
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
StreamTypeVector streams = mEngine->getStreamTypesForProductStrategy(productStrategy);
auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
@@ -7342,7 +7377,7 @@
if (!desc->supportedDevices().containsAtLeastOne(outputDesc->supportedDevices())) {
continue;
}
- ALOGVV("%s() %s (curDevice %s)", __func__,
+ ALOGVV("%s() output %s %s (curDevice %s)", __func__, desc->info().c_str(),
mute ? "muting" : "unmuting", curDevices.toString().c_str());
setStrategyMute(productStrategy, mute, desc, mute ? 0 : delayMs);
if (desc->isStrategyActive(productStrategy)) {
@@ -7395,7 +7430,8 @@
return 0;
}
-uint32_t AudioPolicyManager::setOutputDevices(const sp<SwAudioOutputDescriptor>& outputDesc,
+uint32_t AudioPolicyManager::setOutputDevices(const char *caller,
+ const sp<SwAudioOutputDescriptor>& outputDesc,
const DeviceVector &devices,
bool force,
int delayMs,
@@ -7404,13 +7440,15 @@
bool skipMuteDelay)
{
// TODO(b/262404095): Consider if the output need to be reopened.
- ALOGV("%s device %s delayMs %d", __func__, devices.toString().c_str(), delayMs);
+ std::string logPrefix = std::string("caller ") + caller + outputDesc->info();
+ ALOGV("%s %s device %s delayMs %d", __func__, logPrefix.c_str(),
+ devices.toString().c_str(), delayMs);
uint32_t muteWaitMs;
if (outputDesc->isDuplicated()) {
- muteWaitMs = setOutputDevices(outputDesc->subOutput1(), devices, force, delayMs,
+ muteWaitMs = setOutputDevices(__func__, outputDesc->subOutput1(), devices, force, delayMs,
nullptr /* patchHandle */, requiresMuteCheck, skipMuteDelay);
- muteWaitMs += setOutputDevices(outputDesc->subOutput2(), devices, force, delayMs,
+ muteWaitMs += setOutputDevices(__func__, outputDesc->subOutput2(), devices, force, delayMs,
nullptr /* patchHandle */, requiresMuteCheck, skipMuteDelay);
return muteWaitMs;
}
@@ -7420,7 +7458,8 @@
DeviceVector prevDevices = outputDesc->devices();
DeviceVector availPrevDevices = mAvailableOutputDevices.filter(prevDevices);
- ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
+ ALOGV("%s %s prevDevice %s", __func__, logPrefix.c_str(),
+ prevDevices.toString().c_str());
if (!filteredDevices.isEmpty()) {
outputDesc->setDevices(filteredDevices);
@@ -7430,7 +7469,8 @@
if (requiresMuteCheck) {
muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevices, delayMs);
} else {
- ALOGV("%s: suppressing checkDeviceMuteStrategies", __func__);
+ ALOGV("%s: %s suppressing checkDeviceMuteStrategies", __func__,
+ logPrefix.c_str());
muteWaitMs = 0;
}
@@ -7440,7 +7480,8 @@
// output profile or if new device is not supported AND previous device(s) is(are) still
// available (otherwise reset device must be done on the output)
if (!devices.isEmpty() && filteredDevices.isEmpty() && !availPrevDevices.empty()) {
- ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
+ ALOGV("%s: %s unsupported device %s for output", __func__, logPrefix.c_str(),
+ devices.toString().c_str());
// restore previous device after evaluating strategy mute state
outputDesc->setDevices(prevDevices);
return muteWaitMs;
@@ -7453,16 +7494,19 @@
// AND the output is connected by a valid audio patch.
// Doing this check here allows the caller to call setOutputDevices() without conditions
if ((filteredDevices.isEmpty() || filteredDevices == prevDevices) && !force && outputRouted) {
- ALOGV("%s setting same device %s or null device, force=%d, patch handle=%d", __func__,
- filteredDevices.toString().c_str(), force, outputDesc->getPatchHandle());
+ ALOGV("%s %s setting same device %s or null device, force=%d, patch handle=%d",
+ __func__, logPrefix.c_str(), filteredDevices.toString().c_str(), force,
+ outputDesc->getPatchHandle());
if (requiresVolumeCheck && !filteredDevices.isEmpty()) {
- ALOGV("%s setting same device on routed output, force apply volumes", __func__);
+ ALOGV("%s %s setting same device on routed output, force apply volumes",
+ __func__, logPrefix.c_str());
applyStreamVolumes(outputDesc, filteredDevices.types(), delayMs, true /*force*/);
}
return muteWaitMs;
}
- ALOGV("%s changing device to %s", __func__, filteredDevices.toString().c_str());
+ ALOGV("%s %s changing device to %s", __func__, logPrefix.c_str(),
+ filteredDevices.toString().c_str());
// do the routing
if (filteredDevices.isEmpty() || mAvailableOutputDevices.filter(filteredDevices).empty()) {
@@ -8405,6 +8449,7 @@
if (mPrimaryOutput == nullptr && profile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
ALOGV("%s(): re-assigning mPrimaryOutput", __func__);
mPrimaryOutput = desc;
+ mPrimaryModuleHandle = mPrimaryOutput->getModuleHandle();
}
return desc;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 863c785..6365962 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -292,6 +292,9 @@
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes);
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes);
+ virtual status_t updatePolicyMix(
+ const AudioMix& mix,
+ const std::vector<AudioMixMatchCriterion>& updatedCriteria) override;
virtual status_t setUidDeviceAffinities(uid_t uid,
const AudioDeviceTypeAddrVector& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
@@ -526,6 +529,7 @@
/**
* @brief setOutputDevices change the route of the specified output.
+ * @param caller of the method
* @param outputDesc to be considered
* @param device to be considered to route the output
* @param force if true, force the routing even if no change.
@@ -539,7 +543,8 @@
* @return the number of ms we have slept to allow new routing to take effect in certain
* cases.
*/
- uint32_t setOutputDevices(const sp<SwAudioOutputDescriptor>& outputDesc,
+ uint32_t setOutputDevices(const char *caller,
+ const sp<SwAudioOutputDescriptor>& outputDesc,
const DeviceVector &device,
bool force = false,
int delayMs = 0,
@@ -815,10 +820,10 @@
bool isPrimaryModule(const sp<HwModule> &module) const
{
- if (module == 0 || !hasPrimaryOutput()) {
+ if (module == nullptr || mPrimaryModuleHandle == AUDIO_MODULE_HANDLE_NONE) {
return false;
}
- return module->getHandle() == mPrimaryOutput->getModuleHandle();
+ return module->getHandle() == mPrimaryModuleHandle;
}
DeviceVector availablePrimaryOutputDevices() const
{
@@ -930,6 +935,8 @@
EngineInstance mEngine; // Audio Policy Engine instance
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
sp<SwAudioOutputDescriptor> mPrimaryOutput; // primary output descriptor
+ // mPrimaryModuleHandle is cached mPrimaryOutput->getModuleHandle();
+ audio_module_handle_t mPrimaryModuleHandle = AUDIO_MODULE_HANDLE_NONE;
// list of descriptors for outputs currently opened
sp<SwAudioOutputDescriptor> mSpatializerOutput;
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index f4fc8f1..c674909 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -7,26 +7,8 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_library_shared {
- name: "libaudiopolicyservice",
-
- defaults: [
- "latest_android_media_audio_common_types_cpp_shared",
- ],
-
- srcs: [
- "AudioPolicyClientImpl.cpp",
- "AudioPolicyEffects.cpp",
- "AudioPolicyInterfaceImpl.cpp",
- "AudioPolicyService.cpp",
- "CaptureStateNotifier.cpp",
- "Spatializer.cpp",
- "SpatializerPoseController.cpp",
- ],
-
- include_dirs: [
- "frameworks/av/services/audioflinger"
- ],
+cc_defaults {
+ name: "libaudiopolicyservice_dependencies",
shared_libs: [
"libactivitymanager_aidl",
@@ -41,7 +23,6 @@
"libaudioutils",
"libbinder",
"libcutils",
- "libeffectsconfig",
"libhardware_legacy",
"libheadtracking",
"libheadtracking-binding",
@@ -67,6 +48,36 @@
],
static_libs: [
+ "libeffectsconfig",
+ "libaudiopolicycomponents",
+ ]
+}
+
+cc_library {
+ name: "libaudiopolicyservice",
+
+ defaults: [
+ "libaudiopolicyservice_dependencies",
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
+ srcs: [
+ "AudioRecordClient.cpp",
+ "AudioPolicyClientImpl.cpp",
+ "AudioPolicyEffects.cpp",
+ "AudioPolicyInterfaceImpl.cpp",
+ "AudioPolicyService.cpp",
+ "CaptureStateNotifier.cpp",
+ "Spatializer.cpp",
+ "SpatializerPoseController.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/services/audioflinger"
+ ],
+
+
+ static_libs: [
"framework-permission-aidl-cpp",
],
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 70a1785..85b7ad9 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -44,10 +44,7 @@
AudioPolicyEffects::AudioPolicyEffects(const sp<EffectsFactoryHalInterface>& effectsFactoryHal) {
// load xml config with effectsFactoryHal
status_t loadResult = loadAudioEffectConfig(effectsFactoryHal);
- if (loadResult == NO_ERROR) {
- mDefaultDeviceEffectFuture =
- std::async(std::launch::async, &AudioPolicyEffects::initDefaultDeviceEffects, this);
- } else if (loadResult < 0) {
+ if (loadResult < 0) {
ALOGW("Failed to query effect configuration, fallback to load .conf");
// load automatic audio effect modules
if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
@@ -60,6 +57,11 @@
}
}
+void AudioPolicyEffects::setDefaultDeviceEffects() {
+ mDefaultDeviceEffectFuture = std::async(
+ std::launch::async, &AudioPolicyEffects::initDefaultDeviceEffects, this);
+}
+
AudioPolicyEffects::~AudioPolicyEffects()
{
size_t i = 0;
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index 9f65a96..e17df48 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -117,6 +117,8 @@
// Remove the default stream effect from wherever it's attached.
status_t removeStreamDefaultEffect(audio_unique_id_t id);
+ void setDefaultDeviceEffects();
+
private:
void initDefaultDeviceEffects();
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 5d86e7c..ce9fb92 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#include "AudioPolicyService.h"
+#include "AudioRecordClient.h"
#include "TypeConverter.h"
#include <media/AidlConversion.h>
#include <media/AudioPolicy.h>
@@ -1763,6 +1764,22 @@
}
}
+Status AudioPolicyService::updatePolicyMixes(
+ const ::std::vector<::android::media::AudioMixUpdate>& updates) {
+ Mutex::Autolock _l(mLock);
+ for (const auto& update : updates) {
+ AudioMix mix = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_AudioMix(update.audioMix));
+ std::vector<AudioMixMatchCriterion> newCriteria =
+ VALUE_OR_RETURN_BINDER_STATUS(convertContainer<std::vector<AudioMixMatchCriterion>>(
+ update.newCriteria, aidl2legacy_AudioMixMatchCriterion));
+ int status;
+ if((status = mAudioPolicyManager->updatePolicyMix(mix, newCriteria)) != NO_ERROR) {
+ return binderStatusFromStatusT(status);
+ }
+ }
+ return binderStatusFromStatusT(NO_ERROR);
+}
+
Status AudioPolicyService::setUidDeviceAffinities(
int32_t uidAidl,
const std::vector<AudioDevice>& devicesAidl) {
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 041aa1c..aa2b6f5 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -25,7 +25,6 @@
#include <sys/time.h>
#include <dlfcn.h>
-#include <android/content/pm/IPackageManagerNative.h>
#include <audio_utils/clock.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
@@ -35,6 +34,7 @@
#include <binder/IResultReceiver.h>
#include <utils/String16.h>
#include <utils/threads.h>
+#include "AudioRecordClient.h"
#include "AudioPolicyService.h"
#include <hardware_legacy/power.h>
#include <media/AidlConversion.h>
@@ -120,6 +120,7 @@
BINDER_METHOD_ENTRY(releaseSoundTriggerSession) \
BINDER_METHOD_ENTRY(getPhoneState) \
BINDER_METHOD_ENTRY(registerPolicyMixes) \
+BINDER_METHOD_ENTRY(updatePolicyMixes) \
BINDER_METHOD_ENTRY(setUidDeviceAffinities) \
BINDER_METHOD_ENTRY(removeUidDeviceAffinities) \
BINDER_METHOD_ENTRY(setUserIdDeviceAffinities) \
@@ -216,27 +217,6 @@
{
delete interface;
}
-
-namespace {
-int getTargetSdkForPackageName(std::string_view packageName) {
- const auto binder = defaultServiceManager()->checkService(String16{"package_native"});
- int targetSdk = -1;
- if (binder != nullptr) {
- const auto pm = interface_cast<content::pm::IPackageManagerNative>(binder);
- if (pm != nullptr) {
- const auto status = pm->getTargetSdkVersionForPackage(
- String16{packageName.data(), packageName.size()}, &targetSdk);
- ALOGI("Capy check package %s, sdk %d", packageName.data(), targetSdk);
- return status.isOk() ? targetSdk : -1;
- }
- }
- return targetSdk;
-}
-
-bool doesPackageTargetAtLeastU(std::string_view packageName) {
- return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__;
-}
-} // anonymous
// ----------------------------------------------------------------------------
AudioPolicyService::AudioPolicyService()
@@ -327,6 +307,9 @@
}
}
AudioSystem::audioPolicyReady();
+ // AudioFlinger will handle effect creation and register these effects on audio_policy
+ // service. Hence, audio_policy service must be ready.
+ audioPolicyEffects->setDefaultDeviceEffects();
}
void AudioPolicyService::unloadAudioPolicyManager()
@@ -1186,20 +1169,6 @@
return false;
}
-/* static */
-bool AudioPolicyService::isAppOpSource(audio_source_t source)
-{
- switch (source) {
- case AUDIO_SOURCE_FM_TUNER:
- case AUDIO_SOURCE_ECHO_REFERENCE:
- case AUDIO_SOURCE_REMOTE_SUBMIX:
- return false;
- default:
- break;
- }
- return true;
-}
-
void AudioPolicyService::setAppState_l(sp<AudioRecordClient> client, app_state_t state)
{
AutoCallerClear acc;
@@ -1339,6 +1308,7 @@
case TRANSACTION_isStreamActiveRemotely:
case TRANSACTION_isSourceActive:
case TRANSACTION_registerPolicyMixes:
+ case TRANSACTION_updatePolicyMixes:
case TRANSACTION_setMasterMono:
case TRANSACTION_getSurroundFormats:
case TRANSACTION_getReportedSurroundFormats:
@@ -1900,113 +1870,6 @@
return binder::Status::ok();
}
-// ----------- AudioPolicyService::OpRecordAudioMonitor implementation ----------
-
-// static
-sp<AudioPolicyService::OpRecordAudioMonitor>
-AudioPolicyService::OpRecordAudioMonitor::createIfNeeded(
- const AttributionSourceState& attributionSource, const audio_attributes_t& attr,
- wp<AudioCommandThread> commandThread)
-{
- if (isAudioServerOrRootUid(attributionSource.uid)) {
- ALOGV("not silencing record for audio or root source %s",
- attributionSource.toString().c_str());
- return nullptr;
- }
-
- if (!AudioPolicyService::isAppOpSource(attr.source)) {
- ALOGD("not monitoring app op for uid %d and source %d",
- attributionSource.uid, attr.source);
- return nullptr;
- }
-
- if (!attributionSource.packageName.has_value()
- || attributionSource.packageName.value().size() == 0) {
- return nullptr;
- }
- return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), commandThread);
-}
-
-AudioPolicyService::OpRecordAudioMonitor::OpRecordAudioMonitor(
- const AttributionSourceState& attributionSource, int32_t appOp,
- wp<AudioCommandThread> commandThread) :
- mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp),
- mCommandThread(commandThread)
-{
-}
-
-AudioPolicyService::OpRecordAudioMonitor::~OpRecordAudioMonitor()
-{
- if (mOpCallback != 0) {
- mAppOpsManager.stopWatchingMode(mOpCallback);
- }
- mOpCallback.clear();
-}
-
-void AudioPolicyService::OpRecordAudioMonitor::onFirstRef()
-{
- checkOp();
- 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);
-}
-
-bool AudioPolicyService::OpRecordAudioMonitor::hasOp() const {
- return mHasOp.load();
-}
-
-// Called by RecordAudioOpCallback when the app op corresponding to this OpRecordAudioMonitor
-// is updated in AppOp callback and in onFirstRef()
-// Note this method is never called (and never to be) for audio server / root track
-// 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 AudioPolicyService::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(""))));
- const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
- // verbose logging only log when appOp changed
- ALOGI_IF(hasIt != mHasOp.load(),
- "App op %d missing, %ssilencing record %s",
- mAppOp, hasIt ? "un" : "", mAttributionSource.toString().c_str());
- mHasOp.store(hasIt);
-
- if (updateUidStates) {
- sp<AudioCommandThread> commandThread = mCommandThread.promote();
- if (commandThread != nullptr) {
- commandThread->updateUidStatesCommand();
- }
- }
-}
-
-AudioPolicyService::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
- const wp<OpRecordAudioMonitor>& monitor) : mMonitor(monitor)
-{ }
-
-void AudioPolicyService::OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op,
- const String16& packageName __unused) {
- sp<OpRecordAudioMonitor> monitor = mMonitor.promote();
- if (monitor != NULL) {
- if (op != monitor->getOp()) {
- return;
- }
- monitor->checkOp(true);
- }
-}
-
-
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index d0cde64..ff29305 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2009 The Android Open Source Project
*
@@ -28,7 +27,6 @@
#include <utils/Vector.h>
#include <utils/SortedVector.h>
#include <binder/ActivityManager.h>
-#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IUidObserver.h>
#include <system/audio.h>
@@ -64,6 +62,12 @@
// ----------------------------------------------------------------------------
+namespace media::audiopolicy {
+ class AudioRecordClient;
+}
+
+using ::android::media::audiopolicy::AudioRecordClient;
+
class AudioPolicyService :
public BinderService<AudioPolicyService>,
public media::BnAudioPolicyService,
@@ -194,6 +198,8 @@
binder::Status getPhoneState(AudioMode* _aidl_return) override;
binder::Status registerPolicyMixes(const std::vector<media::AudioMix>& mixes,
bool registration) override;
+ binder::Status updatePolicyMixes(
+ const ::std::vector<::android::media::AudioMixUpdate>& updates) override;
binder::Status setUidDeviceAffinities(int32_t uid,
const std::vector<AudioDevice>& devices) override;
binder::Status removeUidDeviceAffinities(int32_t uid) override;
@@ -401,7 +407,6 @@
// Handles binder shell commands
virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
- class AudioRecordClient;
// Sets whether the given UID records only silence
virtual void setAppState_l(sp<AudioRecordClient> client, app_state_t state) REQUIRES(mLock);
@@ -542,6 +547,7 @@
// Thread used to send audio config commands to audio flinger
// For audio config commands, it is necessary because audio flinger requires that the calling
// process (user) has permission to modify audio settings.
+ public:
class AudioCommandThread : public Thread {
class AudioCommand;
public:
@@ -732,6 +738,7 @@
wp<AudioPolicyService> mService;
};
+ private:
class AudioPolicyClient : public AudioPolicyClientInterface
{
public:
@@ -906,6 +913,7 @@
bool mAudioVolumeGroupCallbacksEnabled;
};
+ public:
class AudioClient : public virtual RefBase {
public:
AudioClient(const audio_attributes_t attributes,
@@ -927,82 +935,8 @@
const audio_port_handle_t deviceId; // selected input device port ID
bool active; // Playback/Capture is active or inactive
};
-
- // Checks and monitors app ops for AudioRecordClient
- class OpRecordAudioMonitor : public RefBase {
- public:
- ~OpRecordAudioMonitor() override;
- bool hasOp() const;
- int32_t getOp() const { return mAppOp; }
-
- static sp<OpRecordAudioMonitor> createIfNeeded(
- const AttributionSourceState& attributionSource,
- const audio_attributes_t& attr, wp<AudioCommandThread> commandThread);
-
private:
- OpRecordAudioMonitor(const AttributionSourceState& attributionSource, int32_t appOp,
- wp<AudioCommandThread> commandThread);
- void onFirstRef() override;
-
- AppOpsManager mAppOpsManager;
-
- class RecordAudioOpCallback : public BnAppOpsCallback {
- public:
- explicit RecordAudioOpCallback(const wp<OpRecordAudioMonitor>& monitor);
- void opChanged(int32_t op, const String16& packageName) override;
-
- private:
- const wp<OpRecordAudioMonitor> mMonitor;
- };
-
- sp<RecordAudioOpCallback> mOpCallback;
- // called by RecordAudioOpCallback when the app op for this OpRecordAudioMonitor is updated
- // in AppOp callback and in onFirstRef()
- // updateUidStates is true when the silenced state of active AudioRecordClients must be
- // re-evaluated
- void checkOp(bool updateUidStates = false);
-
- std::atomic_bool mHasOp;
- const AttributionSourceState mAttributionSource;
- const int32_t mAppOp;
- wp<AudioCommandThread> mCommandThread;
- };
-
- // --- AudioRecordClient ---
- // Information about each registered AudioRecord client
- // (between calls to getInputForAttr() and releaseInput())
- class AudioRecordClient : public AudioClient {
- public:
- AudioRecordClient(const audio_attributes_t attributes,
- const audio_io_handle_t io,
- const audio_session_t session, audio_port_handle_t portId,
- const audio_port_handle_t deviceId,
- const AttributionSourceState& attributionSource,
- bool canCaptureOutput, bool canCaptureHotword,
- wp<AudioCommandThread> commandThread) :
- AudioClient(attributes, io, attributionSource,
- session, portId, deviceId), attributionSource(attributionSource),
- startTimeNs(0), canCaptureOutput(canCaptureOutput),
- canCaptureHotword(canCaptureHotword), silenced(false),
- mOpRecordAudioMonitor(
- OpRecordAudioMonitor::createIfNeeded(attributionSource,
- attributes, commandThread)) {}
- ~AudioRecordClient() override = default;
-
- bool hasOp() const {
- return mOpRecordAudioMonitor ? mOpRecordAudioMonitor->hasOp() : true;
- }
-
- const AttributionSourceState attributionSource; // attribution source of client
- nsecs_t startTimeNs;
- const bool canCaptureOutput;
- const bool canCaptureHotword;
- bool silenced;
-
- private:
- sp<OpRecordAudioMonitor> mOpRecordAudioMonitor;
- };
// --- AudioPlaybackClient ---
diff --git a/services/audiopolicy/service/AudioRecordClient.cpp b/services/audiopolicy/service/AudioRecordClient.cpp
new file mode 100644
index 0000000..a89a84d
--- /dev/null
+++ b/services/audiopolicy/service/AudioRecordClient.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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/content/pm/IPackageManagerNative.h>
+
+#include "AudioRecordClient.h"
+#include "AudioPolicyService.h"
+
+namespace android::media::audiopolicy {
+
+using android::AudioPolicyService;
+
+namespace {
+bool isAppOpSource(audio_source_t source)
+{
+ switch (source) {
+ case AUDIO_SOURCE_FM_TUNER:
+ case AUDIO_SOURCE_ECHO_REFERENCE:
+ case AUDIO_SOURCE_REMOTE_SUBMIX:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+int getTargetSdkForPackageName(std::string_view packageName) {
+ const auto binder = defaultServiceManager()->checkService(String16{"package_native"});
+ int targetSdk = -1;
+ if (binder != nullptr) {
+ const auto pm = interface_cast<content::pm::IPackageManagerNative>(binder);
+ if (pm != nullptr) {
+ const auto status = pm->getTargetSdkVersionForPackage(
+ String16{packageName.data(), packageName.size()}, &targetSdk);
+ return status.isOk() ? targetSdk : -1;
+ }
+ }
+ return targetSdk;
+}
+
+bool doesPackageTargetAtLeastU(std::string_view packageName) {
+ return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__;
+}
+}
+
+// static
+sp<OpRecordAudioMonitor>
+OpRecordAudioMonitor::createIfNeeded(
+ const AttributionSourceState& attributionSource, const audio_attributes_t& attr,
+ wp<AudioPolicyService::AudioCommandThread> commandThread)
+{
+ if (isAudioServerOrRootUid(attributionSource.uid)) {
+ ALOGV("not silencing record for audio or root source %s",
+ attributionSource.toString().c_str());
+ return nullptr;
+ }
+
+ if (!isAppOpSource(attr.source)) {
+ ALOGD("not monitoring app op for uid %d and source %d",
+ attributionSource.uid, attr.source);
+ return nullptr;
+ }
+
+ if (!attributionSource.packageName.has_value()
+ || attributionSource.packageName.value().size() == 0) {
+ return nullptr;
+ }
+ return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), commandThread);
+}
+
+OpRecordAudioMonitor::OpRecordAudioMonitor(
+ const AttributionSourceState& attributionSource, int32_t appOp,
+ wp<AudioPolicyService::AudioCommandThread> commandThread) :
+ mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp),
+ mCommandThread(commandThread)
+{
+}
+
+OpRecordAudioMonitor::~OpRecordAudioMonitor()
+{
+ if (mOpCallback != 0) {
+ mAppOpsManager.stopWatchingMode(mOpCallback);
+ }
+ mOpCallback.clear();
+}
+
+void OpRecordAudioMonitor::onFirstRef()
+{
+ checkOp();
+ 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);
+}
+
+bool OpRecordAudioMonitor::hasOp() const {
+ return mHasOp.load();
+}
+
+// Called by RecordAudioOpCallback when the app op corresponding to this OpRecordAudioMonitor
+// is updated in AppOp callback and in onFirstRef()
+// Note this method is never called (and never to be) for audio server / root track
+// 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(""))));
+ const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
+ // verbose logging only log when appOp changed
+ ALOGI_IF(hasIt != mHasOp.load(),
+ "App op %d missing, %ssilencing record %s",
+ mAppOp, hasIt ? "un" : "", mAttributionSource.toString().c_str());
+ mHasOp.store(hasIt);
+
+ if (updateUidStates) {
+ sp<AudioPolicyService::AudioCommandThread> commandThread = mCommandThread.promote();
+ if (commandThread != nullptr) {
+ commandThread->updateUidStatesCommand();
+ }
+ }
+}
+
+OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
+ const wp<OpRecordAudioMonitor>& monitor) : mMonitor(monitor)
+{ }
+
+void OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op,
+ const String16& packageName __unused) {
+ sp<OpRecordAudioMonitor> monitor = mMonitor.promote();
+ if (monitor != NULL) {
+ if (op != monitor->getOp()) {
+ return;
+ }
+ monitor->checkOp(true);
+ }
+}
+
+} // android::media::audiopolicy::internal
diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h
new file mode 100644
index 0000000..d3be316
--- /dev/null
+++ b/services/audiopolicy/service/AudioRecordClient.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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/content/AttributionSourceState.h>
+#include <binder/AppOpsManager.h>
+#include <system/audio.h>
+#include <utils/RefBase.h>
+
+#include <cstdint>
+
+#include "AudioPolicyService.h"
+
+namespace android::media::audiopolicy {
+
+using ::android::content::AttributionSourceState;
+
+// Checks and monitors app ops for AudioRecordClient
+class OpRecordAudioMonitor : public RefBase {
+public:
+ ~OpRecordAudioMonitor() override;
+ bool hasOp() const;
+ int32_t getOp() const { return mAppOp; }
+
+ static sp<OpRecordAudioMonitor> createIfNeeded(
+ const AttributionSourceState& attributionSource,
+ const audio_attributes_t& attr,
+ wp<AudioPolicyService::AudioCommandThread> commandThread);
+
+private:
+ OpRecordAudioMonitor(const AttributionSourceState& attributionSource, int32_t appOp,
+ wp<AudioPolicyService::AudioCommandThread> commandThread);
+
+ void onFirstRef() override;
+
+ AppOpsManager mAppOpsManager;
+
+ class RecordAudioOpCallback : public BnAppOpsCallback {
+ public:
+ explicit RecordAudioOpCallback(const wp<OpRecordAudioMonitor>& monitor);
+ void opChanged(int32_t op, const String16& packageName) override;
+
+ private:
+ const wp<OpRecordAudioMonitor> mMonitor;
+ };
+
+ sp<RecordAudioOpCallback> mOpCallback;
+ // called by RecordAudioOpCallback when the app op for this OpRecordAudioMonitor is updated
+ // in AppOp callback and in onFirstRef()
+ // updateUidStates is true when the silenced state of active AudioRecordClients must be
+ // re-evaluated
+ void checkOp(bool updateUidStates = false);
+
+ std::atomic_bool mHasOp;
+ const AttributionSourceState mAttributionSource;
+ const int32_t mAppOp;
+ wp<AudioPolicyService::AudioCommandThread> mCommandThread;
+};
+
+// --- AudioRecordClient ---
+// Information about each registered AudioRecord client
+// (between calls to getInputForAttr() and releaseInput())
+class AudioRecordClient : public AudioPolicyService::AudioClient {
+public:
+ AudioRecordClient(const audio_attributes_t attributes,
+ const audio_io_handle_t io,
+ const audio_session_t session, audio_port_handle_t portId,
+ const audio_port_handle_t deviceId,
+ const AttributionSourceState& attributionSource,
+ bool canCaptureOutput, bool canCaptureHotword,
+ wp<AudioPolicyService::AudioCommandThread> commandThread) :
+ AudioClient(attributes, io, attributionSource,
+ session, portId, deviceId), attributionSource(attributionSource),
+ startTimeNs(0), canCaptureOutput(canCaptureOutput),
+ canCaptureHotword(canCaptureHotword), silenced(false),
+ mOpRecordAudioMonitor(
+ OpRecordAudioMonitor::createIfNeeded(attributionSource,
+ attributes, commandThread)) {}
+ ~AudioRecordClient() override = default;
+
+ bool hasOp() const {
+ return mOpRecordAudioMonitor ? mOpRecordAudioMonitor->hasOp() : true;
+ }
+
+ const AttributionSourceState attributionSource; // attribution source of client
+ nsecs_t startTimeNs;
+ const bool canCaptureOutput;
+ const bool canCaptureHotword;
+ bool silenced;
+
+private:
+ sp<OpRecordAudioMonitor> mOpRecordAudioMonitor;
+};
+
+}; // namespace android::media::audiopolicy::internal
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 15eae14..6eb59ad 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -26,7 +26,9 @@
#define LOG_TAG "APM_Test"
#include <Serializer.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android/content/AttributionSourceState.h>
+#include <hardware/audio_effect.h>
#include <media/AudioPolicy.h>
#include <media/PatchBuilder.h>
#include <media/RecordingActivityTracker.h>
@@ -185,6 +187,7 @@
bool* isBitPerfect = nullptr);
void getInputForAttr(
const audio_attributes_t &attr,
+ audio_io_handle_t *input,
audio_session_t session,
audio_unique_id_t riid,
audio_port_handle_t *selectedDeviceId,
@@ -296,6 +299,7 @@
void AudioPolicyManagerTest::getInputForAttr(
const audio_attributes_t &attr,
+ audio_io_handle_t *input,
const audio_session_t session,
audio_unique_id_t riid,
audio_port_handle_t *selectedDeviceId,
@@ -304,7 +308,6 @@
int sampleRate,
audio_input_flags_t flags,
audio_port_handle_t *portId) {
- audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
config.sample_rate = sampleRate;
config.channel_mask = channelMask;
@@ -315,7 +318,7 @@
AudioPolicyInterface::input_type_t inputType;
AttributionSourceState attributionSource = createAttributionSourceState(/*uid=*/ 0);
ASSERT_EQ(OK, mManager->getInputForAttr(
- &attr, &input, riid, session, attributionSource, &config, flags,
+ &attr, input, riid, session, attributionSource, &config, flags,
selectedDeviceId, &inputType, portId));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
@@ -945,10 +948,13 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t mixPortId = AUDIO_PORT_HANDLE_NONE;
audio_source_t source = AUDIO_SOURCE_VOICE_COMMUNICATION;
- audio_attributes_t attr = {
- AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000, AUDIO_INPUT_FLAG_VOIP_TX, &mixPortId));
+ audio_attributes_t attr = {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source,
+ AUDIO_FLAG_NONE, ""};
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1,
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_MONO, 8000, AUDIO_INPUT_FLAG_VOIP_TX,
+ &mixPortId));
std::vector<audio_port_v7> ports;
ASSERT_NO_FATAL_FAILURE(
@@ -1708,10 +1714,11 @@
audio_attributes_t attr = {
AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
std::string tags = "addr=" + mMixAddress;
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- getInputForAttr(attr, param.session, mTracker->getRiid(), &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE, &mPortId);
+ getInputForAttr(attr, &input, param.session, mTracker->getRiid(),
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
ASSERT_EQ(extractionPort.id, selectedDeviceId);
@@ -1905,7 +1912,7 @@
audio_io_handle_t mOutput;
audio_stream_type_t mStream = AUDIO_STREAM_DEFAULT;
audio_port_handle_t mSelectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- audio_port_handle_t mPortId;
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
AudioPolicyInterface::output_type_t mOutputType;
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
bool mIsSpatialized;
@@ -1948,14 +1955,18 @@
}
TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
- MmapPlaybackStreamMatchingRenderDapMixSucceeds) {
- // Add render-only mix matching the test uid.
+ MmapPlaybackStreamMatchingRenderDapMixSupportingMmapSucceeds) {
+ // Add render-only mix matching the test uid.
const int testUid = 12345;
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER, AUDIO_DEVICE_OUT_SPEAKER,
- /*mixAddress=*/"", audioConfig, {createUidCriterion(testUid)});
+ // test_audio_policy_configuration.xml declares mmap-capable mix port
+ // for AUDIO_DEVICE_OUT_USB_DEVICE.
+ status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_USB_DEVICE, /*mixAddress=*/"",
+ audioConfig, {createUidCriterion(testUid)});
ASSERT_EQ(NO_ERROR, ret);
- // Geting output for matching uid should succeed for mmaped stream.
+ // Geting output for matching uid should succeed for mmaped stream, because matched mix
+ // redirects to mmap capable device.
audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
ASSERT_EQ(NO_ERROR,
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
@@ -1964,13 +1975,35 @@
&mOutputType, &mIsSpatialized, &mIsBitPerfect));
}
+TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
+ MmapPlaybackStreamMatchingRenderDapMixNotSupportingMmapFails) {
+ // Add render-only mix matching the test uid.
+ const int testUid = 12345;
+ // Per test_audio_policy_configuration.xml AUDIO_DEVICE_OUT_SPEAKER doesn't support mmap.
+ status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_SPEAKER, /*mixAddress=*/"", audioConfig,
+ {createUidCriterion(testUid)});
+ ASSERT_EQ(NO_ERROR, ret);
+
+ // Geting output for matching uid should fail for mmaped stream, because
+ // matched mix redirects to device which doesn't support mmap.
+ audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ ASSERT_EQ(INVALID_OPERATION,
+ mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
+ createAttributionSourceState(testUid), &audioConfig,
+ &outputFlags, &mSelectedDeviceId, &mPortId, {},
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+}
+
INSTANTIATE_TEST_SUITE_P(
MmapPlaybackRerouting, AudioPolicyManagerTestMMapPlaybackRerouting,
testing::Values(DPMmapTestParam(MIX_ROUTE_FLAG_LOOP_BACK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
/*deviceAddress=*/"remote_submix_media"),
DPMmapTestParam(MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- /*deviceAddress=*/"remote_submix_media")));
+ /*deviceAddress=*/"remote_submix_media"),
+ DPMmapTestParam(MIX_ROUTE_FLAG_RENDER, AUDIO_DEVICE_OUT_SPEAKER,
+ /*deviceAddress=*/"")));
class AudioPolicyManagerTestDPMixRecordInjection : public AudioPolicyManagerTestDynamicPolicy,
public testing::WithParamInterface<DPTestParam> {
@@ -2027,9 +2060,10 @@
audio_port_handle_t captureRoutedPortId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
- getInputForAttr(param.attributes, param.session, mTracker->getRiid(), &captureRoutedPortId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE, &portId);
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ getInputForAttr(param.attributes, &input, param.session, mTracker->getRiid(),
+ &captureRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &portId);
if (param.expected_match) {
EXPECT_EQ(mExtractionPort.id, captureRoutedPortId);
} else {
@@ -2212,9 +2246,10 @@
k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
- getInputForAttr({}, AUDIO_SESSION_NONE, tracker.getRiid(), &routedPortId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE);
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ getInputForAttr({}, &input, AUDIO_SESSION_NONE, tracker.getRiid(), &routedPortId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
+ AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -2961,7 +2996,8 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
attr.source = source;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
auto selectedDevice = availableDevices.getDeviceFromId(selectedDeviceId);
@@ -2981,7 +3017,8 @@
mManager->setDevicesRoleForCapturePreset(source, role,
{preferredDevice->getDeviceTypeAddr()}));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(preferredDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -2991,7 +3028,8 @@
ASSERT_EQ(NO_ERROR,
mManager->clearDevicesRoleForCapturePreset(source, role));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -3016,7 +3054,8 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
attr.source = source;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
auto selectedDevice = availableDevices.getDeviceFromId(selectedDeviceId);
@@ -3028,9 +3067,10 @@
mManager->setDevicesRoleForCapturePreset(source, role,
{selectedDevice->getDeviceTypeAddr()}));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
- 48000));
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1,
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_STEREO, 48000));
ASSERT_NE(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
// After clearing disabled device for capture preset, the selected device for input should be
@@ -3038,7 +3078,8 @@
ASSERT_EQ(NO_ERROR,
mManager->clearDevicesRoleForCapturePreset(source, role));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -3074,3 +3115,77 @@
DevicesRoleForCapturePresetParam({AUDIO_SOURCE_HOTWORD, DEVICE_ROLE_PREFERRED})
)
);
+
+
+const effect_descriptor_t TEST_EFFECT_DESC = {
+ {0xf2a4bb20, 0x0c3c, 0x11e3, 0x8b07, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
+ {0xff93e360, 0x0c3c, 0x11e3, 0x8a97, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ EFFECT_FLAG_TYPE_PRE_PROC,
+ 0,
+ 1,
+ "APM test Effect",
+ "The Android Open Source Project",
+};
+
+class AudioPolicyManagerPreProcEffectTest : public AudioPolicyManagerTestWithConfigurationFile {
+};
+
+TEST_F(AudioPolicyManagerPreProcEffectTest, DeviceDisconnectWhileClientActive) {
+ const audio_source_t source = AUDIO_SOURCE_MIC;
+ const std::string address = "BUS00_MIC";
+ const std::string deviceName = "randomName";
+ audio_port_handle_t portId;
+ audio_devices_t type = AUDIO_DEVICE_IN_BUS;
+
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(type,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.c_str(), deviceName.c_str(),
+ AUDIO_FORMAT_DEFAULT));
+ auto availableDevices = mManager->getAvailableInputDevices();
+ ASSERT_GT(availableDevices.size(), 1);
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = source;
+ audio_session_t session = TEST_SESSION_ID;
+ audio_io_handle_t inputClientHandle = 777;
+ int effectId = 666;
+ audio_port_v7 devicePort;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, type, address, &devicePort));
+
+ 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,
+ 48000, AUDIO_INPUT_FLAG_NONE, &portId));
+ ASSERT_EQ(devicePort.id, routedPortId);
+ auto selectedDevice = availableDevices.getDeviceFromId(routedPortId);
+ ASSERT_NE(nullptr, selectedDevice);
+
+ // Add a pre processing effect on the input client session
+ ASSERT_EQ(NO_ERROR, mManager->registerEffect(&TEST_EFFECT_DESC, inputClientHandle,
+ PRODUCT_STRATEGY_NONE, session, effectId));
+
+ ASSERT_EQ(NO_ERROR, mManager->startInput(portId));
+
+ // Force a device disconnection to close the input, no crash expected of APM
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ type, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ address.c_str(), deviceName.c_str(), AUDIO_FORMAT_DEFAULT));
+
+ // Reconnect the device
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ type, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address.c_str(), deviceName.c_str(), AUDIO_FORMAT_DEFAULT));
+
+ inputClientHandle += 1;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, type, address, &devicePort));
+ routedPortId = devicePort.id;
+
+ // Reconnect the client changing voluntarily the io, but keeping the session to get the
+ // effect attached again
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &inputClientHandle, session, 1, &routedPortId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ // unregister effect should succeed since effect shall have been restore on the client session
+ ASSERT_EQ(NO_ERROR, mManager->unregisterEffect(effectId));
+}
\ No newline at end of file
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 50ca26a..9e092c6 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -55,6 +55,16 @@
samplingRates="8000 16000 32000 48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="hifi_output" role="source" flags="AUDIO_OUTPUT_FLAG_BIT_PERFECT"/>
+ <mixPort name="mmap_no_irq_out" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus_input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
@@ -79,6 +89,8 @@
</devicePort>
<devicePort tagName="USB Device In" type="AUDIO_DEVICE_IN_USB_DEVICE" role="source">
</devicePort>
+ <devicePort tagName="BUS Device In" type="AUDIO_DEVICE_IN_BUS" role="source" address="BUS00_MIC">
+ </devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
@@ -96,7 +108,9 @@
<route type="mix" sink="BT A2DP Out"
sources="primary output,hifi_output"/>
<route type="mix" sink="USB Device Out"
- sources="primary output,hifi_output"/>
+ sources="primary output,hifi_output,mmap_no_irq_out"/>
+ <route type="mix" sink="mixport_bus_input"
+ sources="BUS Device In"/>
</routes>
</module>
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index a45365a..abc3ecf 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -35,9 +35,92 @@
],
}
-cc_library_shared {
+cc_defaults {
+ name: "libcameraservice_deps",
+
+ shared_libs: [
+ "libactivitymanager_aidl",
+ "libbase",
+ "libdl",
+ "libui",
+ "liblog",
+ "libutilscallstack",
+ "libutils",
+ "libbinder",
+ "libbinder_ndk",
+ "libactivitymanager_aidl",
+ "libpermission",
+ "libcutils",
+ "libexif",
+ "libmedia",
+ "libmediautils",
+ "libcamera_client",
+ "libcamera_metadata",
+ "libfmq",
+ "libgui",
+ "libhardware",
+ "libhidlbase",
+ "libimage_io",
+ "libjpeg",
+ "libultrahdr",
+ "libmedia_codeclist",
+ "libmedia_omx",
+ "libmemunreachable",
+ "libprocessgroup",
+ "libprocinfo",
+ "libsensorprivacy",
+ "libstagefright",
+ "libstagefright_foundation",
+ "libxml2",
+ "libyuv",
+ "android.hardware.camera.common@1.0",
+ "android.hardware.camera.device@1.0",
+ "android.hardware.camera.device@3.2",
+ "android.hardware.camera.device@3.3",
+ "android.hardware.camera.device@3.4",
+ "android.hardware.camera.device@3.5",
+ "android.hardware.camera.device@3.6",
+ "android.hardware.camera.device@3.7",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "android.hardware.graphics.common-V4-ndk",
+ "camera_platform_flags_c_lib",
+ "media_permission-aidl-cpp",
+ ],
+
+ static_libs: [
+ "android.frameworks.cameraservice.common@2.0",
+ "android.frameworks.cameraservice.service@2.0",
+ "android.frameworks.cameraservice.service@2.1",
+ "android.frameworks.cameraservice.service@2.2",
+ "android.frameworks.cameraservice.device@2.0",
+ "android.frameworks.cameraservice.device@2.1",
+ "android.frameworks.cameraservice.common-V1-ndk",
+ "android.frameworks.cameraservice.service-V1-ndk",
+ "android.frameworks.cameraservice.device-V1-ndk",
+ "android.hardware.camera.common-V1-ndk",
+ "android.hardware.camera.device-V2-ndk",
+ "android.hardware.camera.metadata-V2-ndk",
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.camera.provider@2.5",
+ "android.hardware.camera.provider@2.6",
+ "android.hardware.camera.provider@2.7",
+ "android.hardware.camera.provider-V2-ndk",
+ "libaidlcommonsupport",
+ "libbinderthreadstateutils",
+ "libcameraservice_device_independent",
+ "libdynamic_depth",
+ "libprocessinfoservice_aidl",
+ "media_permission-aidl-cpp",
+ ],
+}
+
+cc_library {
name: "libcameraservice",
+ defaults: [
+ "libcameraservice_deps",
+ ],
// Camera service source
srcs: [
@@ -105,6 +188,7 @@
"utils/CameraThreadState.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
+ "utils/SchedulingPolicyUtils.cpp",
"utils/SessionConfigurationUtils.cpp",
"utils/SessionConfigurationUtilsHidl.cpp",
"utils/SessionStatsBuilder.cpp",
@@ -119,73 +203,6 @@
"libmediametrics_headers",
],
- shared_libs: [
- "libactivitymanager_aidl",
- "libbase",
- "libdl",
- "libexif",
- "libui",
- "liblog",
- "libutilscallstack",
- "libutils",
- "libbinder",
- "libbinder_ndk",
- "libactivitymanager_aidl",
- "libpermission",
- "libcutils",
- "libmedia",
- "libmediautils",
- "libcamera_client",
- "libcamera_metadata",
- "libdynamic_depth",
- "libfmq",
- "libgui",
- "libhardware",
- "libhidlbase",
- "libimage_io",
- "libjpeg",
- "libultrahdr",
- "libmedia_codeclist",
- "libmedia_omx",
- "libmemunreachable",
- "libsensorprivacy",
- "libstagefright",
- "libstagefright_foundation",
- "libxml2",
- "libyuv",
- "android.frameworks.cameraservice.common@2.0",
- "android.frameworks.cameraservice.service@2.0",
- "android.frameworks.cameraservice.service@2.1",
- "android.frameworks.cameraservice.service@2.2",
- "android.frameworks.cameraservice.device@2.0",
- "android.frameworks.cameraservice.device@2.1",
- "android.frameworks.cameraservice.common-V1-ndk",
- "android.frameworks.cameraservice.service-V1-ndk",
- "android.frameworks.cameraservice.device-V1-ndk",
- "android.hardware.camera.common@1.0",
- "android.hardware.camera.provider@2.4",
- "android.hardware.camera.provider@2.5",
- "android.hardware.camera.provider@2.6",
- "android.hardware.camera.provider@2.7",
- "android.hardware.camera.provider-V2-ndk",
- "android.hardware.camera.device@3.2",
- "android.hardware.camera.device@3.3",
- "android.hardware.camera.device@3.4",
- "android.hardware.camera.device@3.5",
- "android.hardware.camera.device@3.6",
- "android.hardware.camera.device@3.7",
- "android.hardware.camera.device-V2-ndk",
- "media_permission-aidl-cpp",
- ],
-
- static_libs: [
- "libaidlcommonsupport",
- "libprocessinfoservice_aidl",
- "libbinderthreadstateutils",
- "media_permission-aidl-cpp",
- "libcameraservice_device_independent",
- ],
-
export_shared_lib_headers: [
"libbinder",
"libactivitymanager_aidl",
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 31ac392..8add05e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -47,6 +47,7 @@
#include <binder/PermissionController.h>
#include <binder/IResultReceiver.h>
#include <binderthreadstate/CallerUtils.h>
+#include <com_android_internal_camera_flags.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/misc.h>
@@ -89,6 +90,7 @@
const char* kActivityServiceName = "activity";
const char* kSensorPrivacyServiceName = "sensor_privacy";
const char* kAppopsServiceName = "appops";
+ const char* kProcessInfoServiceName = "processinfo";
}; // namespace anonymous
namespace android {
@@ -104,6 +106,7 @@
using hardware::camera2::ICameraInjectionSession;
using hardware::camera2::utils::CameraIdAndSessionConfiguration;
using hardware::camera2::utils::ConcurrentCameraIdCombination;
+namespace flags = com::android::internal::camera::flags;
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
@@ -123,6 +126,8 @@
static const std::string sManageCameraPermission("android.permission.MANAGE_CAMERA");
static const std::string sCameraPermission("android.permission.CAMERA");
static const std::string sSystemCameraPermission("android.permission.SYSTEM_CAMERA");
+static const std::string sCameraHeadlessSystemUserPermission(
+ "android.permission.CAMERA_HEADLESS_SYSTEM_USER");
static const std::string
sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
static const std::string sCameraOpenCloseListenerPermission(
@@ -694,25 +699,125 @@
broadcastTorchModeStatus(cameraId, newStatus, systemCameraKind);
}
-static bool hasPermissionsForSystemCamera(int callingPid, int callingUid) {
+static bool isAutomotiveDevice() {
+ // Checks the property ro.hardware.type and returns true if it is
+ // automotive.
+ char value[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.hardware.type", value, "");
+ return strncmp(value, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
+static bool isHeadlessSystemUserMode() {
+ // Checks if the device is running in headless system user mode
+ // by checking the property ro.fw.mu.headless_system_user.
+ char value[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.fw.mu.headless_system_user", value, "");
+ return strncmp(value, "true", PROPERTY_VALUE_MAX) == 0;
+}
+
+static bool isAutomotivePrivilegedClient(int32_t uid) {
+ // Returns false if this is not an automotive device type.
+ if (!isAutomotiveDevice())
+ return false;
+
+ // Returns true if the uid is AID_AUTOMOTIVE_EVS which is a
+ // privileged client uid used for safety critical use cases such as
+ // rear view and surround view.
+ return uid == AID_AUTOMOTIVE_EVS;
+}
+
+bool CameraService::isAutomotiveExteriorSystemCamera(const std::string& cam_id) const{
+ // Returns false if this is not an automotive device type.
+ if (!isAutomotiveDevice())
+ return false;
+
+ // Returns false if no camera id is provided.
+ if (cam_id.empty())
+ return false;
+
+ SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+ if (getSystemCameraKind(cam_id, &systemCameraKind) != OK) {
+ // This isn't a known camera ID, so it's not a system camera.
+ ALOGE("%s: Unknown camera id %s, ", __FUNCTION__, cam_id.c_str());
+ return false;
+ }
+
+ if (systemCameraKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) {
+ ALOGE("%s: camera id %s is not a system camera", __FUNCTION__, cam_id.c_str());
+ return false;
+ }
+
+ CameraMetadata cameraInfo;
+ status_t res = mCameraProviderManager->getCameraCharacteristics(
+ cam_id, false, &cameraInfo, false);
+ if (res != OK){
+ ALOGE("%s: Not able to get camera characteristics for camera id %s",__FUNCTION__,
+ cam_id.c_str());
+ return false;
+ }
+
+ camera_metadata_entry auto_location = cameraInfo.find(ANDROID_AUTOMOTIVE_LOCATION);
+ if (auto_location.count != 1)
+ return false;
+
+ uint8_t location = auto_location.data.u8[0];
+ if ((location != ANDROID_AUTOMOTIVE_LOCATION_EXTERIOR_FRONT) &&
+ (location != ANDROID_AUTOMOTIVE_LOCATION_EXTERIOR_REAR) &&
+ (location != ANDROID_AUTOMOTIVE_LOCATION_EXTERIOR_LEFT) &&
+ (location != ANDROID_AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CameraService::checkPermission(const std::string& cameraId, const std::string& permission,
+ const AttributionSourceState& attributionSource, const std::string& message,
+ int32_t attributedOpCode) const{
+ if (isAutomotivePrivilegedClient(attributionSource.uid)) {
+ // If cameraId is empty, then it means that this check is not used for the
+ // purpose of accessing a specific camera, hence grant permission just
+ // based on uid to the automotive privileged client.
+ if (cameraId.empty())
+ return true;
+ // If this call is used for accessing a specific camera then cam_id must be provided.
+ // In that case, only pre-grants the permission for accessing the exterior system only
+ // camera.
+ return isAutomotiveExteriorSystemCamera(cameraId);
+ }
+
permission::PermissionChecker permissionChecker;
+ return permissionChecker.checkPermissionForPreflight(toString16(permission), attributionSource,
+ toString16(message), attributedOpCode)
+ != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+}
+
+bool CameraService::hasPermissionsForSystemCamera(const std::string& cameraId, int callingPid,
+ int callingUid) const{
AttributionSourceState attributionSource{};
attributionSource.pid = callingPid;
attributionSource.uid = callingUid;
- bool checkPermissionForSystemCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sSystemCameraPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
- bool checkPermissionForCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sCameraPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+ bool checkPermissionForSystemCamera = checkPermission(cameraId,
+ sSystemCameraPermission, attributionSource, std::string(), AppOpsManager::OP_NONE);
+ bool checkPermissionForCamera = checkPermission(cameraId,
+ sCameraPermission, attributionSource, std::string(), AppOpsManager::OP_NONE);
return checkPermissionForSystemCamera && checkPermissionForCamera;
}
+bool CameraService::hasPermissionsForCameraHeadlessSystemUser(const std::string& cameraId,
+ int callingPid, int callingUid) const{
+ AttributionSourceState attributionSource{};
+ attributionSource.pid = callingPid;
+ attributionSource.uid = callingUid;
+ return checkPermission(cameraId, sCameraHeadlessSystemUserPermission, attributionSource,
+ std::string(), AppOpsManager::OP_NONE);
+}
+
Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
bool hasSystemCameraPermissions =
- hasPermissionsForSystemCamera(CameraThreadState::getCallingPid(),
+ hasPermissionsForSystemCamera(std::string(), CameraThreadState::getCallingPid(),
CameraThreadState::getCallingUid());
switch (type) {
case CAMERA_TYPE_BACKWARD_COMPATIBLE:
@@ -738,8 +843,7 @@
return Status::ok();
}
-Status CameraService::remapCameraIds(const hardware::CameraIdRemapping&
- cameraIdRemapping) {
+Status CameraService::remapCameraIds(const hardware::CameraIdRemapping& cameraIdRemapping) {
if (!checkCallingPermission(toString16(sCameraInjectExternalCameraPermission))) {
const int pid = CameraThreadState::getCallingPid();
const int uid = CameraThreadState::getCallingUid();
@@ -764,21 +868,20 @@
std::string cameraIdToReplace, updatedCameraId;
for(const auto& packageIdRemapping: cameraIdRemapping.packageIdRemappings) {
packageName = packageIdRemapping.packageName;
- if (packageName == "") {
+ if (packageName.empty()) {
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT,
"CameraIdRemapping: Package name cannot be empty");
}
-
if (packageIdRemapping.cameraIdsToReplace.size()
!= packageIdRemapping.updatedCameraIds.size()) {
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"CameraIdRemapping: Mismatch in CameraId Remapping lists sizes for package %s",
- packageName.c_str());
+ packageName.c_str());
}
for(size_t i = 0; i < packageIdRemapping.cameraIdsToReplace.size(); i++) {
cameraIdToReplace = packageIdRemapping.cameraIdsToReplace[i];
updatedCameraId = packageIdRemapping.updatedCameraIds[i];
- if (cameraIdToReplace == "" || updatedCameraId == "") {
+ if (cameraIdToReplace.empty() || updatedCameraId.empty()) {
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"CameraIdRemapping: Camera Id cannot be empty for package %s",
packageName.c_str());
@@ -880,7 +983,7 @@
if (packageName.empty()) {
packageNameVal = getPackageNameFromUid(clientUid);
}
- if (clientUid < AID_APP_START || packageNameVal.empty()) {
+ if (clientUid < AID_APP_START || packageNameVal.empty()) {
// We shouldn't remap cameras for processes with system/vendor UIDs.
return inputCameraId;
}
@@ -918,9 +1021,8 @@
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera subsystem is not available");
}
- bool hasSystemCameraPermissions =
- hasPermissionsForSystemCamera(CameraThreadState::getCallingPid(),
- CameraThreadState::getCallingUid());
+ bool hasSystemCameraPermissions = hasPermissionsForSystemCamera(std::to_string(cameraId),
+ CameraThreadState::getCallingPid(), CameraThreadState::getCallingUid());
int cameraIdBound = mNumberOfCamerasWithoutSystemCamera;
if (hasSystemCameraPermissions) {
cameraIdBound = mNumberOfCameras;
@@ -949,13 +1051,12 @@
const std::vector<std::string> *deviceIds = &mNormalDeviceIdsWithoutSystemCamera;
auto callingPid = CameraThreadState::getCallingPid();
auto callingUid = CameraThreadState::getCallingUid();
- permission::PermissionChecker permissionChecker;
AttributionSourceState attributionSource{};
attributionSource.pid = callingPid;
attributionSource.uid = callingUid;
- bool checkPermissionForSystemCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sSystemCameraPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+ bool checkPermissionForSystemCamera = checkPermission(std::to_string(cameraIdInt),
+ sSystemCameraPermission, attributionSource, std::string(),
+ AppOpsManager::OP_NONE);
if (checkPermissionForSystemCamera || getpid() == callingPid) {
deviceIds = &mNormalDeviceIds;
}
@@ -1029,13 +1130,11 @@
// If it's not calling from cameraserver, check the permission only if
// android.permission.CAMERA is required. If android.permission.SYSTEM_CAMERA was needed,
// it would've already been checked in shouldRejectSystemCameraConnection.
- permission::PermissionChecker permissionChecker;
AttributionSourceState attributionSource{};
attributionSource.pid = callingPid;
attributionSource.uid = callingUid;
- bool checkPermissionForCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sCameraPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+ bool checkPermissionForCamera = checkPermission(cameraId, sCameraPermission,
+ attributionSource, std::string(), AppOpsManager::OP_NONE);
if ((callingPid != getpid()) &&
(deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
!checkPermissionForCamera) {
@@ -1069,7 +1168,7 @@
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
const std::string cameraId = resolveCameraId(
- unresolvedCameraId, CameraThreadState::getCallingUid());
+ unresolvedCameraId, CameraThreadState::getCallingUid());
if (!mInitialized) {
ALOGE("%s: Camera HAL couldn't be initialized.", __FUNCTION__);
return STATUS_ERROR(ERROR_DISCONNECTED, "Camera HAL couldn't be initialized.");
@@ -1188,7 +1287,8 @@
int api1CameraId, int facing, int sensorOrientation, int clientPid, uid_t clientUid,
int servicePid, std::pair<int, IPCTransport> deviceVersionAndTransport,
apiLevel effectiveApiLevel, bool overrideForPerfClass, bool overrideToPortrait,
- bool forceSlowJpegMode, const std::string& originalCameraId, /*out*/sp<BasicClient>* client) {
+ bool forceSlowJpegMode, const std::string& originalCameraId,
+ /*out*/sp<BasicClient>* client) {
// For HIDL devices
if (deviceVersionAndTransport.second == IPCTransport::HIDL) {
// Create CameraClient based on device version reported by the HAL.
@@ -1477,7 +1577,6 @@
Status CameraService::validateClientPermissionsLocked(const std::string& cameraId,
const std::string& clientName, int& clientUid, int& clientPid,
/*out*/int& originalClientPid) const {
- permission::PermissionChecker permissionChecker;
AttributionSourceState attributionSource{};
int callingPid = CameraThreadState::getCallingPid();
@@ -1529,9 +1628,8 @@
attributionSource.pid = clientPid;
attributionSource.uid = clientUid;
attributionSource.packageName = clientName;
- bool checkPermissionForCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sCameraPermission), attributionSource, String16(), AppOpsManager::OP_NONE)
- != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+ bool checkPermissionForCamera = checkPermission(cameraId, sCameraPermission, attributionSource,
+ std::string(), AppOpsManager::OP_NONE);
if (callingPid != getpid() &&
(deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) && !checkPermissionForCamera) {
ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid);
@@ -1552,8 +1650,12 @@
callingUid, procState);
}
- // If sensor privacy is enabled then prevent access to the camera
- if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
+ // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for use cases
+ // such as rear view and surround view cannot be disabled and are exempt from sensor privacy
+ // policy. In all other cases,if sensor privacy is enabled then prevent access to the camera.
+ if ((!isAutomotivePrivilegedClient(callingUid) ||
+ !isAutomotiveExteriorSystemCamera(cameraId)) &&
+ mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
ALOGE("Access Denial: cannot use the camera when sensor privacy is enabled");
return STATUS_ERROR_FMT(ERROR_DISABLED,
"Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" when sensor privacy "
@@ -1579,6 +1681,20 @@
clientUserId, cameraId.c_str());
}
+ if (flags::camera_hsum_permission()) {
+ // If the System User tries to access the camera when the device is running in
+ // headless system user mode, ensure that client has the required permission
+ // CAMERA_HEADLESS_SYSTEM_USER.
+ if (isHeadlessSystemUserMode() && (clientUserId == USER_SYSTEM) &&
+ !hasPermissionsForCameraHeadlessSystemUser(cameraId, callingPid, callingUid)) {
+ ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, 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(), clientUid, clientPid, cameraId.c_str());
+ }
+ }
+
return Status::ok();
}
@@ -1671,33 +1787,6 @@
}
}
- // Get current active client PIDs
- std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
- ownerPids.push_back(clientPid);
-
- std::vector<int> priorityScores(ownerPids.size());
- std::vector<int> states(ownerPids.size());
-
- // Get priority scores of all active PIDs
- status_t err = ProcessInfoService::getProcessStatesScoresFromPids(
- ownerPids.size(), &ownerPids[0], /*out*/&states[0],
- /*out*/&priorityScores[0]);
- if (err != OK) {
- ALOGE("%s: Priority score query failed: %d",
- __FUNCTION__, err);
- return err;
- }
-
- // Update all active clients' priorities
- std::map<int,resource_policy::ClientPriority> pidToPriorityMap;
- for (size_t i = 0; i < ownerPids.size() - 1; i++) {
- pidToPriorityMap.emplace(ownerPids[i],
- resource_policy::ClientPriority(priorityScores[i], states[i],
- /* isVendorClient won't get copied over*/ false,
- /* oomScoreOffset won't get copied over*/ 0));
- }
- mActiveClientManager.updatePriorities(pidToPriorityMap);
-
// Get state for the given cameraId
auto state = getCameraState(cameraId);
if (state == nullptr) {
@@ -1707,16 +1796,57 @@
return BAD_VALUE;
}
- int32_t actualScore = priorityScores[priorityScores.size() - 1];
- int32_t actualState = states[states.size() - 1];
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->checkService(String16(kProcessInfoServiceName));
+ if (!binder && isAutomotivePrivilegedClient(CameraThreadState::getCallingUid())) {
+ // If processinfo service is not available and the client is automotive privileged
+ // client used for safety critical uses cases such as rear-view and surround-view which
+ // needs to be available before android boot completes, then use the hardcoded values
+ // for the process state and priority score. As this scenario is before android system
+ // services are up and client is native client, hence using NATIVE_ADJ as the priority
+ // score and state as PROCESS_STATE_BOUND_TOP as such automotive apps need to be
+ // visible on the top.
+ clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
+ sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
+ state->getConflicting(), resource_policy::NATIVE_ADJ, clientPid,
+ ActivityManager::PROCESS_STATE_BOUND_TOP, oomScoreOffset, systemNativeClient);
+ } else {
+ // Get current active client PIDs
+ std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
+ ownerPids.push_back(clientPid);
- // Make descriptor for incoming client. We store the oomScoreOffset
- // since we might need it later on new handleEvictionsLocked and
- // ProcessInfoService would not take that into account.
- clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
- sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
- state->getConflicting(), actualScore, clientPid, actualState,
- oomScoreOffset, systemNativeClient);
+ std::vector<int> priorityScores(ownerPids.size());
+ std::vector<int> states(ownerPids.size());
+
+ // Get priority scores of all active PIDs
+ status_t err = ProcessInfoService::getProcessStatesScoresFromPids(ownerPids.size(),
+ &ownerPids[0], /*out*/&states[0], /*out*/&priorityScores[0]);
+ if (err != OK) {
+ ALOGE("%s: Priority score query failed: %d", __FUNCTION__, err);
+ return err;
+ }
+
+ // Update all active clients' priorities
+ std::map<int,resource_policy::ClientPriority> pidToPriorityMap;
+ for (size_t i = 0; i < ownerPids.size() - 1; i++) {
+ pidToPriorityMap.emplace(ownerPids[i],
+ resource_policy::ClientPriority(priorityScores[i], states[i],
+ /* isVendorClient won't get copied over*/ false,
+ /* oomScoreOffset won't get copied over*/ 0));
+ }
+ mActiveClientManager.updatePriorities(pidToPriorityMap);
+
+ int32_t actualScore = priorityScores[priorityScores.size() - 1];
+ int32_t actualState = states[states.size() - 1];
+
+ // Make descriptor for incoming client. We store the oomScoreOffset
+ // since we might need it later on new handleEvictionsLocked and
+ // ProcessInfoService would not take that into account.
+ clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
+ sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
+ state->getConflicting(), actualScore, clientPid, actualState,
+ oomScoreOffset, systemNativeClient);
+ }
resource_policy::ClientPriority clientPriority = clientDescriptor->getPriority();
@@ -1895,7 +2025,7 @@
// have android.permission.SYSTEM_CAMERA permissions.
if (!isVendorListener && (systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA ||
(systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
- !hasPermissionsForSystemCamera(clientPid, clientUid)))) {
+ !hasPermissionsForSystemCamera(std::string(), clientPid, clientUid)))) {
return true;
}
return false;
@@ -1935,7 +2065,7 @@
// characteristics) even if clients don't have android.permission.CAMERA. We do not want the
// same behavior for system camera devices.
if (!systemClient && systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
- !hasPermissionsForSystemCamera(cPid, cUid)) {
+ !hasPermissionsForSystemCamera(cameraId, cPid, cUid)) {
ALOGW("Rejecting access to system only camera %s, inadequete permissions",
cameraId.c_str());
return true;
@@ -1986,16 +2116,20 @@
clientUserId = multiuser_get_user_id(callingUid);
}
- if (mCameraServiceProxyWrapper->isCameraDisabled(clientUserId)) {
+ // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for use cases
+ // such as rear view and surround view cannot be disabled.
+ if ((!isAutomotivePrivilegedClient(callingUid) || !isAutomotiveExteriorSystemCamera(cameraId))
+ && mCameraServiceProxyWrapper->isCameraDisabled(clientUserId)) {
std::string msg = "Camera disabled by device policy";
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(ERROR_DISABLED, msg.c_str());
}
// enforce system camera permissions
- if (oomScoreOffset > 0 &&
- !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid()) &&
- !isTrustedCallingUid(CameraThreadState::getCallingUid())) {
+ if (oomScoreOffset > 0
+ && !hasPermissionsForSystemCamera(cameraId, callingPid,
+ CameraThreadState::getCallingUid())
+ && !isTrustedCallingUid(CameraThreadState::getCallingUid())) {
std::string msg = fmt::sprintf("Cannot change the priority of a client %s pid %d for "
"camera id %s without SYSTEM_CAMERA permissions",
clientPackageNameAdj.c_str(), callingPid, cameraId.c_str());
@@ -2006,10 +2140,10 @@
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb,
cameraId, /*api1CameraId*/-1, clientPackageNameAdj, systemNativeClient, clientFeatureId,
clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, oomScoreOffset,
- targetSdkVersion, overrideToPortrait, /*forceSlowJpegMode*/false,
- unresolvedCameraId, /*out*/client);
+ targetSdkVersion, overrideToPortrait, /*forceSlowJpegMode*/false, unresolvedCameraId,
+ /*out*/client);
- if (!ret.isOk()) {
+ if(!ret.isOk()) {
logRejected(cameraId, callingPid, clientPackageNameAdj, toStdString(ret.toString8()));
return ret;
}
@@ -2082,6 +2216,8 @@
bool isNonSystemNdk = false;
std::string clientPackageName;
+ int packageUid = (clientUid == USE_CALLING_UID) ?
+ CameraThreadState::getCallingUid() : clientUid;
if (clientPackageNameMaybe.size() <= 0) {
// NDK calls don't come with package names, but we need one for various cases.
// Generally, there's a 1:1 mapping between UID and package name, but shared UIDs
@@ -2089,8 +2225,6 @@
// same permissions, so picking any associated package name is sufficient. For some
// other cases, this may give inaccurate names for clients in logs.
isNonSystemNdk = true;
- int packageUid = (clientUid == USE_CALLING_UID) ?
- CameraThreadState::getCallingUid() : clientUid;
clientPackageName = getPackageNameFromUid(packageUid);
} else {
clientPackageName = clientPackageNameMaybe;
@@ -2287,32 +2421,38 @@
clientPackageName));
}
- // Set camera muting behavior
- bool isCameraPrivacyEnabled =
- mSensorPrivacyPolicy->isCameraPrivacyEnabled();
- if (client->supportsCameraMute()) {
- client->setCameraMute(
- mOverrideCameraMuteMode || isCameraPrivacyEnabled);
- } else if (isCameraPrivacyEnabled) {
- // no camera mute supported, but privacy is on! => disconnect
- ALOGI("Camera mute not supported for package: %s, camera id: %s",
- client->getPackageName().c_str(), cameraId.c_str());
- // Do not hold mServiceLock while disconnecting clients, but
- // retain the condition blocking other clients from connecting
- // in mServiceLockWrapper if held.
- mServiceLock.unlock();
- // Clear caller identity temporarily so client disconnect PID
- // checks work correctly
- int64_t token = CameraThreadState::clearCallingIdentity();
- // Note AppOp to trigger the "Unblock" dialog
- client->noteAppOp();
- client->disconnect();
- CameraThreadState::restoreCallingIdentity(token);
- // Reacquire mServiceLock
- mServiceLock.lock();
+ // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for use
+ // cases such as rear view and surround view cannot be disabled and are exempt from camera
+ // privacy policy.
+ if ((!isAutomotivePrivilegedClient(packageUid) ||
+ !isAutomotiveExteriorSystemCamera(cameraId))) {
+ // Set camera muting behavior.
+ bool isCameraPrivacyEnabled =
+ mSensorPrivacyPolicy->isCameraPrivacyEnabled();
+ if (client->supportsCameraMute()) {
+ client->setCameraMute(
+ mOverrideCameraMuteMode || isCameraPrivacyEnabled);
+ } else if (isCameraPrivacyEnabled) {
+ // no camera mute supported, but privacy is on! => disconnect
+ ALOGI("Camera mute not supported for package: %s, camera id: %s",
+ client->getPackageName().c_str(), cameraId.c_str());
+ // Do not hold mServiceLock while disconnecting clients, but
+ // retain the condition blocking other clients from connecting
+ // in mServiceLockWrapper if held.
+ mServiceLock.unlock();
+ // Clear caller identity temporarily so client disconnect PID
+ // checks work correctly
+ int64_t token = CameraThreadState::clearCallingIdentity();
+ // Note AppOp to trigger the "Unblock" dialog
+ client->noteAppOp();
+ client->disconnect();
+ CameraThreadState::restoreCallingIdentity(token);
+ // Reacquire mServiceLock
+ mServiceLock.lock();
- return STATUS_ERROR_FMT(ERROR_DISABLED,
- "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
+ return STATUS_ERROR_FMT(ERROR_DISABLED,
+ "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
+ }
}
if (shimUpdateOnly) {
@@ -2449,8 +2589,7 @@
}
Status CameraService::turnOnTorchWithStrengthLevel(const std::string& unresolvedCameraId,
- int32_t torchStrength,
- const sp<IBinder>& clientBinder) {
+ int32_t torchStrength, const sp<IBinder>& clientBinder) {
Mutex::Autolock lock(mServiceLock);
ATRACE_CALL();
@@ -2577,8 +2716,7 @@
return Status::ok();
}
-Status CameraService::setTorchMode(const std::string& unresolvedCameraId,
- bool enabled,
+Status CameraService::setTorchMode(const std::string& unresolvedCameraId, bool enabled,
const sp<IBinder>& clientBinder) {
Mutex::Autolock lock(mServiceLock);
@@ -2921,13 +3059,11 @@
// Check for camera permissions
int callingPid = CameraThreadState::getCallingPid();
int callingUid = CameraThreadState::getCallingUid();
- permission::PermissionChecker permissionChecker;
AttributionSourceState attributionSource{};
attributionSource.pid = callingPid;
attributionSource.uid = callingUid;
- bool checkPermissionForCamera = permissionChecker.checkPermissionForPreflight(
- toString16(sCameraPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+ bool checkPermissionForCamera = checkPermission(std::string(),
+ sCameraPermission, attributionSource, std::string(), AppOpsManager::OP_NONE);
if ((callingPid != getpid()) && !checkPermissionForCamera) {
ALOGE("%s: pid %d doesn't have camera permissions", __FUNCTION__, callingPid);
return STATUS_ERROR(ERROR_PERMISSION_DENIED,
@@ -2975,13 +3111,13 @@
auto clientUid = CameraThreadState::getCallingUid();
auto clientPid = CameraThreadState::getCallingPid();
- permission::PermissionChecker permissionChecker;
AttributionSourceState attributionSource{};
attributionSource.uid = clientUid;
attributionSource.pid = clientPid;
- bool openCloseCallbackAllowed = permissionChecker.checkPermissionForPreflight(
- toString16(sCameraOpenCloseListenerPermission), attributionSource, String16(),
- AppOpsManager::OP_NONE) != permission::PermissionChecker::PERMISSION_HARD_DENIED;
+
+ bool openCloseCallbackAllowed = checkPermission(std::string(),
+ sCameraOpenCloseListenerPermission, attributionSource, std::string(),
+ AppOpsManager::OP_NONE);
Mutex::Autolock lock(mServiceLock);
@@ -5119,6 +5255,7 @@
state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind,
&logicalCameraIds]
(const std::string& cameraId, StatusInternal status) {
+
if (status != StatusInternal::ENUMERATING) {
// Update torch status if it has a flash unit.
Mutex::Autolock al(mTorchStatusMutex);
@@ -6015,8 +6152,10 @@
" clear-stream-use-case-override clear the stream use case override\n"
" set-zoom-override <-1/0/1> enable or disable zoom override\n"
" Valid values -1: do not override, 0: override to OFF, 1: override to ZOOM\n"
- " remap-camera-id <PACKAGE> <Id0> <Id1> remaps camera ids. Must use adb root\n"
+ " set-watchdog <VALUE> enables or disables the camera service watchdog\n"
+ " Valid values 0=disable, 1=enable\n"
" watch <start|stop|dump|print|clear> manages tag monitoring in connected clients\n"
+ " remap-camera-id <PACKAGE> <Id0> <Id1> remaps camera ids. Must use adb root\n"
" help print this message\n");
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 68f7f73..6819136 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+#include <android/content/AttributionSourceState.h>
#include <android/hardware/BnCameraService.h>
#include <android/hardware/BnSensorPrivacyListener.h>
#include <android/hardware/ICameraServiceListener.h>
@@ -103,6 +104,9 @@
// Event log ID
static const int SN_EVENT_LOG_ID = 0x534e4554;
+ // Keep this in sync with frameworks/base/core/java/android/os/UserHandle.java
+ static const userid_t USER_SYSTEM = 0;
+
// Register camera service
static void instantiate();
@@ -617,6 +621,13 @@
int32_t updateAudioRestrictionLocked();
private:
+ /**
+ * Returns true if the device is an automotive device and cameraId is system
+ * only camera which has characteristic AUTOMOTIVE_LOCATION value as either
+ * AUTOMOTIVE_LOCATION_EXTERIOR_LEFT,AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT,
+ * AUTOMOTIVE_LOCATION_EXTERIOR_FRONT or AUTOMOTIVE_LOCATION_EXTERIOR_REAR.
+ */
+ bool isAutomotiveExteriorSystemCamera(const std::string& cameraId) const;
// TODO: b/263304156 update this to make use of a death callback for more
// robust/fault tolerant logging
@@ -633,6 +644,25 @@
}
/**
+ * Pre-grants the permission if the attribution source uid is for an automotive
+ * privileged client. Otherwise uses system service permission checker to check
+ * for the appropriate permission. If this function is called for accessing a specific
+ * camera,then the cameraID must not be empty. CameraId is used only in case of automotive
+ * privileged client so that permission is pre-granted only to access system camera device
+ * which is located outside of the vehicle body frame because camera located inside the vehicle
+ * cabin would need user permission.
+ */
+ bool checkPermission(const std::string& cameraId, const std::string& permission,
+ const content::AttributionSourceState& attributionSource, const std::string& message,
+ int32_t attributedOpCode) const;
+
+ bool hasPermissionsForSystemCamera(const std::string& cameraId, int callingPid, int callingUid)
+ const;
+
+ bool hasPermissionsForCameraHeadlessSystemUser(const std::string& cameraId, int callingPid,
+ int callingUid) const;
+
+ /**
* Typesafe version of device status, containing both the HAL-layer and the service interface-
* layer values.
*/
@@ -892,7 +922,7 @@
// Should a device status update be skipped for a particular camera device ? (this can happen
// under various conditions. For example if a camera device is advertised as
// system only or hidden secure camera, amongst possible others.
- static bool shouldSkipStatusUpdates(SystemCameraKind systemCameraKind, bool isVendorListener,
+ bool shouldSkipStatusUpdates(SystemCameraKind systemCameraKind, bool isVendorListener,
int clientPid, int clientUid);
// Gets the kind of camera device (i.e public, hidden secure or system only)
@@ -1421,11 +1451,11 @@
*/
static std::string getFormattedCurrentTime();
- static binder::Status makeClient(
- const sp<CameraService>& cameraService, const sp<IInterface>& cameraCb,
- const std::string& packageName, bool systemNativeClient,
- const std::optional<std::string>& featureId, const std::string& cameraId, int api1CameraId,
- int facing, int sensorOrientation, int clientPid, uid_t clientUid, int servicePid,
+ static binder::Status makeClient(const sp<CameraService>& cameraService,
+ const sp<IInterface>& cameraCb, const std::string& packageName,
+ bool systemNativeClient, const std::optional<std::string>& featureId,
+ const std::string& cameraId, int api1CameraId, int facing, int sensorOrientation,
+ int clientPid, uid_t clientUid, int servicePid,
std::pair<int, IPCTransport> deviceVersionAndIPCTransport, apiLevel effectiveApiLevel,
bool overrideForPerfClass, bool overrideToPortrait, bool forceSlowJpegMode,
const std::string& originalCameraId,
diff --git a/services/camera/libcameraservice/CameraServiceWatchdog.h b/services/camera/libcameraservice/CameraServiceWatchdog.h
index 9f25865..afc432d 100644
--- a/services/camera/libcameraservice/CameraServiceWatchdog.h
+++ b/services/camera/libcameraservice/CameraServiceWatchdog.h
@@ -44,7 +44,7 @@
watchThread([&]() { return toMonitor;}, gettid(), __FUNCTION__, cycles, cycleLength);
// Default cycles and cycle length values used to calculate permitted elapsed time
-const static size_t kMaxCycles = 100;
+const static size_t kMaxCycles = 650;
const static uint32_t kCycleLengthMs = 100;
namespace android {
diff --git a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
index 48c804d..3cc843d 100644
--- a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
@@ -77,6 +77,10 @@
{34, {
ANDROID_CONTROL_AUTOFRAMING_AVAILABLE,
ANDROID_CONTROL_AVAILABLE_SETTINGS_OVERRIDES,
+ ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
+ ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
+ ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
+ ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL,
ANDROID_JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS,
ANDROID_JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
ANDROID_JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS,
@@ -108,6 +112,7 @@
ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
ANDROID_EXTENSION_CURRENT_TYPE,
ANDROID_EXTENSION_STRENGTH,
+ ANDROID_FLASH_STRENGTH_LEVEL,
ANDROID_SCALER_RAW_CROP_REGION,
} },
};
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index c27fc90..2e6eb06 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -18,6 +18,7 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include <com_android_internal_camera_flags.h>
#include <cutils/properties.h>
#include <utils/CameraThreadState.h>
#include <utils/Log.h>
@@ -55,6 +56,8 @@
using namespace camera3;
using camera3::camera_stream_rotation_t::CAMERA_STREAM_ROTATION_0;
+namespace flags = com::android::internal::camera::flags;
+
CameraDeviceClientBase::CameraDeviceClientBase(
const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
@@ -439,7 +442,7 @@
CameraDeviceBase::PhysicalCameraSettingsList physicalSettingsList;
for (const auto& it : request.mPhysicalCameraSettings) {
- std::string resolvedId = (mOriginalCameraId == it.id) ? mDevice->getId() : it.id;
+ const std::string resolvedId = (mOriginalCameraId == it.id) ? mDevice->getId() : it.id;
if (it.settings.isEmpty()) {
ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
__FUNCTION__, mCameraIdStr.c_str());
@@ -465,18 +468,19 @@
}
}
+ const std::string &physicalId = resolvedId;
bool hasTestPatternModePhysicalKey = std::find(mSupportedPhysicalRequestKeys.begin(),
mSupportedPhysicalRequestKeys.end(), ANDROID_SENSOR_TEST_PATTERN_MODE) !=
mSupportedPhysicalRequestKeys.end();
bool hasTestPatternDataPhysicalKey = std::find(mSupportedPhysicalRequestKeys.begin(),
mSupportedPhysicalRequestKeys.end(), ANDROID_SENSOR_TEST_PATTERN_DATA) !=
mSupportedPhysicalRequestKeys.end();
- if (resolvedId != mDevice->getId()) {
+ if (physicalId != mDevice->getId()) {
auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),
resolvedId);
if (found == requestedPhysicalIds.end()) {
ALOGE("%s: Camera %s: Physical camera id: %s not part of attached outputs.",
- __FUNCTION__, mCameraIdStr.c_str(), resolvedId.c_str());
+ __FUNCTION__, mCameraIdStr.c_str(), physicalId.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
"Invalid physical camera id");
}
@@ -538,6 +542,13 @@
if (entry.count == 1) {
mVideoStabilizationMode = entry.data.u8[0];
}
+ if (flags::log_ultrawide_usage()) {
+ entry = physicalSettingsList.begin()->metadata.find(
+ ANDROID_CONTROL_ZOOM_RATIO);
+ if (entry.count == 1 && entry.data.f[0] < 1.0f ) {
+ mUsedUltraWide = true;
+ }
+ }
}
mRequestIdCounter++;
@@ -2050,7 +2061,7 @@
}
}
Camera2ClientBase::notifyIdleWithUserTag(requestCount, resultErrorCount, deviceError,
- fullStreamStats, mUserTag, mVideoStabilizationMode);
+ fullStreamStats, mUserTag, mVideoStabilizationMode, mUsedUltraWide);
}
void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 1c19dbd..c2870aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -370,9 +370,11 @@
std::string mUserTag;
// The last set video stabilization mode
int mVideoStabilizationMode = -1;
+ // Whether a zoom_ratio < 1.0 has been used during this session
+ bool mUsedUltraWide = false;
// This only exists in case of camera ID Remapping.
- std::string mOriginalCameraId;
+ const std::string mOriginalCameraId;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 43eb181..5c88cfa 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -269,15 +269,8 @@
template <typename TClientBase>
binder::Status Camera2ClientBase<TClientBase>::disconnect() {
if (mCameraServiceWatchdog != nullptr && mDevice != nullptr) {
- // Timer for the disconnect call should be greater than getExpectedInFlightDuration
- // since this duration is used to error handle methods in the disconnect sequence
- // thus allowing existing error handling methods to execute first
- uint64_t maxExpectedDuration =
- ns2ms(mDevice->getExpectedInFlightDuration() + kBufferTimeDisconnectNs);
-
// Initialization from hal succeeded, time disconnect.
- return mCameraServiceWatchdog->WATCH_CUSTOM_TIMER(disconnectImpl(),
- maxExpectedDuration / kCycleLengthMs, kCycleLengthMs);
+ return mCameraServiceWatchdog->WATCH(disconnectImpl());
}
return disconnectImpl();
}
@@ -403,7 +396,7 @@
void Camera2ClientBase<TClientBase>::notifyIdleWithUserTag(
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::vector<hardware::CameraStreamStats>& streamStats,
- const std::string& userTag, int videoStabilizationMode) {
+ const std::string& userTag, int videoStabilizationMode, bool usedUltraWide) {
if (mDeviceActive) {
status_t res = TClientBase::finishCameraStreamingOps();
if (res != OK) {
@@ -412,7 +405,7 @@
}
mCameraServiceProxyWrapper->logIdle(TClientBase::mCameraIdStr,
requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode,
- streamStats);
+ usedUltraWide, streamStats);
}
mDeviceActive = false;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 30c763d..226881a 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -97,7 +97,8 @@
void notifyIdleWithUserTag(int64_t requestCount, int64_t resultErrorCount,
bool deviceError,
const std::vector<hardware::CameraStreamStats>& streamStats,
- const std::string& userTag, int videoStabilizationMode);
+ const std::string& userTag, int videoStabilizationMode,
+ bool usedUltraWide);
int getCameraId() const;
const sp<CameraDeviceBase>&
@@ -137,9 +138,6 @@
protected:
- // Used for watchdog timeout to monitor disconnect
- static const nsecs_t kBufferTimeDisconnectNs = 3000000000; // 3 sec.
-
// The PID provided in the constructor call
pid_t mInitialClientPid;
bool mOverrideForPerfClass = false;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 23051ef..6ce4d3a 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1487,6 +1487,56 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fixupManualFlashStrengthControlTags() {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+ auto flashSingleStrengthMaxLevelEntry = c.find(ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL);
+ if (flashSingleStrengthMaxLevelEntry.count == 0) {
+ int32_t flashSingleStrengthMaxLevel = 1;
+ res = c.update(ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
+ &flashSingleStrengthMaxLevel, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL: %s (%d)",
+ __FUNCTION__,strerror(-res), res);
+ return res;
+ }
+ }
+ auto flashSingleStrengthDefaultLevelEntry = c.find(ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL);
+ if (flashSingleStrengthDefaultLevelEntry.count == 0) {
+ int32_t flashSingleStrengthDefaultLevel = 1;
+ res = c.update(ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
+ &flashSingleStrengthDefaultLevel, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL: %s (%d)",
+ __FUNCTION__,strerror(-res), res);
+ return res;
+ }
+ }
+ auto flashTorchStrengthMaxLevelEntry = c.find(ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL);
+ if (flashTorchStrengthMaxLevelEntry.count == 0) {
+ int32_t flashTorchStrengthMaxLevel = 1;
+ res = c.update(ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL,
+ &flashTorchStrengthMaxLevel, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL: %s (%d)",
+ __FUNCTION__,strerror(-res), res);
+ return res;
+ }
+ }
+ auto flashTorchStrengthDefaultLevelEntry = c.find(ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL);
+ if (flashTorchStrengthDefaultLevelEntry.count == 0) {
+ int32_t flashTorchStrengthDefaultLevel = 1;
+ res = c.update(ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
+ &flashTorchStrengthDefaultLevel, 1);
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL: %s (%d)",
+ __FUNCTION__,strerror(-res), res);
+ return res;
+ }
+ }
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fixupMonochromeTags() {
status_t res = OK;
auto& c = mCameraCharacteristics;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 28be652..48c1134 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -683,6 +683,7 @@
SystemCameraKind getSystemCameraKind();
status_t fixupMonochromeTags();
status_t fixupTorchStrengthTags();
+ status_t fixupManualFlashStrengthControlTags();
status_t addDynamicDepthTags(bool maxResolution = false);
status_t deriveHeicTags(bool maxResolution = false);
status_t deriveJpegRTags(bool maxResolution = false);
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
index 5e79d6b..085b012 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
@@ -494,6 +494,12 @@
__FUNCTION__, strerror(-res), res);
return;
}
+ res = fixupManualFlashStrengthControlTags();
+ if (OK != res) {
+ ALOGE("%s: Unable to fix up manual flash strength control tags: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return;
+ }
auto stat = addDynamicDepthTags();
if (OK != stat) {
ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-stat),
@@ -607,8 +613,9 @@
aidl::android::hardware::camera::device::CameraMetadata pChars;
status = interface->getPhysicalCameraCharacteristics(id, &pChars);
if (!status.isOk()) {
- ALOGE("%s: Transaction error getting physical camera %s characteristics for %s: %s",
- __FUNCTION__, id.c_str(), id.c_str(), status.getMessage());
+ ALOGE("%s: Transaction error getting physical camera %s characteristics for "
+ "logical id %s: %s", __FUNCTION__, id.c_str(), mId.c_str(),
+ status.getMessage());
return;
}
std::vector<uint8_t> &pMetadata = pChars.metadata;
diff --git a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
index bf7a471..56c0c90 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
@@ -613,6 +613,14 @@
__FUNCTION__, strerror(-res), res);
return;
}
+
+ res = fixupManualFlashStrengthControlTags();
+ if (OK != res) {
+ ALOGE("%s: Unable to fix up manual flash strength control tags: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return;
+ }
+
auto stat = addDynamicDepthTags();
if (OK != stat) {
ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-stat),
@@ -743,8 +751,9 @@
});
if (!ret.isOk()) {
- ALOGE("%s: Transaction error getting physical camera %s characteristics for %s: %s",
- __FUNCTION__, id.c_str(), id.c_str(), ret.description().c_str());
+ ALOGE("%s: Transaction error getting physical camera %s characteristics for"
+ " logical id %s: %s", __FUNCTION__, id.c_str(), mId.c_str(),
+ ret.description().c_str());
return;
}
if (status != Status::OK) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ee4d855..6765c1d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -63,9 +63,9 @@
#include "device3/Camera3InputStream.h"
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3SharedOutputStream.h"
-#include "mediautils/SchedulingPolicyService.h"
#include "utils/CameraThreadState.h"
#include "utils/CameraTraces.h"
+#include "utils/SchedulingPolicyUtils.h"
#include "utils/SessionConfigurationUtils.h"
#include "utils/TraceHFR.h"
@@ -297,13 +297,6 @@
}
}
}
- // Signal to request thread that we're not expecting any
- // more requests. This will be true since once we're in
- // disconnect and we've cleared off the request queue, the
- // request thread can't receive any new requests through
- // binder calls - since disconnect holds
- // mBinderSerialization lock.
- mRequestThread->setRequestClearing();
}
if (mStatus == STATUS_ERROR) {
@@ -2632,8 +2625,8 @@
if (disableFifo != 1) {
// Boost priority of request thread to SCHED_FIFO.
pid_t requestThreadTid = mRequestThread->getTid();
- res = requestPriority(getpid(), requestThreadTid,
- kRequestThreadPriority, /*isForApp*/ false, /*asynchronous*/ false);
+ res = SchedulingPolicyUtils::requestPriorityDirect(getpid(), requestThreadTid,
+ kRequestThreadPriority);
if (res != OK) {
ALOGW("Can't set realtime priority for request processing thread: %s (%d)",
strerror(-res), res);
@@ -3303,8 +3296,12 @@
}
void Camera3Device::RequestThread::requestExit() {
- // Call parent to set up shutdown
- Thread::requestExit();
+ {
+ Mutex::Autolock l(mRequestLock);
+ mRequestClearing = true;
+ // Call parent to set up shutdown
+ Thread::requestExit();
+ }
// The exit from any possible waits
mDoPauseSignal.signal();
mRequestSignal.signal();
@@ -4436,11 +4433,6 @@
return;
}
-void Camera3Device::RequestThread::setRequestClearing() {
- Mutex::Autolock l(mRequestLock);
- mRequestClearing = true;
-}
-
sp<Camera3Device::CaptureRequest>
Camera3Device::RequestThread::waitForNextRequestLocked() {
status_t res;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b36a60a..d812c01 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -941,9 +941,6 @@
*/
void setPaused(bool paused);
- // set mRequestClearing - no new requests are expected to be queued to RequestThread
- void setRequestClearing();
-
/**
* Wait until thread processes the capture request with settings'
* android.request.id == requestId.
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 1e7e337..88a7d69 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -261,7 +261,7 @@
auto mapper = states.rotateAndCropMappers.find(states.cameraId);
if (mapper != states.rotateAndCropMappers.end()) {
- const auto& remappedKeys = iter->second.getRemappedKeys();
+ const auto& remappedKeys = mapper->second.getRemappedKeys();
keysToRemove.insert(remappedKeys.begin(), remappedKeys.end());
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 23afa6e..701c472 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -772,7 +772,8 @@
// Buffer status may be changed, so make a copy of the stream_buffer struct.
camera_stream_buffer b = buffer;
- if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) {
+ if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp
+ && b.status != CAMERA_BUFFER_STATUS_ERROR) {
ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64,
__FUNCTION__, mId, timestamp, mLastTimestamp);
b.status = CAMERA_BUFFER_STATUS_ERROR;
diff --git a/services/camera/libcameraservice/tests/Android.bp b/services/camera/libcameraservice/tests/Android.bp
index 5e2a3fb..b035edd 100644
--- a/services/camera/libcameraservice/tests/Android.bp
+++ b/services/camera/libcameraservice/tests/Android.bp
@@ -31,11 +31,14 @@
"libmedia_headers",
],
+ defaults: [
+ "libcameraservice_deps",
+ ],
+
shared_libs: [
"libbase",
"libbinder",
"libcutils",
- "libcameraservice",
"libhidlbase",
"liblog",
"libcamera_client",
@@ -45,11 +48,6 @@
"libjpeg",
"libexif",
"android.hardware.camera.common@1.0",
- "android.hardware.camera.provider@2.4",
- "android.hardware.camera.provider@2.5",
- "android.hardware.camera.provider@2.6",
- "android.hardware.camera.provider@2.7",
- "android.hardware.camera.provider-V2-ndk",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.4",
@@ -58,6 +56,12 @@
],
static_libs: [
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.camera.provider@2.5",
+ "android.hardware.camera.provider@2.6",
+ "android.hardware.camera.provider@2.7",
+ "android.hardware.camera.provider-V2-ndk",
+ "libcameraservice",
"libgmock",
],
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index d07bf6d..d227606 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -94,7 +94,7 @@
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
sp<hardware::ICameraServiceProxy>& proxyBinder,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
- const std::string& userTag, int32_t videoStabilizationMode,
+ const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
const std::vector<hardware::CameraStreamStats>& streamStats) {
Mutex::Autolock l(mLock);
@@ -104,6 +104,7 @@
mSessionStats.mDeviceError = deviceError;
mSessionStats.mUserTag = userTag;
mSessionStats.mVideoStabilizationMode = videoStabilizationMode;
+ mSessionStats.mUsedUltraWide = usedUltraWide;
mSessionStats.mStreamStats = streamStats;
updateProxyDeviceState(proxyBinder);
@@ -278,7 +279,7 @@
void CameraServiceProxyWrapper::logIdle(const std::string& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
- const std::string& userTag, int32_t videoStabilizationMode,
+ const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
const std::vector<hardware::CameraStreamStats>& streamStats) {
std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
{
@@ -304,7 +305,7 @@
sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag,
- videoStabilizationMode, streamStats);
+ videoStabilizationMode, usedUltraWide, streamStats);
}
void CameraServiceProxyWrapper::logOpen(const std::string& id, int facing,
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index 1afe5b3..4e21e58 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -61,7 +61,7 @@
void onActive(sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps);
void onIdle(sp<hardware::ICameraServiceProxy>& proxyBinder,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
- const std::string& userTag, int32_t videoStabilizationMode,
+ const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
const std::vector<hardware::CameraStreamStats>& streamStats);
std::string updateExtensionSessionStats(
@@ -110,7 +110,7 @@
// Session state becomes idle
void logIdle(const std::string& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
- const std::string& userTag, int32_t videoStabilizationMode,
+ const std::string& userTag, int32_t videoStabilizationMode, bool usedUltraWide,
const std::vector<hardware::CameraStreamStats>& streamStats);
// Ping camera service proxy for user update
diff --git a/services/camera/libcameraservice/utils/SchedulingPolicyUtils.cpp b/services/camera/libcameraservice/utils/SchedulingPolicyUtils.cpp
new file mode 100644
index 0000000..92a1030
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SchedulingPolicyUtils.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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 "SchedulingPolicyUtils.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include "CameraThreadState.h"
+#include <private/android_filesystem_config.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/sched_policy.h>
+#include <procinfo/process.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace camera3 {
+namespace SchedulingPolicyUtils {
+
+int requestPriorityDirect(int pid, int tid, int prio) {
+ android::procinfo::ProcessInfo processInfo;
+ static const int kMinPrio = 1;
+ static const int kMaxPrio = 3;
+
+ if (!android::procinfo::GetProcessInfo(tid, &processInfo)) {
+ ALOGE("%s: Error getting process info", __FUNCTION__);
+ return -EPERM;
+ }
+
+ if (prio < kMinPrio || prio > kMaxPrio || processInfo.pid != pid) {
+ ALOGE("%s: Invalid parameter prio=%d pid=%d procinfo.pid=%d", __FUNCTION__, prio, pid,
+ processInfo.pid);
+ return -EPERM;
+ }
+
+ // Set the thread group as audio system thread group in consistent with the
+ // implementation in SchedulingPolicyService.java when isApp is false in
+ // requestPriority method.
+ if (!SetTaskProfiles(tid, {get_sched_policy_profile_name(SP_AUDIO_SYS)},
+ /*use_fd_cache*/ true)) {
+ ALOGE("%s:Error in SetTaskProfiles", __FUNCTION__);
+ return -EPERM;
+ }
+
+ struct sched_param param;
+ param.sched_priority = prio;
+ return sched_setscheduler(tid, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m);
+}
+
+} // namespace SchedulingPolicyUtils
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/SchedulingPolicyUtils.h b/services/camera/libcameraservice/utils/SchedulingPolicyUtils.h
new file mode 100644
index 0000000..f71fddf
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SchedulingPolicyUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_SERVICE_CAMERA_SCHEDULING_POLICY_UTILS_H
+#define ANDROID_SERVICE_CAMERA_SCHEDULING_POLICY_UTILS_H
+
+namespace android {
+namespace camera3 {
+namespace SchedulingPolicyUtils {
+
+/**
+ * Request elevated priority for thread tid, whose thread group leader must be pid.
+ * Instead of using scheduling policy service, this method uses direct system calls.
+ * The priority parameter is currently restricted from 1 to 3 matching
+ * scheduling policy service implementation.
+ */
+int requestPriorityDirect(int pid, int tid, int prio);
+
+} // SchedulingPolicyUtils
+} // camera3
+} // android
+
+#endif
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 59d1ae4..bd4ac38 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -242,6 +242,7 @@
"channel_count_hardware",
"sample_rate_hardware",
"uid",
+ "sample_rate_client",
};
static constexpr const char * HeadTrackerDeviceEnabledFields[] {
@@ -1379,6 +1380,10 @@
const auto uid = item->getUid();
+ int32_t sampleRateClient = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SAMPLERATECLIENT, &sampleRateClient);
+
LOG(LOG_LEVEL) << "key:" << key
<< " path:" << path
<< " direction:" << direction << "(" << directionStr << ")"
@@ -1402,7 +1407,8 @@
<< " format_hardware:" << formatHardware << "(" << formatHardwareStr << ")"
<< " channel_count_hardware:" << channelCountHardware
<< " sample_rate_hardware: " << sampleRateHardware
- << " uid: " << uid;
+ << " uid: " << uid
+ << " sample_rate_client: " << sampleRateClient;
if (mAudioAnalytics.mDeliverStatistics) {
const stats::media_metrics::BytesField bf_serialized(
@@ -1431,6 +1437,7 @@
, channelCountHardware
, sampleRateHardware
, uid
+ , sampleRateClient
);
std::stringstream ss;
ss << "result:" << result;
@@ -1458,6 +1465,7 @@
, channelCountHardware
, sampleRateHardware
, uid
+ , sampleRateClient
);
ss << " " << fieldsStr;
std::string str = ss.str();
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 1b5255a..f81db53 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -524,8 +524,8 @@
"audiotrack",
// other media
"codec",
- "freeze",
- "judder",
+ "videofreeze",
+ "videojudder",
"extractor",
"mediadrm",
"mediaparser",
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 83b30f3..844f204 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -187,10 +187,12 @@
const nsecs_t timestampNanos = MediaMetricsService::roundTime(item->getTimestamp());
AStatsEvent_writeInt64(event, timestampNanos);
- std::string packageName = item->getPkgName();
+ // packageName deprecated for calling_uid and statsd support as of U-QPR2
+ std::string packageName = "";
AStatsEvent_writeString(event, packageName.c_str());
- int64_t packageVersionCode = item->getPkgVersionCode();
+ // packageVersion depreccated for calling_uid and statsd support as of U-QPR2
+ int64_t packageVersionCode = 0;
AStatsEvent_writeInt64(event, packageVersionCode);
int64_t mediaApexVersion = 0;
@@ -864,6 +866,7 @@
<< " original_qp_p_max:" << qpPMaxOri
<< " original_qp_b_min:" << qpBMinOri
<< " original_qp_b_max:" << qpBMaxOri
+ << " app_uid:" << app_uid
<< " }";
statsdLog->log(stats::media_metrics::MEDIAMETRICS_CODEC_REPORTED, log.str());
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 5d1ba2b..8e7a624 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -53,23 +53,20 @@
std::weak_ptr<DeathNotifier> mDeathNotifier;
};
public:
+ static std::shared_ptr<DeathNotifier> Create(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::shared_ptr<ResourceManagerService>& service,
+ const ClientInfoParcel& clientInfo,
+ bool overrideProcessInfo = false);
+
DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
const std::shared_ptr<ResourceManagerService>& service,
- const ClientInfoParcel& clientInfo,
- AIBinder_DeathRecipient* recipient);
+ const ClientInfoParcel& clientInfo);
virtual ~DeathNotifier() {
unlink();
}
- void unlink() {
- if (mClient != nullptr) {
- // Register for the callbacks by linking to death notification.
- AIBinder_unlinkToDeath(mClient->asBinder().get(), mRecipient, mCookie);
- mClient = nullptr;
- }
- }
-
// Implement death recipient
static void BinderDiedCallback(void* cookie);
static void BinderUnlinkedCallback(void* cookie);
@@ -81,24 +78,34 @@
// The context gets deleted at BinderUnlinkedCallback.
mCookie = new BinderDiedContext{.mDeathNotifier = weak_from_this()};
// Register for the callbacks by linking to death notification.
- AIBinder_linkToDeath(mClient->asBinder().get(), mRecipient, mCookie);
+ AIBinder_linkToDeath(mClient->asBinder().get(), mDeathRecipient.get(), mCookie);
+ }
+
+ void unlink() {
+ if (mClient != nullptr) {
+ // Unlink from the death notification.
+ AIBinder_unlinkToDeath(mClient->asBinder().get(), mDeathRecipient.get(), mCookie);
+ mClient = nullptr;
+ }
}
protected:
std::shared_ptr<IResourceManagerClient> mClient;
std::weak_ptr<ResourceManagerService> mService;
const ClientInfoParcel mClientInfo;
- AIBinder_DeathRecipient* mRecipient;
BinderDiedContext* mCookie;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
};
DeathNotifier::DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
const std::shared_ptr<ResourceManagerService>& service,
- const ClientInfoParcel& clientInfo,
- AIBinder_DeathRecipient* recipient)
+ const ClientInfoParcel& clientInfo)
: mClient(client), mService(service), mClientInfo(clientInfo),
- mRecipient(recipient), mCookie(nullptr) {
- link();
+ mCookie(nullptr),
+ mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
+ AIBinder_DeathRecipient_new(BinderDiedCallback))) {
+ // Setting callback notification when DeathRecipient gets deleted.
+ AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), BinderUnlinkedCallback);
}
//static
@@ -140,9 +147,8 @@
public:
OverrideProcessInfoDeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
const std::shared_ptr<ResourceManagerService>& service,
- const ClientInfoParcel& clientInfo,
- AIBinder_DeathRecipient* recipient)
- : DeathNotifier(client, service, clientInfo, recipient) {}
+ const ClientInfoParcel& clientInfo)
+ : DeathNotifier(client, service, clientInfo) {}
virtual ~OverrideProcessInfoDeathNotifier() {}
@@ -160,6 +166,26 @@
service->removeProcessInfoOverride(mClientInfo.pid);
}
+std::shared_ptr<DeathNotifier> DeathNotifier::Create(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ const std::shared_ptr<ResourceManagerService>& service,
+ const ClientInfoParcel& clientInfo,
+ bool overrideProcessInfo) {
+ std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
+ if (overrideProcessInfo) {
+ deathNotifier = std::make_shared<OverrideProcessInfoDeathNotifier>(
+ client, service, clientInfo);
+ } else {
+ deathNotifier = std::make_shared<DeathNotifier>(client, service, clientInfo);
+ }
+
+ if (deathNotifier) {
+ deathNotifier->link();
+ }
+
+ return deathNotifier;
+}
+
template <typename T>
static String8 getString(const std::vector<T>& items) {
String8 itemsStr;
@@ -377,9 +403,7 @@
mServiceLog(new ServiceLog()),
mSupportsMultipleSecureCodecs(true),
mSupportsSecureWithNonSecureCodec(true),
- mCpuBoostCount(0),
- mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
- AIBinder_DeathRecipient_new(DeathNotifier::BinderDiedCallback))) {
+ mCpuBoostCount(0) {
mSystemCB->noteResetVideo();
// Create ResourceManagerMetrics that handles all the metrics.
mResourceManagerMetrics = std::make_unique<ResourceManagerMetrics>(mProcessInfo);
@@ -535,8 +559,8 @@
}
}
if (info.deathNotifier == nullptr && client != nullptr) {
- info.deathNotifier = std::make_shared<DeathNotifier>(
- client, ref<ResourceManagerService>(), clientInfo, mDeathRecipient.get());
+ info.deathNotifier = DeathNotifier::Create(
+ client, ref<ResourceManagerService>(), clientInfo);
}
if (mObserverService != nullptr && !resourceAdded.empty()) {
mObserverService->onResourceAdded(uid, pid, resourceAdded);
@@ -905,8 +929,8 @@
.uid = 0,
.id = 0,
.name = "<unknown client>"};
- auto deathNotifier = std::make_shared<OverrideProcessInfoDeathNotifier>(
- client, ref<ResourceManagerService>(), clientInfo, mDeathRecipient.get());
+ auto deathNotifier = DeathNotifier::Create(
+ client, ref<ResourceManagerService>(), clientInfo, true);
mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index aa88ac6..637525d 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -204,7 +204,6 @@
bool mSupportsMultipleSecureCodecs;
bool mSupportsSecureWithNonSecureCodec;
int32_t mCpuBoostCount;
- ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
struct ProcessInfoOverride {
std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
std::shared_ptr<IResourceManagerClient> client;
diff --git a/services/mediaresourcemanager/fuzzer/Android.bp b/services/mediaresourcemanager/fuzzer/Android.bp
index a46d87a..bbbc737 100644
--- a/services/mediaresourcemanager/fuzzer/Android.bp
+++ b/services/mediaresourcemanager/fuzzer/Android.bp
@@ -27,21 +27,18 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_fuzz {
- name: "mediaresourcemanager_fuzzer",
- srcs: [
- "mediaresourcemanager_fuzzer.cpp",
+cc_defaults {
+ name: "mediaresourcemanager_fuzzer_defaults",
+ defaults: [
+ "service_fuzzer_defaults",
],
static_libs: [
"liblog",
"libresourcemanagerservice",
],
shared_libs: [
- "libbinder",
- "libbinder_ndk",
"libmedia",
"libmediautils",
- "libutils",
"libstats_media_metrics",
"libstatspull",
"libstatssocket",
@@ -62,3 +59,39 @@
fuzzed_code_usage: "shipped",
},
}
+
+cc_fuzz {
+ name: "mediaresourcemanager_fuzzer",
+ defaults: [
+ "mediaresourcemanager_fuzzer_defaults",
+ ],
+ srcs: [
+ "mediaresourcemanager_fuzzer.cpp",
+ ],
+}
+
+cc_fuzz {
+ name: "resourcemanager_service_fuzzer",
+ defaults: [
+ "mediaresourcemanager_fuzzer_defaults",
+ ],
+ srcs: [
+ "resourcemanager_service_fuzzer.cpp",
+ ],
+}
+
+cc_fuzz {
+ name: "resourceobserver_service_fuzzer",
+ defaults: [
+ "mediaresourcemanager_fuzzer_defaults",
+ ],
+ static_libs: [
+ "resourceobserver_aidl_interface-V1-ndk",
+ ],
+ srcs: [
+ "resourceobserver_service_fuzzer.cpp",
+ ],
+ fuzz_config: {
+ triage_assignee: "waghpawan@google.com",
+ },
+}
diff --git a/services/mediaresourcemanager/fuzzer/resourcemanager_service_fuzzer.cpp b/services/mediaresourcemanager/fuzzer/resourcemanager_service_fuzzer.cpp
new file mode 100644
index 0000000..ca10d20
--- /dev/null
+++ b/services/mediaresourcemanager/fuzzer/resourcemanager_service_fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 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/binder_interface_utils.h>
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "ResourceManagerService.h"
+
+using android::fuzzService;
+using android::ResourceManagerService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto service = SharedRefBase::make<ResourceManagerService>();
+ fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
+ return 0;
+}
diff --git a/services/mediaresourcemanager/fuzzer/resourceobserver_service_fuzzer.cpp b/services/mediaresourcemanager/fuzzer/resourceobserver_service_fuzzer.cpp
new file mode 100644
index 0000000..e69368d
--- /dev/null
+++ b/services/mediaresourcemanager/fuzzer/resourceobserver_service_fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 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/binder_interface_utils.h>
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "ResourceObserverService.h"
+
+using android::fuzzService;
+using android::ResourceObserverService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto service = SharedRefBase::make<ResourceObserverService>();
+ fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
+ return 0;
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 7f228c7..2ef6fe5 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -45,6 +45,8 @@
#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (3 * AAUDIO_NANOS_PER_MILLISECOND)
#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (-1 * AAUDIO_NANOS_PER_MILLISECOND)
+#define AAUDIO_MAX_OPEN_ATTEMPTS 10
+
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
@@ -77,14 +79,23 @@
{AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_16_BIT}
};
-audio_format_t getNextFormatToTry(audio_format_t curFormat, audio_format_t returnedFromAPM) {
- if (returnedFromAPM != AUDIO_FORMAT_DEFAULT) {
- return returnedFromAPM;
- }
+audio_format_t getNextFormatToTry(audio_format_t curFormat) {
const auto it = NEXT_FORMAT_TO_TRY.find(curFormat);
- return it != NEXT_FORMAT_TO_TRY.end() ? it->second : AUDIO_FORMAT_DEFAULT;
+ return it != NEXT_FORMAT_TO_TRY.end() ? it->second : curFormat;
}
+struct configComp {
+ bool operator() (const audio_config_base_t& lhs, const audio_config_base_t& rhs) const {
+ if (lhs.sample_rate != rhs.sample_rate) {
+ return lhs.sample_rate < rhs.sample_rate;
+ } else if (lhs.channel_mask != rhs.channel_mask) {
+ return lhs.channel_mask < rhs.channel_mask;
+ } else {
+ return lhs.format < rhs.format;
+ }
+ }
+};
+
} // namespace
aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
@@ -101,60 +112,66 @@
legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
audio_format_t audioFormat = getFormat();
- std::set<audio_format_t> formatsTried;
- while (true) {
- if (formatsTried.find(audioFormat) != formatsTried.end()) {
+ int32_t sampleRate = getSampleRate();
+ if (sampleRate == AAUDIO_UNSPECIFIED) {
+ sampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
+ }
+
+ const aaudio_direction_t direction = getDirection();
+ audio_config_base_t config;
+ config.format = audioFormat;
+ config.sample_rate = sampleRate;
+ config.channel_mask = AAudio_getChannelMaskForOpen(
+ getChannelMask(), getSamplesPerFrame(), direction == AAUDIO_DIRECTION_INPUT);
+
+ std::set<audio_config_base_t, configComp> configsTried;
+ int32_t numberOfAttempts = 0;
+ while (numberOfAttempts < AAUDIO_MAX_OPEN_ATTEMPTS) {
+ if (configsTried.find(config) != configsTried.end()) {
// APM returning something that has already tried.
- ALOGW("Have already tried to open with format=%#x, but failed before", audioFormat);
+ ALOGW("Have already tried to open with format=%#x and sr=%d, but failed before",
+ config.format, config.sample_rate);
break;
}
- formatsTried.insert(audioFormat);
+ configsTried.insert(config);
- audio_format_t nextFormatToTry = AUDIO_FORMAT_DEFAULT;
- result = openWithFormat(audioFormat, &nextFormatToTry);
+ audio_config_base_t previousConfig = config;
+ result = openWithConfig(&config);
if (result != AAUDIO_ERROR_UNAVAILABLE) {
// Return if it is successful or there is an error that is not
// AAUDIO_ERROR_UNAVAILABLE happens.
- ALOGI("Opened format=%#x with result=%d", audioFormat, result);
+ ALOGI("Opened format=%#x sr=%d, with result=%d", previousConfig.format,
+ previousConfig.sample_rate, result);
break;
}
- nextFormatToTry = getNextFormatToTry(audioFormat, nextFormatToTry);
- ALOGD("%s() %#x failed, perhaps due to format. Try again with %#x",
- __func__, audioFormat, nextFormatToTry);
- audioFormat = nextFormatToTry;
- if (audioFormat == AUDIO_FORMAT_DEFAULT) {
- // Nothing else to try
- break;
+ // Try other formats if the config from APM is the same as our current config.
+ // Some HALs may report its format support incorrectly.
+ if ((previousConfig.format == config.format) &&
+ (previousConfig.sample_rate == config.sample_rate)) {
+ config.format = getNextFormatToTry(config.format);
}
+
+ ALOGD("%s() %#x %d failed, perhaps due to format or sample rate. Try again with %#x %d",
+ __func__, previousConfig.format, previousConfig.sample_rate, config.format,
+ config.sample_rate);
+ numberOfAttempts++;
}
return result;
}
-aaudio_result_t AAudioServiceEndpointMMAP::openWithFormat(
- audio_format_t audioFormat, audio_format_t* nextFormatToTry) {
+aaudio_result_t AAudioServiceEndpointMMAP::openWithConfig(
+ audio_config_base_t* config) {
aaudio_result_t result = AAUDIO_OK;
- audio_config_base_t config;
+ audio_config_base_t currentConfig = *config;
audio_port_handle_t deviceId;
const audio_attributes_t attributes = getAudioAttributesFrom(this);
deviceId = mRequestedDeviceId;
- // Fill in config
- config.format = audioFormat;
-
- int32_t aaudioSampleRate = getSampleRate();
- if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
- aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
- }
- config.sample_rate = aaudioSampleRate;
-
const aaudio_direction_t direction = getDirection();
- config.channel_mask = AAudio_getChannelMaskForOpen(
- getChannelMask(), getSamplesPerFrame(), direction == AAUDIO_DIRECTION_INPUT);
-
if (direction == AAUDIO_DIRECTION_OUTPUT) {
mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
@@ -177,11 +194,11 @@
// Open HAL stream. Set mMmapStream
ALOGD("%s trying to open MMAP stream with format=%#x, "
"sample_rate=%u, channel_mask=%#x, device=%d",
- __func__, config.format, config.sample_rate,
- config.channel_mask, deviceId);
+ __func__, config->format, config->sample_rate,
+ config->channel_mask, deviceId);
const status_t status = MmapStreamInterface::openMmapStream(streamDirection,
&attributes,
- &config,
+ config,
mMmapClient,
&deviceId,
&sessionId,
@@ -195,9 +212,9 @@
// not match the hardware.
ALOGD("%s() - openMmapStream() returned status=%d, suggested format=%#x, sample_rate=%u, "
"channel_mask=%#x",
- __func__, status, config.format, config.sample_rate, config.channel_mask);
- *nextFormatToTry = config.format != audioFormat ? config.format
- : *nextFormatToTry;
+ __func__, status, config->format, config->sample_rate, config->channel_mask);
+ // Keep the channel mask of the current config
+ config->channel_mask = currentConfig.channel_mask;
return AAUDIO_ERROR_UNAVAILABLE;
}
@@ -217,7 +234,7 @@
setSessionId(actualSessionId);
ALOGD("%s(format = 0x%X) deviceId = %d, sessionId = %d",
- __func__, audioFormat, getDeviceId(), getSessionId());
+ __func__, config->format, getDeviceId(), getSessionId());
// Create MMAP/NOIRQ buffer.
result = createMmapBuffer();
@@ -227,11 +244,11 @@
// Get information about the stream and pass it back to the caller.
setChannelMask(AAudioConvert_androidToAAudioChannelMask(
- config.channel_mask, getDirection() == AAUDIO_DIRECTION_INPUT,
- AAudio_isChannelIndexMask(config.channel_mask)));
+ config->channel_mask, getDirection() == AAUDIO_DIRECTION_INPUT,
+ AAudio_isChannelIndexMask(config->channel_mask)));
- setFormat(config.format);
- setSampleRate(config.sample_rate);
+ setFormat(config->format);
+ setSampleRate(config->sample_rate);
setHardwareSampleRate(getSampleRate());
setHardwareFormat(getFormat());
setHardwareSamplesPerFrame(AAudioConvert_channelMaskToCount(getChannelMask()));
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index 38cf0ba..f19005c 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -97,7 +97,7 @@
private:
- aaudio_result_t openWithFormat(audio_format_t audioFormat, audio_format_t* nextFormatToTry);
+ aaudio_result_t openWithConfig(audio_config_base_t* config);
aaudio_result_t createMmapBuffer();
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index bc7ccde..8f51ce4 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -220,18 +220,6 @@
return mSuspended;
}
- bool isCloseNeeded() const {
- return mCloseNeeded.load();
- }
-
- /**
- * Mark this stream as needing to be closed.
- * Once marked for closing, it cannot be unmarked.
- */
- void markCloseNeeded() {
- mCloseNeeded.store(true);
- }
-
virtual const char *getTypeText() const { return "Base"; }
protected:
@@ -419,12 +407,8 @@
aaudio_handle_t mHandle = -1;
bool mFlowing = false;
- // This indicates that a stream that is being referenced by a binder call
- // and needs to closed.
- std::atomic<bool> mCloseNeeded{false}; // TODO remove
-
// This indicate that a running stream should not be processed because of an error,
- // for example a full message queue. Note that this atomic is unrelated to mCloseNeeded.
+ // for example a full message queue.
std::atomic<bool> mSuspended{false};
bool mDisconnected GUARDED_BY(mLock) {false};
diff --git a/services/oboeservice/AAudioThread.cpp b/services/oboeservice/AAudioThread.cpp
index 549fa59..502d773 100644
--- a/services/oboeservice/AAudioThread.cpp
+++ b/services/oboeservice/AAudioThread.cpp
@@ -75,7 +75,9 @@
aaudio_result_t AAudioThread::stop() {
if (!mHasThread) {
- ALOGE("stop() but no thread running");
+ // There can be cases that the thread is just created but not started.
+ // Logging as warning to attract attention but not too serious.
+ ALOGW("stop() but no thread running");
return AAUDIO_ERROR_INVALID_STATE;
}
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index c5080a4..3521979 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -78,12 +78,38 @@
]
+cc_defaults {
+ name: "libaaudioservice_dependencies",
-cc_library {
+ shared_libs: [
+ "libaaudio_internal",
+ "libaudioclient",
+ "libaudioutils",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "aaudio-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ "packagemanager_aidl-cpp",
+ ],
+
+ static_libs: [
+ "libaudioflinger",
+ ]
+}
+
+cc_library_static {
name: "libaaudioservice",
defaults: [
+ "libaaudioservice_dependencies",
"latest_android_media_audio_common_types_cpp_shared",
],
@@ -116,25 +142,6 @@
"-Werror",
],
- shared_libs: [
- "libaaudio_internal",
- "libaudioclient",
- "libaudioflinger",
- "libaudioutils",
- "libmedia_helper",
- "libmediametrics",
- "libmediautils",
- "libbase",
- "libbinder",
- "libcutils",
- "liblog",
- "libutils",
- "aaudio-aidl-cpp",
- "framework-permission-aidl-cpp",
- "libaudioclient_aidl_conversion",
- "packagemanager_aidl-cpp",
- ],
-
export_shared_lib_headers: [
"libaaudio_internal",
"framework-permission-aidl-cpp",
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
index f047065..f5c2e6c 100644
--- a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -403,13 +403,6 @@
request.getConfiguration().setBufferCapacity(fdp.ConsumeIntegral<int32_t>());
- request.getConfiguration().setHardwareSampleRate(fdp.ConsumeIntegral<int32_t>());
- request.getConfiguration().setHardwareSamplesPerFrame(fdp.ConsumeIntegral<int32_t>());
- request.getConfiguration().setHardwareFormat((audio_format_t)(
- fdp.ConsumeBool()
- ? fdp.ConsumeIntegral<int32_t>()
- : kAAudioFormats[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioFormats - 1)]));
-
auto streamHandleInfo = mClient->openStream(request, configurationOutput);
if (streamHandleInfo.getHandle() < 0) {
// invalid request, stream not opened.
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
index ea5139d..e29d520 100644
--- a/services/tuner/Android.bp
+++ b/services/tuner/Android.bp
@@ -86,6 +86,7 @@
"android.hardware.tv.tuner@1.1",
"android.hardware.tv.tuner-V2-ndk",
"libbase",
+ "libcutils",
"libbinder",
"libfmq",
"libhidlbase",
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index 617622d..be482c4 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -355,6 +355,11 @@
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}
+ // Call to HAL to make sure the transport FD was able to be closed by binder.
+ // This is a tricky workaround for a problem in Binder.
+ // TODO:[b/192048842] When that problem is fixed we may be able to remove or change this code.
+ mFilter_1_1->getId([&](HidlResult /* r */, uint32_t /* filterId*/){});
+
return ::ndk::ScopedAStatus::ok();
}
diff --git a/services/tuner/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
index 90f1731..6dee324 100644
--- a/services/tuner/main_tunerservice.cpp
+++ b/services/tuner/main_tunerservice.cpp
@@ -17,6 +17,7 @@
#include <android-base/logging.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
#include <utils/Log.h>
#include <hidl/HidlTransportSupport.h>
@@ -31,6 +32,12 @@
int main() {
ALOGD("Tuner service starting");
+ if (!property_get_bool("tuner.server.enable", false)
+ && !property_get_bool("ro.tuner.lazyhal", false)) {
+ ALOGD("tuner is not enabled, terminating");
+ return 0;
+ }
+
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
hardware::configureRpcThreadpool(16, true);