Merge "aaudio: prevent busy loop on shared stream standby" into main
diff --git a/aidl/com/android/media/permission/INativePermissionController.aidl b/aidl/com/android/media/permission/INativePermissionController.aidl
index 5766e33..a14092d 100644
--- a/aidl/com/android/media/permission/INativePermissionController.aidl
+++ b/aidl/com/android/media/permission/INativePermissionController.aidl
@@ -16,6 +16,7 @@
package com.android.media.permission;
+import com.android.media.permission.PermissionEnum;
import com.android.media.permission.UidPackageState;
/**
@@ -33,4 +34,13 @@
* If the list is empty, the package no longer exists.
*/
void updatePackagesForUid(in UidPackageState newPackageState);
+ /**
+ * Populate or replace the list of uids which holds a particular permission.
+ * Runtime permissions will need additional checks, and should not use the cache as-is.
+ * Not virtual device aware.
+ * Is is possible for updates to the permission state to be delayed during high traffic.
+ * @param perm - Enum representing the permission for which holders are being supplied
+ * @param uids - Uids (not app-ids) which hold the permission. Should be sorted
+ */
+ void populatePermissionState(in PermissionEnum perm, in int[] uids);
}
diff --git a/aidl/com/android/media/permission/PermissionEnum.aidl b/aidl/com/android/media/permission/PermissionEnum.aidl
new file mode 100644
index 0000000..834a275
--- /dev/null
+++ b/aidl/com/android/media/permission/PermissionEnum.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.permission;
+
+/**
+ * Enumerates permissions which are tracked/pushed by NativePermissionController
+ * {@hide}
+ */
+enum PermissionEnum {
+ MODIFY_AUDIO_ROUTING = 0,
+ MODIFY_PHONE_STATE = 1,
+ CALL_AUDIO_INTERCEPTION = 2,
+ // This is a runtime + WIU permission, which means data delivery should be protected by AppOps
+ // We query the controller only for early fails/hard errors
+ RECORD_AUDIO = 3,
+ ENUM_SIZE = 4, // Not for actual usage
+}
diff --git a/aidl/com/android/media/permission/UidPackageState.aidl b/aidl/com/android/media/permission/UidPackageState.aidl
index 41b8c65..747a7ef 100644
--- a/aidl/com/android/media/permission/UidPackageState.aidl
+++ b/aidl/com/android/media/permission/UidPackageState.aidl
@@ -20,6 +20,7 @@
* Entity representing the package names associated with a particular uid/app-id
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable UidPackageState {
int uid;
@utf8InCpp List<String> packageNames;
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index cba26da..645a3d4 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2429,29 +2429,33 @@
*
* <p>Flash strength level to use in capture mode i.e. when the applications control
* flash with either SINGLE or TORCH mode.</p>
- * <p>Use android.flash.info.singleStrengthMaxLevel and
- * android.flash.info.torchStrengthMaxLevel to check whether the device supports
+ * <p>Use ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL and
+ * ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL to check whether the device supports
* flash strength control or not.
* If the values of android.flash.info.singleStrengthMaxLevel and
- * android.flash.info.torchStrengthMaxLevel are greater than 1,
+ * ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL are greater than 1,
* then the device supports manual flash strength control.</p>
* <p>If the ACAMERA_FLASH_MODE <code>==</code> TORCH the value must be >= 1
- * and <= android.flash.info.torchStrengthMaxLevel.
+ * and <= ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL.
* If the application doesn't set the key and
- * android.flash.info.torchStrengthMaxLevel > 1,
+ * ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL > 1,
* then the flash will be fired at the default level set by HAL in
- * android.flash.info.torchStrengthDefaultLevel.
+ * ACAMERA_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL.
* If the ACAMERA_FLASH_MODE <code>==</code> SINGLE, then the value must be >= 1
- * and <= android.flash.info.singleStrengthMaxLevel.
+ * and <= ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL.
* If the application does not set this key and
- * android.flash.info.singleStrengthMaxLevel > 1,
+ * ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL > 1,
* then the flash will be fired at the default level set by HAL
- * in android.flash.info.singleStrengthDefaultLevel.
+ * in ACAMERA_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL.
* If ACAMERA_CONTROL_AE_MODE is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
* ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
*
* @see ACAMERA_CONTROL_AE_MODE
* @see ACAMERA_FLASH_MODE
+ * @see ACAMERA_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL
+ * @see ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL
+ * @see ACAMERA_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL
+ * @see ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL
*/
ACAMERA_FLASH_STRENGTH_LEVEL = // int32
ACAMERA_FLASH_START + 6,
@@ -2487,9 +2491,11 @@
* </ul></p>
*
* <p>If flash unit is available this will be greater than or equal to 1 and less
- * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>.
+ * or equal to ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL.
* Note for devices that do not support the manual flash strength control
* feature, this level will always be equal to 1.</p>
+ *
+ * @see ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL
*/
ACAMERA_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL = // int32
ACAMERA_FLASH_START + 8,
@@ -2509,12 +2515,14 @@
* otherwise the value will be equal to 1.</p>
* <p>Note that this level is just a number of supported levels(the granularity of control).
* There is no actual physical power units tied to this level.
- * There is no relation between android.flash.info.torchStrengthMaxLevel and
- * android.flash.info.singleStrengthMaxLevel i.e. the ratio of
- * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel
+ * There is no relation between ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL and
+ * ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL i.e. the ratio of
+ * ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL:ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL
* is not guaranteed to be the ratio of actual brightness.</p>
*
* @see ACAMERA_FLASH_MODE
+ * @see ACAMERA_FLASH_SINGLE_STRENGTH_MAX_LEVEL
+ * @see ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL
*/
ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL = // int32
ACAMERA_FLASH_START + 9,
@@ -2529,9 +2537,11 @@
* </ul></p>
*
* <p>If flash unit is available this will be greater than or equal to 1 and less
- * or equal to android.flash.info.torchStrengthMaxLevel.
+ * or equal to ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL.
* Note for the devices that do not support the manual flash strength control feature,
* this level will always be equal to 1.</p>
+ *
+ * @see ACAMERA_FLASH_TORCH_STRENGTH_MAX_LEVEL
*/
ACAMERA_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL = // int32
ACAMERA_FLASH_START + 10,
diff --git a/camera/tests/fuzzer/camera_fuzzer.cpp b/camera/tests/fuzzer/camera_fuzzer.cpp
index 0812096..b0f59f1 100644
--- a/camera/tests/fuzzer/camera_fuzzer.cpp
+++ b/camera/tests/fuzzer/camera_fuzzer.cpp
@@ -89,6 +89,7 @@
bool initCamera();
void invokeCamera();
void invokeSetParameters();
+ native_handle_t* createNativeHandle();
sp<Camera> mCamera = nullptr;
FuzzedDataProvider* mFDP = nullptr;
@@ -103,6 +104,18 @@
};
};
+native_handle_t* CameraFuzzer::createNativeHandle() {
+ int32_t numFds = mFDP->ConsumeIntegralInRange<int32_t>(kMinElements, kMaxElements);
+ int32_t numInts = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
+ native_handle_t* handle = native_handle_create(numFds, numInts);
+ for (int32_t i = 0; i < numFds; ++i) {
+ std::string filename = mFDP->ConsumeRandomLengthString(kMaxBytes);
+ int32_t fd = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC);
+ handle->data[i] = fd;
+ }
+ return handle;
+}
+
bool CameraFuzzer::initCamera() {
ProcessState::self()->startThreadPool();
sp<IServiceManager> sm = defaultServiceManager();
@@ -291,15 +304,11 @@
},
[&]() {
int64_t timestamp = mFDP->ConsumeIntegral<int64_t>();
- int32_t numFds = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- int32_t numInts = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- native_handle_t* handle = native_handle_create(numFds, numInts);
+ native_handle_t* handle = createNativeHandle();
mCamera->recordingFrameHandleCallbackTimestamp(timestamp, handle);
},
[&]() {
- int32_t numFds = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- int32_t numInts = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- native_handle_t* handle = native_handle_create(numFds, numInts);
+ native_handle_t* handle = createNativeHandle();
mCamera->releaseRecordingFrameHandle(handle);
},
[&]() { mCamera->releaseRecordingFrame(iMem); },
@@ -308,9 +317,7 @@
for (int8_t i = 0;
i < mFDP->ConsumeIntegralInRange<int8_t>(kMinElements, kMaxElements);
++i) {
- int32_t numFds = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- int32_t numInts = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- native_handle_t* handle = native_handle_create(numFds, numInts);
+ native_handle_t* handle = createNativeHandle();
handles.push_back(handle);
}
mCamera->releaseRecordingFrameHandleBatch(handles);
@@ -320,9 +327,7 @@
for (int8_t i = 0;
i < mFDP->ConsumeIntegralInRange<int8_t>(kMinElements, kMaxElements);
++i) {
- int32_t numFds = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- int32_t numInts = mFDP->ConsumeIntegralInRange<int32_t>(kNumMin, kNumMax);
- native_handle_t* handle = native_handle_create(numFds, numInts);
+ native_handle_t* handle = createNativeHandle();
handles.push_back(handle);
}
std::vector<nsecs_t> timestamps;
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index 9f64a28..d662585 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -44,6 +44,16 @@
}
flag {
+ name: "input_surface_throttle"
+ namespace: "codec_fwk"
+ description: "Bugfix flag for input surface throttle"
+ bug: "342269852"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "large_audio_frame_finish"
namespace: "codec_fwk"
description: "Implementation flag for large audio frame finishing tasks"
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
index ec45e2f..de8aca7 100644
--- a/media/audio/aconfig/Android.bp
+++ b/media/audio/aconfig/Android.bp
@@ -118,6 +118,13 @@
visibility: ["//frameworks/base/api"],
}
+aconfig_declarations {
+ name: "android.media.soundtrigger-aconfig",
+ package: "android.media.soundtrigger",
+ container: "system",
+ srcs: ["soundtrigger.aconfig"],
+}
+
java_aconfig_library {
name: "android.media.audio-aconfig-java",
aconfig_declarations: "android.media.audio-aconfig",
@@ -141,6 +148,12 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.media.soundtrigger-aconfig-java",
+ aconfig_declarations: "android.media.soundtrigger-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
cc_aconfig_library {
name: "android.media.audiopolicy-aconfig-cc",
aconfig_declarations: "android.media.audiopolicy-aconfig",
@@ -153,5 +166,6 @@
"android.media.audio-aconfig-java",
"android.media.audiopolicy-aconfig-java",
"android.media.midi-aconfig-java",
+ "android.media.soundtrigger-aconfig-java",
],
}
diff --git a/media/audio/aconfig/OWNERS b/media/audio/aconfig/OWNERS
new file mode 100644
index 0000000..fb1e866
--- /dev/null
+++ b/media/audio/aconfig/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 48436
+atneya@google.com
+elaurent@google.com
+include platform/frameworks/av:/media/janitors/audio_OWNERS #{LAST_RESORT_SUGGESTION}
diff --git a/media/audio/aconfig/aaudio.aconfig b/media/audio/aconfig/aaudio.aconfig
index c160109..f9fb4c7 100644
--- a/media/audio/aconfig/aaudio.aconfig
+++ b/media/audio/aconfig/aaudio.aconfig
@@ -11,3 +11,10 @@
description: "Enable the AAudio sample rate converter."
bug: "219533889"
}
+
+flag {
+ name: "start_stop_client_from_command_thread"
+ namespace: "media_audio"
+ description: "Start or stop client from command thread."
+ bug: "341627085"
+}
diff --git a/media/audio/aconfig/audio.aconfig b/media/audio/aconfig/audio.aconfig
index c642a94..4c3e25a 100644
--- a/media/audio/aconfig/audio.aconfig
+++ b/media/audio/aconfig/audio.aconfig
@@ -68,6 +68,14 @@
}
flag {
+ name: "replace_stream_bt_sco"
+ namespace: "media_audio"
+ description:
+ "Replace internally STREAM_BLUETOOTH_SCO with STREAM_VOICE_CALL"
+ bug: "345024266"
+}
+
+flag {
name: "ringer_mode_affects_alarm"
namespace: "media_audio"
description:
diff --git a/media/audio/aconfig/audioserver.aconfig b/media/audio/aconfig/audioserver.aconfig
index 96e2b75..94b60e2 100644
--- a/media/audio/aconfig/audioserver.aconfig
+++ b/media/audio/aconfig/audioserver.aconfig
@@ -39,3 +39,12 @@
"This feature helps reduce audio glitching caused by low priority blocking threads."
bug: "209491695"
}
+
+flag {
+ name: "fix_input_sharing_logic"
+ namespace: "media_audio"
+ description:
+ "Fix the audio policy logic that decides to reuse or close "
+ "input streams when resources are exhausted"
+ bug: "338446410"
+}
diff --git a/media/audio/aconfig/soundtrigger.aconfig b/media/audio/aconfig/soundtrigger.aconfig
new file mode 100644
index 0000000..e61e6a3
--- /dev/null
+++ b/media/audio/aconfig/soundtrigger.aconfig
@@ -0,0 +1,14 @@
+# Flags for sound trigger
+#
+# Please add flags in alphabetical order.
+
+package: "android.media.soundtrigger"
+container: "system"
+
+flag {
+ name: "sound_trigger_generic_model_api"
+ is_exported: true
+ namespace: "soundtrigger"
+ description: "Feature flag for adding GenericSoundModel to SystemApi"
+ bug: "339267254"
+}
diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp
index e74fb91..ac2fcbe 100644
--- a/media/audioserver/Android.bp
+++ b/media/audioserver/Android.bp
@@ -21,6 +21,7 @@
],
header_libs: [
+ "audiopolicyservicelocal_headers",
"libaudiohal_headers",
"libmedia_headers",
"libmediametrics_headers",
diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp
index 55847f4..5d7daa4 100644
--- a/media/audioserver/main_audioserver.cpp
+++ b/media/audioserver/main_audioserver.cpp
@@ -168,6 +168,7 @@
ALOGW_IF(AudioSystem::setLocalAudioFlinger(af) != OK,
"%s: AudioSystem already has an AudioFlinger instance!", __func__);
const auto aps = sp<AudioPolicyService>::make();
+ af->initAudioPolicyLocal(aps);
ALOGD("%s: AudioPolicy created", __func__);
ALOGW_IF(AudioSystem::setLocalAudioPolicyService(aps) != OK,
"%s: AudioSystem already has an AudioPolicyService instance!", __func__);
diff --git a/media/codec2/components/avc/Android.bp b/media/codec2/components/avc/Android.bp
index a7ae85b..8ccb9ac 100644
--- a/media/codec2/components/avc/Android.bp
+++ b/media/codec2/components/avc/Android.bp
@@ -17,6 +17,10 @@
static_libs: ["libavcdec"],
+ cflags: [
+ "-DKEEP_THREADS_ACTIVE=1",
+ ],
+
srcs: ["C2SoftAvcDec.cpp"],
export_include_dirs: ["."],
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 3385b95..77fdeb9 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -16,6 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2SoftAvcDec"
+#ifndef KEEP_THREADS_ACTIVE
+#define KEEP_THREADS_ACTIVE 0
+#endif
#include <log/log.h>
#include <media/stagefright/foundation/MediaDefs.h>
@@ -416,7 +419,7 @@
ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
- s_create_ip.u4_keep_threads_active = 1;
+ s_create_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat;
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
index 7b63e75..780660e 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.cpp
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -155,7 +155,7 @@
mSignalledError = false;
mSignalledOutputEos = false;
mIsFirstFrame = true;
- mAnchorTimeStamp = 0ull;
+ mAnchorTimeStamp = 0;
mProcessedSamples = 0u;
mEncoderWriteData = false;
mEncoderReturnedNbBytes = 0;
@@ -186,7 +186,7 @@
mSignalledError = false;
mSignalledOutputEos = false;
mIsFirstFrame = true;
- mAnchorTimeStamp = 0ull;
+ mAnchorTimeStamp = 0;
mProcessedSamples = 0u;
mEncoderWriteData = false;
mEncoderReturnedNbBytes = 0;
@@ -236,7 +236,7 @@
inSize, (int)work->input.ordinal.timestamp.peeku(),
(int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
if (mIsFirstFrame && inSize) {
- mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekll();
mIsFirstFrame = false;
}
@@ -405,7 +405,7 @@
C2WriteView wView = mOutputBlock->map().get();
uint8_t* outData = wView.data();
const uint32_t sampleRate = mIntf->getSampleRate();
- const uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate;
+ const int64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate;
ALOGV("writing %zu bytes of encoded data on output", bytes);
// increment mProcessedSamples to maintain audio synchronization during
// play back
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.h b/media/codec2/components/flac/C2SoftFlacEnc.h
index 1f3be3c..ed9c298 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.h
+++ b/media/codec2/components/flac/C2SoftFlacEnc.h
@@ -72,7 +72,7 @@
bool mSignalledOutputEos;
uint32_t mBlockSize;
bool mIsFirstFrame;
- uint64_t mAnchorTimeStamp;
+ int64_t mAnchorTimeStamp;
uint64_t mProcessedSamples;
// should the data received by the callback be written to the output port
bool mEncoderWriteData;
diff --git a/media/codec2/components/hevc/Android.bp b/media/codec2/components/hevc/Android.bp
index d1388b9..cb9c2ae 100644
--- a/media/codec2/components/hevc/Android.bp
+++ b/media/codec2/components/hevc/Android.bp
@@ -15,6 +15,10 @@
"libcodec2_soft_sanitize_cfi-defaults",
],
+ cflags: [
+ "-DKEEP_THREADS_ACTIVE=1",
+ ],
+
srcs: ["C2SoftHevcDec.cpp"],
static_libs: ["libhevcdec"],
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 81db2a1..64aa7a4 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -16,6 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2SoftHevcDec"
+#ifndef KEEP_THREADS_ACTIVE
+#define KEEP_THREADS_ACTIVE 0
+#endif
#include <log/log.h>
#include <media/stagefright/foundation/MediaDefs.h>
@@ -407,7 +410,7 @@
ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
- s_create_ip.u4_keep_threads_active = 1;
+ s_create_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
diff --git a/media/codec2/components/mpeg2/Android.bp b/media/codec2/components/mpeg2/Android.bp
index a58044c..e644ee3 100644
--- a/media/codec2/components/mpeg2/Android.bp
+++ b/media/codec2/components/mpeg2/Android.bp
@@ -14,6 +14,10 @@
"libcodec2_soft_sanitize_signed-defaults",
],
+ cflags: [
+ "-DKEEP_THREADS_ACTIVE=0",
+ ],
+
srcs: ["C2SoftMpeg2Dec.cpp"],
static_libs: ["libmpeg2dec"],
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 491098d..562dcf5 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -16,6 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2SoftMpeg2Dec"
+#ifndef KEEP_THREADS_ACTIVE
+#define KEEP_THREADS_ACTIVE 0
+#endif
#include <log/log.h>
#include <media/stagefright/foundation/MediaDefs.h>
@@ -433,7 +436,7 @@
s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
s_fill_mem_ip.u4_share_disp_buf = 0;
- s_fill_mem_ip.u4_keep_threads_active = 1;
+ s_fill_mem_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
s_fill_mem_ip.e_output_format = mIvColorformat;
s_fill_mem_ip.u4_deinterlace = 1;
s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
@@ -475,7 +478,7 @@
s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
s_init_ip.u4_share_disp_buf = 0;
s_init_ip.u4_deinterlace = 1;
- s_init_ip.u4_keep_threads_active = 1;
+ s_init_ip.u4_keep_threads_active = KEEP_THREADS_ACTIVE;
s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 08e2fa6..3e88acd 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -465,6 +465,7 @@
mTemporalPatternIdx(0),
mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
mSignalledOutputEos(false),
+ mHeaderGenerated(false),
mSignalledError(false) {
for (int i = 0; i < MAXTEMPORALLAYERS; i++) {
mTemporalLayerBitrateRatio[i] = 1.0f;
@@ -494,6 +495,7 @@
// this one is not allocated by us
mCodecInterface = nullptr;
+ mHeaderGenerated = false;
}
c2_status_t C2SoftVpxEnc::onStop() {
@@ -558,6 +560,7 @@
(uint32_t)mBitrateControlMode, mTemporalLayers, mIntf->getSyncFramePeriod(),
mMinQuantizer, mMaxQuantizer);
+ mHeaderGenerated = false;
mCodecConfiguration = new vpx_codec_enc_cfg_t;
if (!mCodecConfiguration) goto CleanUp;
codec_return = vpx_codec_enc_config_default(mCodecInterface,
@@ -873,6 +876,27 @@
return;
}
+ // Header generation is limited to Android V and above, as MediaMuxer did not handle
+ // CSD for VP9 correctly in Android U and before.
+ if (isAtLeastV() && !mHeaderGenerated) {
+ vpx_fixed_buf_t* codec_private_data = vpx_codec_get_global_headers(mCodecContext);
+ if (codec_private_data) {
+ std::unique_ptr<C2StreamInitDataInfo::output> csd =
+ C2StreamInitDataInfo::output::AllocUnique(codec_private_data->sz, 0u);
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ work->workletsProcessed = 1u;
+ return;
+ }
+ memcpy(csd->m.value, codec_private_data->buf, codec_private_data->sz);
+ work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+ ALOGV("CSD Produced of size %zu bytes", codec_private_data->sz);
+ }
+ mHeaderGenerated = true;
+ }
+
const C2ConstGraphicBlock inBuffer =
inputBuffer->data().graphicBlocks().front();
if (inBuffer.width() < mSize->width ||
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 980de04..87d24f9 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -207,6 +207,9 @@
// Signalled EOS
bool mSignalledOutputEos;
+ // Header generated
+ bool mHeaderGenerated;
+
// Signalled Error
bool mSignalledError;
diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp
index 7d5740b..c205dcd 100644
--- a/media/codec2/core/Android.bp
+++ b/media/codec2/core/Android.bp
@@ -26,9 +26,6 @@
"//apex_available:platform",
"com.android.media.swcodec",
],
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: ["C2.cpp"],
diff --git a/media/codec2/hal/client/Android.bp b/media/codec2/hal/client/Android.bp
index af6f4ae..864eeb8 100644
--- a/media/codec2/hal/client/Android.bp
+++ b/media/codec2/hal/client/Android.bp
@@ -33,6 +33,13 @@
"libcodec2-aidl-client-defaults",
],
+ // http://b/343951602#comment4 Explicitly set cpp_std to gnu++20. The
+ // default inherited from libcodec2-impl-defaults sets it to gnu++17 which
+ // causes a segfault when mixing global std::string symbols built with
+ // gnu++17 and gnu++20. TODO(b/343951602): clean this after
+ // libcodec2-impl-defaults opt into gnu++17 is removed.
+ cpp_std: "gnu++20",
+
header_libs: [
"libcodec2_internal", // private
],
diff --git a/media/codec2/sfplugin/C2AidlNode.cpp b/media/codec2/sfplugin/C2AidlNode.cpp
index 93c9d8b..4e46ad6 100644
--- a/media/codec2/sfplugin/C2AidlNode.cpp
+++ b/media/codec2/sfplugin/C2AidlNode.cpp
@@ -68,10 +68,15 @@
}
::ndk::ScopedAStatus C2AidlNode::submitBuffer(
- int32_t buffer, const ::aidl::android::hardware::HardwareBuffer& hBuffer,
+ int32_t buffer,
+ const std::optional<::aidl::android::hardware::HardwareBuffer>& hBuffer,
int32_t flags, int64_t timestamp, const ::ndk::ScopedFileDescriptor& fence) {
sp<GraphicBuffer> gBuf;
- AHardwareBuffer *ahwb = hBuffer.get();
+ AHardwareBuffer *ahwb = nullptr;
+ if (hBuffer.has_value()) {
+ ahwb = hBuffer.value().get();
+ }
+
if (ahwb) {
gBuf = AHardwareBuffer_to_GraphicBuffer(ahwb);
}
@@ -105,6 +110,10 @@
return mImpl->onInputBufferDone(index);
}
+void C2AidlNode::onInputBufferEmptied() {
+ return mImpl->onInputBufferEmptied();
+}
+
android_dataspace C2AidlNode::getDataspace() {
return mImpl->getDataspace();
}
diff --git a/media/codec2/sfplugin/C2AidlNode.h b/media/codec2/sfplugin/C2AidlNode.h
index 365a41d..95290fd 100644
--- a/media/codec2/sfplugin/C2AidlNode.h
+++ b/media/codec2/sfplugin/C2AidlNode.h
@@ -49,7 +49,7 @@
::ndk::ScopedAStatus submitBuffer(
int32_t buffer,
- const ::aidl::android::hardware::HardwareBuffer& hBuffer,
+ const std::optional<::aidl::android::hardware::HardwareBuffer>& hBuffer,
int32_t flags,
int64_t timestampUs,
const ::ndk::ScopedFileDescriptor& fence) override;
@@ -68,13 +68,19 @@
void setFrameSize(uint32_t width, uint32_t height);
/**
- * Clean up work item reference.
+ * Notify that the input buffer reference is no longer needed by the component.
+ * Clean up if necessary.
*
* \param index input work index
*/
void onInputBufferDone(c2_cntr64_t index);
/**
+ * Notify input buffer is emptied.
+ */
+ void onInputBufferEmptied();
+
+ /**
* Returns dataspace information from GraphicBufferSource.
*/
android_dataspace getDataspace();
diff --git a/media/codec2/sfplugin/C2NodeImpl.cpp b/media/codec2/sfplugin/C2NodeImpl.cpp
index 6f53e0f..585072d 100644
--- a/media/codec2/sfplugin/C2NodeImpl.cpp
+++ b/media/codec2/sfplugin/C2NodeImpl.cpp
@@ -25,6 +25,7 @@
#include <C2Debug.h>
#include <C2PlatformSupport.h>
+#include <android_media_codec.h>
#include <android/fdsan.h>
#include <media/stagefright/foundation/ColorUtils.h>
#include <ui/Fence.h>
@@ -373,7 +374,10 @@
}
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- mBufferIdsInUse.lock()->emplace(work->input.ordinal.frameIndex.peeku(), buffer);
+ {
+ Mutexed<BuffersTracker>::Locked buffers(mBuffersTracker);
+ buffers->mIdsInUse.emplace(work->input.ordinal.frameIndex.peeku(), buffer);
+ }
mQueueThread->queue(comp, fenceFd, std::move(work), std::move(fd0), std::move(fd1));
return OK;
@@ -405,29 +409,74 @@
}
void C2NodeImpl::onInputBufferDone(c2_cntr64_t index) {
- if (mAidlHal) {
- if (!mAidlBufferSource) {
- ALOGD("Buffer source not set (index=%llu)", index.peekull());
- return;
- }
- } else {
- if (!mBufferSource) {
- ALOGD("Buffer source not set (index=%llu)", index.peekull());
- return;
- }
- }
-
- int32_t bufferId = 0;
- {
- decltype(mBufferIdsInUse)::Locked bufferIds(mBufferIdsInUse);
- auto it = bufferIds->find(index.peeku());
- if (it == bufferIds->end()) {
+ if (android::media::codec::provider_->input_surface_throttle()) {
+ Mutexed<BuffersTracker>::Locked buffers(mBuffersTracker);
+ auto it = buffers->mIdsInUse.find(index.peeku());
+ if (it == buffers->mIdsInUse.end()) {
ALOGV("Untracked input index %llu (maybe already removed)", index.peekull());
return;
}
- bufferId = it->second;
- (void)bufferIds->erase(it);
+ int32_t bufferId = it->second;
+ (void)buffers->mIdsInUse.erase(it);
+ buffers->mAvailableIds.push_back(bufferId);
+ } else {
+ if (!hasBufferSource()) {
+ return;
+ }
+ int32_t bufferId = 0;
+ {
+ Mutexed<BuffersTracker>::Locked buffers(mBuffersTracker);
+ auto it = buffers->mIdsInUse.find(index.peeku());
+ if (it == buffers->mIdsInUse.end()) {
+ ALOGV("Untracked input index %llu (maybe already removed)", index.peekull());
+ return;
+ }
+ bufferId = it->second;
+ (void)buffers->mIdsInUse.erase(it);
+ }
+ notifyInputBufferEmptied(bufferId);
}
+}
+
+void C2NodeImpl::onInputBufferEmptied() {
+ if (!android::media::codec::provider_->input_surface_throttle()) {
+ ALOGE("onInputBufferEmptied should not be called "
+ "when input_surface_throttle is false");
+ return;
+ }
+ if (!hasBufferSource()) {
+ return;
+ }
+ int32_t bufferId = 0;
+ {
+ Mutexed<BuffersTracker>::Locked buffers(mBuffersTracker);
+ if (buffers->mAvailableIds.empty()) {
+ ALOGV("The codec is ready to take more input buffers "
+ "but no input buffers are ready yet.");
+ return;
+ }
+ bufferId = buffers->mAvailableIds.front();
+ buffers->mAvailableIds.pop_front();
+ }
+ notifyInputBufferEmptied(bufferId);
+}
+
+bool C2NodeImpl::hasBufferSource() {
+ if (mAidlHal) {
+ if (!mAidlBufferSource) {
+ ALOGD("Buffer source not set");
+ return false;
+ }
+ } else {
+ if (!mBufferSource) {
+ ALOGD("Buffer source not set");
+ return false;
+ }
+ }
+ return true;
+}
+
+void C2NodeImpl::notifyInputBufferEmptied(int32_t bufferId) {
if (mAidlHal) {
::ndk::ScopedFileDescriptor nullFence;
(void)mAidlBufferSource->onInputBufferEmptied(bufferId, nullFence);
diff --git a/media/codec2/sfplugin/C2NodeImpl.h b/media/codec2/sfplugin/C2NodeImpl.h
index e060fd8..cc826b4 100644
--- a/media/codec2/sfplugin/C2NodeImpl.h
+++ b/media/codec2/sfplugin/C2NodeImpl.h
@@ -73,13 +73,19 @@
void setFrameSize(uint32_t width, uint32_t height);
/**
- * Clean up work item reference.
+ * Notify that the input buffer reference is no longer needed by the component.
+ * Clean up if necessary.
*
* \param index input work index
*/
void onInputBufferDone(c2_cntr64_t index);
/**
+ * Notify input buffer is emptied.
+ */
+ void onInputBufferEmptied();
+
+ /**
* Returns dataspace information from GraphicBufferSource.
*/
android_dataspace getDataspace();
@@ -118,12 +124,24 @@
c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame
c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame
- Mutexed<std::map<uint64_t, uint32_t>> mBufferIdsInUse;
+ // Tracks the status of buffers
+ struct BuffersTracker {
+ BuffersTracker() = default;
+
+ // Keeps track of buffers that are used by the component. Maps timestamp -> ID
+ std::map<uint64_t, uint32_t> mIdsInUse;
+ // Keeps track of the buffer IDs that are available after being released from the component.
+ std::list<uint32_t> mAvailableIds;
+ };
+ Mutexed<BuffersTracker> mBuffersTracker;
class QueueThread;
sp<QueueThread> mQueueThread;
bool mAidlHal;
+
+ bool hasBufferSource();
+ void notifyInputBufferEmptied(int32_t bufferId);
};
} // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index ce02c88..98e25e2 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -291,6 +291,10 @@
return mImpl->onInputBufferDone(index);
}
+void C2OMXNode::onInputBufferEmptied() {
+ return mImpl->onInputBufferEmptied();
+}
+
android_dataspace C2OMXNode::getDataspace() {
return mImpl->getDataspace();
}
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index d077202..5549b88 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -86,13 +86,19 @@
void setFrameSize(uint32_t width, uint32_t height);
/**
- * Clean up work item reference.
+ * Notify that the input buffer reference is no longer needed by the component.
+ * Clean up if necessary.
*
* \param index input work index
*/
void onInputBufferDone(c2_cntr64_t index);
/**
+ * Notify input buffer is emptied.
+ */
+ void onInputBufferEmptied();
+
+ /**
* Returns dataspace information from GraphicBufferSource.
*/
android_dataspace getDataspace();
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index a897fa0..ca0aabb 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -444,6 +444,10 @@
mNode->onInputBufferDone(index);
}
+ void onInputBufferEmptied() override {
+ mNode->onInputBufferEmptied();
+ }
+
android_dataspace getDataspace() override {
return mNode->getDataspace();
}
@@ -663,6 +667,10 @@
mNode->onInputBufferDone(index);
}
+ void onInputBufferEmptied() override {
+ mNode->onInputBufferEmptied();
+ }
+
android_dataspace getDataspace() override {
return mNode->getDataspace();
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index c7ab82f..f0a4180 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1069,6 +1069,10 @@
return;
}
}
+ if (android::media::codec::provider_->input_surface_throttle()
+ && mInputSurface != nullptr) {
+ mInputSurface->onInputBufferEmptied();
+ }
size_t numActiveSlots = 0;
while (!mPipelineWatcher.lock()->pipelineFull()) {
sp<MediaCodecBuffer> inBuffer;
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index 4bf6cd0..c158c5b 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -102,6 +102,7 @@
}
/**
+ * Notify that the input buffer reference is no longer needed.
* Clean up C2Work related references if necessary. No-op by default.
*
* \param index index of input work.
@@ -109,6 +110,12 @@
virtual void onInputBufferDone(c2_cntr64_t /* index */) {}
/**
+ * Signal one input buffer as emptied.
+ * No-op by default.
+ */
+ virtual void onInputBufferEmptied() {}
+
+ /**
* Returns dataspace information from GraphicBufferSource.
*/
virtual android_dataspace getDataspace() { return mDataSpace; }
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index fc8ad77..ba231c1 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -48,6 +48,7 @@
"aaudio-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
+ "audio-permission-aidl-cpp",
"audiopolicy-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"av-types-aidl-cpp",
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index ac4e2b3..9bbaf3e 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -24,6 +24,7 @@
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
+#include <android/media/audio/common/AudioMMapPolicy.h>
#include <android/media/audio/common/AudioMMapPolicyInfo.h>
#include <android/media/audio/common/AudioMMapPolicyType.h>
#include <media/AudioSystem.h>
@@ -40,11 +41,14 @@
using namespace aaudio;
+using android::media::audio::common::AudioMMapPolicy;
using android::media::audio::common::AudioMMapPolicyInfo;
using android::media::audio::common::AudioMMapPolicyType;
#define AAUDIO_MMAP_POLICY_DEFAULT AAUDIO_POLICY_NEVER
#define AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT AAUDIO_POLICY_NEVER
+#define AAUDIO_MMAP_POLICY_DEFAULT_AIDL AudioMMapPolicy::NEVER
+#define AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT_AIDL AudioMMapPolicy::NEVER
// These values are for a pre-check before we ask the lower level service to open a stream.
// So they are just outside the maximum conceivable range of value,
@@ -116,7 +120,8 @@
aaudio_policy_t mmapPolicy = AudioGlobal_getMMapPolicy();
if (android::AudioSystem::getMmapPolicyInfo(
AudioMMapPolicyType::DEFAULT, &policyInfos) == NO_ERROR) {
- aaudio_policy_t systemMmapPolicy = AAudio_getAAudioPolicy(policyInfos);
+ aaudio_policy_t systemMmapPolicy = AAudio_getAAudioPolicy(
+ policyInfos, AAUDIO_MMAP_POLICY_DEFAULT_AIDL);
if (mmapPolicy == AAUDIO_POLICY_ALWAYS && systemMmapPolicy == AAUDIO_POLICY_NEVER) {
// No need to try as AAudioService is not created and the client only wants MMAP path.
return AAUDIO_ERROR_NO_SERVICE;
@@ -145,7 +150,8 @@
aaudio_policy_t mmapExclusivePolicy = AAUDIO_UNSPECIFIED;
if (android::AudioSystem::getMmapPolicyInfo(
AudioMMapPolicyType::EXCLUSIVE, &policyInfos) == NO_ERROR) {
- mmapExclusivePolicy = AAudio_getAAudioPolicy(policyInfos);
+ mmapExclusivePolicy = AAudio_getAAudioPolicy(
+ policyInfos, AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT_AIDL);
}
if (mmapExclusivePolicy == AAUDIO_UNSPECIFIED) {
mmapExclusivePolicy = AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 0cbf79d..3df23ee 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -680,12 +680,16 @@
} // namespace
-aaudio_policy_t AAudio_getAAudioPolicy(const std::vector<AudioMMapPolicyInfo>& policyInfos) {
- if (policyInfos.empty()) return AAUDIO_POLICY_AUTO;
- for (size_t i = 1; i < policyInfos.size(); ++i) {
- if (policyInfos.at(i).mmapPolicy != policyInfos.at(0).mmapPolicy) {
+aaudio_policy_t AAudio_getAAudioPolicy(const std::vector<AudioMMapPolicyInfo>& policyInfos,
+ AudioMMapPolicy defaultPolicy) {
+ AudioMMapPolicy policy = defaultPolicy;
+ for (const auto& policyInfo : policyInfos) {
+ if (policyInfo.mmapPolicy == AudioMMapPolicy::NEVER) {
+ policy = policyInfo.mmapPolicy;
+ } else if (policyInfo.mmapPolicy == AudioMMapPolicy::AUTO ||
+ policyInfo.mmapPolicy == AudioMMapPolicy::ALWAYS) {
return AAUDIO_POLICY_AUTO;
}
}
- return aidl2legacy_aaudio_policy(policyInfos.at(0).mmapPolicy);
+ return aidl2legacy_aaudio_policy(policy);
}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index d44bbab..7c351e1 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -348,9 +348,19 @@
AAUDIO_CHANNEL_INDEX_MASK_24 = AAUDIO_CHANNEL_BIT_INDEX | (1 << 24) - 1,
};
-// The aaudio policy will be ALWAYS, NEVER, UNSPECIFIED only when all policy info are
-// ALWAYS, NEVER or UNSPECIFIED. Otherwise, the aaudio policy will be AUTO.
+/**
+ * Returns the aaudio mmap policy based on the vector of mmap policy info. The rule as
+ * 1. Returns AUTO if any of the policy is AUTO or ALWAYS
+ * 2. Returns NEVER if all of the policies are NEVER or UNSPECIFIED
+ * 3. Returns default policy if all of the policies are UNSPECIFIED
+ *
+ * @param policyInfos
+ * @param defaultPolicy
+ * @return
+ */
aaudio_policy_t AAudio_getAAudioPolicy(
- const std::vector<android::media::audio::common::AudioMMapPolicyInfo>& policyInfos);
+ const std::vector<android::media::audio::common::AudioMMapPolicyInfo>& policyInfos,
+ android::media::audio::common::AudioMMapPolicy defaultPolicy =
+ android::media::audio::common::AudioMMapPolicy::NEVER);
#endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index e667964..5785537 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -458,6 +458,7 @@
"latest_android_media_audio_common_types_import_interface",
],
imports: [
+ "audio-permission-aidl",
"audioclient-types-aidl",
"audiopolicy-types-aidl",
"capture_state_listener-aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index aa51652..3f4fcfd 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1289,6 +1289,21 @@
(void) status;
}
+status_t AudioSystem::setDeviceAbsoluteVolumeEnabled(audio_devices_t deviceType,
+ const char *address,
+ bool enabled,
+ audio_stream_type_t streamToDriveAbs) {
+ const sp<IAudioPolicyService> aps = get_audio_policy_service();
+ if (aps == nullptr) return PERMISSION_DENIED;
+
+ AudioDevice deviceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_device_AudioDevice(deviceType, address));
+ AudioStreamType streamToDriveAbsAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(streamToDriveAbs));
+ return statusTFromBinderStatus(
+ aps->setDeviceAbsoluteVolumeEnabled(deviceAidl, enabled, streamToDriveAbsAidl));
+}
+
status_t AudioSystem::initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax) {
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 633493c..ac42ea9 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -58,6 +58,8 @@
import android.media.audio.common.AudioUuid;
import android.media.audio.common.Int;
+import com.android.media.permission.INativePermissionController;
+
/**
* IAudioPolicyService interface (see AudioPolicyInterface for method descriptions).
*
@@ -114,6 +116,10 @@
void releaseInput(int /* audio_port_handle_t */ portId);
+ oneway void setDeviceAbsoluteVolumeEnabled(in AudioDevice device,
+ boolean enabled,
+ AudioStreamType streamToDriveAbs);
+
void initStreamVolume(AudioStreamType stream,
int indexMin,
int indexMax);
@@ -471,6 +477,11 @@
int /* uid_t */ uid);
+ /**
+ * Get the native permission controller for audioserver, to push package and permission info
+ * required to control audio access.
+ */
+ INativePermissionController getPermissionController();
// When adding a new method, please review and update
// AudioPolicyService.cpp AudioPolicyService::onTransact()
// AudioPolicyService.cpp IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index a0c535d..11955c9 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -22,6 +22,7 @@
name: "libaudioclient_aidl_fuzzer_defaults",
static_libs: [
"android.hardware.audio.common@7.0-enums",
+ "audiopermissioncontroller",
"libaudiomockhal",
"libcgrouprc",
"libcgrouprc_format",
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 5c9a7c6..9cfd540 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -374,9 +374,13 @@
static status_t startInput(audio_port_handle_t portId);
static status_t stopInput(audio_port_handle_t portId);
static void releaseInput(audio_port_handle_t portId);
+ static status_t setDeviceAbsoluteVolumeEnabled(audio_devices_t deviceType,
+ const char *address,
+ bool enabled,
+ audio_stream_type_t streamToDriveAbs);
static status_t initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax);
+ int indexMin,
+ int indexMax);
static status_t setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device);
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 055da5b..9c3ad44 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -122,6 +122,7 @@
],
static_libs: [
"android.hardware.audio.common@7.0-enums",
+ "audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index 032533c..1990858 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -387,7 +387,7 @@
return runCb([](CbRef cb) { cb->onWriteReady(); });
}
ndk::ScopedAStatus onError() override {
- return runCb([](CbRef cb) { cb->onError(); });
+ return runCb([](CbRef cb) { cb->onError(true /*isHardError*/); });
}
ndk::ScopedAStatus onDrainReady() override {
return runCb([](CbRef cb) { cb->onDrainReady(); });
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.cpp b/media/libaudiohal/impl/Hal2AidlMapper.cpp
index 453f9e2..cbade70 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.cpp
+++ b/media/libaudiohal/impl/Hal2AidlMapper.cpp
@@ -136,8 +136,8 @@
// 'sinks' will not be updated because 'setAudioPatch' only needs IDs. Here we log
// the source arguments, where only the audio configuration and device specifications
// are relevant.
- ALOGD("%s: [disregard IDs] sources: %s, sinks: %s",
- __func__, ::android::internal::ToString(sources).c_str(),
+ ALOGD("%s: patch ID: %d, [disregard IDs] sources: %s, sinks: %s",
+ __func__, *patchId, ::android::internal::ToString(sources).c_str(),
::android::internal::ToString(sinks).c_str());
auto fillPortConfigs = [&](
const std::vector<AudioPortConfig>& configs,
@@ -209,11 +209,24 @@
// that there can only be one patch for an I/O thread.
PatchMatch match = sourceIsDevice && sinkIsDevice ?
MATCH_BOTH : (sourceIsDevice ? MATCH_SINKS : MATCH_SOURCES);
+ auto requestedPatch = patch;
RETURN_STATUS_IF_ERROR(findOrCreatePatch(patch, match,
&patch, &created));
// No cleanup of the patch is needed, it is managed by the framework.
*patchId = patch.id;
if (!created) {
+ requestedPatch.id = patch.id;
+ if (patch != requestedPatch) {
+ ALOGI("%s: Updating transient patch. Current: %s, new: %s",
+ __func__, patch.toString().c_str(), requestedPatch.toString().c_str());
+ // Since matching may be done by mix port only, update the patch if the device port
+ // config has changed.
+ patch = requestedPatch;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mModule->setAudioPatch(patch, &patch)));
+ existingPatchIt = mPatches.find(patch.id);
+ existingPatchIt->second = patch;
+ }
// The framework might have "created" a patch which already existed due to
// stream creation. Need to release the ownership from the stream.
for (auto& s : mStreams) {
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 84c1a8a..2fb4756 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -257,20 +257,71 @@
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
- const auto state = getState();
- StreamDescriptor::Reply reply;
- if (state == StreamDescriptor::State::STANDBY) {
- RETURN_STATUS_IF_ERROR(sendCommand(makeHalCommand<HalCommand::Tag::start>(), &reply, true));
- return sendCommand(makeHalCommand<HalCommand::Tag::burst>(0), &reply, true);
+ if (!mContext.isMmapped()) {
+ return BAD_VALUE;
}
-
- return INVALID_OPERATION;
+ StreamDescriptor::Reply reply;
+ RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply));
+ switch (reply.state) {
+ case StreamDescriptor::State::STANDBY:
+ RETURN_STATUS_IF_ERROR(
+ sendCommand(makeHalCommand<HalCommand::Tag::start>(), &reply, true));
+ if (reply.state != StreamDescriptor::State::IDLE) {
+ ALOGE("%s: unexpected stream state: %s (expected IDLE)",
+ __func__, toString(reply.state).c_str());
+ return INVALID_OPERATION;
+ }
+ FALLTHROUGH_INTENDED;
+ case StreamDescriptor::State::IDLE:
+ RETURN_STATUS_IF_ERROR(
+ sendCommand(makeHalCommand<HalCommand::Tag::burst>(0), &reply, true));
+ if (reply.state != StreamDescriptor::State::ACTIVE) {
+ ALOGE("%s: unexpected stream state: %s (expected ACTIVE)",
+ __func__, toString(reply.state).c_str());
+ return INVALID_OPERATION;
+ }
+ FALLTHROUGH_INTENDED;
+ case StreamDescriptor::State::ACTIVE:
+ return OK;
+ case StreamDescriptor::State::DRAINING:
+ RETURN_STATUS_IF_ERROR(
+ sendCommand(makeHalCommand<HalCommand::Tag::start>(), &reply, true));
+ if (reply.state != StreamDescriptor::State::ACTIVE) {
+ ALOGE("%s: unexpected stream state: %s (expected ACTIVE)",
+ __func__, toString(reply.state).c_str());
+ return INVALID_OPERATION;
+ }
+ return OK;
+ default:
+ ALOGE("%s: not supported from %s stream state %s",
+ __func__, mIsInput ? "input" : "output", toString(reply.state).c_str());
+ return INVALID_OPERATION;
+ }
}
status_t StreamHalAidl::stop() {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
+ TIME_CHECK();
if (!mStream) return NO_INIT;
- return standby();
+ if (!mContext.isMmapped()) {
+ return BAD_VALUE;
+ }
+ StreamDescriptor::Reply reply;
+ RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply));
+ if (const auto state = reply.state; state == StreamDescriptor::State::ACTIVE) {
+ return drain(false /*earlyNotify*/, nullptr);
+ } else if (state == StreamDescriptor::State::DRAINING) {
+ RETURN_STATUS_IF_ERROR(pause());
+ return flush();
+ } else if (state == StreamDescriptor::State::PAUSED) {
+ return flush();
+ } else if (state != StreamDescriptor::State::IDLE &&
+ state != StreamDescriptor::State::STANDBY) {
+ ALOGE("%s: not supported from %s stream state %s",
+ __func__, mIsInput ? "input" : "output", toString(state).c_str());
+ return INVALID_OPERATION;
+ }
+ return OK;
}
status_t StreamHalAidl::getLatency(uint32_t *latency) {
@@ -905,10 +956,10 @@
}
}
-void StreamOutHalAidl::onError() {
+void StreamOutHalAidl::onError(bool isHardError) {
onAsyncError();
if (auto clientCb = mClientCallback.load().promote(); clientCb != nullptr) {
- clientCb->onError();
+ clientCb->onError(isHardError);
}
}
diff --git a/media/libaudiohal/impl/StreamHalAidl.h b/media/libaudiohal/impl/StreamHalAidl.h
index 4523cc5..9cb2cff 100644
--- a/media/libaudiohal/impl/StreamHalAidl.h
+++ b/media/libaudiohal/impl/StreamHalAidl.h
@@ -386,7 +386,7 @@
// StreamOutHalInterfaceCallback
void onWriteReady() override;
void onDrainReady() override;
- void onError() override;
+ void onError(bool isHardError) override;
private:
friend class sp<StreamOutHalAidl>;
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 9e22700..a931fdd 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -997,7 +997,7 @@
sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onError");
- callback->onError();
+ callback->onError(false /*isHardError*/);
}
void StreamOutHalHidl::onCodecFormatChanged(const std::vector<uint8_t>& metadataBs) {
diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
index 585a895..4bd7e3d 100644
--- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
@@ -107,7 +107,7 @@
public:
virtual void onWriteReady() {}
virtual void onDrainReady() {}
- virtual void onError() {}
+ virtual void onError(bool /*isHardError*/) {}
protected:
StreamOutHalInterfaceCallback() = default;
diff --git a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
index 5106874..0bd6fb0 100644
--- a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
+++ b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
@@ -39,6 +39,7 @@
using ::aidl::android::hardware::audio::core::VendorParameter;
using ::aidl::android::media::audio::common::AudioChannelLayout;
using ::aidl::android::media::audio::common::AudioConfig;
+using ::aidl::android::media::audio::common::AudioDevice;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
using ::aidl::android::media::audio::common::AudioFormatDescription;
@@ -160,6 +161,24 @@
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})};
Configuration c;
+ AudioPort micInDevice =
+ createPort(c.nextPortId++, "Built-In Mic", 0, true,
+ createPortDeviceExt(AudioDeviceType::IN_MICROPHONE,
+ 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
+ micInDevice.profiles = standardPcmAudioProfiles;
+ c.ports.push_back(micInDevice);
+
+ AudioPort micInBackDevice =
+ createPort(c.nextPortId++, "Built-In Back Mic", 0, true,
+ createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0));
+ micInDevice.profiles = standardPcmAudioProfiles;
+ c.ports.push_back(micInBackDevice);
+
+ AudioPort primaryInMix =
+ createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 1));
+ primaryInMix.profiles = standardPcmAudioProfiles;
+ c.ports.push_back(primaryInMix);
+
AudioPort btOutDevice =
createPort(c.nextPortId++, "BT A2DP Out", 0, false,
createPortDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
@@ -172,6 +191,7 @@
btOutMix.profiles = standardPcmAudioProfiles;
c.ports.push_back(btOutMix);
+ c.routes.push_back(createRoute({micInDevice, micInBackDevice}, primaryInMix));
c.routes.push_back(createRoute({btOutMix}, btOutDevice));
return c;
@@ -184,6 +204,11 @@
explicit ModuleMock(const Configuration& config) : mConfig(config) {}
bool isScreenTurnedOn() const { return mIsScreenTurnedOn; }
ScreenRotation getScreenRotation() const { return mScreenRotation; }
+ std::vector<AudioPatch> getPatches() {
+ std::vector<AudioPatch> result;
+ getAudioPatches(&result);
+ return result;
+ }
private:
ndk::ScopedAStatus setModuleDebug(
@@ -1141,3 +1166,51 @@
EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id));
EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId));
}
+
+TEST_F(Hal2AidlMapperTest, ChangeTransientPatchDevice) {
+ std::mutex mutex; // Only needed for cleanups.
+ auto mapperAccessor = std::make_unique<LockedAccessor<Hal2AidlMapper>>(*mMapper, mutex);
+ Hal2AidlMapper::Cleanups cleanups(*mapperAccessor);
+ AudioConfig config;
+ config.base.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+ AudioChannelLayout::LAYOUT_STEREO);
+ config.base.format =
+ AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT};
+ config.base.sampleRate = 48000;
+ AudioDevice defaultDevice;
+ defaultDevice.type.type = AudioDeviceType::IN_DEFAULT;
+ AudioPortConfig mixPortConfig;
+ AudioPatch transientPatch;
+ ASSERT_EQ(OK, mMapper->prepareToOpenStream(43 /*ioHandle*/, defaultDevice,
+ AudioIoFlags::make<AudioIoFlags::input>(0),
+ AudioSource::DEFAULT, &cleanups, &config,
+ &mixPortConfig, &transientPatch));
+ cleanups.disarmAll();
+ ASSERT_NE(0, transientPatch.id);
+ ASSERT_NE(0, mixPortConfig.id);
+ sp<StreamHalInterface> stream = sp<StreamHalMock>::make();
+ mMapper->addStream(stream, mixPortConfig.id, transientPatch.id);
+
+ AudioPatch patch{};
+ int32_t patchId;
+ AudioPortConfig backMicPortConfig;
+ backMicPortConfig.channelMask = config.base.channelMask;
+ backMicPortConfig.format = config.base.format;
+ backMicPortConfig.sampleRate = aidl::android::media::audio::common::Int{config.base.sampleRate};
+ backMicPortConfig.flags = AudioIoFlags::make<AudioIoFlags::input>(0);
+ backMicPortConfig.ext = createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0);
+ ASSERT_EQ(OK, mMapper->createOrUpdatePatch({backMicPortConfig}, {mixPortConfig}, &patchId,
+ &cleanups));
+ cleanups.disarmAll();
+ ASSERT_EQ(android::OK,
+ mMapper->findPortConfig(backMicPortConfig.ext.get<AudioPortExt::device>().device,
+ &backMicPortConfig));
+ EXPECT_NE(0, backMicPortConfig.id);
+
+ EXPECT_EQ(transientPatch.id, patchId);
+ auto patches = mModule->getPatches();
+ auto patchIt = findById(patches, patchId);
+ ASSERT_NE(patchIt, patches.end());
+ EXPECT_EQ(std::vector<int32_t>{backMicPortConfig.id}, patchIt->sourcePortConfigIds);
+ EXPECT_EQ(std::vector<int32_t>{mixPortConfig.id}, patchIt->sinkPortConfigIds);
+}
diff --git a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
index 44ea2a4..3ae3edc 100644
--- a/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/aidl/ReverbContext.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "ReverbContext"
#include <android-base/logging.h>
#include <Utils.h>
+#include <audio_utils/primitives.h>
#include "ReverbContext.h"
#include "VectorArithmetic.h"
@@ -347,6 +348,15 @@
mCommon.output.base.channelMask);
int frameCount = mCommon.input.frameCount;
+ if (mBypass) {
+ if (isAuxiliary()) {
+ memset(out, 0, getOutputFrameSize() * frameCount);
+ } else {
+ memcpy_to_float_from_float_with_clamping(out, in, samples, 1);
+ }
+ return {STATUS_OK, samples, outChannels * frameCount};
+ }
+
// Reverb only effects the stereo channels in multichannel source.
if (channels < 1 || channels > LVM_MAX_CHANNELS) {
LOG(ERROR) << __func__ << " process invalid PCM channels " << channels;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 840897f..9cd0e6e 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -81,9 +81,6 @@
cc_library_shared {
name: "libmedia_omx",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
diff --git a/media/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 649f813..b5867a6 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -30,9 +30,6 @@
name: "libmedia_helper",
vendor_available: true,
min_sdk_version: "29",
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
"AudioParameter.cpp",
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 3ab32f0..f0e1b9e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -2119,6 +2119,11 @@
if (tsLayers > 1) {
uint32_t bLayers = std::min(2u, tsLayers - 1); // use up-to 2 B-layers
+ // TODO(b/341121900): Remove this once B frames are handled correctly in screen recorder
+ // use case in case of mic only
+ if (mAudioSource == AUDIO_SOURCE_MIC && mVideoSource == VIDEO_SOURCE_SURFACE) {
+ bLayers = 0;
+ }
uint32_t pLayers = tsLayers - bLayers;
format->setString(
"ts-schema", AStringPrintf("android.generic.%u+%u", pLayers, bLayers));
diff --git a/media/libmediaplayerservice/fuzzer/Android.bp b/media/libmediaplayerservice/fuzzer/Android.bp
index 0031855..78163e4 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -141,7 +141,6 @@
"libplayerservice_datasource",
],
shared_libs: [
- "libmediaplayerservice",
"libdatasource",
"libdrmframework",
"libstagefright_httplive",
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index aaf7465..1008445 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -249,6 +249,11 @@
sampleMetaData.setInt32(kKeyIsMuxerData, 1);
}
+ if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
+ sampleMetaData.setInt32(kKeyIsCodecConfig, true);
+ ALOGV("BUFFER_FLAG_CODEC_CONFIG");
+ }
+
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
sampleMetaData.setInt32(kKeyIsEndOfStream, 1);
ALOGV("BUFFER_FLAG_EOS");
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 79ab009..630817c 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -20,9 +20,6 @@
cc_library_shared {
name: "libstagefright_omx",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
@@ -218,9 +215,6 @@
cc_library_shared {
name: "libstagefright_omx_utils",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: ["OMXUtils.cpp"],
export_include_dirs: [
diff --git a/media/libstagefright/renderfright/Android.bp b/media/libstagefright/renderfright/Android.bp
index 22b13f6..bb850ca 100644
--- a/media/libstagefright/renderfright/Android.bp
+++ b/media/libstagefright/renderfright/Android.bp
@@ -84,9 +84,6 @@
name: "librenderfright",
defaults: ["librenderfright_defaults"],
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
cflags: [
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 7d1442b..e20a08d 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -354,6 +354,17 @@
}
MetaDataBase &md = buffer->meta_data();
+
+ if (mType == kVideoType) {
+ int32_t isCodecConfig = 0;
+ if (md.findInt32(kKeyIsCodecConfig, &isCodecConfig) && isCodecConfig) {
+ ALOGI("ignoring CSD for video track");
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+ }
+
CHECK(md.findInt64(kKeyTime, ×tampUs));
if (mStartTimeUs == kUninitialized) {
mStartTimeUs = timestampUs;
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index 2f204f9..2c5e81a 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -16,9 +16,6 @@
cc_library_shared {
name: "libstagefright_xmlparser",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
diff --git a/media/module/aidlpersistentsurface/aidl/android/media/IAidlNode.aidl b/media/module/aidlpersistentsurface/aidl/android/media/IAidlNode.aidl
index cf880c2..fe3caf3 100644
--- a/media/module/aidlpersistentsurface/aidl/android/media/IAidlNode.aidl
+++ b/media/module/aidlpersistentsurface/aidl/android/media/IAidlNode.aidl
@@ -44,7 +44,7 @@
void setInputSurface(IAidlBufferSource bufferSource);
void submitBuffer(
int buffer,
- in HardwareBuffer hBuffer,
+ in @nullable HardwareBuffer hBuffer,
int flags,
long timestampUs,
in @nullable ParcelFileDescriptor fence);
diff --git a/media/module/aidlpersistentsurface/wrapper/WAidlGraphicBufferSource.cpp b/media/module/aidlpersistentsurface/wrapper/WAidlGraphicBufferSource.cpp
index 5526b10..a5c72d6 100644
--- a/media/module/aidlpersistentsurface/wrapper/WAidlGraphicBufferSource.cpp
+++ b/media/module/aidlpersistentsurface/wrapper/WAidlGraphicBufferSource.cpp
@@ -51,19 +51,24 @@
int32_t bufferId, uint32_t flags,
const sp<GraphicBuffer> &buffer,
int64_t timestamp, int fenceFd) override {
- AHardwareBuffer *ahwBuffer = nullptr;
- ::aidl::android::hardware::HardwareBuffer hBuffer;
+ ::ndk::ScopedFileDescriptor fence(fenceFd);
if (buffer.get()) {
- ahwBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get());
+ ::aidl::android::hardware::HardwareBuffer hBuffer;
+ AHardwareBuffer *ahwBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get());
AHardwareBuffer_acquire(ahwBuffer);
hBuffer.reset(ahwBuffer);
- }
- ::ndk::ScopedFileDescriptor fence(fenceFd);
+ return fromAidlStatus(mNode->submitBuffer(
+ bufferId,
+ std::move(hBuffer),
+ flags,
+ timestamp,
+ fence));
+ }
return fromAidlStatus(mNode->submitBuffer(
bufferId,
- hBuffer,
+ {},
flags,
timestamp,
fence));
diff --git a/media/module/bqhelper/Android.bp b/media/module/bqhelper/Android.bp
index c4dadd0..f9b7dea 100644
--- a/media/module/bqhelper/Android.bp
+++ b/media/module/bqhelper/Android.bp
@@ -69,9 +69,6 @@
name: "libstagefright_bufferqueue_helper",
defaults: ["libstagefright_bufferqueue-defaults"],
vendor_available: true,
- vndk: {
- enabled: true,
- },
min_sdk_version: "29",
shared_libs: [ "libgui" ],
diff --git a/media/module/bufferpool/2.0/Android.bp b/media/module/bufferpool/2.0/Android.bp
index 930b026..bdab103 100644
--- a/media/module/bufferpool/2.0/Android.bp
+++ b/media/module/bufferpool/2.0/Android.bp
@@ -60,7 +60,4 @@
vendor_available: true,
// TODO: b/147147992
double_loadable: true,
- vndk: {
- enabled: true,
- },
}
diff --git a/media/module/foundation/Android.bp b/media/module/foundation/Android.bp
index dc8384d..edf4cb5 100644
--- a/media/module/foundation/Android.bp
+++ b/media/module/foundation/Android.bp
@@ -33,9 +33,6 @@
cc_defaults {
name: "libstagefright_foundation_defaults",
vendor_available: true,
- vndk: {
- enabled: true,
- },
host_supported: true,
double_loadable: true,
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 5b7319a..e340b40 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -113,6 +113,7 @@
export_shared_lib_headers: [
"libpermission",
+ "packagemanager_aidl-cpp",
],
required: [
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 9016420..b763f09 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -218,6 +218,8 @@
],
header_libs: [
+ "audiopermissioncontroller_headers",
+ "audiopolicyservicelocal_headers",
"libaaudio_headers",
"libaudioclient_headers",
"libaudiohal_headers",
@@ -225,6 +227,8 @@
"libmedia_headers",
],
+ export_header_lib_headers: ["audiopolicyservicelocal_headers"],
+
export_shared_lib_headers: [
"libpermission",
],
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index af67a56..c1d1a63 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2189,6 +2189,13 @@
}
}
+void AudioFlinger::onHardError(std::set<audio_port_handle_t>& trackPortIds) {
+ ALOGI("releasing tracks due to a hard error occurred on an I/O thread");
+ for (const auto portId : trackPortIds) {
+ AudioSystem::releaseOutput(portId);
+ }
+}
+
// removeClient_l() must be called with AudioFlinger::clientMutex() held
void AudioFlinger::removeClient_l(pid_t pid)
{
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 4711dc5..143a766 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -33,6 +33,7 @@
#include <audio_utils/FdToString.h>
#include <audio_utils/SimpleLog.h>
#include <media/IAudioFlinger.h>
+#include <media/IAudioPolicyServiceLocal.h>
#include <media/MediaMetricsItem.h>
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <mediautils/ServiceUtilities.h>
@@ -401,6 +402,8 @@
void onSupportedLatencyModesChanged(
audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) final
EXCLUDES_AudioFlinger_ClientMutex;
+ void onHardError(std::set<audio_port_handle_t>& trackPortIds) final
+ EXCLUDES_AudioFlinger_ClientMutex;
// ---- end of IAfThreadCallback interface
@@ -424,6 +427,13 @@
const sp<MmapStreamCallback>& callback,
sp<MmapStreamInterface>& interface,
audio_port_handle_t *handle) EXCLUDES_AudioFlinger_Mutex;
+
+ void initAudioPolicyLocal(sp<media::IAudioPolicyServiceLocal> audioPolicyLocal) {
+ if (mAudioPolicyServiceLocal.load() == nullptr) {
+ mAudioPolicyServiceLocal = std::move(audioPolicyLocal);
+ }
+ }
+
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;
@@ -782,6 +792,9 @@
// Bluetooth Variable latency control logic is enabled or disabled
std::atomic<bool> mBluetoothLatencyModesEnabled = true;
+
+ // Local interface to AudioPolicyService, late inited, but logically const
+ mediautils::atomic_sp<media::IAudioPolicyServiceLocal> mAudioPolicyServiceLocal;
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index 204cc63..4518d48 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -116,9 +116,11 @@
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0) EXCLUDES_AudioFlinger_ClientMutex = 0;
virtual void onNonOffloadableGlobalEffectEnable() EXCLUDES_AudioFlinger_Mutex = 0;
- virtual void onSupportedLatencyModesChanged(
- audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes)
+ virtual void onSupportedLatencyModesChanged(audio_io_handle_t output,
+ const std::vector<audio_latency_mode_t>& modes)
EXCLUDES_AudioFlinger_ClientMutex = 0;
+
+ virtual void onHardError(std::set<audio_port_handle_t>& trackPortIds) = 0;
};
class IAfThreadBase : public virtual RefBase {
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d59697d..66e89e4 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3007,6 +3007,23 @@
}
}
+std::set<audio_port_handle_t> PlaybackThread::getTrackPortIds_l()
+{
+ std::set<int32_t> result;
+ for (const auto& t : mTracks) {
+ if (t->isExternalTrack()) {
+ result.insert(t->portId());
+ }
+ }
+ return result;
+}
+
+std::set<audio_port_handle_t> PlaybackThread::getTrackPortIds()
+{
+ audio_utils::lock_guard _l(mutex());
+ return getTrackPortIds_l();
+}
+
String8 PlaybackThread::getParameters(const String8& keys)
{
audio_utils::lock_guard _l(mutex());
@@ -3060,9 +3077,9 @@
mCallbackThread->resetDraining();
}
-void PlaybackThread::onError()
+void PlaybackThread::onError(bool isHardError)
{
- mCallbackThread->setAsyncError();
+ mCallbackThread->setAsyncError(isHardError);
}
void PlaybackThread::onCodecFormatChanged(
@@ -5428,11 +5445,15 @@
broadcast_l();
}
-void PlaybackThread::onAsyncError()
+void PlaybackThread::onAsyncError(bool isHardError)
{
+ auto allTrackPortIds = getTrackPortIds();
for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
invalidateTracks((audio_stream_type_t)i);
}
+ if (isHardError) {
+ mAfThreadCallback->onHardError(allTrackPortIds);
+ }
}
void MixerThread::threadLoop_mix()
@@ -7189,7 +7210,7 @@
mPlaybackThread(playbackThread),
mWriteAckSequence(0),
mDrainSequence(0),
- mAsyncError(false)
+ mAsyncError(ASYNC_ERROR_NONE)
{
}
@@ -7203,7 +7224,7 @@
while (!exitPending()) {
uint32_t writeAckSequence;
uint32_t drainSequence;
- bool asyncError;
+ AsyncError asyncError;
{
audio_utils::unique_lock _l(mutex());
@@ -7224,7 +7245,7 @@
drainSequence = mDrainSequence;
mDrainSequence &= ~1;
asyncError = mAsyncError;
- mAsyncError = false;
+ mAsyncError = ASYNC_ERROR_NONE;
}
{
const sp<PlaybackThread> playbackThread = mPlaybackThread.promote();
@@ -7235,8 +7256,8 @@
if (drainSequence & 1) {
playbackThread->resetDraining(drainSequence >> 1);
}
- if (asyncError) {
- playbackThread->onAsyncError();
+ if (asyncError != ASYNC_ERROR_NONE) {
+ playbackThread->onAsyncError(asyncError == ASYNC_ERROR_HARD);
}
}
}
@@ -7286,10 +7307,10 @@
}
}
-void AsyncCallbackThread::setAsyncError()
+void AsyncCallbackThread::setAsyncError(bool isHardError)
{
audio_utils::lock_guard _l(mutex());
- mAsyncError = true;
+ mAsyncError = isHardError ? ASYNC_ERROR_HARD : ASYNC_ERROR_SOFT;
mWaitWorkCV.notify_one();
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7e9bef1..4b2ade4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -948,7 +948,7 @@
// StreamOutHalInterfaceCallback implementation
virtual void onWriteReady();
virtual void onDrainReady();
- virtual void onError();
+ virtual void onError(bool /*isHardError*/);
public: // AsyncCallbackThread
void resetWriteBlocked(uint32_t sequence);
@@ -960,7 +960,7 @@
virtual bool shouldStandby_l() REQUIRES(mutex(), ThreadBase_ThreadLoop);
virtual void onAddNewTrack_l() REQUIRES(mutex());
public: // AsyncCallbackThread
- void onAsyncError(); // error reported by AsyncCallbackThread
+ void onAsyncError(bool isHardError); // error reported by AsyncCallbackThread
protected:
// StreamHalInterfaceCodecFormatCallback implementation
void onCodecFormatChanged(
@@ -1378,6 +1378,8 @@
bool destroyTrack_l(const sp<IAfTrack>& track) final REQUIRES(mutex());
void removeTrack_l(const sp<IAfTrack>& track) REQUIRES(mutex());
+ std::set<audio_port_handle_t> getTrackPortIds_l() REQUIRES(mutex());
+ std::set<audio_port_handle_t> getTrackPortIds();
void readOutputParameters_l() REQUIRES(mutex());
MetadataUpdate updateMetadata_l() final REQUIRES(mutex());
@@ -1841,7 +1843,7 @@
void resetWriteBlocked();
void setDraining(uint32_t sequence);
void resetDraining();
- void setAsyncError();
+ void setAsyncError(bool isHardError);
private:
const wp<PlaybackThread> mPlaybackThread;
@@ -1855,7 +1857,8 @@
uint32_t mDrainSequence;
audio_utils::condition_variable mWaitWorkCV;
mutable audio_utils::mutex mMutex{audio_utils::MutexOrder::kAsyncCallbackThread_Mutex};
- bool mAsyncError;
+ enum AsyncError { ASYNC_ERROR_NONE, ASYNC_ERROR_SOFT, ASYNC_ERROR_HARD };
+ AsyncError mAsyncError;
audio_utils::mutex& mutex() const RETURN_CAPABILITY(audio_utils::AsyncCallbackThread_Mutex) {
return mMutex;
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index de6eed8..e91e2a3 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -180,10 +180,16 @@
// volume control functions
//
+ // notifies the audio policy manager that the absolute volume mode is enabled/disabled on
+ // the passed device. Also specifies the stream that is controlling the absolute volume.
+ virtual status_t setDeviceAbsoluteVolumeEnabled(audio_devices_t device,
+ const char *address,
+ bool enabled,
+ audio_stream_type_t streamToDriveAbs) = 0;
// initialises stream volume conversion parameters by specifying volume index range.
virtual void initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax) = 0;
+ int indexMin,
+ int indexMax) = 0;
// sets the new stream volume at a level corresponding to the supplied index for the
// supplied device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME means
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index ca78ce7..3f9ae19 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -22,6 +22,7 @@
#include <string>
#include <string>
#include <vector>
+#include <unordered_map>
#define LOG_TAG "APM::AudioPolicyEngine/Config"
//#define LOG_NDEBUG 0
@@ -51,6 +52,27 @@
namespace {
+ConversionResult<std::string> aidl2legacy_AudioHalProductStrategy_ProductStrategyType(int id) {
+ using AudioProductStrategyType = media::audio::common::AudioProductStrategyType;
+
+#define STRATEGY_ENTRY(name) {static_cast<int>(AudioProductStrategyType::name), "STRATEGY_" #name}
+ static const std::unordered_map<int, std::string> productStrategyMap = {STRATEGY_ENTRY(MEDIA),
+ STRATEGY_ENTRY(PHONE),
+ STRATEGY_ENTRY(SONIFICATION),
+ STRATEGY_ENTRY(SONIFICATION_RESPECTFUL),
+ STRATEGY_ENTRY(DTMF),
+ STRATEGY_ENTRY(ENFORCED_AUDIBLE),
+ STRATEGY_ENTRY(TRANSMITTED_THROUGH_SPEAKER),
+ STRATEGY_ENTRY(ACCESSIBILITY)};
+#undef STRATEGY_ENTRY
+
+ auto it = productStrategyMap.find(id);
+ if (it == productStrategyMap.end()) {
+ return base::unexpected(BAD_VALUE);
+ }
+ return it->second;
+}
+
ConversionResult<AttributesGroup> aidl2legacy_AudioHalAttributeGroup_AttributesGroup(
const media::audio::common::AudioHalAttributesGroup& aidl) {
AttributesGroup legacy;
@@ -65,7 +87,8 @@
ConversionResult<ProductStrategy> aidl2legacy_AudioHalProductStrategy_ProductStrategy(
const media::audio::common::AudioHalProductStrategy& aidl) {
ProductStrategy legacy;
- legacy.name = "strategy_" + std::to_string(aidl.id);
+ legacy.name = VALUE_OR_RETURN(
+ aidl2legacy_AudioHalProductStrategy_ProductStrategyType(aidl.id));
legacy.attributesGroups = VALUE_OR_RETURN(convertContainer<AttributesGroups>(
aidl.attributesGroups,
aidl2legacy_AudioHalAttributeGroup_AttributesGroup));
diff --git a/services/audiopolicy/fuzzer/aidl/Android.bp b/services/audiopolicy/fuzzer/aidl/Android.bp
index 2c85955..a91ecd7 100644
--- a/services/audiopolicy/fuzzer/aidl/Android.bp
+++ b/services/audiopolicy/fuzzer/aidl/Android.bp
@@ -44,6 +44,7 @@
"packagemanager_aidl-cpp",
],
static_libs: [
+ "audiopermissioncontroller",
"libaudiomockhal",
"libfakeservicemanager",
"libmediaplayerservice",
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 87b6c3d..2a99fd8 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -396,8 +396,12 @@
// Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic
// parameters on newly connected devices (instead of opening the inputs...)
broadcastDeviceConnectionState(device, media::DeviceConnectedState::CONNECTED);
+ // Propagate device availability to Engine
+ setEngineDeviceConnectionState(device, state);
if (checkInputsForDevice(device, state) != NO_ERROR) {
+ setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE);
+
mAvailableInputDevices.remove(device);
broadcastDeviceConnectionState(device, media::DeviceConnectedState::DISCONNECTED);
@@ -431,6 +435,9 @@
// remove device from mReportedFormatsMap cache
mReportedFormatsMap.erase(device);
+
+ // Propagate device availability to Engine
+ setEngineDeviceConnectionState(device, state);
} break;
default:
@@ -438,9 +445,6 @@
return BAD_VALUE;
}
- // Propagate device availability to Engine
- setEngineDeviceConnectionState(device, state);
-
checkCloseInputs();
// As the input device list can impact the output device selection, update
// getDeviceForStrategy() cache
@@ -3337,6 +3341,23 @@
releaseInput(portId);
}
+bool AudioPolicyManager::checkCloseInput(const sp<AudioInputDescriptor>& input) {
+ if (input->clientsList().size() == 0
+ || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())) {
+ return true;
+ }
+ for (const auto& client : input->clientsList()) {
+ sp<DeviceDescriptor> device =
+ mEngine->getInputDeviceForAttributes(client->attributes(), client->uid(),
+ client->session());
+ if (!input->supportedDevices().contains(device)) {
+ return true;
+ }
+ }
+ setInputDevice(input->mIoHandle, getNewInputDevice(input));
+ return false;
+}
+
void AudioPolicyManager::checkCloseInputs() {
// After connecting or disconnecting an input device, close input if:
// - it has no client (was just opened to check profile) OR
@@ -3345,35 +3366,37 @@
// devices anymore. Otherwise update device selection
std::vector<audio_io_handle_t> inputsToClose;
for (size_t i = 0; i < mInputs.size(); i++) {
- const sp<AudioInputDescriptor> input = mInputs.valueAt(i);
- if (input->clientsList().size() == 0
- || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())) {
+ if (checkCloseInput(mInputs.valueAt(i))) {
inputsToClose.push_back(mInputs.keyAt(i));
- } else {
- bool close = false;
- for (const auto& client : input->clientsList()) {
- sp<DeviceDescriptor> device =
- mEngine->getInputDeviceForAttributes(client->attributes(), client->uid(),
- client->session());
- if (!input->supportedDevices().contains(device)) {
- close = true;
- break;
- }
- }
- if (close) {
- inputsToClose.push_back(mInputs.keyAt(i));
- } else {
- setInputDevice(input->mIoHandle, getNewInputDevice(input));
- }
}
}
-
for (const audio_io_handle_t handle : inputsToClose) {
ALOGV("%s closing input %d", __func__, handle);
closeInput(handle);
}
}
+status_t AudioPolicyManager::setDeviceAbsoluteVolumeEnabled(audio_devices_t deviceType,
+ const char *address __unused,
+ bool enabled,
+ audio_stream_type_t streamToDriveAbs)
+{
+ audio_attributes_t attributesToDriveAbs = mEngine->getAttributesForStreamType(streamToDriveAbs);
+ if (attributesToDriveAbs == AUDIO_ATTRIBUTES_INITIALIZER) {
+ ALOGW("%s: no attributes for stream %s, bailing out", __func__,
+ toString(streamToDriveAbs).c_str());
+ return BAD_VALUE;
+ }
+
+ if (enabled) {
+ mAbsoluteVolumeDrivingStreams[deviceType] = attributesToDriveAbs;
+ } else {
+ mAbsoluteVolumeDrivingStreams.erase(deviceType);
+ }
+
+ return NO_ERROR;
+}
+
void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
{
ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
@@ -4487,6 +4510,13 @@
dst->appendFormat("\nPolicy Engine dump:\n");
mEngine->dump(dst);
+
+ dst->appendFormat("\nAbsolute volume devices with driving streams:\n");
+ for (const auto it : mAbsoluteVolumeDrivingStreams) {
+ dst->appendFormat(" - device type: %s, driving stream %d\n",
+ dumpDeviceTypes({it.first}).c_str(),
+ mEngine->getVolumeGroupForAttributes(it.second));
+ }
}
status_t AudioPolicyManager::dump(int fd)
@@ -6837,14 +6867,14 @@
status_t AudioPolicyManager::checkInputsForDevice(const sp<DeviceDescriptor>& device,
audio_policy_dev_state_t state)
{
- sp<AudioInputDescriptor> desc;
-
if (audio_device_is_digital(device->type())) {
// erase all current sample rates, formats and channel masks
device->clearAudioProfiles();
}
if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+ sp<AudioInputDescriptor> desc;
+
// first call getAudioPort to get the supported attributes from the HAL
struct audio_port_v7 port = {};
device->toAudioPort(&port);
@@ -6935,6 +6965,11 @@
device->importAudioPortAndPickAudioProfile(profile);
}
ALOGV("checkInputsForDevice(): adding input %d", input);
+
+ if (checkCloseInput(desc)) {
+ ALOGV("%s closing input %d", __func__, input);
+ closeInput(input);
+ }
}
} // end scan profiles
@@ -7986,14 +8021,57 @@
return nullptr;
}
+float AudioPolicyManager::adjustDeviceAttenuationForAbsVolume(IVolumeCurves &curves,
+ VolumeSource volumeSource,
+ int index,
+ const DeviceTypeSet &deviceTypes)
+{
+ audio_devices_t volumeDevice = Volume::getDeviceForVolume(deviceTypes);
+ device_category deviceCategory = Volume::getDeviceCategory({volumeDevice});
+ float volumeDb = curves.volIndexToDb(deviceCategory, index);
+
+ if (com_android_media_audio_abs_volume_index_fix()) {
+ if (mAbsoluteVolumeDrivingStreams.find(volumeDevice) !=
+ mAbsoluteVolumeDrivingStreams.end()) {
+ audio_attributes_t attributesToDriveAbs = mAbsoluteVolumeDrivingStreams[volumeDevice];
+ auto groupToDriveAbs = mEngine->getVolumeGroupForAttributes(attributesToDriveAbs);
+ if (groupToDriveAbs == VOLUME_GROUP_NONE) {
+ ALOGD("%s: no group matching with %s", __FUNCTION__,
+ toString(attributesToDriveAbs).c_str());
+ return volumeDb;
+ }
+
+ float volumeDbMax = curves.volIndexToDb(deviceCategory, curves.getVolumeIndexMax());
+ VolumeSource vsToDriveAbs = toVolumeSource(groupToDriveAbs);
+ if (vsToDriveAbs == volumeSource) {
+ // attenuation is applied by the abs volume controller
+ return volumeDbMax;
+ } else {
+ IVolumeCurves &curvesAbs = getVolumeCurves(vsToDriveAbs);
+ int indexAbs = curvesAbs.getVolumeIndex({volumeDevice});
+ float volumeDbAbs = curvesAbs.volIndexToDb(deviceCategory, indexAbs);
+ float volumeDbAbsMax = curvesAbs.volIndexToDb(deviceCategory,
+ curvesAbs.getVolumeIndexMax());
+ float newVolumeDb = fminf(volumeDb + volumeDbAbsMax - volumeDbAbs, volumeDbMax);
+ ALOGV("%s: abs vol stream %d with attenuation %f is adjusting stream %d from "
+ "attenuation %f to attenuation %f %f", __func__, vsToDriveAbs, volumeDbAbs,
+ volumeSource, volumeDb, newVolumeDb, volumeDbMax);
+ return newVolumeDb;
+ }
+ }
+ return volumeDb;
+ } else {
+ return volumeDb;
+ }
+}
+
float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index,
const DeviceTypeSet& deviceTypes,
bool computeInternalInteraction)
{
- float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(deviceTypes), index);
-
+ float volumeDb = adjustDeviceAttenuationForAbsVolume(curves, volumeSource, index, deviceTypes);
ALOGV("%s volume source %d, index %d, devices %s, compute internal %b ", __func__,
volumeSource, index, dumpDeviceTypes(deviceTypes).c_str(), computeInternalInteraction);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 83597d8..011e867 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -103,7 +103,7 @@
virtual status_t setDeviceConnectionState(audio_policy_dev_state_t state,
const android::media::audio::common::AudioPort& port, audio_format_t encodedFormat);
virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device,
- const char *device_address);
+ const char *device_address);
virtual status_t handleDeviceConfigChange(audio_devices_t device,
const char *device_address,
const char *device_name,
@@ -151,6 +151,10 @@
virtual status_t stopInput(audio_port_handle_t portId);
virtual void releaseInput(audio_port_handle_t portId);
virtual void checkCloseInputs();
+ virtual status_t setDeviceAbsoluteVolumeEnabled(audio_devices_t deviceType,
+ const char *address,
+ bool enabled,
+ audio_stream_type_t streamToDriveAbs);
/**
* @brief initStreamVolume: even if the engine volume files provides min and max, keep this
* api for compatibility reason.
@@ -529,6 +533,7 @@
void addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc);
void removeOutput(audio_io_handle_t output);
void addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc);
+ bool checkCloseInput(const sp<AudioInputDescriptor>& input);
/**
* @brief setOutputDevices change the route of the specified output.
@@ -1388,6 +1393,15 @@
audio_session_t sessionId) const;
void updateClientsInternalMute(const sp<SwAudioOutputDescriptor>& desc);
+
+ float adjustDeviceAttenuationForAbsVolume(IVolumeCurves &curves,
+ VolumeSource volumeSource,
+ int index,
+ const DeviceTypeSet &deviceTypes);
+
+ // Contains for devices that support absolute volume the audio attributes
+ // corresponding to the streams that are driving the volume changes
+ std::unordered_map<audio_devices_t, audio_attributes_t> mAbsoluteVolumeDrivingStreams;
};
};
diff --git a/services/audiopolicy/permission/Android.bp b/services/audiopolicy/permission/Android.bp
new file mode 100644
index 0000000..d5f59a0
--- /dev/null
+++ b/services/audiopolicy/permission/Android.bp
@@ -0,0 +1,111 @@
+package {
+ default_team: "trendy_team_android_media_audio_framework",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+ name: "audiopermissioncontroller_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "audiopermissioncontroller",
+
+ srcs: [
+ "NativePermissionController.cpp",
+ "ValidatedAttributionSourceState.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "libcutils_headers",
+ "liberror_headers",
+ ],
+ export_header_lib_headers: [
+ "liberror_headers",
+ ],
+ static_libs: [
+ "audio-permission-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ "liblog",
+ ],
+
+ host_supported: true,
+ sanitize: {
+ integer_overflow: true,
+ },
+ cflags: [
+ "-Wall",
+ "-Wdeprecated",
+ "-Wextra",
+ "-Werror=format",
+ "-Wextra-semi",
+ "-Wthread-safety",
+ "-Wconditional-uninitialized",
+ "-Wimplicit-fallthrough",
+ "-Wreorder-init-list",
+ "-Werror=reorder-init-list",
+ "-Wshadow-all",
+ "-Wunreachable-code-aggressive",
+ "-Werror",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ ],
+ tidy: true,
+ tidy_checks: [
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "google-*",
+ "misc-*",
+ "modernize-*",
+ "performance-*",
+ ],
+ tidy_checks_as_errors: [
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "google-*",
+ "misc-*",
+ "modernize-*",
+ "performance-*",
+ ],
+}
+
+cc_test {
+ name: "audiopermissioncontroller_test",
+ host_supported: true,
+ defaults: [
+ "libmediautils_tests_config",
+ ],
+ static_libs: [
+ "audio-permission-aidl-cpp",
+ "audiopermissioncontroller",
+ "framework-permission-aidl-cpp",
+ "libgmock",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+ srcs: [
+ "tests/NativePermissionControllerTest.cpp",
+ "tests/ValidatedAttributionSourceStateTest.cpp",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["general-tests"],
+}
diff --git a/services/audiopolicy/permission/NativePermissionController.cpp b/services/audiopolicy/permission/NativePermissionController.cpp
new file mode 100644
index 0000000..9fcf22d
--- /dev/null
+++ b/services/audiopolicy/permission/NativePermissionController.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/NativePermissionController.h>
+
+#include <algorithm>
+#include <optional>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <cutils/android_filesystem_config.h>
+#include <utils/Errors.h>
+
+using ::android::base::unexpected;
+using ::android::binder::Status;
+using ::android::error::Result;
+
+namespace com::android::media::permission {
+static std::optional<std::string> getFixedPackageName(uid_t uid) {
+ // These values are in sync with AppOpsService
+ switch (uid % AID_USER_OFFSET) {
+ case AID_ROOT:
+ return "root";
+ case AID_SYSTEM:
+ return "system";
+ case AID_SHELL:
+ return "shell";
+ case AID_MEDIA:
+ return "media";
+ case AID_AUDIOSERVER:
+ return "audioserver";
+ case AID_CAMERASERVER:
+ return "cameraserver";
+ // These packages are not handled by AppOps, but labeling may be useful for us
+ case AID_RADIO:
+ return "telephony";
+ case AID_BLUETOOTH:
+ return "bluetooth";
+ default:
+ return std::nullopt;
+ }
+}
+
+// -- Begin Binder methods
+Status NativePermissionController::populatePackagesForUids(
+ const std::vector<UidPackageState>& initialPackageStates) {
+ std::lock_guard l{m_};
+ if (!is_package_populated_) is_package_populated_ = true;
+ package_map_.clear();
+ std::transform(initialPackageStates.begin(), initialPackageStates.end(),
+ std::inserter(package_map_, package_map_.end()),
+ [](const auto& x) -> std::pair<uid_t, std::vector<std::string>> {
+ return {x.uid, x.packageNames};
+ });
+ std::erase_if(package_map_, [](const auto& x) { return x.second.empty(); });
+ return Status::ok();
+}
+
+Status NativePermissionController::updatePackagesForUid(const UidPackageState& newPackageState) {
+ std::lock_guard l{m_};
+ package_map_.insert_or_assign(newPackageState.uid, newPackageState.packageNames);
+ const auto& cursor = package_map_.find(newPackageState.uid);
+
+ if (newPackageState.packageNames.empty()) {
+ if (cursor != package_map_.end()) {
+ package_map_.erase(cursor);
+ }
+ } else {
+ if (cursor != package_map_.end()) {
+ cursor->second = newPackageState.packageNames;
+ } else {
+ package_map_.insert({newPackageState.uid, newPackageState.packageNames});
+ }
+ }
+ return Status::ok();
+}
+
+Status NativePermissionController::populatePermissionState(PermissionEnum perm,
+ const std::vector<int>& uids) {
+ if (perm >= PermissionEnum::ENUM_SIZE || static_cast<int>(perm) < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+ std::lock_guard l{m_};
+ auto& cursor = permission_map_[static_cast<size_t>(perm)];
+ cursor = std::vector<uid_t>{uids.begin(), uids.end()};
+ // should be sorted
+ std::sort(cursor.begin(), cursor.end());
+ return Status::ok();
+}
+
+// -- End Binder methods
+
+Result<std::vector<std::string>> NativePermissionController::getPackagesForUid(uid_t uid) const {
+ uid = uid % AID_USER_OFFSET;
+ const auto fixed_package_opt = getFixedPackageName(uid);
+ if (fixed_package_opt.has_value()) {
+ return Result<std::vector<std::string>>{std::in_place_t{}, {fixed_package_opt.value()}};
+ }
+ std::lock_guard l{m_};
+ if (!is_package_populated_) return unexpected{::android::NO_INIT};
+ const auto cursor = package_map_.find(uid);
+ if (cursor != package_map_.end()) {
+ return cursor->second;
+ } else {
+ return unexpected{::android::BAD_VALUE};
+ }
+}
+
+Result<bool> NativePermissionController::validateUidPackagePair(
+ uid_t uid, const std::string& packageName) const {
+ uid = uid % AID_USER_OFFSET;
+ const auto fixed_package_opt = getFixedPackageName(uid);
+ if (fixed_package_opt.has_value()) {
+ return packageName == fixed_package_opt.value();
+ }
+ std::lock_guard l{m_};
+ if (!is_package_populated_) return unexpected{::android::NO_INIT};
+ const auto cursor = package_map_.find(uid);
+ return (cursor != package_map_.end()) &&
+ (std::find(cursor->second.begin(), cursor->second.end(), packageName) !=
+ cursor->second.end());
+}
+
+Result<bool> NativePermissionController::checkPermission(PermissionEnum perm, uid_t uid) const {
+ std::lock_guard l{m_};
+ const auto& uids = permission_map_[static_cast<size_t>(perm)];
+ if (!uids.empty()) {
+ return std::binary_search(uids.begin(), uids.end(), uid);
+ } else {
+ return unexpected{::android::NO_INIT};
+ }
+}
+
+} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/ValidatedAttributionSourceState.cpp b/services/audiopolicy/permission/ValidatedAttributionSourceState.cpp
new file mode 100644
index 0000000..2c32289
--- /dev/null
+++ b/services/audiopolicy/permission/ValidatedAttributionSourceState.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/ValidatedAttributionSourceState.h>
+
+#include <binder/IPCThreadState.h>
+#include <error/expected_utils.h>
+#include <utils/Log.h>
+
+namespace com::android::media::permission {
+
+using ::android::base::unexpected;
+
+Result<ValidatedAttributionSourceState> ValidatedAttributionSourceState::createFromBinderContext(
+ AttributionSourceState attr, const IPermissionProvider& provider) {
+ attr.pid = ::android::IPCThreadState::self()->getCallingPid();
+ attr.uid = ::android::IPCThreadState::self()->getCallingUid();
+ return createFromTrustedUidNoPackage(std::move(attr), provider);
+}
+
+Result<ValidatedAttributionSourceState>
+ValidatedAttributionSourceState::createFromTrustedUidNoPackage(
+ AttributionSourceState attr, const IPermissionProvider& provider) {
+ if (attr.packageName.has_value() && attr.packageName->size() != 0) {
+ if (VALUE_OR_RETURN(provider.validateUidPackagePair(attr.uid, attr.packageName.value()))) {
+ return ValidatedAttributionSourceState{std::move(attr)};
+ } else {
+ return unexpected{::android::PERMISSION_DENIED};
+ }
+ } else {
+ // For APIs which don't appropriately pass attribution sources or packages, we need
+ // to populate the package name with our best guess.
+ const auto packageNames = VALUE_OR_RETURN(provider.getPackagesForUid(attr.uid));
+ LOG_ALWAYS_FATAL_IF(packageNames.empty());
+ attr.packageName = std::move(packageNames[0]);
+ return ValidatedAttributionSourceState{std::move(attr)};
+ }
+}
+
+} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/include/media/IPermissionProvider.h b/services/audiopolicy/permission/include/media/IPermissionProvider.h
new file mode 100644
index 0000000..27a61ea
--- /dev/null
+++ b/services/audiopolicy/permission/include/media/IPermissionProvider.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <optional>
+#include <vector>
+
+#include <com/android/media/permission/PermissionEnum.h>
+#include <error/Result.h>
+
+namespace com::android::media::permission {
+
+class IPermissionProvider {
+ public:
+ // Get all package names which run under a certain app-id. Returns non-empty.
+ // Not user specific, since packages are across users. Special app-ids (system,
+ // shell, etc.) are handled. Fails if the provider does not know about the
+ // app-id.
+ virtual ::android::error::Result<std::vector<std::string>> getPackagesForUid(
+ uid_t uid) const = 0;
+ // True iff the provided package name runs under the app-id of uid.
+ // Special app-ids (system, shell, etc.) are handled.
+ // Fails if the provider does not know about the app-id.
+ virtual ::android::error::Result<bool> validateUidPackagePair(
+ uid_t uid, const std::string& packageName) const = 0;
+
+ // True iff the uid holds the permission (user aware).
+ // Fails with NO_INIT if cache hasn't been populated.
+ virtual ::android::error::Result<bool> checkPermission(PermissionEnum permission,
+ uid_t uid) const = 0;
+ virtual ~IPermissionProvider() = default;
+};
+} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/include/media/NativePermissionController.h b/services/audiopolicy/permission/include/media/NativePermissionController.h
new file mode 100644
index 0000000..d464023
--- /dev/null
+++ b/services/audiopolicy/permission/include/media/NativePermissionController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+
+#include "IPermissionProvider.h"
+
+#include <android-base/thread_annotations.h>
+#include <com/android/media/permission/BnNativePermissionController.h>
+
+namespace com::android::media::permission {
+
+class NativePermissionController : public BnNativePermissionController, public IPermissionProvider {
+ using Status = ::android::binder::Status;
+
+ public:
+ Status populatePackagesForUids(const std::vector<UidPackageState>& initialPackageStates) final;
+ Status updatePackagesForUid(const UidPackageState& newPackageState) final;
+ Status populatePermissionState(PermissionEnum permission, const std::vector<int>& uids) final;
+ // end binder methods
+
+ ::android::error::Result<std::vector<std::string>> getPackagesForUid(uid_t uid) const final;
+ ::android::error::Result<bool> validateUidPackagePair(
+ uid_t uid, const std::string& packageName) const final;
+ ::android::error::Result<bool> checkPermission(PermissionEnum permission,
+ uid_t uid) const final;
+
+ private:
+ mutable std::mutex m_;
+ // map of app_ids to the set of packages names which could run in them (should be 1)
+ std::unordered_map<uid_t, std::vector<std::string>> package_map_ GUARDED_BY(m_);
+ bool is_package_populated_ GUARDED_BY(m_);
+ // (logical) map of PermissionEnum to list of uids (not appid) which hold the perm
+ std::array<std::vector<uid_t>, static_cast<size_t>(PermissionEnum::ENUM_SIZE)> permission_map_
+ GUARDED_BY(m_);
+};
+} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/include/media/ValidatedAttributionSourceState.h b/services/audiopolicy/permission/include/media/ValidatedAttributionSourceState.h
new file mode 100644
index 0000000..8d9da05
--- /dev/null
+++ b/services/audiopolicy/permission/include/media/ValidatedAttributionSourceState.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/content/AttributionSourceState.h>
+#include <error/Result.h>
+
+#include "IPermissionProvider.h"
+
+namespace com::android::media::permission {
+
+using ::android::content::AttributionSourceState;
+using ::android::error::Result;
+
+class ValidatedAttributionSourceState {
+ public:
+ /**
+ * Validates an attribution source from within the context of a binder transaction.
+ * Overwrites the uid/pid and validates the packageName
+ */
+ static Result<ValidatedAttributionSourceState> createFromBinderContext(
+ AttributionSourceState attr, const IPermissionProvider& provider);
+
+ /**
+ * Creates a ValidatedAttributionSourceState in cases where the source is passed from a
+ * trusted entity which already performed validation.
+ */
+ static ValidatedAttributionSourceState createFromTrustedSource(AttributionSourceState attr) {
+ return ValidatedAttributionSourceState(attr);
+ }
+
+ /**
+ * Create a ValidatedAttribubtionSourceState in cases where the uid/pid is trusted, but the
+ * packages have not been validated. Proper use of the previous two methods should avoid the
+ * necessity of this, but it is useful for migration purposes as well as testing this class.
+ */
+ static Result<ValidatedAttributionSourceState> createFromTrustedUidNoPackage(
+ AttributionSourceState attr, const IPermissionProvider& provider);
+
+ operator AttributionSourceState() const { return state_; }
+
+ operator const AttributionSourceState&() const { return state_; }
+
+ AttributionSourceState unwrapInto() && { return std::move(state_); }
+
+ bool operator==(const ValidatedAttributionSourceState& other) const {
+ return operator==(other.state_);
+ }
+
+ bool operator==(const AttributionSourceState& other) const { return state_ == other; }
+
+ private:
+ ValidatedAttributionSourceState(AttributionSourceState attr) : state_(attr) {}
+
+ AttributionSourceState state_;
+};
+} // namespace com::android::media::permission
diff --git a/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
new file mode 100644
index 0000000..3f6b787
--- /dev/null
+++ b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/NativePermissionController.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/expected.h>
+
+using ::android::base::unexpected;
+using ::android::binder::Status;
+using com::android::media::permission::NativePermissionController;
+using com::android::media::permission::PermissionEnum;
+using com::android::media::permission::UidPackageState;
+
+class NativePermissionControllerTest : public ::testing::Test {
+ protected:
+ android::sp<NativePermissionController> holder_ =
+ android::sp<NativePermissionController>::make();
+ NativePermissionController& controller_ = *holder_;
+};
+static UidPackageState createState(uid_t uid, std::vector<std::string> packagesNames) {
+ UidPackageState out{};
+ out.uid = uid;
+ out.packageNames = std::move(packagesNames);
+ return out;
+}
+
+static std::vector<std::string> makeVector(const char* one) {
+ return {one};
+}
+
+static std::vector<std::string> makeVector(const char* one, const char* two) {
+ return {one, two};
+}
+
+#define UNWRAP_EQ(expr, desired_expr) \
+ do { \
+ auto tmp_ = (expr); \
+ EXPECT_TRUE(tmp_.has_value()); \
+ if (tmp_.has_value()) EXPECT_EQ(*tmp_, desired_expr); \
+ } while (0)
+
+// --- Tests for non-populated ----
+TEST_F(NativePermissionControllerTest, getPackagesForUid_NotPopulated) {
+ // Verify errors are returned
+ EXPECT_EQ(controller_.getPackagesForUid(10000), unexpected{android::NO_INIT});
+ EXPECT_EQ(controller_.getPackagesForUid(10001), unexpected{android::NO_INIT});
+
+ // fixed uids should work
+ UNWRAP_EQ(controller_.getPackagesForUid(1000), makeVector("system"));
+}
+
+TEST_F(NativePermissionControllerTest, validateUidPackagePair_NotPopulated) {
+ // Verify errors are returned
+ EXPECT_EQ(controller_.validateUidPackagePair(10000, "com.package"),
+ unexpected{android::NO_INIT});
+
+ // fixed uids should work
+ UNWRAP_EQ(controller_.validateUidPackagePair(1000, "system"), true);
+}
+// --- Tests for populatePackagesForUids ----
+TEST_F(NativePermissionControllerTest, populatePackages_EmptyInput) {
+ std::vector<UidPackageState> input;
+
+ // succeeds
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ // Verify unknown uid behavior
+ const auto res1 = controller_.getPackagesForUid(10000);
+ ASSERT_FALSE(res1.has_value());
+ EXPECT_EQ(res1.error(), ::android::BAD_VALUE);
+}
+
+TEST_F(NativePermissionControllerTest, populatePackages_ValidInput) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2"}),
+ createState(10001, {"com.example2.app1"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ UNWRAP_EQ(controller_.getPackagesForUid(10000),
+ makeVector("com.example.app1", "com.example.app2"));
+ UNWRAP_EQ(controller_.getPackagesForUid(10001), makeVector("com.example2.app1"));
+}
+
+// --- Tests for updatePackagesForUid ---
+TEST_F(NativePermissionControllerTest, updatePackages_NewUid) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2"}),
+ createState(10001, {"com.example2.app1"}),
+ };
+ UidPackageState newState = createState(12000, {"com.example.other"});
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+ EXPECT_TRUE(controller_.updatePackagesForUid(newState).isOk());
+
+ // Verify the results: only the updated package should be changed
+ UNWRAP_EQ(controller_.getPackagesForUid(10000),
+ makeVector("com.example.app1", "com.example.app2"));
+ UNWRAP_EQ(controller_.getPackagesForUid(10001), makeVector("com.example2.app1"));
+ UNWRAP_EQ(controller_.getPackagesForUid(12000), makeVector("com.example.other"));
+}
+
+TEST_F(NativePermissionControllerTest, updatePackages_ExistingUid) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2", "com.example.app3"}),
+ createState(10001, {"com.example2.app1"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+ // Update packages for existing uid
+ UidPackageState newState = createState(10000, {"com.example.other", "com.example.new"});
+ EXPECT_TRUE(controller_.updatePackagesForUid(newState).isOk());
+
+ // Verify update
+ UNWRAP_EQ(controller_.getPackagesForUid(10000),
+ makeVector("com.example.other", "com.example.new"));
+}
+
+TEST_F(NativePermissionControllerTest, updatePackages_EmptyRemovesEntry) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ UidPackageState newState{}; // Empty package list
+ newState.uid = 10000;
+ EXPECT_TRUE(controller_.updatePackagesForUid(newState).isOk());
+ // getPackages for unknown UID should error out
+ const auto res = controller_.getPackagesForUid(10000);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::BAD_VALUE);
+}
+
+TEST_F(NativePermissionControllerTest, validateUidPackagePair_ValidPair) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ UNWRAP_EQ(controller_.validateUidPackagePair(10000, "com.example.app1"), true);
+}
+
+TEST_F(NativePermissionControllerTest, validateUidPackagePair_InvalidPackage) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ UNWRAP_EQ(controller_.validateUidPackagePair(10000, "com.example.other"), false);
+}
+
+TEST_F(NativePermissionControllerTest, validateUidPackagePair_UnknownUid) {
+ std::vector<UidPackageState> input{
+ createState(10000, {"com.example.app1", "com.example.app2"}),
+ };
+
+ EXPECT_TRUE(controller_.populatePackagesForUids(input).isOk());
+
+ UNWRAP_EQ(controller_.validateUidPackagePair(12000, "any.package"), false);
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_InvalidPermission) {
+ EXPECT_EQ(controller_.populatePermissionState(PermissionEnum::ENUM_SIZE, {}).exceptionCode(),
+ Status::EX_ILLEGAL_ARGUMENT);
+ EXPECT_EQ(controller_
+ .populatePermissionState(
+ static_cast<PermissionEnum>(
+ static_cast<int>(PermissionEnum::ENUM_SIZE) + 1),
+ {})
+ .exceptionCode(),
+ Status::EX_ILLEGAL_ARGUMENT);
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_HoldsPermission) {
+ // Unsorted
+ std::vector<int> uids{3, 1, 2, 4, 5};
+
+ EXPECT_TRUE(
+ controller_.populatePermissionState(PermissionEnum::MODIFY_AUDIO_ROUTING, uids).isOk());
+
+ EXPECT_TRUE(*controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 3));
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_DoesNotHoldPermission) {
+ // Unsorted
+ std::vector<int> uids{3, 1, 2, 4, 5};
+
+ EXPECT_TRUE(
+ controller_.populatePermissionState(PermissionEnum::MODIFY_AUDIO_ROUTING, uids).isOk());
+
+ EXPECT_FALSE(*controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 6));
+}
+
+TEST_F(NativePermissionControllerTest, populatePermissionState_NotInitialized) {
+ const auto res = controller_.checkPermission(PermissionEnum::MODIFY_AUDIO_ROUTING, 3);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::NO_INIT);
+}
diff --git a/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp b/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
new file mode 100644
index 0000000..efc318b
--- /dev/null
+++ b/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/ValidatedAttributionSourceState.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/expected.h>
+#include <media/IPermissionProvider.h>
+
+using ::android::base::unexpected;
+using ::android::binder::Status;
+using ::android::content::AttributionSourceState;
+using ::android::error::Result;
+using ::com::android::media::permission::IPermissionProvider;
+using ::com::android::media::permission::PermissionEnum;
+using ::com::android::media::permission::ValidatedAttributionSourceState;
+using ::testing::Return;
+
+class MockPermissionProvider : public IPermissionProvider {
+ public:
+ MOCK_METHOD(Result<std::vector<std::string>>, getPackagesForUid, (uid_t uid),
+ (override, const));
+ MOCK_METHOD(Result<bool>, validateUidPackagePair, (uid_t uid, const std::string&),
+ (override, const));
+ MOCK_METHOD(Result<bool>, checkPermission, (PermissionEnum perm, uid_t), (override, const));
+};
+
+class ValidatedAttributionSourceStateTest : public ::testing::Test {
+ protected:
+ MockPermissionProvider mMockProvider;
+ const uid_t mUid = 10001;
+ const std::vector<std::string> mPackageList{"com.package1", "com.package2"};
+};
+
+#define UNWRAP_EQ(expr, desired_expr) \
+ do { \
+ auto tmp_ = (expr); \
+ EXPECT_TRUE(tmp_.has_value()); \
+ if (tmp_.has_value()) EXPECT_EQ(*tmp_, desired_expr); \
+ } while (0)
+
+TEST_F(ValidatedAttributionSourceStateTest, providedPackageValid) {
+ const std::string package = "com.package1";
+ EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(true));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ attr.packageName = package;
+ UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
+ attr);
+}
+
+TEST_F(ValidatedAttributionSourceStateTest, providedPackageInvalid) {
+ const std::string package = "com.package.spoof";
+ EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(false));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ attr.packageName = package;
+ const auto res =
+ ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::PERMISSION_DENIED);
+}
+
+TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenMissingPackage) {
+ EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ AttributionSourceState expectedAttr;
+ expectedAttr.uid = mUid;
+ expectedAttr.packageName = "com.package1";
+ UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
+ expectedAttr);
+}
+
+TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenEmptyPackage) {
+ EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ attr.packageName = std::string{};
+ AttributionSourceState expectedAttr;
+ expectedAttr.uid = mUid;
+ expectedAttr.packageName = "com.package1";
+ UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
+ expectedAttr);
+}
+
+TEST_F(ValidatedAttributionSourceStateTest, controllerNotInitialized) {
+ EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
+ .WillOnce(Return(unexpected{::android::NO_INIT}));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ attr.packageName = std::string{};
+ AttributionSourceState expectedAttr;
+ expectedAttr.uid = mUid;
+ expectedAttr.packageName = "com.package1";
+ const auto res =
+ ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::NO_INIT);
+}
+
+TEST_F(ValidatedAttributionSourceStateTest, uidNotFound) {
+ EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
+ .WillOnce(Return(unexpected{::android::BAD_VALUE}));
+ AttributionSourceState attr;
+ attr.uid = mUid;
+ attr.packageName = std::string{};
+ const auto res =
+ ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
+ ASSERT_FALSE(res.has_value());
+ EXPECT_EQ(res.error(), ::android::BAD_VALUE);
+}
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index 9b7a470..84458f9 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -11,6 +11,8 @@
cc_defaults {
name: "libaudiopolicyservice_dependencies",
+ header_libs: ["audiopolicyservicelocal_headers"],
+
shared_libs: [
"android.media.audiopolicy-aconfig-cc",
"audioclient-types-aidl-cpp",
@@ -52,6 +54,8 @@
],
static_libs: [
+ "audio-permission-aidl-cpp",
+ "audiopermissioncontroller",
"libaudiopolicycomponents",
"libeffectsconfig",
],
@@ -81,10 +85,11 @@
],
static_libs: [
- "framework-permission-aidl-cpp",
+ "framework-permission-aidl-cpp", // TODO remove when unnnecessary
],
header_libs: [
+ "audiopolicyservicelocal_headers",
"libaudiohal_headers",
"libaudiopolicycommon",
"libaudiopolicyengine_interface_headers",
@@ -112,5 +117,16 @@
cc_library_headers {
name: "libaudiopolicyservice_headers",
host_supported: true,
- export_include_dirs: ["."],
+ export_include_dirs: [
+ ".",
+ "include",
+ ],
+}
+
+cc_library_headers {
+ name: "audiopolicyservicelocal_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ header_libs: ["audiopermissioncontroller_headers"],
+ export_header_lib_headers: ["audiopermissioncontroller_headers"],
}
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index a862037..8ba2814 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#define LOG_TAG "AudioPolicyIntefaceImpl"
+#define LOG_TAG "AudioPolicyInterfaceImpl"
//#define LOG_NDEBUG 0
#include "AudioPolicyService.h"
#include "AudioRecordClient.h"
#include "TypeConverter.h"
+
+#include <android/content/AttributionSourceState.h>
#include <android_media_audiopolicy.h>
#include <media/AidlConversion.h>
#include <media/AudioPolicy.h>
@@ -27,7 +29,6 @@
#include <media/MediaMetricsItem.h>
#include <media/PolicyAidlConversion.h>
#include <utils/Log.h>
-#include <android/content/AttributionSourceState.h>
#define VALUE_OR_RETURN_BINDER_STATUS(x) \
({ auto _tmp = (x); \
@@ -49,6 +50,7 @@
namespace audiopolicy_flags = android::media::audiopolicy;
using binder::Status;
using aidl_utils::binderStatusFromStatusT;
+using com::android::media::permission::NativePermissionController;
using content::AttributionSourceState;
using media::audio::common::AudioConfig;
using media::audio::common::AudioConfigBase;
@@ -1020,6 +1022,32 @@
return Status::ok();
}
+Status AudioPolicyService::setDeviceAbsoluteVolumeEnabled(const AudioDevice& deviceAidl,
+ bool enabled,
+ AudioStreamType streamToDriveAbsAidl) {
+ audio_stream_type_t streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+ audio_devices_t deviceType;
+ std::string address;
+ RETURN_BINDER_STATUS_IF_ERROR(
+ aidl2legacy_AudioDevice_audio_device(deviceAidl, &deviceType, &address));
+
+ if (mAudioPolicyManager == nullptr) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ if (!settingsAllowed()) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
+ }
+ if (uint32_t(streamToDriveAbs) >= AUDIO_STREAM_PUBLIC_CNT) {
+ return binderStatusFromStatusT(BAD_VALUE);
+ }
+ audio_utils::lock_guard _l(mMutex);
+ AutoCallerClear acc;
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setDeviceAbsoluteVolumeEnabled(deviceType, address.c_str(),
+ enabled, streamToDriveAbs));
+}
+
Status AudioPolicyService::initStreamVolume(AudioStreamType streamAidl,
int32_t indexMinAidl,
int32_t indexMaxAidl) {
@@ -2643,4 +2671,9 @@
mAudioPolicyManager->clearPreferredMixerAttributes(&attr, portId, uid));
}
+Status AudioPolicyService::getPermissionController(sp<INativePermissionController>* out) {
+ *out = mPermissionController;
+ return Status::ok();
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index f372c71..cc67481 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -79,6 +79,7 @@
BINDER_METHOD_ENTRY(startInput) \
BINDER_METHOD_ENTRY(stopInput) \
BINDER_METHOD_ENTRY(releaseInput) \
+BINDER_METHOD_ENTRY(setDeviceAbsoluteVolumeEnabled) \
BINDER_METHOD_ENTRY(initStreamVolume) \
BINDER_METHOD_ENTRY(setStreamVolumeIndex) \
BINDER_METHOD_ENTRY(getStreamVolumeIndex) \
@@ -164,6 +165,7 @@
BINDER_METHOD_ENTRY(getPreferredMixerAttributes) \
BINDER_METHOD_ENTRY(clearPreferredMixerAttributes) \
BINDER_METHOD_ENTRY(getRegisteredPolicyMixes) \
+BINDER_METHOD_ENTRY(getPermissionController) \
\
// singleton for Binder Method Statistics for IAudioPolicyService
static auto& getIAudioPolicyServiceStatistics() {
@@ -226,7 +228,9 @@
mCaptureStateNotifier(false),
mCreateAudioPolicyManager(createAudioPolicyManager),
mDestroyAudioPolicyManager(destroyAudioPolicyManager),
- mUsecaseValidator(media::createUsecaseValidator()) {
+ mUsecaseValidator(media::createUsecaseValidator()),
+ mPermissionController(sp<NativePermissionController>::make())
+{
setMinSchedulerPolicy(SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
setInheritRt(true);
}
@@ -313,6 +317,10 @@
AudioSystem::audioPolicyReady();
}
+const IPermissionProvider& AudioPolicyService::getPermissionProvider() const {
+ return *mPermissionController;
+}
+
void AudioPolicyService::onAudioSystemReady() {
sp<AudioPolicyEffects> audioPolicyEffects;
{
@@ -1321,6 +1329,7 @@
case TRANSACTION_setPhoneState:
//FIXME: Allow setForceUse calls from system apps until a better use case routing API is available
// case TRANSACTION_setForceUse:
+ case TRANSACTION_setDeviceAbsoluteVolumeEnabled:
case TRANSACTION_initStreamVolume:
case TRANSACTION_setStreamVolumeIndex:
case TRANSACTION_setVolumeIndexForAttributes:
@@ -1382,6 +1391,17 @@
break;
}
+ switch (code) {
+ case TRANSACTION_getPermissionController: {
+ if (!isAudioServerOrSystemServerUid(IPCThreadState::self()->getCallingUid())) {
+ ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ return INVALID_OPERATION;
+ }
+ }
+ }
+
const std::string methodName = getIAudioPolicyServiceStatistics().getMethodForCode(code);
mediautils::TimeCheck check(
std::string("IAudioPolicyService::").append(methodName),
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index eb9d081..699cacf 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -21,6 +21,7 @@
#include <android/media/GetSpatializerResponse.h>
#include <android-base/thread_annotations.h>
#include <audio_utils/mutex.h>
+#include <com/android/media/permission/INativePermissionController.h>
#include <cutils/misc.h>
#include <cutils/config_utils.h>
#include <cutils/compiler.h>
@@ -35,6 +36,8 @@
#include <media/ToneGenerator.h>
#include <media/AudioEffect.h>
#include <media/AudioPolicy.h>
+#include <media/IAudioPolicyServiceLocal.h>
+#include <media/NativePermissionController.h>
#include <media/UsecaseValidator.h>
#include <mediautils/ServiceUtilities.h>
#include "AudioPolicyEffects.h"
@@ -68,12 +71,16 @@
}
using ::android::media::audiopolicy::AudioRecordClient;
+using ::com::android::media::permission::INativePermissionController;
+using ::com::android::media::permission::NativePermissionController;
+using ::com::android::media::permission::IPermissionProvider;
class AudioPolicyService :
public BinderService<AudioPolicyService>,
public media::BnAudioPolicyService,
public IBinder::DeathRecipient,
- public SpatializerPolicyCallback
+ public SpatializerPolicyCallback,
+ public media::IAudioPolicyServiceLocal
{
friend class sp<AudioPolicyService>;
@@ -121,6 +128,9 @@
binder::Status startInput(int32_t portId) override;
binder::Status stopInput(int32_t portId) override;
binder::Status releaseInput(int32_t portId) override;
+ binder::Status setDeviceAbsoluteVolumeEnabled(const AudioDevice& device,
+ bool enabled,
+ AudioStreamType streamToDriveAbs) override;
binder::Status initStreamVolume(AudioStreamType stream, int32_t indexMin,
int32_t indexMax) override;
binder::Status setStreamVolumeIndex(AudioStreamType stream,
@@ -314,8 +324,14 @@
binder::Status getRegisteredPolicyMixes(
std::vector <::android::media::AudioMix>* mixes) override;
+ // Should only be called by AudioService to push permission data down to audioserver
+ binder::Status getPermissionController(sp<INativePermissionController>* out) override;
+
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+ // -- IAudioPolicyLocal methods
+ const IPermissionProvider& getPermissionProvider() const override;
+
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -1048,6 +1064,7 @@
CreateAudioPolicyManagerInstance mCreateAudioPolicyManager;
DestroyAudioPolicyManagerInstance mDestroyAudioPolicyManager;
std::unique_ptr<media::UsecaseValidator> mUsecaseValidator;
+ const sp<NativePermissionController> mPermissionController;
};
} // namespace android
diff --git a/services/audiopolicy/service/include/media/IAudioPolicyServiceLocal.h b/services/audiopolicy/service/include/media/IAudioPolicyServiceLocal.h
new file mode 100644
index 0000000..6776ff9
--- /dev/null
+++ b/services/audiopolicy/service/include/media/IAudioPolicyServiceLocal.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <media/IPermissionProvider.h>
+#include <utils/RefBase.h>
+
+namespace android::media {
+
+class IAudioPolicyServiceLocal : public virtual RefBase {
+ public:
+ virtual const ::com::android::media::permission::IPermissionProvider&
+ getPermissionProvider() const = 0;
+
+ virtual ~IAudioPolicyServiceLocal() = default;
+};
+
+} // namespace android::media
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index 30894c1..ba0a1eb 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -70,6 +70,22 @@
return BAD_VALUE;
}
*input = mNextIoHandle++;
+ mOpenedInputs.insert(*input);
+ ALOGD("%s: opened input %d", __func__, *input);
+ return NO_ERROR;
+ }
+
+ status_t closeInput(audio_io_handle_t input) override {
+ if (mOpenedInputs.erase(input) != 1) {
+ if (input >= mNextIoHandle) {
+ ALOGE("%s: I/O handle %d has not been allocated yet (next is %d)",
+ __func__, input, mNextIoHandle);
+ } else {
+ ALOGE("%s: Attempt to close input %d twice", __func__, input);
+ }
+ return BAD_VALUE;
+ }
+ ALOGD("%s: closed input %d", __func__, input);
return NO_ERROR;
}
@@ -124,6 +140,8 @@
return &it->second;
};
+ size_t getOpenedInputsCount() const { return mOpenedInputs.size(); }
+
audio_module_handle_t peekNextModuleHandle() const { return mNextModuleHandle; }
void swapAllowedModuleNames(std::set<std::string>&& names = {}) {
@@ -256,6 +274,7 @@
std::set<audio_format_t> mSupportedFormats;
std::set<audio_channel_mask_t> mSupportedChannelMasks;
std::map<audio_port_handle_t, bool> mTracksInternalMute;
+ std::set<audio_io_handle_t> mOpenedInputs;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index aa7c9cd..34ceeab 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -34,6 +34,7 @@
using AudioPolicyManager::getInputs;
using AudioPolicyManager::getAvailableOutputDevices;
using AudioPolicyManager::getAvailableInputDevices;
+ using AudioPolicyManager::checkInputsForDevice;
using AudioPolicyManager::setSurroundFormatEnabled;
using AudioPolicyManager::releaseMsdOutputPatches;
using AudioPolicyManager::setMsdOutputPatches;
@@ -44,6 +45,7 @@
using AudioPolicyManager::deviceToAudioPort;
using AudioPolicyManager::handleDeviceConfigChange;
uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
+ HwModuleCollection getHwModules() const { return mHwModules; }
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 4f9e98e..4c98687 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -1204,6 +1204,35 @@
"", "", AUDIO_FORMAT_DEFAULT));
}
+TEST_F(AudioPolicyManagerTestWithConfigurationFile, CheckInputsForDeviceClosesStreams) {
+ mClient->addSupportedFormat(AUDIO_FORMAT_PCM_16_BIT);
+ mClient->addSupportedFormat(AUDIO_FORMAT_PCM_24_BIT_PACKED);
+ mClient->addSupportedChannelMask(AUDIO_CHANNEL_IN_MONO);
+ mClient->addSupportedChannelMask(AUDIO_CHANNEL_IN_STEREO);
+ // Since 'checkInputsForDevice' is called as part of the 'setDeviceConnectionState',
+ // call it directly here, as we need to ensure that it does not keep all intermediate
+ // streams opened, as it may cause a rejection from the HAL based on the cap.
+ const size_t streamCountBefore = mClient->getOpenedInputsCount();
+ sp<DeviceDescriptor> device = mManager->getHwModules().getDeviceDescriptor(
+ AUDIO_DEVICE_IN_USB_DEVICE, "", "", AUDIO_FORMAT_DEFAULT, true /*allowToCreate*/);
+ ASSERT_NE(nullptr, device.get());
+ EXPECT_EQ(NO_ERROR,
+ mManager->checkInputsForDevice(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE));
+ EXPECT_EQ(streamCountBefore, mClient->getOpenedInputsCount());
+}
+
+TEST_F(AudioPolicyManagerTestWithConfigurationFile, SetDeviceConnectionStateClosesStreams) {
+ mClient->addSupportedFormat(AUDIO_FORMAT_PCM_16_BIT);
+ mClient->addSupportedFormat(AUDIO_FORMAT_PCM_24_BIT_PACKED);
+ mClient->addSupportedChannelMask(AUDIO_CHANNEL_IN_MONO);
+ mClient->addSupportedChannelMask(AUDIO_CHANNEL_IN_STEREO);
+ const size_t streamCountBefore = mClient->getOpenedInputsCount();
+ EXPECT_EQ(NO_ERROR, mManager->setDeviceConnectionState(AUDIO_DEVICE_IN_USB_DEVICE,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "", "", AUDIO_FORMAT_DEFAULT));
+ EXPECT_EQ(streamCountBefore, mClient->getOpenedInputsCount());
+}
+
class AudioPolicyManagerTestDynamicPolicy : public AudioPolicyManagerTestWithConfigurationFile {
protected:
void TearDown() override;
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 1a299c6..bbc19fa 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -99,7 +99,7 @@
<route type="mix" sink="primary input"
sources="Built-In Mic,Hdmi-In Mic,USB Device In"/>
<route type="mix" sink="voip_tx"
- sources="Built-In Mic"/>
+ sources="Built-In Mic,USB Device In"/>
<route type="mix" sink="Hdmi"
sources="primary output"/>
<route type="mix" sink="BT SCO"
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.cc b/services/camera/virtualcamera/VirtualCameraDevice.cc
index ba4ea6b..e455378 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.cc
+++ b/services/camera/virtualcamera/VirtualCameraDevice.cc
@@ -618,6 +618,10 @@
return maxResolution.value();
}
+int VirtualCameraDevice::allocateInputStreamId() {
+ return mNextInputStreamId++;
+}
+
std::shared_ptr<VirtualCameraDevice> VirtualCameraDevice::sharedFromThis() {
// SharedRefBase which BnCameraDevice inherits from breaks
// std::enable_shared_from_this. This is recommended replacement for
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.h b/services/camera/virtualcamera/VirtualCameraDevice.h
index 8a7875f..296383f 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -104,6 +104,9 @@
// Returns largest supported input resolution.
Resolution getMaxInputResolution() const;
+ // Allocate and return next id for input stream (input surface).
+ int allocateInputStreamId();
+
// Maximal number of RAW streams - virtual camera doesn't support RAW streams.
static constexpr int32_t kMaxNumberOfRawStreams = 0;
@@ -126,9 +129,8 @@
// Default JPEG orientation.
static constexpr uint8_t kDefaultJpegOrientation = 0;
- // TODO(b/342674104) CDD requires <= 15.
- // Change this to lower value after confirming it doesn't cause any issue (timeouts).
- static constexpr int kMinFps = 15;
+ // Lowest min fps advertised in supported fps ranges.
+ static constexpr int kMinFps = 1;
// Default Make and Model for Exif
static constexpr char kDefaultMakeAndModel[] = "Android Virtual Camera";
@@ -149,6 +151,8 @@
const std::vector<
aidl::android::companion::virtualcamera::SupportedStreamConfiguration>
mSupportedInputConfigurations;
+
+ std::atomic_int mNextInputStreamId;
};
} // namespace virtualcamera
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index 08b2698..9a5bd1e 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -313,7 +313,8 @@
: mCameraDeviceCallback(cameraDeviceCallback),
mInputSurfaceSize(inputSurfaceSize),
mReportedSensorSize(reportedSensorSize),
- mSessionContext(sessionContext) {
+ mSessionContext(sessionContext),
+ mInputSurfaceFuture(mInputSurfacePromise.get_future()) {
}
VirtualCameraRenderThread::~VirtualCameraRenderThread() {
@@ -373,7 +374,7 @@
}
sp<Surface> VirtualCameraRenderThread::getInputSurface() {
- return mInputSurfacePromise.get_future().get();
+ return mInputSurfaceFuture.get();
}
std::unique_ptr<ProcessCaptureRequestTask>
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index ce8965f..b23c30c 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -204,6 +204,7 @@
std::unique_ptr<EglSurfaceTexture> mEglSurfaceTexture;
std::promise<sp<Surface>> mInputSurfacePromise;
+ std::shared_future<sp<Surface>> mInputSurfaceFuture;
};
} // namespace virtualcamera
diff --git a/services/camera/virtualcamera/VirtualCameraService.cc b/services/camera/virtualcamera/VirtualCameraService.cc
index b78f120..724ec62 100644
--- a/services/camera/virtualcamera/VirtualCameraService.cc
+++ b/services/camera/virtualcamera/VirtualCameraService.cc
@@ -66,7 +66,7 @@
constexpr int kVgaWidth = 640;
constexpr int kVgaHeight = 480;
constexpr int kMaxFps = 60;
-constexpr int kTestCameraInputFps = 30;
+constexpr int kTestCameraDefaultInputFps = 30;
constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
constexpr char kHelp[] = "help";
@@ -78,6 +78,7 @@
Options:
--camera_id=(ID) - override numerical ID for test camera instance
--lens_facing=(front|back|external) - specifies lens facing for test camera instance
+ --input_fps=(fps) - specify input fps for test camera, valid values are from 1 to 1000
* disable_test_camera
)";
constexpr char kCreateVirtualDevicePermission[] =
@@ -421,6 +422,18 @@
}
}
+ std::optional<int> inputFps;
+ it = options.find("input_fps");
+ if (it != options.end()) {
+ inputFps = parseInt(it->second);
+ if (!inputFps.has_value() || inputFps.value() < 1 ||
+ inputFps.value() > 1000) {
+ dprintf(err, "Invalid input fps: %s\n, must be integer in <1,1000> range.",
+ it->second.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ }
+
sp<BBinder> token = sp<BBinder>::make();
mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
@@ -432,7 +445,8 @@
.maxFps = kMaxFps});
configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
configuration.virtualCameraCallback =
- ndk::SharedRefBase::make<VirtualCameraTestInstance>(kTestCameraInputFps);
+ ndk::SharedRefBase::make<VirtualCameraTestInstance>(
+ inputFps.value_or(kTestCameraDefaultInputFps));
registerCamera(mTestCameraToken, configuration, cameraId.value_or(sNextId++),
kDefaultDeviceId, &ret);
if (ret) {
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index 7f0adc3..e1815c7 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -212,6 +212,27 @@
return Resolution(inputConfig.width, inputConfig.height);
}
+std::optional<Resolution> resolutionFromSurface(const sp<Surface> surface) {
+ Resolution res{0, 0};
+ if (surface == nullptr) {
+ ALOGE("%s: Cannot get resolution from null surface", __func__);
+ return std::nullopt;
+ }
+
+ int status = surface->query(NATIVE_WINDOW_WIDTH, &res.width);
+ if (status != NO_ERROR) {
+ ALOGE("%s: Failed to get width from surface", __func__);
+ return std::nullopt;
+ }
+
+ status = surface->query(NATIVE_WINDOW_HEIGHT, &res.height);
+ if (status != NO_ERROR) {
+ ALOGE("%s: Failed to get height from surface", __func__);
+ return std::nullopt;
+ }
+ return res;
+}
+
std::optional<SupportedStreamConfiguration> pickInputConfigurationForStreams(
const std::vector<Stream>& requestedStreams,
const std::vector<SupportedStreamConfiguration>& supportedInputConfigs) {
@@ -292,13 +313,13 @@
ndk::ScopedAStatus VirtualCameraSession::close() {
ALOGV("%s", __func__);
-
- if (mVirtualCameraClientCallback != nullptr) {
- mVirtualCameraClientCallback->onStreamClosed(/*streamId=*/0);
- }
-
{
std::lock_guard<std::mutex> lock(mLock);
+
+ if (mVirtualCameraClientCallback != nullptr) {
+ mVirtualCameraClientCallback->onStreamClosed(mCurrentInputStreamId);
+ }
+
if (mRenderThread != nullptr) {
mRenderThread->stop();
mRenderThread = nullptr;
@@ -339,6 +360,7 @@
}
sp<Surface> inputSurface = nullptr;
+ int inputStreamId = -1;
std::optional<SupportedStreamConfiguration> inputConfig;
{
std::lock_guard<std::mutex> lock(mLock);
@@ -358,13 +380,49 @@
__func__, in_requestedConfiguration.toString().c_str());
return cameraStatus(Status::ILLEGAL_ARGUMENT);
}
- if (mRenderThread == nullptr) {
- mRenderThread = std::make_unique<VirtualCameraRenderThread>(
- mSessionContext, resolutionFromInputConfig(*inputConfig),
- virtualCamera->getMaxInputResolution(), mCameraDeviceCallback);
- mRenderThread->start();
- inputSurface = mRenderThread->getInputSurface();
+
+ if (mRenderThread != nullptr) {
+ // If there's already a render thread, it means this is not a first
+ // configuration call. If the surface has the same resolution and pixel
+ // format as the picked config, we don't need to do anything, the current
+ // render thread is capable of serving new set of configuration. However
+ // if it differens, we need to discard the current surface and
+ // reinitialize the render thread.
+
+ std::optional<Resolution> currentInputResolution =
+ resolutionFromSurface(mRenderThread->getInputSurface());
+ if (currentInputResolution.has_value() &&
+ *currentInputResolution == resolutionFromInputConfig(*inputConfig)) {
+ ALOGI(
+ "%s: Newly configured set of streams matches existing client "
+ "surface (%dx%d)",
+ __func__, currentInputResolution->width,
+ currentInputResolution->height);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ if (mVirtualCameraClientCallback != nullptr) {
+ mVirtualCameraClientCallback->onStreamClosed(mCurrentInputStreamId);
+ }
+
+ ALOGV(
+ "%s: Newly requested output streams are not suitable for "
+ "pre-existing surface (%dx%d), creating new surface (%dx%d)",
+ __func__, currentInputResolution->width,
+ currentInputResolution->height, inputConfig->width,
+ inputConfig->height);
+
+ mRenderThread->flush();
+ mRenderThread->stop();
}
+
+ mRenderThread = std::make_unique<VirtualCameraRenderThread>(
+ mSessionContext, resolutionFromInputConfig(*inputConfig),
+ virtualCamera->getMaxInputResolution(), mCameraDeviceCallback);
+ mRenderThread->start();
+ inputSurface = mRenderThread->getInputSurface();
+ inputStreamId = mCurrentInputStreamId =
+ virtualCamera->allocateInputStreamId();
}
if (mVirtualCameraClientCallback != nullptr && inputSurface != nullptr) {
@@ -372,7 +430,7 @@
// support for multiple input streams is implemented. For now we always
// create single texture.
mVirtualCameraClientCallback->onStreamConfigured(
- /*streamId=*/0, aidl::android::view::Surface(inputSurface.get()),
+ inputStreamId, aidl::android::view::Surface(inputSurface.get()),
inputConfig->width, inputConfig->height, inputConfig->pixelFormat);
}
@@ -519,6 +577,7 @@
std::shared_ptr<ICameraDeviceCallback> cameraCallback = nullptr;
RequestSettings requestSettings;
+ int currentInputStreamId;
{
std::lock_guard<std::mutex> lock(mLock);
@@ -537,6 +596,7 @@
requestSettings = createSettingsFromMetadata(mCurrentRequestMetadata);
cameraCallback = mCameraDeviceCallback;
+ currentInputStreamId = mCurrentInputStreamId;
}
if (cameraCallback == nullptr) {
@@ -574,7 +634,7 @@
if (mVirtualCameraClientCallback != nullptr) {
auto status = mVirtualCameraClientCallback->onProcessCaptureRequest(
- /*streamId=*/0, request.frameNumber);
+ currentInputStreamId, request.frameNumber);
if (!status.isOk()) {
ALOGE(
"Failed to invoke onProcessCaptureRequest client callback for frame "
diff --git a/services/camera/virtualcamera/VirtualCameraSession.h b/services/camera/virtualcamera/VirtualCameraSession.h
index 556314f..c2044b9 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.h
+++ b/services/camera/virtualcamera/VirtualCameraSession.h
@@ -143,6 +143,8 @@
GUARDED_BY(mLock);
std::unique_ptr<VirtualCameraRenderThread> mRenderThread GUARDED_BY(mLock);
+
+ int mCurrentInputStreamId GUARDED_BY(mLock);
};
} // namespace virtualcamera
diff --git a/services/camera/virtualcamera/VirtualCameraTestInstance.cc b/services/camera/virtualcamera/VirtualCameraTestInstance.cc
index bae871d..ff4a2d8 100644
--- a/services/camera/virtualcamera/VirtualCameraTestInstance.cc
+++ b/services/camera/virtualcamera/VirtualCameraTestInstance.cc
@@ -107,7 +107,7 @@
}
// Render the test pattern and update timestamp.
- testPatternProgram.draw(frameNumber++);
+ testPatternProgram.draw(ts);
eglDisplayContext.swapBuffers();
lastFrameTs = getCurrentTimestamp();
}
@@ -125,10 +125,18 @@
ALOGV("%s: streamId %d, %dx%d pixFmt=%s", __func__, streamId, width, height,
toString(pixelFormat).c_str());
- std::lock_guard<std::mutex> lock(mLock);
- mRenderer = std::make_shared<TestPatternRenderer>(
+ auto renderer = std::make_shared<TestPatternRenderer>(
nativeWindowFromSurface(surface), mFps);
- mRenderer->start();
+
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mInputRenderers.try_emplace(streamId, renderer).second) {
+ renderer->start();
+ } else {
+ ALOGE(
+ "%s: Input stream with id %d is already active, ignoring "
+ "onStreamConfigured call",
+ __func__, streamId);
+ }
return ScopedAStatus::ok();
}
@@ -141,10 +149,17 @@
ScopedAStatus VirtualCameraTestInstance::onStreamClosed(const int32_t streamId) {
ALOGV("%s: streamId %d", __func__, streamId);
- std::lock_guard<std::mutex> lock(mLock);
- if (mRenderer != nullptr) {
- mRenderer->stop();
- mRenderer.reset();
+ std::shared_ptr<TestPatternRenderer> renderer;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto it = mInputRenderers.find(streamId);
+ if (it != mInputRenderers.end()) {
+ renderer = std::move(it->second);
+ mInputRenderers.erase(it);
+ }
+ }
+ if (renderer != nullptr) {
+ renderer->stop();
}
return ScopedAStatus::ok();
}
diff --git a/services/camera/virtualcamera/VirtualCameraTestInstance.h b/services/camera/virtualcamera/VirtualCameraTestInstance.h
index 43e33d5..c130645 100644
--- a/services/camera/virtualcamera/VirtualCameraTestInstance.h
+++ b/services/camera/virtualcamera/VirtualCameraTestInstance.h
@@ -17,7 +17,7 @@
#define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERATESTINSTANCE_H
#include <atomic>
-#include <condition_variable>
+#include <map>
#include <memory>
#include <thread>
@@ -80,7 +80,10 @@
const int mFps;
std::mutex mLock;
- std::shared_ptr<TestPatternRenderer> mRenderer GUARDED_BY(mLock);
+ // Map maintaining streamId -> TestPatternRenderer mapping for active
+ // input streams.
+ std::map<int, std::shared_ptr<TestPatternRenderer>> mInputRenderers
+ GUARDED_BY(mLock);
};
} // namespace virtualcamera
diff --git a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
index 550227e..50977d8 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
@@ -488,6 +488,23 @@
Eq(STATUS_BAD_VALUE));
}
+TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithInputFps) {
+ EXPECT_THAT(execute_shell_command("enable_test_camera --input_fps=15"),
+ Eq(NO_ERROR));
+
+ std::vector<std::string> cameraIds = getCameraIds();
+ ASSERT_THAT(cameraIds, SizeIs(1));
+}
+
+TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithInvalidInputFps) {
+ EXPECT_THAT(execute_shell_command("enable_test_camera --input_fps=1001"),
+ Eq(STATUS_BAD_VALUE));
+ EXPECT_THAT(execute_shell_command("enable_test_camera --input_fps=0"),
+ Eq(STATUS_BAD_VALUE));
+ EXPECT_THAT(execute_shell_command("enable_test_camera --input_fps=foo"),
+ Eq(STATUS_BAD_VALUE));
+}
+
} // namespace
} // namespace virtualcamera
} // namespace companion
diff --git a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
index 671e031..93f90b4 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
@@ -379,6 +379,92 @@
.isOk());
}
+TEST_F(VirtualCameraSessionInputChoiceTest, reconfigureSwitchesInputStream) {
+ // Create camera configured to support SVGA YUV input and RGB QVGA input.
+ auto virtualCameraSession = createSession(
+ {SupportedStreamConfiguration{.width = kSvgaWidth,
+ .height = kSvgaHeight,
+ .pixelFormat = Format::YUV_420_888,
+ .maxFps = kMaxFps},
+ SupportedStreamConfiguration{.width = kQvgaWidth,
+ .height = kQvgaHeight,
+ .pixelFormat = Format::RGBA_8888,
+ .maxFps = kMaxFps}});
+
+ // First configure QVGA stream.
+ StreamConfiguration streamConfiguration;
+ streamConfiguration.streams = {createStream(
+ kStreamId, kQvgaWidth, kQvgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
+ std::vector<HalStream> halStreams;
+
+ // Expect QVGA input configuragion to be chosen.
+ EXPECT_CALL(*mMockVirtualCameraClientCallback,
+ onStreamConfigured(kStreamId, _, kQvgaWidth, kQvgaHeight,
+ Format::RGBA_8888));
+ EXPECT_TRUE(
+ virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+ .isOk());
+
+ // Reconfigure with additional VGA stream.
+ streamConfiguration.streams.push_back(
+ createStream(kStreamId + 1, kVgaWidth, kVgaHeight,
+ PixelFormat::IMPLEMENTATION_DEFINED));
+
+ // Expect original surface to be discarded.
+ EXPECT_CALL(*mMockVirtualCameraClientCallback, onStreamClosed(kStreamId));
+
+ // Expect SVGA input configuragion to be chosen.
+ EXPECT_CALL(*mMockVirtualCameraClientCallback,
+ onStreamConfigured(kStreamId + 1, _, kSvgaWidth, kSvgaHeight,
+ Format::YUV_420_888));
+ EXPECT_TRUE(
+ virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+ .isOk());
+}
+
+TEST_F(VirtualCameraSessionInputChoiceTest,
+ reconfigureKeepsInputStreamIfUnchanged) {
+ // Create camera configured to support SVGA YUV input and RGB QVGA input.
+ auto virtualCameraSession = createSession(
+ {SupportedStreamConfiguration{.width = kSvgaWidth,
+ .height = kSvgaHeight,
+ .pixelFormat = Format::YUV_420_888,
+ .maxFps = kMaxFps},
+ SupportedStreamConfiguration{.width = kQvgaWidth,
+ .height = kQvgaHeight,
+ .pixelFormat = Format::RGBA_8888,
+ .maxFps = kMaxFps}});
+
+ // First configure SVGA stream.
+ StreamConfiguration streamConfiguration;
+ streamConfiguration.streams = {createStream(
+ kStreamId, kSvgaWidth, kSvgaHeight, PixelFormat::IMPLEMENTATION_DEFINED)};
+ std::vector<HalStream> halStreams;
+
+ // Expect SVGA input configuragion to be chosen.
+ EXPECT_CALL(*mMockVirtualCameraClientCallback,
+ onStreamConfigured(kStreamId, _, kSvgaWidth, kSvgaHeight,
+ Format::YUV_420_888));
+ EXPECT_TRUE(
+ virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+ .isOk());
+
+ // Reconfigure with VGA + QVA stream. Because we only allow downscaling,
+ // this will be matched to SVGA input resolution.
+ streamConfiguration.streams = {
+ createStream(kStreamId + 1, kVgaWidth, kVgaHeight,
+ PixelFormat::IMPLEMENTATION_DEFINED),
+ createStream(kStreamId + 2, kVgaWidth, kVgaHeight,
+ PixelFormat::IMPLEMENTATION_DEFINED)};
+
+ // Expect the onStreamConfigured callback not to be invoked, since the
+ // original Surface is still best fit for current output streams.
+ EXPECT_CALL(*mMockVirtualCameraClientCallback, onStreamConfigured).Times(0);
+ EXPECT_TRUE(
+ virtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+ .isOk());
+}
+
} // namespace
} // namespace virtualcamera
} // namespace companion
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index 7498fbc..9c97118 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -229,13 +229,12 @@
kTextureCoords.size(), kTextureCoords.data());
}
-bool EglTestPatternProgram::draw(int frameNumber) {
+bool EglTestPatternProgram::draw(const std::chrono::nanoseconds timestamp) {
// Load compiled shader.
glUseProgram(mProgram);
checkEglError("glUseProgram");
- // Compute point in complex plane corresponding to fractal for this frame number.
- float time = float(frameNumber) / 120.0f;
+ float time = float(timestamp.count() / 1e9) / 10;
const std::complex<float> c(std::sin(time) * 0.78f, std::cos(time) * 0.78f);
// Pass "C" constant value determining the Julia set to the shader.
diff --git a/services/camera/virtualcamera/util/EglProgram.h b/services/camera/virtualcamera/util/EglProgram.h
index d09f11e..a88308d 100644
--- a/services/camera/virtualcamera/util/EglProgram.h
+++ b/services/camera/virtualcamera/util/EglProgram.h
@@ -18,6 +18,7 @@
#define ANDROID_COMPANION_VIRTUALCAMERA_EGLPROGRAM_H
#include <array>
+#include <chrono>
#include "GLES/gl.h"
@@ -46,7 +47,7 @@
public:
EglTestPatternProgram();
- bool draw(int frameNumber);
+ bool draw(std::chrono::nanoseconds timestamp);
private:
int mPositionHandle = -1;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 7d88d9d..20a5ea8 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -22,6 +22,7 @@
#include <iostream>
#include <mutex>
+#include <com_android_media_aaudio.h>
#include <media/MediaMetricsItem.h>
#include <media/TypeConverter.h>
#include <mediautils/SchedulingPolicyService.h>
@@ -219,7 +220,7 @@
return closeAndClear();
}
-aaudio_result_t AAudioServiceStreamBase::startDevice() {
+aaudio_result_t AAudioServiceStreamBase::startDevice_l() {
mClientHandle = AUDIO_PORT_HANDLE_NONE;
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
@@ -274,7 +275,7 @@
mAtomicStreamTimestamp.clear();
mClientHandle = AUDIO_PORT_HANDLE_NONE;
- result = startDevice();
+ result = startDevice_l();
if (result != AAUDIO_OK) goto error;
// This should happen at the end of the start.
@@ -525,6 +526,18 @@
: exitStandby_l(param->mParcelable);
standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
} break;
+ case START_CLIENT: {
+ auto param = (StartClientParam *) command->parameter.get();
+ command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT
+ : startClient_l(param->mClient,
+ param->mAttr,
+ param->mClientHandle);
+ } break;
+ case STOP_CLIENT: {
+ auto param = (StopClientParam *) command->parameter.get();
+ command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT
+ : stopClient_l(param->mClientHandle);
+ } break;
default:
ALOGE("Invalid command op code: %d", command->operationCode);
break;
@@ -737,6 +750,26 @@
return mCommandQueue.sendCommand(command);
}
+aaudio_result_t AAudioServiceStreamBase::sendStartClientCommand(const android::AudioClient &client,
+ const audio_attributes_t *attr,
+ audio_port_handle_t *clientHandle) {
+ auto command = std::make_shared<AAudioCommand>(
+ START_CLIENT,
+ std::make_shared<StartClientParam>(client, attr, clientHandle),
+ true /*waitForReply*/,
+ TIMEOUT_NANOS);
+ return mCommandQueue.sendCommand(command);
+}
+
+aaudio_result_t AAudioServiceStreamBase::sendStopClientCommand(audio_port_handle_t clientHandle) {
+ auto command = std::make_shared<AAudioCommand>(
+ STOP_CLIENT,
+ std::make_shared<StopClientParam>(clientHandle),
+ true /*waitForReply*/,
+ TIMEOUT_NANOS);
+ return mCommandQueue.sendCommand(command);
+}
+
void AAudioServiceStreamBase::onVolumeChanged(float volume) {
sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 498424a..20737bc 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -279,7 +279,7 @@
* Device specific startup.
* @return AAUDIO_OK or negative error.
*/
- virtual aaudio_result_t startDevice();
+ virtual aaudio_result_t startDevice_l() REQUIRES(mLock);
aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command)
EXCLUDES(mUpMessageQueueLock);
@@ -288,6 +288,12 @@
aaudio_result_t sendXRunCount(int32_t xRunCount);
+ aaudio_result_t sendStartClientCommand(const android::AudioClient& client,
+ const audio_attributes_t *attr,
+ audio_port_handle_t *clientHandle) EXCLUDES(mLock);
+
+ aaudio_result_t sendStopClientCommand(audio_port_handle_t clientHandle) EXCLUDES(mLock);
+
/**
* @param positionFrames
* @param timeNanos
@@ -347,6 +353,40 @@
}
virtual void reportData_l() REQUIRES(mLock) { return; }
+ class StartClientParam : public AAudioCommandParam {
+ public:
+ StartClientParam(const android::AudioClient& client, const audio_attributes_t* attr,
+ audio_port_handle_t* clientHandle)
+ : AAudioCommandParam(), mClient(client), mAttr(attr), mClientHandle(clientHandle) {
+ }
+ ~StartClientParam() override = default;
+
+ android::AudioClient mClient;
+ const audio_attributes_t* mAttr;
+ audio_port_handle_t* mClientHandle;
+ };
+ virtual aaudio_result_t startClient_l(
+ const android::AudioClient& client,
+ const audio_attributes_t *attr __unused,
+ audio_port_handle_t *clientHandle __unused) REQUIRES(mLock) {
+ ALOGD("AAudioServiceStreamBase::startClient_l(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ class StopClientParam : public AAudioCommandParam {
+ public:
+ explicit StopClientParam(audio_port_handle_t clientHandle)
+ : AAudioCommandParam(), mClientHandle(clientHandle) {
+ }
+ ~StopClientParam() override = default;
+
+ audio_port_handle_t mClientHandle;
+ };
+ virtual aaudio_result_t stopClient_l(audio_port_handle_t clientHandle) REQUIRES(mLock) {
+ ALOGD("AAudioServiceStreamBase::stopClient(%d) AAUDIO_ERROR_UNAVAILABLE", clientHandle);
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
pid_t mRegisteredClientThread = ILLEGAL_THREAD_ID;
std::mutex mUpMessageQueueLock;
@@ -363,6 +403,8 @@
UNREGISTER_AUDIO_THREAD,
GET_DESCRIPTION,
EXIT_STANDBY,
+ START_CLIENT,
+ STOP_CLIENT,
};
AAudioThread mCommandThread;
std::atomic_bool mThreadEnabled{false};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 89f6e33..fc53949 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -24,6 +24,7 @@
#include <iostream>
#include <stdint.h>
+#include <com_android_media_aaudio.h>
#include <utils/String16.h>
#include <media/nbaio/AudioStreamOutSink.h>
#include <media/MmapStreamInterface.h>
@@ -83,11 +84,12 @@
}
// Start the flow of data.
-aaudio_result_t AAudioServiceStreamMMAP::startDevice() {
- aaudio_result_t result = AAudioServiceStreamBase::startDevice();
+aaudio_result_t AAudioServiceStreamMMAP::startDevice_l() {
+ aaudio_result_t result = AAudioServiceStreamBase::startDevice_l();
if (!mInService && result == AAUDIO_OK) {
// Note that this can sometimes take 200 to 300 msec for a cold start!
- result = startClient(mMmapClient, nullptr /*const audio_attributes_t* */, &mClientHandle);
+ result = startClient_l(
+ mMmapClient, nullptr /*const audio_attributes_t* */, &mClientHandle);
}
return result;
}
@@ -100,7 +102,7 @@
aaudio_result_t result = AAudioServiceStreamBase::pause_l();
// TODO put before base::pause()?
if (!mInService) {
- (void) stopClient(mClientHandle);
+ (void) stopClient_l(mClientHandle);
}
return result;
}
@@ -112,7 +114,7 @@
aaudio_result_t result = AAudioServiceStreamBase::stop_l();
// TODO put before base::stop()?
if (!mInService) {
- (void) stopClient(mClientHandle);
+ (void) stopClient_l(mClientHandle);
}
return result;
}
@@ -149,6 +151,37 @@
aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
const audio_attributes_t *attr,
audio_port_handle_t *clientHandle) {
+ if (com::android::media::aaudio::start_stop_client_from_command_thread()) {
+ return sendStartClientCommand(client, attr, clientHandle);
+ } else {
+ sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+ if (endpoint == nullptr) {
+ ALOGE("%s() has no endpoint", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ // Start the client on behalf of the application. Generate a new porthandle.
+ aaudio_result_t result = endpoint->startClient(client, attr, clientHandle);
+ return result;
+ }
+}
+
+aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
+ if (com::android::media::aaudio::start_stop_client_from_command_thread()) {
+ return sendStopClientCommand(clientHandle);
+ } else {
+ sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+ if (endpoint == nullptr) {
+ ALOGE("%s() has no endpoint", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ aaudio_result_t result = endpoint->stopClient(clientHandle);
+ return result;
+ }
+}
+
+aaudio_result_t AAudioServiceStreamMMAP::startClient_l(const android::AudioClient& client,
+ const audio_attributes_t *attr,
+ audio_port_handle_t *clientHandle) {
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
@@ -159,7 +192,7 @@
return result;
}
-aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
+aaudio_result_t AAudioServiceStreamMMAP::stopClient_l(audio_port_handle_t clientHandle) {
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index cbbba89..f20ea10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -96,7 +96,13 @@
* Device specific startup.
* @return AAUDIO_OK or negative error.
*/
- aaudio_result_t startDevice() override;
+ aaudio_result_t startDevice_l() REQUIRES(mLock) override;
+
+ aaudio_result_t startClient_l(const android::AudioClient& client,
+ const audio_attributes_t *attr,
+ audio_port_handle_t *clientHandle) REQUIRES(mLock) override;
+
+ aaudio_result_t stopClient_l(audio_port_handle_t clientHandle) REQUIRES(mLock) override;
private: