Merge "codec2 hal: AIDL type conversion, step 3" into main am: b092d2b376 am: 14b97bf28d am: a878f5b125 am: 82242ead07 am: 5bf1aacacf
Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/2620623
Change-Id: I21e51bed6504412dd2ef325a34ce81df392dcebf
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..e5ae954 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"],
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/camera_platform.aconfig b/camera/camera_platform.aconfig
new file mode 100644
index 0000000..fc11518
--- /dev/null
+++ b/camera/camera_platform.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.internal.camera.flags"
+
+flag {
+ namespace: "camera_platform"
+ name: "initial_test_flag"
+ description: "Flag infrastructure test flag"
+ bug: "292631208"
+}
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/ndk/Android.bp b/camera/ndk/Android.bp
index 24a11e3..921aab2 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -177,6 +177,7 @@
shared_libs: [
"libcamera2ndk_vendor",
"libcamera_metadata",
+ "libhidlbase",
"libmediandk",
"libnativewindow",
"libutils",
@@ -186,6 +187,7 @@
],
static_libs: [
"android.hardware.camera.common@1.0-helper",
+ "android.hidl.token@1.0",
],
cflags: [
"-D__ANDROID_VNDK__",
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index af00e55..4a589bc 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -584,6 +584,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of ACAMERA_SENSOR_EXPOSURE_TIME and
* ACAMERA_SENSOR_FRAME_DURATION.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link ACameraDevice_createCaptureSessionWithSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
*
* @see ACAMERA_SENSOR_EXPOSURE_TIME
* @see ACAMERA_SENSOR_FRAME_DURATION
@@ -1128,6 +1135,12 @@
* ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link ACameraDevice_createCaptureSessionWithSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* (ACAMERA_LENS_OPTICAL_STABILIZATION_MODE), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
index 7f6ea9d..74c6cad 100644
--- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -31,10 +31,13 @@
#include <stdio.h>
#include <android/log.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <android/hidl/token/1.0/ITokenManager.h>
#include <camera/NdkCameraError.h>
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraCaptureSession.h>
+#include <hidl/ServiceManagement.h>
#include <media/NdkImage.h>
#include <media/NdkImageReader.h>
#include <cutils/native_handle.h>
@@ -50,6 +53,8 @@
static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;
using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache;
+using android::hidl::manager::V1_0::IServiceManager;
+using android::hidl::token::V1_0::ITokenManager;
using ConfiguredWindows = std::set<const native_handle_t *>;
class CameraHelper {
@@ -981,11 +986,19 @@
TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) {
+ auto transport = android::hardware::defaultServiceManager()->getTransport(ITokenManager::descriptor, "default");
+ if (transport.isOk() && transport == IServiceManager::Transport::EMPTY) {
+ GTEST_SKIP() << "This device no longer supports AImageReader_getWindowNativeHandle";
+ }
testBasicTakePictures(/*prepareSurfaces*/ false);
testBasicTakePictures(/*prepareSurfaces*/ true);
}
TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) {
+ auto transport = android::hardware::defaultServiceManager()->getTransport(ITokenManager::descriptor, "default");
+ if (transport.isOk() && transport == IServiceManager::Transport::EMPTY) {
+ GTEST_SKIP() << "This device no longer supports AImageReader_getWindowNativeHandle";
+ }
for (auto & v2 : {true, false}) {
testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/, v2);
testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/, v2);
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/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/dav1d/Android.bp b/media/codec2/components/dav1d/Android.bp
new file mode 100644
index 0000000..f7850ad
--- /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",
+ ],
+
+ cflags: [
+ "-DCODECNAME=\"c2.android.dav1d-av1.decoder\"",
+ "-Wno-unused-variable",
+ ],
+
+ srcs: ["C2SoftDav1dDec.cpp"],
+ static_libs: [
+ "libyuv_static",
+ "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..b0cef41
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.cpp
@@ -0,0 +1,1493 @@
+/*
+ * 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 <libyuv.h>
+#include <log/log.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include "C2SoftDav1dDec.h"
+
+// 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
+
+namespace android {
+
+// Flag to enable dumping the bitsteram and the decoded pictures to files.
+static const bool ENABLE_DUMPING_FILES_DEFAULT = false;
+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";
+
+// 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() {
+ nsecs_t now = systemTime();
+#ifdef FILE_DUMP_ENABLE
+ snprintf(mInDataFileName, 256, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now, INPUT_DATA_DUMP_EXT);
+ snprintf(mInSizeFileName, 256, "%s_%" PRId64 "d.%s", DUMP_FILE_PATH, now, INPUT_SIZE_DUMP_EXT);
+ snprintf(mDav1dOutYuvFileName, 256, "%s_%" PRId64 "dx.%s", DUMP_FILE_PATH, now,
+ OUTPUT_YUV_DUMP_EXT);
+
+ bool enableDumping = android::base::GetBoolProperty(ENABLE_DUMPING_FILES_PROPERTY,
+ ENABLE_DUMPING_FILES_DEFAULT);
+
+ num_frames_to_dump =
+ android::base::GetIntProperty(NUM_FRAMES_TO_DUMP_PROPERTY, NUM_FRAMES_TO_DUMP_DEFAULT);
+
+ if (enableDumping) {
+ ALOGD("enableDumping = %d, num_frames_to_dump = %d", enableDumping, num_frames_to_dump);
+
+ 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);
+ }
+ }
+#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
+ if (mInDataFile != nullptr) {
+ fclose(mInDataFile);
+ mInDataFile = nullptr;
+ }
+
+ if (mInSizeFile != nullptr) {
+ fclose(mInSizeFile);
+ mInSizeFile = nullptr;
+ }
+
+ if (mDav1dOutYuvFile != nullptr) {
+ fclose(mDav1dOutYuvFile);
+ mDav1dOutYuvFile = nullptr;
+ }
+#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);
+ }
+}
+
+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);
+ }
+
+ // insert OBU TD if it is not present.
+ // TODO: b/286852962
+ uint8_t obu_type = (bitstream[0] >> 3) & 0xf;
+ Dav1dData data;
+
+ uint8_t* ptr = (obu_type == DAV1D_OBU_TD) ? dav1d_data_create(&data, inSize)
+ : dav1d_data_create(&data, inSize + 2);
+ if (ptr == nullptr) {
+ ALOGE("dav1d_data_create failed!");
+ i_ret = -1;
+
+ } else {
+ data.m.timestamp = in_frameIndex;
+
+ int new_Size;
+ if (obu_type != DAV1D_OBU_TD) {
+ new_Size = (int)(inSize + 2);
+
+ // OBU TD
+ ptr[0] = 0x12;
+ ptr[1] = 0;
+
+ memcpy(ptr + 2, bitstream, inSize);
+ } else {
+ new_Size = (int)(inSize);
+ // TODO: b/277797541 - investigate how to wrap this pointer in Dav1dData to
+ // avoid memcopy operations.
+ memcpy(ptr, bitstream, new_Size);
+ }
+
+ // ALOGV("memcpy(ptr,bitstream,inSize=%ld,new_Size=%d,in_frameIndex=%ld,timestamp=%ld,"
+ // "ptr[0,1,2,3,4]=%x,%x,%x,%x,%x)",
+ // inSize, new_Size, frameIndex, data.m.timestamp, ptr[0], ptr[1], ptr[2],
+ // ptr[3], ptr[4]);
+
+ // Dump the bitstream data (inputBuffer) if dumping is enabled.
+#ifdef FILE_DUMP_ENABLE
+ if (mInDataFile) {
+ int ret = fwrite(ptr, 1, new_Size, mInDataFile);
+
+ if (ret != new_Size) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mInDataFileName,
+ new_Size, ret);
+ }
+ }
+
+ // Dump the size per inputBuffer if dumping is enabled.
+ if (mInSizeFile) {
+ int ret = fwrite(&new_Size, 1, 4, mInSizeFile);
+
+ if (ret != 4) {
+ ALOGE("Error in fwrite %s, requested %d, returned %d", mInSizeFileName, 4,
+ ret);
+ }
+ }
+#endif
+
+ bool b_draining = false;
+ int res;
+
+ 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;
+}
+
+#ifdef FILE_DUMP_ENABLE
+void C2SoftDav1dDec::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];
+ }
+ }
+ }
+ }
+}
+#endif
+
+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;
+
+#if LIBYUV_VERSION < 1779
+ if (!(img.p.layout != DAV1D_PIXEL_LAYOUT_I400 || img.p.layout != DAV1D_PIXEL_LAYOUT_I420)) {
+ ALOGE("image_format %d not supported", img.p.layout);
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return false;
+ }
+#endif
+
+ 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 !HAVE_LIBYUV_I410_I210_TO_AB30
+ if ((format == HAL_PIXEL_FORMAT_RGBA_1010102) &&
+ (is_img_ready ? img.p.layout == DAV1D_PIXEL_LAYOUT_I420
+ : buffer->image_format != libgav1::kImageFormatYuv420)) {
+ ALOGE("Only YUV420 output is supported when targeting RGBA_1010102");
+ mSignalledError = true;
+ work->result = C2_OMITTED;
+ work->workletsProcessed = 1u;
+ return false;
+ }
+#endif
+ }
+
+ 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;
+
+ 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) {
+ bool processed = false;
+#if HAVE_LIBYUV_I410_I210_TO_AB30
+ if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
+ libyuv::I410ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
+ dstYStride, &libyuv::kYuvV2020Constants, mWidth, mHeight);
+ processed = true;
+ } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
+ libyuv::I210ToAB30Matrix(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
+ dstYStride, &libyuv::kYuvV2020Constants, mWidth, mHeight);
+ processed = true;
+ }
+#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);
+ 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, srcUStride, srcVStride,
+ dstYStride / sizeof(uint32_t), mWidth, mHeight,
+ std::static_pointer_cast<const C2ColorAspectsStruct>(codedColorAspects));
+ }
+ } else if (format == HAL_PIXEL_FORMAT_YCBCR_P010) {
+ dstYStride /= 2;
+ dstUStride /= 2;
+ dstVStride /= 2;
+#if LIBYUV_VERSION >= 1779
+ if ((img.p.layout == DAV1D_PIXEL_LAYOUT_I444) ||
+ (img.p.layout == DAV1D_PIXEL_LAYOUT_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 * mHeight + dstUStride * align(mHeight, 2);
+ if (!allocTmpFrameBuffer(tmpSize)) {
+ ALOGE("Error allocating temp conversion buffer (%zu bytes)", tmpSize);
+ setError(work, C2_NO_MEMORY);
+ return false;
+ }
+ uint16_t* const tmpY = mTmpFrameBuffer.get();
+ uint16_t* const tmpU = tmpY + dstYStride * mHeight;
+ uint16_t* const tmpV = tmpU + dstUStride * align(mHeight, 2) / 2;
+ if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
+ libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+ dstYStride, tmpU, dstUStride, tmpV, dstUStride, mWidth,
+ mHeight);
+ } else {
+ libyuv::I210ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+ dstYStride, tmpU, dstUStride, tmpV, dstUStride, mWidth,
+ mHeight);
+ }
+ libyuv::I010ToP010(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstVStride,
+ (uint16_t*)dstY, dstYStride, (uint16_t*)dstU, dstUStride, mWidth,
+ mHeight);
+ } else {
+ convertYUV420Planar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
+ srcYStride, srcUStride, srcVStride, dstYStride,
+ dstUStride, mWidth, mHeight, isMonochrome);
+ }
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToP010((uint16_t*)dstY, (uint16_t*)dstU, srcY, srcU, srcV,
+ srcYStride, srcUStride, srcVStride, dstYStride, dstUStride,
+ mWidth, mHeight, isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
+ } else {
+#if LIBYUV_VERSION >= 1779
+ if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
+ // TODO(https://crbug.com/libyuv/950): replace this block with libyuv::I410ToI420
+ // when it's available.
+ const size_t 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;
+ }
+ uint16_t* const tmpY = mTmpFrameBuffer.get();
+ uint16_t* const tmpU = tmpY + dstYStride * mHeight;
+ uint16_t* const tmpV = tmpU + dstUStride * align(mHeight, 2) / 2;
+ libyuv::I410ToI010(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, tmpY,
+ dstYStride, tmpU, dstUStride, tmpV, dstVStride, mWidth, mHeight);
+ libyuv::I010ToI420(tmpY, dstYStride, tmpU, dstUStride, tmpV, dstUStride, dstY,
+ dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
+ } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
+ libyuv::I210ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
+ dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
+ } else {
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+ srcUStride, srcVStride, dstYStride, dstUStride, mWidth,
+ mHeight, isMonochrome);
+ }
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, mWidth, mHeight,
+ isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
+ }
+
+ // Dump the output buffer if dumping is enabled (debug only).
+#ifdef FILE_DUMP_ENABLE
+ FILE* fp_out = mDav1dOutYuvFile;
+
+ // if(mOutputBufferIndex % 100 == 0)
+ ALOGV("output a 10bit picture %dx%d from dav1d "
+ "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
+ mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
+
+ if (fp_out && mOutputBufferIndex <= num_frames_to_dump) {
+ for (int i = 0; i < mHeight; i++) {
+ int ret = fwrite((uint8_t*)srcY + i * srcYStride * 2, 1, mWidth * 2, fp_out);
+ if (ret != mWidth * 2) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth * 2, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < mHeight / 2; i++) {
+ int ret = fwrite((uint8_t*)srcU + i * srcUStride * 2, 1, mWidth, fp_out);
+ if (ret != mWidth) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < mHeight / 2; i++) {
+ int ret = fwrite((uint8_t*)srcV + i * srcVStride * 2, 1, mWidth, fp_out);
+ if (ret != mWidth) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
+ break;
+ }
+ }
+ }
+#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];
+
+ // Dump the output buffer is dumping is enabled (debug only)
+#ifdef FILE_DUMP_ENABLE
+ FILE* fp_out = mDav1dOutYuvFile;
+ // if(mOutputBufferIndex % 100 == 0)
+ ALOGV("output a 8bit picture %dx%d from dav1d "
+ "(mInputBufferIndex=%d,mOutputBufferIndex=%d,format=%d).",
+ mWidth, mHeight, mInputBufferIndex, mOutputBufferIndex, format);
+
+ if (fp_out && mOutputBufferIndex <= num_frames_to_dump) {
+ for (int i = 0; i < mHeight; i++) {
+ int ret = fwrite((uint8_t*)srcY + i * srcYStride, 1, mWidth, fp_out);
+ if (ret != mWidth) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < mHeight / 2; i++) {
+ int ret = fwrite((uint8_t*)srcU + i * srcUStride, 1, mWidth / 2, fp_out);
+ if (ret != mWidth / 2) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth / 2, ret);
+ break;
+ }
+ }
+
+ for (int i = 0; i < mHeight / 2; i++) {
+ int ret = fwrite((uint8_t*)srcV + i * srcVStride, 1, mWidth / 2, fp_out);
+ if (ret != mWidth / 2) {
+ ALOGE("Error in fwrite, requested %d, returned %d", mWidth / 2, ret);
+ break;
+ }
+ }
+ }
+#endif
+ if (img.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
+ libyuv::I444ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
+ dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
+ } else if (img.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
+ libyuv::I422ToI420(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, dstY,
+ dstYStride, dstU, dstUStride, dstV, dstVStride, mWidth, mHeight);
+ } else {
+ convertYUV420Planar8ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride, srcUStride,
+ srcVStride, dstYStride, dstUStride, dstVStride, mWidth,
+ mHeight, isMonochrome);
+ }
+ }
+
+ 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..5201456
--- /dev/null
+++ b/media/codec2/components/dav1d/C2SoftDav1dDec.h
@@ -0,0 +1,143 @@
+/*
+ * 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>
+
+//#define FILE_DUMP_ENABLE 1
+#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 {
+
+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
+ char mInDataFileName[256];
+ char mInSizeFileName[256];
+ char mDav1dOutYuvFileName[256];
+
+ FILE* mInDataFile = nullptr;
+ FILE* mInSizeFile = nullptr;
+ FILE* mDav1dOutYuvFile = nullptr;
+
+ void writeDav1dOutYuvFile(const Dav1dPicture& p);
+
+ int num_frames_to_dump = 0;
+#endif
+
+ C2_DO_NOT_COPY(C2SoftDav1dDec);
+};
+
+} // namespace android
+
+#endif // ANDROID_C2_SOFT_DAV1D_DEC_H_
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 00820eb..b5cb546 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>
@@ -1907,6 +1908,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/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index a75ce70..1153fb6 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2137,7 +2137,7 @@
}
std::map<size_t, sp<MediaCodecBuffer>> clientInputBuffers;
- status_t err = mChannel->prepareInitialInputBuffers(&clientInputBuffers);
+ status_t err = mChannel->prepareInitialInputBuffers(&clientInputBuffers, true);
if (err != OK) {
if (err == NO_MEMORY) {
// NO_MEMORY happens here when all the buffers are still
@@ -2160,7 +2160,6 @@
const std::unique_ptr<Config> &config = *configLocked;
return config->mBuffersBoundToCodec;
}());
-
{
Mutexed<State>::Locked state(mState);
if (state->get() != RESUMING) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 29afcee..7665d6b 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -25,6 +25,8 @@
#include <atomic>
#include <list>
#include <numeric>
+#include <thread>
+#include <chrono>
#include <C2AllocatorGralloc.h>
#include <C2PlatformSupport.h>
@@ -598,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};
@@ -1615,22 +1619,31 @@
}
status_t CCodecBufferChannel::prepareInitialInputBuffers(
- std::map<size_t, sp<MediaCodecBuffer>> *clientInputBuffers) {
+ std::map<size_t, sp<MediaCodecBuffer>> *clientInputBuffers, bool retry) {
if (mInputSurface) {
return OK;
}
size_t numInputSlots = mInput.lock()->numSlots;
-
- {
- Mutexed<Input>::Locked input(mInput);
- while (clientInputBuffers->size() < numInputSlots) {
- size_t index;
- sp<MediaCodecBuffer> buffer;
- if (!input->buffers->requestNewBuffer(&index, &buffer)) {
- break;
+ int retryCount = 1;
+ for (; clientInputBuffers->empty() && retryCount >= 0; retryCount--) {
+ {
+ Mutexed<Input>::Locked input(mInput);
+ while (clientInputBuffers->size() < numInputSlots) {
+ size_t index;
+ sp<MediaCodecBuffer> buffer;
+ if (!input->buffers->requestNewBuffer(&index, &buffer)) {
+ break;
+ }
+ clientInputBuffers->emplace(index, buffer);
}
- clientInputBuffers->emplace(index, buffer);
+ }
+ if (!retry || (retryCount <= 0)) {
+ break;
+ }
+ if (clientInputBuffers->empty()) {
+ // wait: buffer may be in transit from component.
+ std::this_thread::sleep_for(std::chrono::milliseconds(4));
}
}
if (clientInputBuffers->empty()) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 41f5ae2..f60b6fa 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -140,7 +140,8 @@
* initial input buffers.
*/
status_t prepareInitialInputBuffers(
- std::map<size_t, sp<MediaCodecBuffer>> *clientInputBuffers);
+ std::map<size_t, sp<MediaCodecBuffer>> *clientInputBuffers,
+ bool retry = false);
/**
* Request initial input buffers as prepared in clientInputBuffers.
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index f272499..0803dc3 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <cutils/native_handle.h>
+#include <drm/drm_fourcc.h>
#include <gralloctypes/Gralloc4.h>
#include <hardware/gralloc.h>
#include <ui/GraphicBufferAllocator.h>
@@ -478,7 +479,25 @@
// 'NATIVE' on Android means LITTLE_ENDIAN
constexpr C2PlaneInfo::endianness_t kEndianness = C2PlaneInfo::NATIVE;
- switch (mFormat) {
+ // Try to resolve IMPLEMENTATION_DEFINED format to accurate format if
+ // possible.
+ uint32_t format = mFormat;
+ uint32_t fourCc;
+ if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ !GraphicBufferMapper::get().getPixelFormatFourCC(mBuffer, &fourCc)) {
+ switch (fourCc) {
+ case DRM_FORMAT_XBGR8888:
+ format = static_cast<uint32_t>(PixelFormat4::RGBX_8888);
+ break;
+ case DRM_FORMAT_ABGR8888:
+ format = static_cast<uint32_t>(PixelFormat4::RGBA_8888);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (format) {
case static_cast<uint32_t>(PixelFormat4::RGBA_1010102): {
// TRICKY: this is used for media as YUV444 in the case when it is queued directly to a
// Surface. In all other cases it is RGBA. We don't know which case it is here, so
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/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 6616197..e815190 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -108,7 +108,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 +132,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 +145,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 +156,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 +878,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();
}
@@ -2093,8 +2097,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/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/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..0215f3c 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);
@@ -876,6 +877,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 19d1abc..e6916cc 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -53,7 +53,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";
@@ -87,7 +87,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));
@@ -98,7 +98,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));
@@ -180,7 +180,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));
@@ -188,7 +188,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));
@@ -199,7 +199,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/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 2ae76b3..40fd022 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/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 6b79653..51cd1f9 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 017f403..41a322a 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -173,6 +173,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/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 ea24126..aeee3a8 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>
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 41e9aff..bc57ef7 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -105,6 +105,7 @@
mRTCPAddr = mRTPAddr;
mRTCPAddr.sin_port = htons(ntohs(mRTPAddr.sin_port) | 1);
+ mVPSBuf = NULL;
mSPSBuf = NULL;
mPPSBuf = NULL;
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/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/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index caee37d..7b19ac0 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -570,6 +570,9 @@
}
}
+// The LL-NDK API is now deprecated. New devices will no longer have the token
+// manager service installed, so createHalToken will return false and this
+// will return AMEDIA_ERROR_UNKNOWN on those devices.
media_status_t AImageReader::getWindowNativeHandle(native_handle **handle) {
if (mWindowHandle != nullptr) {
*handle = mWindowHandle;
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/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index b6dcaae..48a0a82 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -540,9 +540,11 @@
*
* @return AMEDIA_OK if the method call succeeds.
* AMEDIA_ERROR_INVALID_PARAMETER if reader or handle are NULL.
- * AMEDIA_ERROR_UNKNOWN if some other error is encountered.
+ * AMEDIA_ERROR_UNKNOWN if some other error is encountered or
+ * the device no longer has android.hidl.token service to
+ * satisfy the request because it is deprecated.
*/
-media_status_t AImageReader_getWindowNativeHandle(
+[[deprecated]] media_status_t AImageReader_getWindowNativeHandle(
AImageReader *reader, /* out */native_handle_t **handle);
#endif
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..be8b2b4 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
@@ -322,7 +322,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..306aeee 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,6 +21,7 @@
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
public class FrameReleaseQueue {
private static final String TAG = "FrameReleaseQueue";
@@ -28,7 +29,7 @@
private MediaCodec mCodec;
private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
private ReleaseThread mReleaseThread;
- private boolean doFrameRelease = false;
+ private AtomicBoolean doFrameRelease = new AtomicBoolean(false);
private boolean mRender = false;
private int mWaitTime = 40; // milliseconds per frame
private int mWaitTimeCorrection = 0;
@@ -49,19 +50,19 @@
private class ReleaseThread extends Thread {
public void run() {
- int nextReleaseTime = 0;
+ long nextReleaseTime = 0;
int loopCount = 0;
- while (doFrameRelease || mFrameInfoQueue.size() > 0) {
+ while (doFrameRelease.get() || mFrameInfoQueue.size() > 0) {
FrameInfo curFrameInfo = mFrameInfoQueue.peek();
if (curFrameInfo == null) {
nextReleaseTime += 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) {
+ } else if (!doFrameRelease.get() && mFrameInfoQueue.size() == 1) {
// EOS
Log.i(TAG, "EOS");
popAndRelease(curFrameInfo, false);
@@ -83,12 +84,13 @@
}
if (curFrameInfo != null && curFrameInfo.displayTime > curMediaTime) {
if ((curFrameInfo.displayTime - curMediaTime) < THRESHOLD_TIME) {
+ // release the frame now as we are already there
popAndRelease(curFrameInfo, true);
}
}
}
}
- int sleepTime = nextReleaseTime - getCurSysTime();
+ long sleepTime = nextReleaseTime - getCurSysTime();
if (sleepTime > 0) {
try {
mReleaseThread.sleep(sleepTime);
@@ -109,7 +111,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;
@@ -163,7 +165,7 @@
}
public void stopFrameRelease() {
- doFrameRelease = false;
+ doFrameRelease.set(false);
try {
mReleaseThread.join();
Log.i(TAG, "Joined frame release thread");
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..7663250 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -138,10 +138,57 @@
],
}
-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",
+ "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 +211,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 69a6faa..1198787 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1096,7 +1096,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
@@ -2094,6 +2094,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) {
@@ -3550,6 +3553,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::mLock held
void AudioFlinger::dumpToThreadLog_l(const sp<IAfThreadBase> &thread)
{
@@ -4275,7 +4314,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));
@@ -4283,24 +4322,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);
Mutex::Autolock _l(mLock);
- 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_l(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;
}
@@ -4437,6 +4494,49 @@
return status;
}
+
+// moveEffectChain_l must be called with both srcThread (if not null) and dstThread (if not null)
+// mLocks held
+status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId,
+ IAfRecordThread* srcThread, IAfRecordThread* dstThread)
+NO_THREAD_SAFETY_ANALYSIS // requires srcThread and dstThread locks
+{
+ 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)
{
@@ -4552,6 +4652,11 @@
bool AudioFlinger::updateOrphanEffectChains(const sp<IAfEffectModule>& effect)
{
Mutex::Autolock _l(mLock);
+ 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 ef36d50..9aefb6b 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -505,6 +505,9 @@
// used by IAfDeviceEffectManagerCallback, IAfPatchPanelCallback, IAfThreadCallback
audio_unique_id_t nextUniqueId(audio_unique_id_use_t use) final;
+ status_t moveEffectChain_l(audio_session_t sessionId,
+ IAfRecordThread* srcThread, IAfRecordThread* dstThread);
+
// return thread associated with primary hardware device, or NULL
DeviceTypeSet primaryOutputDevice_l() const;
@@ -536,6 +539,9 @@
std::vector< sp<IAfEffectModule> > purgeStaleEffects_l();
+ std::vector< sp<IAfEffectModule> > purgeOrphanEffectChains_l();
+ bool updateOrphanEffectChains_l(const sp<IAfEffectModule>& effect);
+
void broadcastParametersToRecordThreads_l(const String8& keyValuePairs);
void forwardParametersToDownstreamPatches_l(
audio_io_handle_t upStream, const String8& keyValuePairs,
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 6636717..588616d 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);
Mutex::Autolock _l(mLock);
- 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__);
Mutex::Autolock _l(mLock);
- 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);
+ Mutex::Autolock _l(mLock);
+ 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 @@
Mutex::Autolock _l(mLock);
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);
}
}
}
@@ -185,8 +215,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) {
@@ -197,7 +229,20 @@
size_t DeviceEffectManager::removeEffect(const sp<IAfDeviceEffectProxy>& effect)
{
Mutex::Autolock _l(mLock);
- 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 6111030..cb7fad1 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -73,6 +73,9 @@
void onCreateAudioPatch(audio_patch_handle_t handle,
const IAfPatchPanel::Patch& patch) final;
void onReleaseAudioPatch(audio_patch_handle_t handle) final;
+ void onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) final;
private:
status_t checkEffectCompatibility(const effect_descriptor_t *desc);
@@ -80,7 +83,7 @@
Mutex mLock;
const sp<IAfDeviceEffectManagerCallback> mAfDeviceEffectManagerCallback;
const sp<DeviceEffectManagerCallback> mMyCallback;
- std::map<AudioDeviceTypeAddr, sp<IAfDeviceEffectProxy>> mDeviceEffects;
+ std::map<AudioDeviceTypeAddr, std::vector<sp<IAfDeviceEffectProxy>>> mDeviceEffects;
};
class DeviceEffectManagerCallback : public EffectCallbackInterface {
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 21211ab..610ce26 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];
@@ -1008,8 +1009,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.
@@ -3302,6 +3304,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__);
+ Mutex::Autolock _l(mProxyLock);
+ 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;
@@ -3315,6 +3334,9 @@
}
if (status == NO_ERROR || status == ALREADY_EXISTS) {
Mutex::Autolock _l(mProxyLock);
+ 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,
@@ -3347,18 +3369,21 @@
if (mDescriptor.flags & EFFECT_FLAG_HW_ACC_TUNNEL) {
Mutex::Autolock _l(mProxyLock);
- 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();
@@ -3446,6 +3471,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) {
+ Mutex::Autolock _l(mProxyLock);
+ 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;
@@ -3512,7 +3554,7 @@
}
if (locked) {
- mLock.unlock();
+ mProxyLock.unlock();
}
}
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 82ad486..79b4e63 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -668,6 +668,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;
@@ -681,6 +684,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 7393448..c4f0de3 100644
--- a/services/audioflinger/IAfEffect.h
+++ b/services/audioflinger/IAfEffect.h
@@ -124,6 +124,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:
@@ -132,11 +137,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;
@@ -361,6 +361,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 be51d51..6ee1ec9 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -398,7 +398,8 @@
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
- bool isBitPerfect) = 0;
+ bool isBitPerfect,
+ audio_output_flags_t* afTrackFlags) = 0;
virtual status_t addTrack_l(const sp<IAfTrack>& track) = 0;
virtual bool destroyTrack_l(const sp<IAfTrack>& track) = 0;
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index 77bd45b..5dc2e8a 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -233,7 +233,17 @@
std::lock_guard _afl(mAfMelReporterCallback->mutex());
std::lock_guard _l(mLock);
- 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 78c6c0c..e26397c 100644
--- a/services/audioflinger/MelReporter.h
+++ b/services/audioflinger/MelReporter.h
@@ -84,6 +84,9 @@
void onCreateAudioPatch(audio_patch_handle_t handle,
const IAfPatchPanel::Patch& patch) final;
void onReleaseAudioPatch(audio_patch_handle_t handle) final;
+ void onUpdateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch) final;
/**
* 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 c3259f1..8d5eb9f 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()
NO_THREAD_SAFETY_ANALYSIS // bug in clang compiler.
{
@@ -104,6 +114,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;
@@ -145,6 +170,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 a312fb7..66018d7 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 */) {}
@@ -54,6 +58,9 @@
void createAudioPatch(audio_patch_handle_t handle, const IAfPatchPanel::Patch& patch);
void releaseAudioPatch(audio_patch_handle_t handle);
+ void updateAudioPatch(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch);
// Thread virtuals
void onFirstRef() override;
@@ -64,7 +71,9 @@
void createAudioPatchCommand(audio_patch_handle_t handle,
const IAfPatchPanel::Patch& patch);
void releaseAudioPatchCommand(audio_patch_handle_t handle);
-
+ void updateAudioPatchCommand(audio_patch_handle_t oldHandle,
+ audio_patch_handle_t newHandle,
+ const IAfPatchPanel::Patch& patch);
private:
class CommandData;
@@ -98,6 +107,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);
std::string mThreadName;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 400a793..c477c93 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);
}
}
@@ -436,7 +442,12 @@
*handle = static_cast<audio_patch_handle_t>(
mAfPatchPanelCallback->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH));
newPatch.mHalHandle = halHandle;
- mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(*handle, newPatch);
+ if (reuseExistingHalPatch) {
+ mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
+ oldhandle, *handle, newPatch);
+ } else {
+ mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(*handle, newPatch);
+ }
if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
addSoftwarePatchToInsertedModules(insertedModule, *handle, &newPatch.mAudioPatch);
}
@@ -792,10 +803,12 @@
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 b8b7b79..a95c601 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -75,7 +75,38 @@
audio_module_handle_t module, audio_patch_handle_t handle,
const struct audio_patch *patch);
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 5f54e11..3e5501f 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -62,8 +62,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 194f0f0..157764f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2378,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;
@@ -2696,6 +2697,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,
@@ -10734,14 +10736,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;
@@ -10798,8 +10810,8 @@
void MmapPlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
Mutex::Autolock _l(mLock);
+ mStreamTypes[stream].volume = value;
if (stream == mStreamType) {
- mStreamVolume = value;
broadcast_l();
}
}
@@ -10807,17 +10819,14 @@
float MmapPlaybackThread::streamVolume(audio_stream_type_t stream) const
{
Mutex::Autolock _l(mLock);
- if (stream == mStreamType) {
- return mStreamVolume;
- }
- return 0.0f;
+ return mStreamTypes[stream].volume;
}
void MmapPlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
Mutex::Autolock _l(mLock);
+ mStreamTypes[stream].mute = muted;
if (stream == mStreamType) {
- mStreamMute= muted;
broadcast_l();
}
}
@@ -10857,14 +10866,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));
@@ -10898,8 +10906,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*/,
@@ -11012,7 +11020,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 c6c585b..18b6840 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -935,7 +935,8 @@
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
bool isSpatialized,
- bool isBitPerfect) final;
+ bool isBitPerfect,
+ audio_output_flags_t* afTrackFlags) final;
bool isTrackActive(const sp<IAfTrack>& track) const final {
return mActiveTracks.indexOf(track) >= 0;
@@ -2220,12 +2221,17 @@
protected:
void dumpInternals_l(int fd, const Vector<String16>& args) final;
+ float streamVolume_l() const {
+ return mStreamTypes[mStreamType].volume;
+ }
+ bool streamMuted_l() const {
+ return mStreamTypes[mStreamType].mute;
+ }
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
audio_stream_type_t mStreamType;
float mMasterVolume;
- float mStreamVolume;
bool mMasterMute;
- bool mStreamMute;
AudioStreamOut* mOutput;
mediautils::atomic_sp<audio_utils::MelProcessor> mMelProcessor;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index ecea9eb..e93046e 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).string(), 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).string());
sp<OpPlayAudioMonitor> monitor = mMonitor.promote();
if (monitor != NULL) {
monitor->checkPlayAudioForUsage(/*doBroadcast=*/true);
@@ -1640,22 +1638,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/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 097ce50..39c80d8 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -498,8 +498,7 @@
switch (device) {
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
- // TODO(b/278265907): enable A2DP when we can distinguish A2DP headsets
- // case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
case AUDIO_DEVICE_OUT_USB_HEADSET:
case AUDIO_DEVICE_OUT_BLE_HEADSET:
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/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..c42f9c3 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -289,12 +289,20 @@
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 (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
+ 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 && !mixDevice->isMmap()) {
+ ALOGD("%s: Rejecting MMAP_NOIRQ request matched to dynamic audio policy "
+ "mix pointing to device %s which doesn't support mmap", __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/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 972e6c5..b907305 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -337,7 +337,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 &&
@@ -459,9 +459,9 @@
status_t AudioPolicyManager::deviceToAudioPort(audio_devices_t device, const char* device_address,
const char* device_name,
media::AudioPortFw* aidlPort) {
- DeviceDescriptorBase devDescr(device, device_address);
- devDescr.setName(device_name);
- return devDescr.writeToParcelable(aidlPort);
+ const auto devDescr = sp<DeviceDescriptorBase>::make(device, device_address);
+ devDescr->setName(device_name);
+ return devDescr->writeToParcelable(aidlPort);
}
void AudioPolicyManager::setEngineDeviceConnectionState(const sp<DeviceDescriptor> device,
@@ -728,7 +728,7 @@
// 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);
+ 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
@@ -888,7 +888,7 @@
disconnectTelephonyAudioSource(mCallRxSourceClient);
disconnectTelephonyAudioSource(mCallTxSourceClient);
}
- setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
+ setOutputDevices(__func__, mPrimaryOutput, rxDevices, force, 0);
}
}
@@ -906,7 +906,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 +1325,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 +2339,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 +2423,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 +2525,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 +2548,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 +2651,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 +2781,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 +2808,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 +3119,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 +3481,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;
}
@@ -3938,8 +3952,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 +4941,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 +5200,7 @@
return BAD_VALUE;
}
- setOutputDevices(outputDesc,
+ setOutputDevices(__func__, outputDesc,
getNewOutputDevices(outputDesc, true /*fromCache*/),
true,
0,
@@ -5239,7 +5254,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 +5391,7 @@
outputsToReopen.emplace(mOutputs.keyAt(j), newDevices);
continue;
}
- setOutputDevices(outputDesc, newDevices, false);
+ setOutputDevices(__func__, outputDesc, newDevices, false);
}
}
reopenOutputsWithDevices(outputsToReopen);
@@ -6258,7 +6273,7 @@
outputDesc->close();
} else {
addOutput(output, outputDesc);
- setOutputDevices(outputDesc,
+ setOutputDevices(__func__, outputDesc,
DeviceVector(supportedDevice),
true,
0,
@@ -6458,8 +6473,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 +6769,7 @@
mpClientInterface->onAudioPatchListUpdate();
}
+ mEffects.putOrphanEffectsForIo(input);
inputDesc->close();
mInputs.removeItem(input);
@@ -7342,7 +7358,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 +7411,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 +7421,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 +7439,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 +7450,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 +7461,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 +7475,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()) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 863c785..509cc79 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -526,6 +526,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 +540,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,
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..a7b2a56 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>
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 041aa1c..4133526 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>
@@ -216,27 +216,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 +306,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 +1168,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;
@@ -1900,113 +1868,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..94b48ea 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,
@@ -401,7 +405,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 +545,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 +736,7 @@
wp<AudioPolicyService> mService;
};
+ private:
class AudioPolicyClient : public AudioPolicyClientInterface
{
public:
@@ -906,6 +911,7 @@
bool mAudioVolumeGroupCallbacksEnabled;
};
+ public:
class AudioClient : public virtual RefBase {
public:
AudioClient(const audio_attributes_t attributes,
@@ -927,82 +933,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..110e6bf 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,6 +1975,26 @@
&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,
@@ -2027,9 +2058,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 +2244,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 +2994,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 +3015,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 +3026,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 +3052,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 +3065,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 +3076,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 +3113,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..ea467e7 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -35,9 +35,93 @@
],
}
-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",
+ "media_permission-aidl-cpp",
+ "server_configurable_flags",
+ ],
+
+ 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",
+ "camera_platform_flags_c_lib",
+ "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 +189,7 @@
"utils/CameraThreadState.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
+ "utils/SchedulingPolicyUtils.cpp",
"utils/SessionConfigurationUtils.cpp",
"utils/SessionConfigurationUtilsHidl.cpp",
"utils/SessionStatsBuilder.cpp",
@@ -119,73 +204,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 da64b8d..c0194b9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -89,6 +89,7 @@
const char* kActivityServiceName = "activity";
const char* kSensorPrivacyServiceName = "sensor_privacy";
const char* kAppopsServiceName = "appops";
+ const char* kProcessInfoServiceName = "processinfo";
}; // namespace anonymous
namespace android {
@@ -318,7 +319,8 @@
std::vector<std::string> remappedCameraIds =
findOriginalIdsForRemappedCameraId(cameraId, i->getListenerUid());
for (auto& remappedCameraId : remappedCameraIds) {
- ret = i->getListener()->onTorchStatusChanged(mapToInterface(status), remappedCameraId);
+ ret = i->getListener()->onTorchStatusChanged(mapToInterface(status),
+ std::string(remappedCameraId));
i->handleBinderStatus(ret, "%s: Failed to trigger onTorchStatusChanged for %d:%d: %d",
__FUNCTION__, i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
}
@@ -694,17 +696,100 @@
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 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;
}
@@ -712,7 +797,7 @@
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 +823,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 +848,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());
@@ -860,7 +943,7 @@
}
std::string CameraService::resolveCameraId(const std::string& inputCameraId) {
- return resolveCameraId(inputCameraId, std::string(""));
+ return resolveCameraId(inputCameraId, "");
}
std::string CameraService::resolveCameraId(
@@ -875,7 +958,7 @@
if (auto packageMapIter = mCameraIdRemapping.find(packageNameVal);
packageMapIter != mCameraIdRemapping.end()) {
ALOGI("%s: resolveCameraId: packageName found %s",
- __FUNCTION__, packageNameVal.c_str());
+ __FUNCTION__, std::string(packageNameVal).c_str());
auto packageMap = packageMapIter->second;
if (auto replacementIdIter = packageMap.find(inputCameraId);
replacementIdIter != packageMap.end()) {
@@ -903,9 +986,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;
@@ -934,13 +1016,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;
}
@@ -1013,13 +1094,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) {
@@ -1048,10 +1127,11 @@
return ret;
}
-Status CameraService::getTorchStrengthLevel(const std::string& cameraId,
+Status CameraService::getTorchStrengthLevel(const std::string& unresolvedCameraId,
int32_t* torchStrength) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
+ const std::string cameraId = resolveCameraId(unresolvedCameraId);
if (!mInitialized) {
ALOGE("%s: Camera HAL couldn't be initialized.", __FUNCTION__);
return STATUS_ERROR(ERROR_DISCONNECTED, "Camera HAL couldn't be initialized.");
@@ -1170,7 +1250,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.
@@ -1457,7 +1538,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();
@@ -1509,9 +1589,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);
@@ -1532,8 +1611,13 @@
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 "
@@ -1651,33 +1735,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) {
@@ -1687,16 +1744,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();
@@ -1872,7 +1970,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;
@@ -1912,7 +2010,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;
@@ -1960,16 +2058,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());
@@ -1980,10 +2082,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;
}
@@ -2056,6 +2158,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
@@ -2063,8 +2167,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;
@@ -2261,32 +2363,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) {
@@ -2423,8 +2531,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();
@@ -2552,8 +2659,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);
@@ -2896,13 +3002,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,
@@ -2950,13 +3054,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);
@@ -3167,9 +3271,7 @@
Status CameraService::isHiddenPhysicalCamera(const std::string& unresolvedCameraId,
/*out*/ bool *isSupported) {
ATRACE_CALL();
-
const std::string cameraId = resolveCameraId(unresolvedCameraId);
-
ALOGV("%s: for camera ID = %s", __FUNCTION__, cameraId.c_str());
*isSupported = mCameraProviderManager->isHiddenPhysicalCamera(cameraId);
@@ -5100,6 +5202,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);
@@ -5996,8 +6099,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 647b2ef..48954ca 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>
@@ -617,6 +618,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 +641,22 @@
}
/**
+ * 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;
+
+ /**
* Typesafe version of device status, containing both the HAL-layer and the service interface-
* layer values.
*/
@@ -892,7 +916,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)
@@ -1423,11 +1447,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/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 3761f75..939f969 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -439,7 +439,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 +465,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");
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 68bd958..86a94e2 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -371,7 +371,7 @@
int mVideoStabilizationMode = -1;
// 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/aidl/AidlProviderInfo.cpp b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
index 5e79d6b..9659db8 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
@@ -607,8 +607,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..aeb0abc 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
@@ -743,8 +743,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 b653094..597b9aa 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -62,9 +62,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"
@@ -2628,8 +2628,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);
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/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/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/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index ea76bcd..5e3e654 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;
@@ -654,6 +656,10 @@
}
AStatsEvent_writeInt32(event, componentColorFormat);
+ uid_t app_uid = item->getUid();
+ metrics_proto.set_caller_uid(app_uid);
+ AStatsEvent_writeInt32(event, app_uid);
+
int64_t firstRenderTimeUs = -1;
item->getInt64("android.media.mediacodec.first-render-time-us", &firstRenderTimeUs);
int64_t framesReleased = -1;
@@ -854,6 +860,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/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/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
index 90f1731..acfaf8a 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,11 @@
int main() {
ALOGD("Tuner service starting");
+ if (!property_get_bool("tuner.server.enable", false)) {
+ ALOGD("tuner is not enabled, terminating");
+ return 0;
+ }
+
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
hardware::configureRpcThreadpool(16, true);