Merge "Support non-numerical id (with prefix) for virtual camera" 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/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/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/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/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/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/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 0f23205..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);
}
diff --git a/media/codec2/sfplugin/C2AidlNode.h b/media/codec2/sfplugin/C2AidlNode.h
index 9dd3504..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;
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/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/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/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.h b/services/audioflinger/AudioFlinger.h
index a8f983b..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>
@@ -426,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;
@@ -784,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/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/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7ec5f48..6a94c81 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,29 +3366,10 @@
// 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);
@@ -6865,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);
@@ -6963,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
@@ -8238,7 +8245,7 @@
if (deviceTypes.empty()) {
deviceTypes = outputDesc->devices().types();
index = curves.getVolumeIndex(deviceTypes);
- ALOGD("%s if deviceTypes is change from none to device %s, need get index %d",
+ ALOGV("%s if deviceTypes is change from none to device %s, need get index %d",
__func__, dumpDeviceTypes(deviceTypes).c_str(), index);
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 585f982..011e867 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -533,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.
diff --git a/services/audiopolicy/permission/Android.bp b/services/audiopolicy/permission/Android.bp
index ce7b43c..d5f59a0 100644
--- a/services/audiopolicy/permission/Android.bp
+++ b/services/audiopolicy/permission/Android.bp
@@ -3,6 +3,12 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+cc_library_headers {
+ name: "audiopermissioncontroller_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_library {
name: "audiopermissioncontroller",
diff --git a/services/audiopolicy/permission/NativePermissionController.cpp b/services/audiopolicy/permission/NativePermissionController.cpp
index d88c69f..9fcf22d 100644
--- a/services/audiopolicy/permission/NativePermissionController.cpp
+++ b/services/audiopolicy/permission/NativePermissionController.cpp
@@ -88,6 +88,19 @@
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 {
@@ -120,4 +133,15 @@
(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/include/media/IPermissionProvider.h b/services/audiopolicy/permission/include/media/IPermissionProvider.h
index 9184ffb..27a61ea 100644
--- a/services/audiopolicy/permission/include/media/IPermissionProvider.h
+++ b/services/audiopolicy/permission/include/media/IPermissionProvider.h
@@ -21,6 +21,7 @@
#include <optional>
#include <vector>
+#include <com/android/media/permission/PermissionEnum.h>
#include <error/Result.h>
namespace com::android::media::permission {
@@ -38,6 +39,11 @@
// 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
index c0e717c..d464023 100644
--- a/services/audiopolicy/permission/include/media/NativePermissionController.h
+++ b/services/audiopolicy/permission/include/media/NativePermissionController.h
@@ -33,16 +33,22 @@
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/tests/NativePermissionControllerTest.cpp b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
index f23a8b3..3f6b787 100644
--- a/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
+++ b/services/audiopolicy/permission/tests/NativePermissionControllerTest.cpp
@@ -24,6 +24,7 @@
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 {
@@ -177,3 +178,41 @@
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
index f4d6556..efc318b 100644
--- a/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
+++ b/services/audiopolicy/permission/tests/ValidatedAttributionSourceStateTest.cpp
@@ -27,6 +27,7 @@
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;
@@ -36,6 +37,7 @@
(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 {
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index f23ec94..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",
@@ -87,6 +89,7 @@
],
header_libs: [
+ "audiopolicyservicelocal_headers",
"libaudiohal_headers",
"libaudiopolicycommon",
"libaudiopolicyengine_interface_headers",
@@ -114,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/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 73c4a15..cc67481 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -317,6 +317,10 @@
AudioSystem::audioPolicyReady();
}
+const IPermissionProvider& AudioPolicyService::getPermissionProvider() const {
+ return *mPermissionController;
+}
+
void AudioPolicyService::onAudioSystemReady() {
sp<AudioPolicyEffects> audioPolicyEffects;
{
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 41b28c5..699cacf 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -36,6 +36,7 @@
#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>
@@ -72,12 +73,14 @@
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>;
@@ -326,6 +329,9 @@
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);
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 7122a11..c3be62b 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 c13dcbd..a33d4cf 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -106,6 +106,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;
@@ -150,6 +153,8 @@
const std::vector<
aidl::android::companion::virtualcamera::SupportedStreamConfiguration>
mSupportedInputConfigurations;
+
+ std::atomic_int mNextInputStreamId;
};
} // namespace virtualcamera
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/VirtualCameraSessionTest.cc b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
index 9b7c560..a9eb413 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 4e46bbf..20a5ea8 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -398,6 +398,7 @@
}
// implement Runnable, periodically send timestamps to client and process commands from queue.
+// Enter standby mode if idle for a while.
__attribute__((no_sanitize("integer")))
void AAudioServiceStreamBase::run() {
ALOGD("%s() %s entering >>>>>>>>>>>>>> COMMANDS", __func__, getTypeText());
@@ -406,6 +407,7 @@
TimestampScheduler timestampScheduler;
int64_t nextTimestampReportTime;
int64_t nextDataReportTime;
+ // When to try to enter standby.
int64_t standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
// Balance the incStrong from when the thread was launched.
holdStream->decStrong(nullptr);
@@ -417,28 +419,26 @@
int32_t loopCount = 0;
while (mThreadEnabled.load()) {
loopCount++;
- int64_t timeoutNanos = -1;
- if (isDisconnected_l()) {
- if (!isStandby_l()) {
- // If the stream is disconnected but not in standby mode, wait until standby time.
+ int64_t timeoutNanos = -1; // wait forever
+ if (isDisconnected_l() || isIdle_l()) {
+ if (isStandbyImplemented() && !isStandby_l()) {
+ // If not in standby mode, wait until standby time.
timeoutNanos = standbyTime - AudioClock::getNanoseconds();
timeoutNanos = std::max<int64_t>(0, timeoutNanos);
- } // else {
- // If the stream is disconnected and in standby mode, keep `timeoutNanos` as
- // -1 to wait forever until next command as the stream can only be closed.
- // }
- } else if (isRunning() || (isIdle_l() && !isStandby_l())) {
- timeoutNanos = (isRunning() ? std::min(nextTimestampReportTime, nextDataReportTime)
- : standbyTime) - AudioClock::getNanoseconds();
+ }
+ // Otherwise, keep `timeoutNanos` as -1 to wait forever until next command.
+ } else if (isRunning()) {
+ timeoutNanos = std::min(nextTimestampReportTime, nextDataReportTime)
+ - AudioClock::getNanoseconds();
timeoutNanos = std::max<int64_t>(0, timeoutNanos);
}
-
auto command = mCommandQueue.waitForCommand(timeoutNanos);
if (!mThreadEnabled) {
// Break the loop if the thread is disabled.
break;
}
+ // Is it time to send timestamps?
if (isRunning() && !isDisconnected_l()) {
auto currentTimestamp = AudioClock::getNanoseconds();
if (currentTimestamp >= nextDataReportTime) {
@@ -454,19 +454,24 @@
nextTimestampReportTime = timestampScheduler.nextAbsoluteTime();
}
}
- if ((isIdle_l() || isDisconnected_l()) && AudioClock::getNanoseconds() >= standbyTime) {
+
+ // Is it time to enter standby?
+ if ((isIdle_l() || isDisconnected_l())
+ && isStandbyImplemented()
+ && !isStandby_l()
+ && (AudioClock::getNanoseconds() >= standbyTime)) {
+ ALOGD("%s() call standby_l(), %d loops", __func__, loopCount);
aaudio_result_t result = standby_l();
if (result != AAUDIO_OK) {
- // If standby failed because of the function is not implemented, there is no
- // need to retry. Otherwise, retry standby later.
- ALOGW("Failed to enter standby, error=%d", result);
- standbyTime = result == AAUDIO_ERROR_UNIMPLEMENTED
- ? std::numeric_limits<int64_t>::max()
- : AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
+ ALOGW("Failed to enter standby, error = %d", result);
+ // Try again later.
+ standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
}
}
if (command != nullptr) {
+ ALOGD("%s() got COMMAND opcode %d after %d loops",
+ __func__, command->operationCode, loopCount);
std::scoped_lock<std::mutex> _commandLock(command->lock);
switch (command->operationCode) {
case START:
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 8057f87..20737bc 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -316,9 +316,14 @@
mDisconnected = flag;
}
+ // If you implemented this method then please also override isStandbyImplemented().
virtual aaudio_result_t standby_l() REQUIRES(mLock) {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
+ virtual bool isStandbyImplemented() {
+ return false;
+ }
+
class ExitStandbyParam : public AAudioCommandParam {
public:
explicit ExitStandbyParam(AudioEndpointParcelable* parcelable)
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f4ce83d..f20ea10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -73,6 +73,9 @@
aaudio_result_t stop_l() REQUIRES(mLock) override;
aaudio_result_t standby_l() REQUIRES(mLock) override;
+ bool isStandbyImplemented() override {
+ return true;
+ }
aaudio_result_t exitStandby_l(AudioEndpointParcelable* parcelable) REQUIRES(mLock) override;