Merge "compat matrix: add ISap slot2" into main
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index 12bce0b..81c99f7 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "VtsHalAudioCoreTargetTest"
},
{
- "name": "VtsHalAudioCoreConfigTargetTest"
- },
- {
"name": "audio_policy_config_xml_converter_tests"
},
{
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 78b59d4..19b2397 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -73,18 +73,23 @@
"Configuration.cpp",
"EngineConfigXmlConverter.cpp",
"Module.cpp",
+ "ModulePrimary.cpp",
"SoundDose.cpp",
"Stream.cpp",
- "StreamStub.cpp",
"Telephony.cpp",
+ "alsa/Mixer.cpp",
+ "alsa/ModuleAlsa.cpp",
+ "alsa/StreamAlsa.cpp",
+ "alsa/Utils.cpp",
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/RemoteSubmixUtils.cpp",
"r_submix/SubmixRoute.cpp",
"r_submix/StreamRemoteSubmix.cpp",
+ "stub/ModuleStub.cpp",
+ "stub/StreamStub.cpp",
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
"usb/UsbAlsaMixerControl.cpp",
- "usb/UsbAlsaUtils.cpp",
],
generated_sources: [
"audio_policy_configuration_aidl_default",
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index d41ea67..a71c6ea 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -144,10 +144,6 @@
// - no profiles specified
// * "FM Tuner", IN_FM_TUNER
// - no profiles specified
-// * "USB Out", OUT_DEVICE, CONNECTION_USB
-// - no profiles specified
-// * "USB In", IN_DEVICE, CONNECTION_USB
-// - no profiles specified
//
// Mix ports:
// * "primary output", PRIMARY, 1 max open, 1 max active stream
@@ -172,8 +168,7 @@
//
// Routes:
// "primary out", "compressed offload" -> "Speaker"
-// "primary out", "compressed offload" -> "USB Out"
-// "Built-in Mic", "USB In" -> "primary input"
+// "Built-in Mic" -> "primary input"
// "telephony_tx" -> "Telephony Tx"
// "Telephony Rx" -> "telephony_rx"
// "FM Tuner" -> "fm_tuner"
@@ -185,14 +180,6 @@
// * "Telephony Rx" device port: PCM 24-bit; MONO; 48000
// * "FM Tuner" device port: PCM 24-bit; STEREO; 48000
//
-// Profiles for device port connected state:
-// * USB Out":
-// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-// * USB In":
-// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//
std::unique_ptr<Configuration> getPrimaryConfiguration() {
static const Configuration configuration = []() {
const std::vector<AudioProfile> standardPcmAudioProfiles = {
@@ -252,19 +239,6 @@
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
- AudioPort usbOutDevice =
- createPort(c.nextPortId++, "USB Out", 0, false,
- createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
- AudioDeviceDescription::CONNECTION_USB));
- c.ports.push_back(usbOutDevice);
- c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
-
- AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true,
- createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
- AudioDeviceDescription::CONNECTION_USB));
- c.ports.push_back(usbInDevice);
- c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
-
// Mix ports
AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
@@ -323,8 +297,7 @@
c.ports.push_back(fmTunerInMix);
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice));
- c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, usbOutDevice));
- c.routes.push_back(createRoute({micInDevice, usbInDevice}, primaryInMix));
+ c.routes.push_back(createRoute({micInDevice}, primaryInMix));
c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice));
c.routes.push_back(createRoute({telephonyRxInDevice}, telephonyRxInMix));
c.routes.push_back(createRoute({fmTunerInDevice}, fmTunerInMix));
@@ -406,22 +379,31 @@
// Usb configuration:
//
// Device ports:
+// * "USB Device Out", OUT_DEVICE, CONNECTION_USB
+// - no profiles specified
// * "USB Headset Out", OUT_HEADSET, CONNECTION_USB
// - no profiles specified
+// * "USB Device In", IN_DEVICE, CONNECTION_USB
+// - no profiles specified
// * "USB Headset In", IN_HEADSET, CONNECTION_USB
// - no profiles specified
//
// Mix ports:
-// * "usb_headset output", 1 max open, 1 max active stream
+// * "usb_device output", 1 max open, 1 max active stream
// - no profiles specified
-// * "usb_headset input", 1 max open, 1 max active stream
+// * "usb_device input", 1 max open, 1 max active stream
// - no profiles specified
//
+// Routes:
+// * "usb_device output" -> "USB Device Out"
+// * "usb_device output" -> "USB Headset Out"
+// * "USB Device In", "USB Headset In" -> "usb_device input"
+//
// Profiles for device port connected state:
-// * USB Headset Out":
+// * "USB Device Out", "USB Headset Out":
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
-// * USB Headset In":
+// * "USB Device In", "USB Headset In":
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
//
@@ -440,6 +422,13 @@
// Device ports
+ AudioPort usbOutDevice =
+ createPort(c.nextPortId++, "USB Device Out", 0, false,
+ createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
+ AudioDeviceDescription::CONNECTION_USB));
+ c.ports.push_back(usbOutDevice);
+ c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
+
AudioPort usbOutHeadset =
createPort(c.nextPortId++, "USB Headset Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
@@ -447,6 +436,12 @@
c.ports.push_back(usbOutHeadset);
c.connectedProfiles[usbOutHeadset.id] = standardPcmAudioProfiles;
+ AudioPort usbInDevice = createPort(c.nextPortId++, "USB Device In", 0, true,
+ createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
+ AudioDeviceDescription::CONNECTION_USB));
+ c.ports.push_back(usbInDevice);
+ c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
+
AudioPort usbInHeadset =
createPort(c.nextPortId++, "USB Headset In", 0, true,
createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
@@ -456,16 +451,110 @@
// Mix ports
- AudioPort usbHeadsetOutMix =
- createPort(c.nextPortId++, "usb_headset output", 0, false, createPortMixExt(1, 1));
- c.ports.push_back(usbHeadsetOutMix);
+ AudioPort usbDeviceOutMix =
+ createPort(c.nextPortId++, "usb_device output", 0, false, createPortMixExt(1, 1));
+ c.ports.push_back(usbDeviceOutMix);
- AudioPort usbHeadsetInMix =
- createPort(c.nextPortId++, "usb_headset input", 0, true, createPortMixExt(1, 1));
- c.ports.push_back(usbHeadsetInMix);
+ AudioPort usbDeviceInMix =
+ createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1));
+ c.ports.push_back(usbDeviceInMix);
- c.routes.push_back(createRoute({usbHeadsetOutMix}, usbOutHeadset));
- c.routes.push_back(createRoute({usbInHeadset}, usbHeadsetInMix));
+ c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice));
+ c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutHeadset));
+ c.routes.push_back(createRoute({usbInDevice, usbInHeadset}, usbDeviceInMix));
+
+ return c;
+ }();
+ return std::make_unique<Configuration>(configuration);
+}
+
+// Stub configuration:
+//
+// Device ports:
+// * "Test Out", OUT_AFE_PROXY
+// - no profiles specified
+// * "Test In", IN_AFE_PROXY
+// - no profiles specified
+//
+// Mix ports:
+// * "test output", 1 max open, 1 max active stream
+// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
+// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
+// - profile MP3; MONO, STEREO; 44100, 48000
+// * "test input", 2 max open, 2 max active streams
+// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
+// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+//
+// Routes:
+// "test output", "compressed offload" -> "Test Out"
+// "Test In" -> "test input"
+//
+// Initial port configs:
+// * "Test Out" device port: PCM 24-bit; STEREO; 48000
+// * "Test In" device port: PCM 24-bit; MONO; 48000
+//
+std::unique_ptr<Configuration> getStubConfiguration() {
+ static const Configuration configuration = []() {
+ Configuration c;
+
+ // Device ports
+
+ AudioPort testOutDevice = createPort(c.nextPortId++, "Test Out", 0, false,
+ createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0));
+ c.ports.push_back(testOutDevice);
+ c.initialConfigs.push_back(
+ createPortConfig(testOutDevice.id, testOutDevice.id, PcmType::INT_24_BIT,
+ AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
+ createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0)));
+
+ AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
+ createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0));
+ c.ports.push_back(testInDevice);
+ c.initialConfigs.push_back(
+ createPortConfig(testInDevice.id, testInDevice.id, PcmType::INT_24_BIT,
+ AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
+ createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)));
+
+ // Mix ports
+
+ AudioPort testOutMix =
+ createPort(c.nextPortId++, "test output", 0, false, createPortMixExt(1, 1));
+ testOutMix.profiles.push_back(
+ createProfile(PcmType::INT_24_BIT,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+ {8000, 11025, 16000, 32000, 44100, 48000}));
+ c.ports.push_back(testOutMix);
+
+ AudioPort compressedOffloadOutMix =
+ createPort(c.nextPortId++, "compressed offload",
+ makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
+ AudioOutputFlags::COMPRESS_OFFLOAD,
+ AudioOutputFlags::NON_BLOCKING}),
+ false, createPortMixExt(1, 1));
+ compressedOffloadOutMix.profiles.push_back(
+ createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+ {44100, 48000}));
+ c.ports.push_back(compressedOffloadOutMix);
+
+ AudioPort testInMIx =
+ createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
+ testInMIx.profiles.push_back(
+ createProfile(PcmType::INT_16_BIT,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
+ AudioChannelLayout::LAYOUT_FRONT_BACK},
+ {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
+ testInMIx.profiles.push_back(
+ createProfile(PcmType::INT_24_BIT,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
+ AudioChannelLayout::LAYOUT_FRONT_BACK},
+ {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
+ c.ports.push_back(testInMIx);
+
+ c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
+ c.routes.push_back(createRoute({testInDevice}, testInMIx));
+
+ c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
return c;
}();
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
index f3f674f..730c0bf 100644
--- a/audio/aidl/default/EffectConfig.cpp
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -117,53 +117,59 @@
bool EffectConfig::parseEffect(const tinyxml2::XMLElement& xml) {
struct EffectLibraries effectLibraries;
- std::vector<LibraryUuid> libraryUuids;
+ std::vector<Library> libraries;
std::string name = xml.Attribute("name");
RETURN_VALUE_IF(name == "", false, "effectsNoName");
LOG(DEBUG) << __func__ << dump(xml);
- struct LibraryUuid libraryUuid;
+ struct Library library;
if (std::strcmp(xml.Name(), "effectProxy") == 0) {
// proxy lib and uuid
- RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid, true), false, "parseProxyLibFailed");
- effectLibraries.proxyLibrary = libraryUuid;
+ RETURN_VALUE_IF(!parseLibrary(xml, library, true), false, "parseProxyLibFailed");
+ effectLibraries.proxyLibrary = library;
// proxy effect libs and UUID
auto xmlProxyLib = xml.FirstChildElement();
RETURN_VALUE_IF(!xmlProxyLib, false, "noLibForProxy");
while (xmlProxyLib) {
- struct LibraryUuid tempLibraryUuid;
- RETURN_VALUE_IF(!parseLibraryUuid(*xmlProxyLib, tempLibraryUuid), false,
+ struct Library tempLibrary;
+ RETURN_VALUE_IF(!parseLibrary(*xmlProxyLib, tempLibrary), false,
"parseEffectLibFailed");
- libraryUuids.push_back(std::move(tempLibraryUuid));
+ libraries.push_back(std::move(tempLibrary));
xmlProxyLib = xmlProxyLib->NextSiblingElement();
}
} else {
// expect only one library if not proxy
- RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid), false, "parseEffectLibFailed");
- libraryUuids.push_back(std::move(libraryUuid));
+ RETURN_VALUE_IF(!parseLibrary(xml, library), false, "parseEffectLibFailed");
+ libraries.push_back(std::move(library));
}
- effectLibraries.libraries = std::move(libraryUuids);
+ effectLibraries.libraries = std::move(libraries);
mEffectsMap[name] = std::move(effectLibraries);
return true;
}
-bool EffectConfig::parseLibraryUuid(const tinyxml2::XMLElement& xml,
- struct LibraryUuid& libraryUuid, bool isProxy) {
+bool EffectConfig::parseLibrary(const tinyxml2::XMLElement& xml, struct Library& library,
+ bool isProxy) {
// Retrieve library name only if not effectProxy element
if (!isProxy) {
const char* name = xml.Attribute("library");
RETURN_VALUE_IF(!name, false, "noLibraryAttribute");
- libraryUuid.name = name;
+ library.name = name;
}
const char* uuidStr = xml.Attribute("uuid");
RETURN_VALUE_IF(!uuidStr, false, "noUuidAttribute");
- libraryUuid.uuid = stringToUuid(uuidStr);
- RETURN_VALUE_IF((libraryUuid.uuid == getEffectUuidZero()), false, "invalidUuidAttribute");
+ library.uuid = stringToUuid(uuidStr);
+ if (const char* typeUuidStr = xml.Attribute("type")) {
+ library.type = stringToUuid(typeUuidStr);
+ }
+ RETURN_VALUE_IF((library.uuid == getEffectUuidZero()), false, "invalidUuidAttribute");
- LOG(DEBUG) << __func__ << (isProxy ? " proxy " : libraryUuid.name) << " : "
- << ::android::audio::utils::toString(libraryUuid.uuid);
+ LOG(DEBUG) << __func__ << (isProxy ? " proxy " : library.name) << " : uuid "
+ << ::android::audio::utils::toString(library.uuid)
+ << (library.type.has_value()
+ ? ::android::audio::utils::toString(library.type.value())
+ : "");
return true;
}
@@ -241,7 +247,8 @@
return mProcessingMap;
}
-bool EffectConfig::findUuid(const std::string& xmlEffectName, AudioUuid* uuid) {
+bool EffectConfig::findUuid(const std::pair<std::string, struct EffectLibraries>& effectElem,
+ AudioUuid* uuid) {
// Difference from EFFECT_TYPE_LIST_DEF, there could be multiple name mapping to same Effect Type
#define EFFECT_XML_TYPE_LIST_DEF(V) \
V("acoustic_echo_canceler", AcousticEchoCanceler) \
@@ -268,6 +275,7 @@
#define GENERATE_MAP_ENTRY_V(s, symbol) {s, &getEffectTypeUuid##symbol},
+ const std::string xmlEffectName = effectElem.first;
typedef const AudioUuid& (*UuidGetter)(void);
static const std::map<std::string, UuidGetter> uuidMap{
// std::make_pair("s", &getEffectTypeUuidExtension)};
@@ -276,6 +284,14 @@
*uuid = (*it->second)();
return true;
}
+
+ const auto& libs = effectElem.second.libraries;
+ for (const auto& lib : libs) {
+ if (lib.type.has_value()) {
+ *uuid = lib.type.value();
+ return true;
+ }
+ }
return false;
}
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 8ed62c9..96f13ba 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -49,18 +49,18 @@
if (auto spEffect = it.first.lock()) {
LOG(ERROR) << __func__ << " erase remaining instance UUID "
<< ::android::audio::utils::toString(it.second.first);
- destroyEffectImpl(spEffect);
+ destroyEffectImpl_l(spEffect);
}
}
}
}
-ndk::ScopedAStatus Factory::getDescriptorWithUuid(const AudioUuid& uuid, Descriptor* desc) {
+ndk::ScopedAStatus Factory::getDescriptorWithUuid_l(const AudioUuid& uuid, Descriptor* desc) {
RETURN_IF(!desc, EX_NULL_POINTER, "nullDescriptor");
if (mEffectLibMap.count(uuid)) {
auto& entry = mEffectLibMap[uuid];
- getDlSyms(entry);
+ getDlSyms_l(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->queryEffectFunc, EX_NULL_POINTER,
"dlNullQueryEffectFunc");
@@ -75,6 +75,7 @@
const std::optional<AudioUuid>& in_impl_uuid,
const std::optional<AudioUuid>& in_proxy_uuid,
std::vector<Descriptor>* _aidl_return) {
+ std::lock_guard lg(mMutex);
// get the matching list
std::vector<Descriptor::Identity> idList;
std::copy_if(mIdentitySet.begin(), mIdentitySet.end(), std::back_inserter(idList),
@@ -88,7 +89,8 @@
for (const auto& id : idList) {
if (mEffectLibMap.count(id.uuid)) {
Descriptor desc;
- RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid(id.uuid, &desc), "getDescriptorFailed");
+ RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid_l(id.uuid, &desc),
+ "getDescriptorFailed");
// update proxy UUID with information from config xml
desc.common.id.proxy = id.proxy;
_aidl_return->emplace_back(std::move(desc));
@@ -99,18 +101,19 @@
ndk::ScopedAStatus Factory::queryProcessing(const std::optional<Processing::Type>& in_type,
std::vector<Processing>* _aidl_return) {
+ std::lock_guard lg(mMutex);
const auto& processings = mConfig.getProcessingMap();
// Processing stream type
for (const auto& procIter : processings) {
if (!in_type.has_value() || in_type.value() == procIter.first) {
Processing process = {.type = procIter.first /* Processing::Type */};
for (const auto& libs : procIter.second /* std::vector<struct EffectLibraries> */) {
- for (const auto& lib : libs.libraries /* std::vector<struct LibraryUuid> */) {
+ for (const auto& lib : libs.libraries /* std::vector<struct Library> */) {
Descriptor desc;
if (libs.proxyLibrary.has_value()) {
desc.common.id.proxy = libs.proxyLibrary.value().uuid;
}
- RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid(lib.uuid, &desc),
+ RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid_l(lib.uuid, &desc),
"getDescriptorFailed");
process.ids.emplace_back(desc);
}
@@ -125,9 +128,10 @@
ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
std::shared_ptr<IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << ::android::audio::utils::toString(in_impl_uuid);
+ std::lock_guard lg(mMutex);
if (mEffectLibMap.count(in_impl_uuid)) {
auto& entry = mEffectLibMap[in_impl_uuid];
- getDlSyms(entry);
+ getDlSyms_l(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->createEffectFunc, EX_NULL_POINTER,
@@ -152,7 +156,7 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
+ndk::ScopedAStatus Factory::destroyEffectImpl_l(const std::shared_ptr<IEffect>& in_handle) {
std::weak_ptr<IEffect> wpHandle(in_handle);
// find the effect entry with key (std::weak_ptr<IEffect>)
if (auto effectIt = mEffectMap.find(wpHandle); effectIt != mEffectMap.end()) {
@@ -177,7 +181,7 @@
}
// go over the map and cleanup all expired weak_ptrs.
-void Factory::cleanupEffectMap() {
+void Factory::cleanupEffectMap_l() {
for (auto it = mEffectMap.begin(); it != mEffectMap.end();) {
if (nullptr == it->first.lock()) {
it = mEffectMap.erase(it);
@@ -189,13 +193,15 @@
ndk::ScopedAStatus Factory::destroyEffect(const std::shared_ptr<IEffect>& in_handle) {
LOG(DEBUG) << __func__ << ": instance " << in_handle.get();
- ndk::ScopedAStatus status = destroyEffectImpl(in_handle);
+ std::lock_guard lg(mMutex);
+ ndk::ScopedAStatus status = destroyEffectImpl_l(in_handle);
// always do the cleanup
- cleanupEffectMap();
+ cleanupEffectMap_l();
return status;
}
-bool Factory::openEffectLibrary(const AudioUuid& impl, const std::string& path) {
+bool Factory::openEffectLibrary(const AudioUuid& impl,
+ const std::string& path) NO_THREAD_SAFETY_ANALYSIS {
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
@@ -219,9 +225,9 @@
return true;
}
-void Factory::createIdentityWithConfig(const EffectConfig::LibraryUuid& configLib,
- const AudioUuid& typeUuid,
- const std::optional<AudioUuid> proxyUuid) {
+void Factory::createIdentityWithConfig(
+ const EffectConfig::Library& configLib, const AudioUuid& typeUuid,
+ const std::optional<AudioUuid> proxyUuid) NO_THREAD_SAFETY_ANALYSIS {
static const auto& libMap = mConfig.getLibraryMap();
const std::string& libName = configLib.name;
if (auto path = libMap.find(libName); path != libMap.end()) {
@@ -246,8 +252,7 @@
void Factory::loadEffectLibs() {
const auto& configEffectsMap = mConfig.getEffectsMap();
for (const auto& configEffects : configEffectsMap) {
- if (AudioUuid uuid;
- EffectConfig::findUuid(configEffects.first /* xml effect name */, &uuid)) {
+ if (AudioUuid type; EffectConfig::findUuid(configEffects /* xml effect */, &type)) {
const auto& configLibs = configEffects.second;
std::optional<AudioUuid> proxyUuid;
if (configLibs.proxyLibrary.has_value()) {
@@ -255,7 +260,7 @@
proxyUuid = proxyLib.uuid;
}
for (const auto& configLib : configLibs.libraries) {
- createIdentityWithConfig(configLib, uuid, proxyUuid);
+ createIdentityWithConfig(configLib, type, proxyUuid);
}
} else {
LOG(ERROR) << __func__ << ": can not find type UUID for effect " << configEffects.first
@@ -264,7 +269,7 @@
}
}
-void Factory::getDlSyms(DlEntry& entry) {
+void Factory::getDlSyms_l(DlEntry& entry) {
auto& dlHandle = std::get<kMapEntryHandleIndex>(entry);
RETURN_VALUE_IF(!dlHandle, void(), "dlNullHandle");
// Get the reference of the DL interfaces in library map tuple.
diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp
index da1ad11..c81c731 100644
--- a/audio/aidl/default/EffectImpl.cpp
+++ b/audio/aidl/default/EffectImpl.cpp
@@ -76,7 +76,7 @@
}
ndk::ScopedAStatus EffectImpl::setParameter(const Parameter& param) {
- LOG(DEBUG) << getEffectName() << __func__ << " with: " << param.toString();
+ LOG(VERBOSE) << getEffectName() << __func__ << " with: " << param.toString();
const auto tag = param.getTag();
switch (tag) {
@@ -100,7 +100,6 @@
}
ndk::ScopedAStatus EffectImpl::getParameter(const Parameter::Id& id, Parameter* param) {
- LOG(DEBUG) << getEffectName() << __func__ << id.toString();
auto tag = id.getTag();
switch (tag) {
case Parameter::Id::commonTag: {
@@ -117,7 +116,7 @@
break;
}
}
- LOG(DEBUG) << getEffectName() << __func__ << param->toString();
+ LOG(VERBOSE) << getEffectName() << __func__ << id.toString() << param->toString();
return ndk::ScopedAStatus::ok();
}
@@ -254,7 +253,7 @@
for (int i = 0; i < samples; i++) {
*out++ = *in++;
}
- LOG(DEBUG) << getEffectName() << __func__ << " done processing " << samples << " samples";
+ LOG(VERBOSE) << getEffectName() << __func__ << " done processing " << samples << " samples";
return {STATUS_OK, samples, samples};
}
diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp
index 574dc69..cd2ba53 100644
--- a/audio/aidl/default/EffectThread.cpp
+++ b/audio/aidl/default/EffectThread.cpp
@@ -149,8 +149,8 @@
IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
outputMQ->write(buffer, status.fmqProduced);
statusMQ->writeBlocking(&status, 1);
- LOG(DEBUG) << mName << __func__ << ": done processing, effect consumed "
- << status.fmqConsumed << " produced " << status.fmqProduced;
+ LOG(VERBOSE) << mName << __func__ << ": done processing, effect consumed "
+ << status.fmqConsumed << " produced " << status.fmqProduced;
}
}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 48d1458..5478633 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -25,13 +25,12 @@
#include <android/binder_ibinder_platform.h>
#include <error/expected_utils.h>
-#include "core-impl/Bluetooth.h"
#include "core-impl/Module.h"
+#include "core-impl/ModulePrimary.h"
#include "core-impl/ModuleRemoteSubmix.h"
+#include "core-impl/ModuleStub.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/SoundDose.h"
-#include "core-impl/StreamStub.h"
-#include "core-impl/Telephony.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
@@ -110,13 +109,14 @@
// static
std::shared_ptr<Module> Module::createInstance(Type type) {
switch (type) {
- case Module::Type::USB:
- return ndk::SharedRefBase::make<ModuleUsb>(type);
- case Type::R_SUBMIX:
- return ndk::SharedRefBase::make<ModuleRemoteSubmix>(type);
case Type::DEFAULT:
- default:
- return ndk::SharedRefBase::make<Module>(type);
+ return ndk::SharedRefBase::make<ModulePrimary>();
+ case Type::R_SUBMIX:
+ return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
+ case Type::STUB:
+ return ndk::SharedRefBase::make<ModuleStub>();
+ case Type::USB:
+ return ndk::SharedRefBase::make<ModuleUsb>();
}
}
@@ -128,6 +128,9 @@
case Module::Type::R_SUBMIX:
os << "r_submix";
break;
+ case Module::Type::STUB:
+ os << "stub";
+ break;
case Module::Type::USB:
os << "usb";
break;
@@ -283,19 +286,28 @@
return result;
}
+std::unique_ptr<internal::Configuration> Module::initializeConfig() {
+ std::unique_ptr<internal::Configuration> config;
+ switch (getType()) {
+ case Type::DEFAULT:
+ config = std::move(internal::getPrimaryConfiguration());
+ break;
+ case Type::R_SUBMIX:
+ config = std::move(internal::getRSubmixConfiguration());
+ break;
+ case Type::STUB:
+ config = std::move(internal::getStubConfiguration());
+ break;
+ case Type::USB:
+ config = std::move(internal::getUsbConfiguration());
+ break;
+ }
+ return config;
+}
+
internal::Configuration& Module::getConfig() {
if (!mConfig) {
- switch (mType) {
- case Type::DEFAULT:
- mConfig = std::move(internal::getPrimaryConfiguration());
- break;
- case Type::R_SUBMIX:
- mConfig = std::move(internal::getRSubmixConfiguration());
- break;
- case Type::USB:
- mConfig = std::move(internal::getUsbConfiguration());
- break;
- }
+ mConfig = std::move(initializeConfig());
}
return *mConfig;
}
@@ -395,38 +407,26 @@
}
ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
- if (!mTelephony) {
- mTelephony = ndk::SharedRefBase::make<Telephony>();
- }
- *_aidl_return = mTelephony.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+ *_aidl_return = nullptr;
+ LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
- if (!mBluetooth) {
- mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
- }
- *_aidl_return = mBluetooth.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
+ *_aidl_return = nullptr;
+ LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
- if (!mBluetoothA2dp) {
- mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
- }
- *_aidl_return = mBluetoothA2dp.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
+ *_aidl_return = nullptr;
+ LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
- if (!mBluetoothLe) {
- mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
- }
- *_aidl_return = mBluetoothLe.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+ *_aidl_return = nullptr;
+ LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
@@ -1334,22 +1334,6 @@
return mIsMmapSupported.value();
}
-ndk::ScopedAStatus Module::createInputStream(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
- const std::vector<MicrophoneInfo>& microphones,
- std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
- microphones);
-}
-
-ndk::ScopedAStatus Module::createOutputStream(const SourceMetadata& sourceMetadata,
- StreamContext&& context,
- const std::optional<AudioOffloadInfo>& offloadInfo,
- std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
- offloadInfo);
-}
-
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
new file mode 100644
index 0000000..cbb6730
--- /dev/null
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#define LOG_TAG "AHAL_ModulePrimary"
+#include <Utils.h>
+#include <android-base/logging.h>
+
+#include "core-impl/ModulePrimary.h"
+#include "core-impl/StreamStub.h"
+#include "core-impl/Telephony.h"
+
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModulePrimary::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
+ if (!mTelephony) {
+ mTelephony = ndk::SharedRefBase::make<Telephony>();
+ }
+ *_aidl_return = mTelephony.getPtr();
+ LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata,
+ StreamContext&& context,
+ const std::vector<MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) {
+ return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+ microphones);
+}
+
+ndk::ScopedAStatus ModulePrimary::createOutputStream(
+ const SourceMetadata& sourceMetadata, StreamContext&& context,
+ const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+ return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+ offloadInfo);
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 251dea0..215de94 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -110,10 +110,12 @@
if (isConnected) {
reply->observable.frames = mFrameCount;
reply->observable.timeNs = ::android::elapsedRealtimeNano();
- } else {
- reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
- reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
+ if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
+ return;
+ }
}
+ reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
+ reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
}
void StreamWorkerCommonLogic::populateReplyWrongState(
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
new file mode 100644
index 0000000..f0393e3
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHAL_AlsaMixer"
+#include <android-base/logging.h>
+
+#include <cmath>
+
+#include <android/binder_status.h>
+
+#include "Mixer.h"
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+//-----------------------------------------------------------------------------
+
+MixerControl::MixerControl(struct mixer_ctl* ctl)
+ : mCtl(ctl),
+ mNumValues(mixer_ctl_get_num_values(ctl)),
+ mMinValue(mixer_ctl_get_range_min(ctl)),
+ mMaxValue(mixer_ctl_get_range_max(ctl)) {}
+
+unsigned int MixerControl::getNumValues() const {
+ return mNumValues;
+}
+
+int MixerControl::getMaxValue() const {
+ return mMaxValue;
+}
+
+int MixerControl::getMinValue() const {
+ return mMinValue;
+}
+
+int MixerControl::setArray(const void* array, size_t count) {
+ const std::lock_guard guard(mLock);
+ return mixer_ctl_set_array(mCtl, array, count);
+}
+
+//-----------------------------------------------------------------------------
+
+// static
+const std::map<Mixer::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
+ Mixer::kPossibleControls = {
+ {Mixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
+ {Mixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
+ {Mixer::HW_VOLUME,
+ {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
+ {"Headset Playback Volume", MIXER_CTL_TYPE_INT},
+ {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
+
+// static
+std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
+ struct mixer* mixer) {
+ std::map<Mixer::Control, std::shared_ptr<MixerControl>> mixerControls;
+ std::string mixerCtlNames;
+ for (const auto& [control, possibleCtls] : kPossibleControls) {
+ for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
+ struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
+ if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
+ mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
+ if (!mixerCtlNames.empty()) {
+ mixerCtlNames += ",";
+ }
+ mixerCtlNames += ctlName;
+ break;
+ }
+ }
+ }
+ LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
+ return mixerControls;
+}
+
+Mixer::Mixer(struct mixer* mixer)
+ : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
+
+Mixer::~Mixer() {
+ mixer_close(mMixer);
+}
+
+namespace {
+
+int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
+ return minValue + std::ceil((maxValue - minValue) * fValue);
+}
+
+} // namespace
+
+ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
+ auto it = mMixerControls.find(Mixer::MASTER_SWITCH);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ const int numValues = it->second->getNumValues();
+ std::vector<int> values(numValues, muted ? 0 : 1);
+ if (int err = it->second->setArray(values.data(), numValues); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
+ auto it = mMixerControls.find(Mixer::MASTER_VOLUME);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ const int numValues = it->second->getNumValues();
+ std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
+ it->second->getMinValue()));
+ if (int err = it->second->setArray(values.data(), numValues); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
+ auto it = mMixerControls.find(Mixer::HW_VOLUME);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ const int numValues = it->second->getNumValues();
+ if (numValues < 0) {
+ LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
+ }
+ const int maxValue = it->second->getMaxValue();
+ const int minValue = it->second->getMinValue();
+ std::vector<int> values;
+ size_t i = 0;
+ for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
+ values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
+ }
+ if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h
new file mode 100644
index 0000000..de9e6f4
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+
+extern "C" {
+#include <tinyalsa/mixer.h>
+}
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+class MixerControl {
+ public:
+ explicit MixerControl(struct mixer_ctl* ctl);
+
+ unsigned int getNumValues() const;
+ int getMaxValue() const;
+ int getMinValue() const;
+ int setArray(const void* array, size_t count);
+
+ private:
+ std::mutex mLock;
+ // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
+ struct mixer_ctl* mCtl GUARDED_BY(mLock);
+ const unsigned int mNumValues;
+ const int mMinValue;
+ const int mMaxValue;
+};
+
+class Mixer {
+ public:
+ explicit Mixer(struct mixer* mixer);
+
+ ~Mixer();
+
+ bool isValid() const { return mMixer != nullptr; }
+
+ ndk::ScopedAStatus setMasterMute(bool muted);
+ ndk::ScopedAStatus setMasterVolume(float volume);
+ ndk::ScopedAStatus setVolumes(const std::vector<float>& volumes);
+
+ private:
+ enum Control {
+ MASTER_SWITCH,
+ MASTER_VOLUME,
+ HW_VOLUME,
+ };
+ using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
+ static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
+ static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
+ struct mixer* mixer);
+
+ // The mixer object is owned by ALSA and will be released when the mixer is closed.
+ struct mixer* mMixer;
+ // `mMixerControls` will only be initialized in constructor. After that, it wil only be
+ // read but not be modified.
+ const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
+};
+
+} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp
new file mode 100644
index 0000000..8e75d56
--- /dev/null
+++ b/audio/aidl/default/alsa/ModuleAlsa.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHAL_ModuleAlsa"
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "Utils.h"
+#include "core-impl/ModuleAlsa.h"
+
+extern "C" {
+#include "alsa_device_profile.h"
+}
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioProfile;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) {
+ auto deviceProfile = alsa::getDeviceProfile(*audioPort);
+ if (!deviceProfile.has_value()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ auto profile = alsa::readAlsaDeviceInfo(*deviceProfile);
+ if (!profile.has_value()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(&profile.value());
+ std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(&profile.value());
+
+ for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
+ profile->formats[i] != PCM_FORMAT_INVALID;
+ ++i) {
+ auto audioFormatDescription =
+ alsa::c2aidl_pcm_format_AudioFormatDescription(profile->formats[i]);
+ if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
+ LOG(WARNING) << __func__ << ": unknown pcm type=" << profile->formats[i];
+ continue;
+ }
+ AudioProfile audioProfile = {.format = audioFormatDescription,
+ .channelMasks = channels,
+ .sampleRates = sampleRates};
+ audioPort->profiles.push_back(std::move(audioProfile));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
new file mode 100644
index 0000000..17c7feb
--- /dev/null
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+
+#define LOG_TAG "AHAL_StreamAlsa"
+#include <android-base/logging.h>
+
+#include <Utils.h>
+#include <audio_utils/clock.h>
+#include <error/expected_utils.h>
+
+#include "core-impl/StreamAlsa.h"
+
+namespace aidl::android::hardware::audio::core {
+
+StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context)
+ : StreamCommonImpl(metadata, std::move(context)),
+ mFrameSizeBytes(getContext().getFrameSize()),
+ mIsInput(isInput(metadata)),
+ mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {}
+
+::android::status_t StreamAlsa::init() {
+ return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
+}
+
+::android::status_t StreamAlsa::standby() {
+ mAlsaDeviceProxies.clear();
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::start() {
+ decltype(mAlsaDeviceProxies) alsaDeviceProxies;
+ for (const auto& device : getDeviceProfiles()) {
+ auto profile = alsa::readAlsaDeviceInfo(device);
+ if (!profile.has_value()) {
+ LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device;
+ return ::android::UNKNOWN_ERROR;
+ }
+
+ auto proxy = alsa::makeDeviceProxy();
+ // Always ask for alsa configure as required since the configuration should be supported
+ // by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`.
+ if (int err = proxy_prepare(proxy.get(), &profile.value(),
+ const_cast<struct pcm_config*>(&mConfig.value()),
+ true /*require_exact_match*/);
+ err != 0) {
+ LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device
+ << " error=" << err;
+ return ::android::UNKNOWN_ERROR;
+ }
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << device
+ << " error=" << err;
+ return ::android::UNKNOWN_ERROR;
+ }
+ alsaDeviceProxies.push_back(std::move(proxy));
+ }
+ mAlsaDeviceProxies = std::move(alsaDeviceProxies);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+ int32_t* latencyMs) {
+ const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
+ unsigned maxLatency = 0;
+ if (mIsInput) {
+ if (mAlsaDeviceProxies.empty()) {
+ LOG(FATAL) << __func__ << ": no input devices";
+ return ::android::NO_INIT;
+ }
+ // For input case, only support single device.
+ proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
+ maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
+ } else {
+ for (auto& proxy : mAlsaDeviceProxies) {
+ proxy_write(proxy.get(), buffer, bytesToTransfer);
+ maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
+ }
+ }
+ *actualFrameCount = frameCount;
+ maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
+ *latencyMs = maxLatency;
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) {
+ if (mAlsaDeviceProxies.empty()) {
+ LOG(FATAL) << __func__ << ": no input devices";
+ return ::android::NO_INIT;
+ }
+ if (mIsInput) {
+ if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
+ &position->timeNs);
+ ret != 0) {
+ LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
+ return ::android::INVALID_OPERATION;
+ }
+ } else {
+ uint64_t hwFrames;
+ struct timespec timestamp;
+ if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
+ ×tamp);
+ ret == 0) {
+ if (hwFrames > std::numeric_limits<int64_t>::max()) {
+ hwFrames -= std::numeric_limits<int64_t>::max();
+ }
+ position->frames = static_cast<int64_t>(hwFrames);
+ position->timeNs = audio_utils_ns_from_timespec(×tamp);
+ } else {
+ LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
+ return ::android::INVALID_OPERATION;
+ }
+ }
+ return ::android::OK;
+}
+
+void StreamAlsa::shutdown() {
+ mAlsaDeviceProxies.clear();
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp
new file mode 100644
index 0000000..162f852
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <set>
+
+#define LOG_TAG "AHAL_AlsaUtils"
+#include <Utils.h>
+#include <aidl/android/media/audio/common/AudioFormatType.h>
+#include <aidl/android/media/audio/common/PcmType.h>
+#include <android-base/logging.h>
+
+#include "Utils.h"
+#include "core-impl/utils.h"
+
+using aidl::android::hardware::audio::common::getChannelCount;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioPortExt;
+using aidl::android::media::audio::common::PcmType;
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+namespace {
+
+using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
+using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
+using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
+
+AudioChannelLayout getInvalidChannelLayout() {
+ static const AudioChannelLayout invalidChannelLayout =
+ AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
+ return invalidChannelLayout;
+}
+
+static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
+ const std::set<AudioChannelLayout>& channelMasks) {
+ AudioChannelCountToMaskMap channelMaskToCountMap;
+ for (const auto& channelMask : channelMasks) {
+ channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
+ }
+ return channelMaskToCountMap;
+}
+
+#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
+
+const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
+ static const std::set<AudioChannelLayout> supportedOutChannelLayouts = {
+ DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
+ DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
+ DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
+ DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
+ DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
+ };
+ static const AudioChannelCountToMaskMap outLayouts =
+ make_ChannelCountToMaskMap(supportedOutChannelLayouts);
+ return outLayouts;
+}
+
+const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
+ static const std::set<AudioChannelLayout> supportedInChannelLayouts = {
+ DEFINE_CHANNEL_LAYOUT_MASK(MONO),
+ DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
+ };
+ static const AudioChannelCountToMaskMap inLayouts =
+ make_ChannelCountToMaskMap(supportedInChannelLayouts);
+ return inLayouts;
+}
+
+#undef DEFINE_CHANNEL_LAYOUT_MASK
+#define DEFINE_CHANNEL_INDEX_MASK(n) \
+ AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
+
+const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
+ static const std::set<AudioChannelLayout> supportedIndexChannelLayouts = {
+ DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2),
+ DEFINE_CHANNEL_INDEX_MASK(3), DEFINE_CHANNEL_INDEX_MASK(4),
+ DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
+ DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8),
+ DEFINE_CHANNEL_INDEX_MASK(9), DEFINE_CHANNEL_INDEX_MASK(10),
+ DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
+ DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14),
+ DEFINE_CHANNEL_INDEX_MASK(15), DEFINE_CHANNEL_INDEX_MASK(16),
+ DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
+ DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20),
+ DEFINE_CHANNEL_INDEX_MASK(21), DEFINE_CHANNEL_INDEX_MASK(22),
+ DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
+ };
+ static const AudioChannelCountToMaskMap indexLayouts =
+ make_ChannelCountToMaskMap(supportedIndexChannelLayouts);
+ return indexLayouts;
+}
+
+#undef DEFINE_CHANNEL_INDEX_MASK
+
+AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
+ AudioFormatDescription result;
+ result.type = type;
+ return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
+ auto result = make_AudioFormatDescription(AudioFormatType::PCM);
+ result.pcm = pcm;
+ return result;
+}
+
+const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
+ static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
+ {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
+ {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
+ {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
+ {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
+ {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
+ {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
+ };
+ return formatDescToPcmFormatMap;
+}
+
+static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
+ const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
+ PcmFormatToAudioFormatDescMap result;
+ for (const auto& formatPair : formatDescToPcmFormatMap) {
+ result.emplace(formatPair.second, formatPair.first);
+ }
+ return result;
+}
+
+const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
+ static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
+ make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
+ return pcmFormatToFormatDescMap;
+}
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) {
+ return os << "<" << device.card << "," << device.device << ">";
+}
+
+AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
+ return findValueOrDefault(
+ isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
+ channelCount, getInvalidChannelLayout());
+}
+
+AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
+ return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
+ getInvalidChannelLayout());
+}
+
+unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
+ switch (channelMask.getTag()) {
+ case AudioChannelLayout::Tag::layoutMask: {
+ return findKeyOrDefault(
+ isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
+ static_cast<unsigned>(getChannelCount(channelMask)), 0u /*defaultValue*/);
+ }
+ case AudioChannelLayout::Tag::indexMask: {
+ return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
+ static_cast<unsigned>(getChannelCount(channelMask)),
+ 0u /*defaultValue*/);
+ }
+ case AudioChannelLayout::Tag::none:
+ case AudioChannelLayout::Tag::invalid:
+ case AudioChannelLayout::Tag::voiceMask:
+ default:
+ return 0;
+ }
+}
+
+std::vector<AudioChannelLayout> getChannelMasksFromProfile(const alsa_device_profile* profile) {
+ const bool isInput = profile->direction == PCM_IN;
+ std::vector<AudioChannelLayout> channels;
+ for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
+ auto layoutMask =
+ alsa::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
+ if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
+ channels.push_back(layoutMask);
+ }
+ auto indexMask = alsa::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
+ if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
+ channels.push_back(indexMask);
+ }
+ }
+ return channels;
+}
+
+std::optional<DeviceProfile> getDeviceProfile(
+ const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput) {
+ if (audioDevice.address.getTag() != AudioDeviceAddress::Tag::alsa) {
+ LOG(ERROR) << __func__ << ": not alsa address: " << audioDevice.toString();
+ return std::nullopt;
+ }
+ auto& alsaAddress = audioDevice.address.get<AudioDeviceAddress::Tag::alsa>();
+ if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
+ LOG(ERROR) << __func__
+ << ": malformed alsa address: " << ::android::internal::ToString(alsaAddress);
+ return std::nullopt;
+ }
+ return DeviceProfile{.card = alsaAddress[0],
+ .device = alsaAddress[1],
+ .direction = isInput ? PCM_IN : PCM_OUT};
+}
+
+std::optional<DeviceProfile> getDeviceProfile(
+ const ::aidl::android::media::audio::common::AudioPort& audioPort) {
+ if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
+ LOG(ERROR) << __func__ << ": port id " << audioPort.id << " is not a device port";
+ return std::nullopt;
+ }
+ auto& devicePort = audioPort.ext.get<AudioPortExt::Tag::device>();
+ return getDeviceProfile(devicePort.device, audioPort.flags.getTag() == AudioIoFlags::input);
+}
+
+std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput) {
+ struct pcm_config config;
+ config.channels = alsa::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
+ if (config.channels == 0) {
+ LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
+ return std::nullopt;
+ }
+ config.format = alsa::aidl2c_AudioFormatDescription_pcm_format(context.getFormat());
+ if (config.format == PCM_FORMAT_INVALID) {
+ LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
+ return std::nullopt;
+ }
+ config.rate = context.getSampleRate();
+ if (config.rate == 0) {
+ LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
+ return std::nullopt;
+ }
+ return config;
+}
+
+std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile) {
+ std::vector<int> sampleRates;
+ for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
+ profile->sample_rates[i] != 0;
+ i++) {
+ sampleRates.push_back(profile->sample_rates[i]);
+ }
+ return sampleRates;
+}
+
+DeviceProxy makeDeviceProxy() {
+ return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
+ if (proxy != nullptr) {
+ proxy_close(proxy);
+ delete proxy;
+ }
+ });
+}
+
+std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
+ alsa_device_profile profile;
+ profile_init(&profile, deviceProfile.direction);
+ profile.card = deviceProfile.card;
+ profile.device = deviceProfile.device;
+ if (!profile_read_device_info(&profile)) {
+ LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
+ << ", device=" << profile.device;
+ return std::nullopt;
+ }
+ return profile;
+}
+
+AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
+ return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
+}
+
+pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
+ return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
+}
+
+} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h
new file mode 100644
index 0000000..c1b9b38
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/AudioPort.h>
+
+#include "core-impl/Stream.h"
+
+extern "C" {
+#include <tinyalsa/pcm.h>
+#include "alsa_device_profile.h"
+#include "alsa_device_proxy.h"
+}
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+struct DeviceProfile {
+ int card;
+ int device;
+ int direction; /* PCM_OUT or PCM_IN */
+};
+std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
+using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
+using DeviceProxy = std::unique_ptr<alsa_device_proxy, DeviceProxyDeleter>;
+
+::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
+ unsigned int channelCount, int isInput);
+::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
+ unsigned int channelCount);
+unsigned int getChannelCountFromChannelMask(
+ const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
+std::vector<::aidl::android::media::audio::common::AudioChannelLayout> getChannelMasksFromProfile(
+ const alsa_device_profile* profile);
+std::optional<DeviceProfile> getDeviceProfile(
+ const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput);
+std::optional<DeviceProfile> getDeviceProfile(
+ const ::aidl::android::media::audio::common::AudioPort& audioPort);
+std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
+std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
+DeviceProxy makeDeviceProxy();
+std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
+
+::aidl::android::media::audio::common::AudioFormatDescription
+c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
+pcm_format aidl2c_AudioFormatDescription_pcm_format(
+ const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
+
+} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml
index 9636a58..c9d6314 100644
--- a/audio/aidl/default/android.hardware.audio.service-aidl.xml
+++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml
@@ -12,6 +12,11 @@
<hal format="aidl">
<name>android.hardware.audio.core</name>
<version>1</version>
+ <fqname>IModule/stub</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.audio.core</name>
+ <version>1</version>
<fqname>IModule/usb</fqname>
</hal>
<hal format="aidl">
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index 6627ae7..00de797 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -50,7 +50,8 @@
</libraries>
<!-- list of effects to load.
- Each "effect" element must contain a "name", "library" and a "uuid" attribute.
+ Each "effect" element must contain a "name", "library" and a "uuid" attribute, an optional
+ "type" attribute can be used to add any customized effect type.
The value of the "library" attribute must correspond to the name of one library element in
the "libraries" element.
The "name" attribute used to specific effect type, and should be mapping to a key of
@@ -62,8 +63,8 @@
result of IFactory.queryEffects() to decide which effect implementation should be part of
proxy and which not.
- Only "name", "library", and "uuid" attributes in "effects" element are meaningful and
- parsed out by EffectConfig class, all other attributes are ignored.
+ Only "name", "library", "uuid", and "type" attributes in "effects" element are meaningful
+ and parsed out by EffectConfig class, all other attributes are ignored.
Only "name" and "uuid" attributes in "effectProxy" element are meaningful and parsed out
by EffectConfig class, all other attributes are ignored.
-->
@@ -94,7 +95,7 @@
<libsw library="equalizersw" uuid="0bed4300-847d-11df-bb17-0002a5d5c51b"/>
<libsw library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
</effectProxy>
- <effect name="extensioneffect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="extension_effect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002" type="fa81de0e-588b-11ed-9b6a-0242ac120002"/>
<effect name="acoustic_echo_canceler" library="aecsw" uuid="bb392ec0-8d4d-11e0-a896-0002a5d5c51b"/>
<effect name="noise_suppression" library="nssw" uuid="c06c8400-8e06-11e0-9cb6-0002a5d5c51b"/>
</effects>
diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h
new file mode 100644
index 0000000..1b31691
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ChildInterface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_ibinder_platform.h>
+#include <system/thread_defs.h>
+
+namespace aidl::android::hardware::audio::core {
+
+// Helper used for interfaces that require a persistent instance. We hold them via a strong
+// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
+template <class C>
+struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
+ ChildInterface() = default;
+ ChildInterface& operator=(const std::shared_ptr<C>& c) {
+ return operator=(std::shared_ptr<C>(c));
+ }
+ ChildInterface& operator=(std::shared_ptr<C>&& c) {
+ this->first = std::move(c);
+ this->second = this->first->asBinder();
+ AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
+ return *this;
+ }
+ explicit operator bool() const { return !!this->first; }
+ C& operator*() const { return *(this->first); }
+ C* operator->() const { return this->first; }
+ std::shared_ptr<C> getPtr() const { return this->first; }
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 70320e4..25bf7af 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -45,6 +45,7 @@
std::unique_ptr<Configuration> getPrimaryConfiguration();
std::unique_ptr<Configuration> getRSubmixConfiguration();
+std::unique_ptr<Configuration> getStubConfiguration();
std::unique_ptr<Configuration> getUsbConfiguration();
} // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 4a23637..294cc0e 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -16,12 +16,14 @@
#pragma once
+#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <aidl/android/hardware/audio/core/BnModule.h>
+#include "core-impl/ChildInterface.h"
#include "core-impl/Configuration.h"
#include "core-impl/Stream.h"
@@ -31,7 +33,7 @@
public:
// This value is used for all AudioPatches and reported by all streams.
static constexpr int32_t kLatencyMs = 10;
- enum Type : int { DEFAULT, R_SUBMIX, USB };
+ enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
static std::shared_ptr<Module> createInstance(Type type);
@@ -132,26 +134,6 @@
bool forceTransientBurst = false;
bool forceSynchronousDrain = false;
};
- // Helper used for interfaces that require a persistent instance. We hold them via a strong
- // pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
- template <class C>
- struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
- ChildInterface() {}
- ChildInterface& operator=(const std::shared_ptr<C>& c) {
- return operator=(std::shared_ptr<C>(c));
- }
- ChildInterface& operator=(std::shared_ptr<C>&& c) {
- this->first = std::move(c);
- this->second = this->first->asBinder();
- AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
- ANDROID_PRIORITY_AUDIO);
- return *this;
- }
- explicit operator bool() const { return !!this->first; }
- C& operator*() const { return *(this->first); }
- C* operator->() const { return this->first; }
- std::shared_ptr<C> getPtr() const { return this->first; }
- };
// ids of device ports created at runtime via 'connectExternalDevice'.
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
// the connected port. This list can be empty, thus an int->int multimap can't be used.
@@ -164,10 +146,6 @@
std::unique_ptr<internal::Configuration> mConfig;
ModuleDebug mDebug;
VendorDebug mVendorDebug;
- ChildInterface<ITelephony> mTelephony;
- ChildInterface<IBluetooth> mBluetooth;
- ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
- ChildInterface<IBluetoothLe> mBluetoothLe;
ConnectedDevicePorts mConnectedDevicePorts;
Streams mStreams;
Patches mPatches;
@@ -184,13 +162,13 @@
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
- std::shared_ptr<StreamIn>* result);
+ std::shared_ptr<StreamIn>* result) = 0;
virtual ndk::ScopedAStatus createOutputStream(
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
- std::shared_ptr<StreamOut>* result);
+ std::shared_ptr<StreamOut>* result) = 0;
// If the module is unable to populate the connected device port correctly, the returned error
// code must correspond to the errors of `IModule.connectedExternalDevice` method.
virtual ndk::ScopedAStatus populateConnectedDevicePort(
@@ -204,6 +182,7 @@
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
+ virtual std::unique_ptr<internal::Configuration> initializeConfig();
// Utility and helper functions accessible to subclasses.
void cleanUpPatch(int32_t patchId);
@@ -224,6 +203,7 @@
bool getMicMute() const { return mMicMute; }
const Patches& getPatches() const { return mPatches; }
const Streams& getStreams() const { return mStreams; }
+ Type getType() const { return mType; }
bool isMmapSupported();
template <typename C>
std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
@@ -232,4 +212,6 @@
const AudioPatch& newPatch);
};
+std::ostream& operator<<(std::ostream& os, Module::Type t);
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h
new file mode 100644
index 0000000..5815961
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// This class is intended to be used as a base class for implementations
+// that use TinyAlsa. This can be either a primary module or a USB Audio
+// module. This class does not define a complete module implementation,
+// and should never be used on its own. Derived classes are expected to
+// provide necessary overrides for all interface methods omitted here.
+class ModuleAlsa : public Module {
+ public:
+ explicit ModuleAlsa(Module::Type type) : Module(type) {}
+
+ protected:
+ // Extension methods of 'Module'.
+ ndk::ScopedAStatus populateConnectedDevicePort(
+ ::aidl::android::media::audio::common::AudioPort* audioPort) override;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
new file mode 100644
index 0000000..bc808ab
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class ModulePrimary final : public Module {
+ public:
+ ModulePrimary() : Module(Type::DEFAULT) {}
+
+ protected:
+ ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
+
+ ndk::ScopedAStatus createInputStream(
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ StreamContext&& context,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) override;
+ ndk::ScopedAStatus createOutputStream(
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ StreamContext&& context,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo,
+ std::shared_ptr<StreamOut>* result) override;
+
+ private:
+ ChildInterface<ITelephony> mTelephony;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
index 7b1d375..ccfcdd9 100644
--- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
@@ -22,7 +22,7 @@
class ModuleRemoteSubmix : public Module {
public:
- explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {}
+ ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {}
private:
// IModule interfaces
diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h
new file mode 100644
index 0000000..59c343f
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleStub.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class ModuleStub final : public Module {
+ public:
+ ModuleStub() : Module(Type::STUB) {}
+
+ protected:
+ ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
+ ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
+ ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
+
+ ndk::ScopedAStatus createInputStream(
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ StreamContext&& context,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) override;
+ ndk::ScopedAStatus createOutputStream(
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ StreamContext&& context,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo,
+ std::shared_ptr<StreamOut>* result) override;
+
+ private:
+ ChildInterface<IBluetooth> mBluetooth;
+ ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
+ ChildInterface<IBluetoothLe> mBluetoothLe;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h
index 5a5429d..e6b3e66 100644
--- a/audio/aidl/default/include/core-impl/ModuleUsb.h
+++ b/audio/aidl/default/include/core-impl/ModuleUsb.h
@@ -16,13 +16,13 @@
#pragma once
-#include "core-impl/Module.h"
+#include "core-impl/ModuleAlsa.h"
namespace aidl::android::hardware::audio::core {
-class ModuleUsb : public Module {
+class ModuleUsb final : public ModuleAlsa {
public:
- explicit ModuleUsb(Module::Type type) : Module(type) {}
+ ModuleUsb() : ModuleAlsa(Type::USB) {}
private:
// IModule interfaces
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index aaf5860..e64c578 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -184,6 +184,12 @@
virtual ::android::status_t start() = 0;
virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) = 0;
+ // No need to implement 'getPosition' unless the driver can provide more precise
+ // data than just total frame count. For example, the driver may correctly account
+ // for any intermediate buffers.
+ virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) {
+ return ::android::OK;
+ }
virtual void shutdown() = 0; // This function is only called once.
};
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
new file mode 100644
index 0000000..5744d66
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <vector>
+
+#include "Stream.h"
+#include "alsa/Utils.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// This class is intended to be used as a base class for implementations
+// that use TinyAlsa.
+// This class does not define a complete stream implementation,
+// and should never be used on its own. Derived classes are expected to
+// provide necessary overrides for all interface methods omitted here.
+class StreamAlsa : public StreamCommonImpl {
+ public:
+ StreamAlsa(const Metadata& metadata, StreamContext&& context);
+ // Methods of 'DriverInterface'.
+ ::android::status_t init() override;
+ ::android::status_t standby() override;
+ ::android::status_t start() override;
+ ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+ int32_t* latencyMs) override;
+ ::android::status_t getPosition(StreamDescriptor::Position* position) override;
+ void shutdown() override;
+
+ protected:
+ // Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
+ virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
+
+ const size_t mFrameSizeBytes;
+ const bool mIsInput;
+ const std::optional<struct pcm_config> mConfig;
+ // All fields below are only used on the worker thread.
+ std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 2253ec7..1bca910 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -39,6 +39,7 @@
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
+ ::android::status_t getPosition(StreamDescriptor::Position* position) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index 8c40782..44f742a 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -17,55 +17,34 @@
#pragma once
#include <atomic>
-#include <functional>
#include <mutex>
-#include <optional>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
-#include "core-impl/Stream.h"
-
-extern "C" {
-#include <tinyalsa/pcm.h>
-#include "alsa_device_proxy.h"
-}
+#include "StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
-class StreamUsb : public StreamCommonImpl {
+class StreamUsb : public StreamAlsa {
public:
StreamUsb(const Metadata& metadata, StreamContext&& context);
// Methods of 'DriverInterface'.
- ::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
- ::android::status_t standby() override;
- ::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
- void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
- const ConnectedDevices& getConnectedDevices() const override;
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
- private:
- using AlsaDeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
- using AlsaDeviceProxy = std::unique_ptr<alsa_device_proxy, AlsaDeviceProxyDeleter>;
-
- static std::optional<struct pcm_config> maybePopulateConfig(const StreamContext& context,
- bool isInput);
+ protected:
+ std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
mutable std::mutex mLock;
-
- const size_t mFrameSizeBytes;
- const bool mIsInput;
- const std::optional<struct pcm_config> mConfig;
+ std::vector<alsa::DeviceProfile> mConnectedDeviceProfiles GUARDED_BY(mLock);
std::atomic<bool> mConnectedDevicesUpdated = false;
- // All fields below are only used on the worker thread.
- std::vector<AlsaDeviceProxy> mAlsaDeviceProxies;
};
class StreamInUsb final : public StreamUsb, public StreamIn {
@@ -94,7 +73,7 @@
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
- int mChannelCount;
+ const int mChannelCount;
std::vector<float> mHwVolumes;
};
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
index 22cdb6b..698e7a5 100644
--- a/audio/aidl/default/include/effect-impl/EffectContext.h
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -124,11 +124,11 @@
virtual RetCode setCommon(const Parameter::Common& common) {
mCommon = common;
- LOG(INFO) << __func__ << mCommon.toString();
+ LOG(VERBOSE) << __func__ << mCommon.toString();
return RetCode::SUCCESS;
}
virtual Parameter::Common getCommon() {
- LOG(DEBUG) << __func__ << mCommon.toString();
+ LOG(VERBOSE) << __func__ << mCommon.toString();
return mCommon;
}
diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h
index b456817..421429a 100644
--- a/audio/aidl/default/include/effect-impl/EffectWorker.h
+++ b/audio/aidl/default/include/effect-impl/EffectWorker.h
@@ -45,8 +45,8 @@
auto readSamples = inputMQ->availableToRead(), writeSamples = outputMQ->availableToWrite();
if (readSamples && writeSamples) {
auto processSamples = std::min(readSamples, writeSamples);
- LOG(DEBUG) << __func__ << " available to read " << readSamples << " available to write "
- << writeSamples << " process " << processSamples;
+ LOG(VERBOSE) << __func__ << " available to read " << readSamples
+ << " available to write " << writeSamples << " process " << processSamples;
auto buffer = mContext->getWorkBuffer();
inputMQ->read(buffer, processSamples);
@@ -54,8 +54,8 @@
IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
outputMQ->write(buffer, status.fmqProduced);
statusMQ->writeBlocking(&status, 1);
- LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqConsumed
- << " produced " << status.fmqProduced;
+ LOG(VERBOSE) << __func__ << " done processing, effect consumed " << status.fmqConsumed
+ << " produced " << status.fmqProduced;
} else {
// TODO: maybe add some sleep here to avoid busy waiting
}
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
index f8a86e1..344846a 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
@@ -40,14 +40,15 @@
public:
explicit EffectConfig(const std::string& file);
- struct LibraryUuid {
+ struct Library {
std::string name; // library name
- ::aidl::android::media::audio::common::AudioUuid uuid;
+ ::aidl::android::media::audio::common::AudioUuid uuid; // implementation UUID
+ std::optional<::aidl::android::media::audio::common::AudioUuid> type; // optional type UUID
};
// <effects>
struct EffectLibraries {
- std::optional<struct LibraryUuid> proxyLibrary;
- std::vector<struct LibraryUuid> libraries;
+ std::optional<struct Library> proxyLibrary;
+ std::vector<struct Library> libraries;
};
int getSkippedElements() const { return mSkippedElements; }
@@ -56,7 +57,7 @@
return mEffectsMap;
}
- static bool findUuid(const std::string& xmlEffectName,
+ static bool findUuid(const std::pair<std::string, struct EffectLibraries>& effectElem,
::aidl::android::media::audio::common::AudioUuid* uuid);
using ProcessingLibrariesMap = std::map<Processing::Type, std::vector<struct EffectLibraries>>;
@@ -96,8 +97,8 @@
bool parseProcessing(Processing::Type::Tag typeTag, const tinyxml2::XMLElement& xml);
// Function to parse effect.library name and effect.uuid from xml
- bool parseLibraryUuid(const tinyxml2::XMLElement& xml, struct LibraryUuid& libraryUuid,
- bool isProxy = false);
+ bool parseLibrary(const tinyxml2::XMLElement& xml, struct Library& library,
+ bool isProxy = false);
const char* dump(const tinyxml2::XMLElement& element,
tinyxml2::XMLPrinter&& printer = {}) const;
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index ad59ca7..d0b8204 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -24,6 +24,7 @@
#include <vector>
#include <aidl/android/hardware/audio/effect/BnFactory.h>
+#include <android-base/thread_annotations.h>
#include "EffectConfig.h"
namespace aidl::android::hardware::audio::effect {
@@ -82,9 +83,11 @@
private:
const EffectConfig mConfig;
~Factory();
+
+ std::mutex mMutex;
// Set of effect descriptors supported by the devices.
- std::set<Descriptor> mDescSet;
- std::set<Descriptor::Identity> mIdentitySet;
+ std::set<Descriptor> mDescSet GUARDED_BY(mMutex);
+ std::set<Descriptor::Identity> mIdentitySet GUARDED_BY(mMutex);
static constexpr int kMapEntryHandleIndex = 0;
static constexpr int kMapEntryInterfaceIndex = 1;
@@ -94,26 +97,29 @@
std::string /* library name */>
DlEntry;
- std::map<aidl::android::media::audio::common::AudioUuid /* implUUID */, DlEntry> mEffectLibMap;
+ std::map<aidl::android::media::audio::common::AudioUuid /* implUUID */, DlEntry> mEffectLibMap
+ GUARDED_BY(mMutex);
typedef std::pair<aidl::android::media::audio::common::AudioUuid, ndk::SpAIBinder> EffectEntry;
- std::map<std::weak_ptr<IEffect>, EffectEntry, std::owner_less<>> mEffectMap;
+ std::map<std::weak_ptr<IEffect>, EffectEntry, std::owner_less<>> mEffectMap GUARDED_BY(mMutex);
- ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
- void cleanupEffectMap();
+ ndk::ScopedAStatus destroyEffectImpl_l(const std::shared_ptr<IEffect>& in_handle)
+ REQUIRES(mMutex);
+ void cleanupEffectMap_l() REQUIRES(mMutex);
bool openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
const std::string& path);
void createIdentityWithConfig(
- const EffectConfig::LibraryUuid& configLib,
+ const EffectConfig::Library& configLib,
const ::aidl::android::media::audio::common::AudioUuid& typeUuidStr,
const std::optional<::aidl::android::media::audio::common::AudioUuid> proxyUuid);
- ndk::ScopedAStatus getDescriptorWithUuid(
- const aidl::android::media::audio::common::AudioUuid& uuid, Descriptor* desc);
+ ndk::ScopedAStatus getDescriptorWithUuid_l(
+ const aidl::android::media::audio::common::AudioUuid& uuid, Descriptor* desc)
+ REQUIRES(mMutex);
void loadEffectLibs();
/* Get effect_dl_interface_s from library handle */
- void getDlSyms(DlEntry& entry);
+ void getDlSyms_l(DlEntry& entry) REQUIRES(mMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index 12c0c4b..93fb366 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -16,20 +16,21 @@
#include <cstdlib>
#include <ctime>
+#include <sstream>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "core-impl/Config.h"
#include "core-impl/Module.h"
-#include "core-impl/ModuleUsb.h"
using aidl::android::hardware::audio::core::Config;
using aidl::android::hardware::audio::core::Module;
-using aidl::android::hardware::audio::core::ModuleUsb;
int main() {
// Random values are used in the implementation.
@@ -52,18 +53,19 @@
CHECK_EQ(STATUS_OK, status);
// Make modules
- auto createModule = [](Module::Type type, const std::string& instance) {
+ auto createModule = [](Module::Type type) {
auto module = Module::createInstance(type);
ndk::SpAIBinder moduleBinder = module->asBinder();
- const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
+ std::stringstream moduleName;
+ moduleName << Module::descriptor << "/" << type;
AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
- binder_status_t status = AServiceManager_addService(moduleBinder.get(), moduleName.c_str());
+ binder_status_t status =
+ AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str());
CHECK_EQ(STATUS_OK, status);
return std::make_pair(module, moduleBinder);
};
- auto modules = {createModule(Module::Type::DEFAULT, "default"),
- createModule(Module::Type::R_SUBMIX, "r_submix"),
- createModule(Module::Type::USB, "usb")};
+ auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
+ createModule(Module::Type::USB), createModule(Module::Type::STUB)};
(void)modules;
ABinderProcess_joinThreadPool();
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 5af0d91..6d5185b 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -179,6 +179,27 @@
: outWrite(buffer, frameCount, actualFrameCount));
}
+::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
+ sp<MonoPipeReader> source = mCurrentRoute->getSource();
+ if (source == nullptr) {
+ return ::android::NO_INIT;
+ }
+ const ssize_t framesInPipe = source->availableToRead();
+ if (framesInPipe < 0) {
+ return ::android::INVALID_OPERATION;
+ }
+ if (mIsInput) {
+ position->frames += framesInPipe;
+ } else {
+ if (position->frames > framesInPipe) {
+ position->frames -= framesInPipe;
+ } else {
+ position->frames = 0;
+ }
+ }
+ return ::android::OK;
+}
+
// Calculate the maximum size of the pipe buffer in frames for the specified stream.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
auto pipeConfig = mCurrentRoute->mPipeConfig;
diff --git a/audio/aidl/default/stub/ModuleStub.cpp b/audio/aidl/default/stub/ModuleStub.cpp
new file mode 100644
index 0000000..a600752
--- /dev/null
+++ b/audio/aidl/default/stub/ModuleStub.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#define LOG_TAG "AHAL_ModuleStub"
+#include <Utils.h>
+#include <android-base/logging.h>
+
+#include "core-impl/Bluetooth.h"
+#include "core-impl/ModuleStub.h"
+#include "core-impl/StreamStub.h"
+
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModuleStub::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
+ if (!mBluetooth) {
+ mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
+ }
+ *_aidl_return = mBluetooth.getPtr();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
+ if (!mBluetoothA2dp) {
+ mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
+ }
+ *_aidl_return = mBluetoothA2dp.getPtr();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
+ if (!mBluetoothLe) {
+ mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
+ }
+ *_aidl_return = mBluetoothLe.getPtr();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata,
+ StreamContext&& context,
+ const std::vector<MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) {
+ return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+ microphones);
+}
+
+ndk::ScopedAStatus ModuleStub::createOutputStream(
+ const SourceMetadata& sourceMetadata, StreamContext&& context,
+ const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+ return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+ offloadInfo);
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
similarity index 100%
rename from audio/aidl/default/StreamStub.cpp
rename to audio/aidl/default/stub/StreamStub.cpp
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 5c9d477..a812e4d 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -14,68 +14,34 @@
* limitations under the License.
*/
-#define LOG_TAG "AHAL_ModuleUsb"
-
#include <vector>
+#define LOG_TAG "AHAL_ModuleUsb"
#include <Utils.h>
#include <android-base/logging.h>
-#include <tinyalsa/asoundlib.h>
#include "UsbAlsaMixerControl.h"
-#include "UsbAlsaUtils.h"
+#include "alsa/Utils.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/StreamUsb.h"
-extern "C" {
-#include "alsa_device_profile.h"
-}
-
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
-using aidl::android::media::audio::common::AudioChannelLayout;
-using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
-using aidl::android::media::audio::common::AudioDeviceType;
-using aidl::android::media::audio::common::AudioFormatDescription;
-using aidl::android::media::audio::common::AudioFormatType;
-using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::AudioPortExt;
-using aidl::android::media::audio::common::AudioProfile;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
namespace {
-std::vector<AudioChannelLayout> populateChannelMasksFromProfile(const alsa_device_profile* profile,
- bool isInput) {
- std::vector<AudioChannelLayout> channels;
- for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
- auto layoutMask =
- usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
- if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
- channels.push_back(layoutMask);
- }
- auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
- if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
- channels.push_back(indexMask);
- }
- }
- return channels;
-}
-
-std::vector<int> populateSampleRatesFromProfile(const alsa_device_profile* profile) {
- std::vector<int> sampleRates;
- for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
- profile->sample_rates[i] != 0;
- i++) {
- sampleRates.push_back(profile->sample_rates[i]);
- }
- return sampleRates;
+bool isUsbDevicePort(const AudioPort& audioPort) {
+ return audioPort.ext.getTag() == AudioPortExt::Tag::device &&
+ audioPort.ext.get<AudioPortExt::Tag::device>().device.type.connection ==
+ AudioDeviceDescription::CONNECTION_USB;
}
} // namespace
@@ -122,55 +88,11 @@
}
ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) {
- if (audioPort->ext.getTag() != AudioPortExt::Tag::device) {
- LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
- auto& devicePort = audioPort->ext.get<AudioPortExt::Tag::device>();
- if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) {
+ if (!isUsbDevicePort(*audioPort)) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) {
- LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
- auto& alsaAddress = devicePort.device.address.get<AudioDeviceAddress::Tag::alsa>();
- if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
- LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
-
- const bool isInput = audioPort->flags.getTag() == AudioIoFlags::input;
- alsa_device_profile profile;
- profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
- profile.card = alsaAddress[0];
- profile.device = alsaAddress[1];
- if (!profile_read_device_info(&profile)) {
- LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
- << ", device=" << profile.device;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
-
- std::vector<AudioChannelLayout> channels = populateChannelMasksFromProfile(&profile, isInput);
- std::vector<int> sampleRates = populateSampleRatesFromProfile(&profile);
-
- for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
- profile.formats[i] != PCM_FORMAT_INVALID;
- ++i) {
- auto audioFormatDescription =
- usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]);
- if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
- LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i];
- continue;
- }
- AudioProfile audioProfile = {.format = audioFormatDescription,
- .channelMasks = channels,
- .sampleRates = sampleRates};
- audioPort->profiles.push_back(std::move(audioProfile));
- }
-
- return ndk::ScopedAStatus::ok();
+ return ModuleAlsa::populateConnectedDevicePort(audioPort);
}
ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
@@ -191,15 +113,14 @@
void ModuleUsb::onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) {
- if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
+ if (!isUsbDevicePort(audioPort)) {
return;
}
- const auto& address = audioPort.ext.get<AudioPortExt::Tag::device>().device.address;
- if (address.getTag() != AudioDeviceAddress::alsa) {
+ auto profile = alsa::getDeviceProfile(audioPort);
+ if (!profile.has_value()) {
return;
}
- const int card = address.get<AudioDeviceAddress::alsa>()[0];
- usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(),
+ usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(profile->card, getMasterMute(),
getMasterVolume(), connected);
}
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index 17e1ab4..da0ad11 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -23,64 +23,20 @@
#include <error/expected_utils.h>
#include "UsbAlsaMixerControl.h"
-#include "UsbAlsaUtils.h"
-#include "core-impl/Module.h"
#include "core-impl/StreamUsb.h"
-extern "C" {
-#include "alsa_device_profile.h"
-}
-
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDevice;
-using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioOffloadInfo;
-using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
-using android::OK;
-using android::status_t;
namespace aidl::android::hardware::audio::core {
StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
- mFrameSizeBytes(getContext().getFrameSize()),
- mIsInput(isInput(metadata)),
- mConfig(maybePopulateConfig(getContext(), mIsInput)) {}
-
-// static
-std::optional<struct pcm_config> StreamUsb::maybePopulateConfig(const StreamContext& context,
- bool isInput) {
- struct pcm_config config;
- config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
- if (config.channels == 0) {
- LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
- return std::nullopt;
- }
- config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
- if (config.format == PCM_FORMAT_INVALID) {
- LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
- return std::nullopt;
- }
- config.rate = context.getSampleRate();
- if (config.rate == 0) {
- LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
- return std::nullopt;
- }
- return config;
-}
-
-::android::status_t StreamUsb::init() {
- return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
-}
-
-const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const {
- std::lock_guard guard(mLock);
- return mConnectedDevices;
-}
+ : StreamAlsa(metadata, std::move(context)) {}
ndk::ScopedAStatus StreamUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
@@ -89,14 +45,19 @@
<< ") for input stream";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
+ std::vector<alsa::DeviceProfile> connectedDeviceProfiles;
for (const auto& connectedDevice : connectedDevices) {
- if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
- LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
+ auto profile = alsa::getDeviceProfile(connectedDevice, mIsInput);
+ if (!profile.has_value()) {
+ LOG(ERROR) << __func__
+ << ": unsupported device address=" << connectedDevice.address.toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ connectedDeviceProfiles.push_back(*profile);
}
- std::lock_guard guard(mLock);
RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+ std::lock_guard guard(mLock);
+ mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
mConnectedDevicesUpdated.store(true, std::memory_order_release);
return ndk::ScopedAStatus::ok();
}
@@ -119,87 +80,22 @@
::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
- // 'setConnectedDevices' has been called. I/O will be restarted.
+ // 'setConnectedDevices' was called. I/O will be restarted.
*actualFrameCount = 0;
*latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
return ::android::OK;
}
- const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
- unsigned maxLatency = 0;
- if (mIsInput) {
- if (mAlsaDeviceProxies.empty()) {
- LOG(FATAL) << __func__ << ": no input devices";
- return ::android::NO_INIT;
- }
- // For input case, only support single device.
- proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
- maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
- } else {
- for (auto& proxy : mAlsaDeviceProxies) {
- proxy_write(proxy.get(), buffer, bytesToTransfer);
- maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
- }
- }
- *actualFrameCount = frameCount;
- maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
- *latencyMs = maxLatency;
- return ::android::OK;
+ return StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
}
-::android::status_t StreamUsb::standby() {
- mAlsaDeviceProxies.clear();
- return ::android::OK;
-}
-
-void StreamUsb::shutdown() {
- mAlsaDeviceProxies.clear();
-}
-
-::android::status_t StreamUsb::start() {
- std::vector<AudioDeviceAddress> connectedDevices;
+std::vector<alsa::DeviceProfile> StreamUsb::getDeviceProfiles() {
+ std::vector<alsa::DeviceProfile> connectedDevices;
{
std::lock_guard guard(mLock);
- std::transform(mConnectedDevices.begin(), mConnectedDevices.end(),
- std::back_inserter(connectedDevices),
- [](const auto& device) { return device.address; });
+ connectedDevices = mConnectedDeviceProfiles;
mConnectedDevicesUpdated.store(false, std::memory_order_release);
}
- decltype(mAlsaDeviceProxies) alsaDeviceProxies;
- for (const auto& device : connectedDevices) {
- alsa_device_profile profile;
- profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
- profile.card = device.get<AudioDeviceAddress::alsa>()[0];
- profile.device = device.get<AudioDeviceAddress::alsa>()[1];
- if (!profile_read_device_info(&profile)) {
- LOG(ERROR) << __func__
- << ": unable to read device info, device address=" << device.toString();
- return ::android::UNKNOWN_ERROR;
- }
-
- AlsaDeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
- proxy_close(proxy);
- free(proxy);
- });
- // Always ask for alsa configure as required since the configuration should be supported
- // by the connected device. That is guaranteed by `setAudioPortConfig` and
- // `setAudioPatch`.
- if (int err = proxy_prepare(proxy.get(), &profile,
- const_cast<struct pcm_config*>(&mConfig.value()),
- true /*is_bit_perfect*/);
- err != 0) {
- LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString()
- << " error=" << err;
- return ::android::UNKNOWN_ERROR;
- }
- if (int err = proxy_open(proxy.get()); err != 0) {
- LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
- << " error=" << err;
- return ::android::UNKNOWN_ERROR;
- }
- alsaDeviceProxies.push_back(std::move(proxy));
- }
- mAlsaDeviceProxies = std::move(alsaDeviceProxies);
- return ::android::OK;
+ return connectedDevices;
}
StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
@@ -214,9 +110,9 @@
StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
- mChannelCount = getChannelCount(getContext().getChannelLayout());
-}
+ : StreamUsb(sourceMetadata, std::move(context)),
+ StreamOut(offloadInfo),
+ mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
@@ -224,17 +120,17 @@
}
ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
+ // Avoid using mConnectedDeviceProfiles because it requires a lock.
for (const auto& device : getConnectedDevices()) {
- if (device.address.getTag() != AudioDeviceAddress::alsa) {
- LOG(DEBUG) << __func__ << ": skip as the device address is not alsa";
- continue;
- }
- const int card = device.address.get<AudioDeviceAddress::alsa>()[0];
- if (auto result =
- usb::UsbAlsaMixerControl::getInstance().setVolumes(card, in_channelVolumes);
- !result.isOk()) {
- LOG(ERROR) << __func__ << ": failed to set volume for device, card=" << card;
- return result;
+ if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput);
+ deviceProfile.has_value()) {
+ if (auto result = usb::UsbAlsaMixerControl::getInstance().setVolumes(
+ deviceProfile->card, in_channelVolumes);
+ !result.isOk()) {
+ LOG(ERROR) << __func__
+ << ": failed to set volume for device address=" << *deviceProfile;
+ return result;
+ }
}
}
mHwVolumes = in_channelVolumes;
diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
index 6c0c24b..769d739 100644
--- a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
+++ b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
@@ -17,144 +17,12 @@
#define LOG_TAG "AHAL_UsbAlsaMixerControl"
#include <android-base/logging.h>
-#include <cmath>
-#include <string>
-#include <vector>
-
#include <android/binder_status.h>
#include "UsbAlsaMixerControl.h"
namespace aidl::android::hardware::audio::core::usb {
-//-----------------------------------------------------------------------------
-
-MixerControl::MixerControl(struct mixer_ctl* ctl)
- : mCtl(ctl),
- mNumValues(mixer_ctl_get_num_values(ctl)),
- mMinValue(mixer_ctl_get_range_min(ctl)),
- mMaxValue(mixer_ctl_get_range_max(ctl)) {}
-
-unsigned int MixerControl::getNumValues() const {
- return mNumValues;
-}
-
-int MixerControl::getMaxValue() const {
- return mMaxValue;
-}
-
-int MixerControl::getMinValue() const {
- return mMinValue;
-}
-
-int MixerControl::setArray(const void* array, size_t count) {
- const std::lock_guard guard(mLock);
- return mixer_ctl_set_array(mCtl, array, count);
-}
-
-//-----------------------------------------------------------------------------
-
-// static
-const std::map<AlsaMixer::Control, std::vector<AlsaMixer::ControlNamesAndExpectedCtlType>>
- AlsaMixer::kPossibleControls = {
- {AlsaMixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
- {AlsaMixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
- {AlsaMixer::HW_VOLUME,
- {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
- {"Headset Playback Volume", MIXER_CTL_TYPE_INT},
- {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
-
-// static
-std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> AlsaMixer::initializeMixerControls(
- struct mixer* mixer) {
- std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> mixerControls;
- std::string mixerCtlNames;
- for (const auto& [control, possibleCtls] : kPossibleControls) {
- for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
- struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
- if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
- mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
- if (!mixerCtlNames.empty()) {
- mixerCtlNames += ",";
- }
- mixerCtlNames += ctlName;
- break;
- }
- }
- }
- LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
- return mixerControls;
-}
-
-AlsaMixer::AlsaMixer(struct mixer* mixer)
- : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
-
-AlsaMixer::~AlsaMixer() {
- mixer_close(mMixer);
-}
-
-namespace {
-
-int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
- return minValue + std::ceil((maxValue - minValue) * fValue);
-}
-
-} // namespace
-
-ndk::ScopedAStatus AlsaMixer::setMasterMute(bool muted) {
- auto it = mMixerControls.find(AlsaMixer::MASTER_SWITCH);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
- const int numValues = it->second->getNumValues();
- std::vector<int> values(numValues, muted ? 0 : 1);
- if (int err = it->second->setArray(values.data(), numValues); err != 0) {
- LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus AlsaMixer::setMasterVolume(float volume) {
- auto it = mMixerControls.find(AlsaMixer::MASTER_VOLUME);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
- const int numValues = it->second->getNumValues();
- std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
- it->second->getMinValue()));
- if (int err = it->second->setArray(values.data(), numValues); err != 0) {
- LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus AlsaMixer::setVolumes(std::vector<float> volumes) {
- auto it = mMixerControls.find(AlsaMixer::HW_VOLUME);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
- const int numValues = it->second->getNumValues();
- if (numValues < 0) {
- LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
- }
- const int maxValue = it->second->getMaxValue();
- const int minValue = it->second->getMinValue();
- std::vector<int> values;
- size_t i = 0;
- for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
- values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
- }
- if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
- LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- return ndk::ScopedAStatus::ok();
-}
-
-//-----------------------------------------------------------------------------
-
// static
UsbAlsaMixerControl& UsbAlsaMixerControl::getInstance() {
static UsbAlsaMixerControl gInstance;
@@ -170,7 +38,7 @@
PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
return;
}
- auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
+ auto alsaMixer = std::make_shared<alsa::Mixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
@@ -209,7 +77,7 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float> volumes) {
+ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, const std::vector<float>& volumes) {
auto alsaMixer = getAlsaMixer(card);
if (alsaMixer == nullptr) {
LOG(ERROR) << __func__ << ": no mixer control found for card=" << card;
@@ -218,13 +86,13 @@
return alsaMixer->setVolumes(volumes);
}
-std::shared_ptr<AlsaMixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
+std::shared_ptr<alsa::Mixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
const std::lock_guard guard(mLock);
const auto it = mMixerControls.find(card);
return it == mMixerControls.end() ? nullptr : it->second;
}
-std::map<int, std::shared_ptr<AlsaMixer>> UsbAlsaMixerControl::getAlsaMixers() {
+std::map<int, std::shared_ptr<alsa::Mixer>> UsbAlsaMixerControl::getAlsaMixers() {
const std::lock_guard guard(mLock);
return mMixerControls;
}
diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.h b/audio/aidl/default/usb/UsbAlsaMixerControl.h
index cbcddd8..c3265f8 100644
--- a/audio/aidl/default/usb/UsbAlsaMixerControl.h
+++ b/audio/aidl/default/usb/UsbAlsaMixerControl.h
@@ -19,67 +19,15 @@
#include <map>
#include <memory>
#include <mutex>
-#include <optional>
-#include <string>
#include <vector>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
-extern "C" {
-#include <tinyalsa/mixer.h>
-}
+#include "alsa/Mixer.h"
namespace aidl::android::hardware::audio::core::usb {
-class MixerControl {
- public:
- explicit MixerControl(struct mixer_ctl* ctl);
-
- unsigned int getNumValues() const;
- int getMaxValue() const;
- int getMinValue() const;
- int setArray(const void* array, size_t count);
-
- private:
- std::mutex mLock;
- // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
- struct mixer_ctl* mCtl GUARDED_BY(mLock);
- const unsigned int mNumValues;
- const int mMinValue;
- const int mMaxValue;
-};
-
-class AlsaMixer {
- public:
- explicit AlsaMixer(struct mixer* mixer);
-
- ~AlsaMixer();
-
- bool isValid() const { return mMixer != nullptr; }
-
- ndk::ScopedAStatus setMasterMute(bool muted);
- ndk::ScopedAStatus setMasterVolume(float volume);
- ndk::ScopedAStatus setVolumes(std::vector<float> volumes);
-
- private:
- enum Control {
- MASTER_SWITCH,
- MASTER_VOLUME,
- HW_VOLUME,
- };
- using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
- static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
- static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
- struct mixer* mixer);
-
- // The mixer object is owned by ALSA and will be released when the mixer is closed.
- struct mixer* mMixer;
- // `mMixerControls` will only be initialized in constructor. After that, it wil only be
- // read but not be modified.
- const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
-};
-
class UsbAlsaMixerControl {
public:
static UsbAlsaMixerControl& getInstance();
@@ -91,16 +39,16 @@
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
// The volume settings can be different on sound cards. It is controlled by streams.
- ndk::ScopedAStatus setVolumes(int card, std::vector<float> volumes);
+ ndk::ScopedAStatus setVolumes(int card, const std::vector<float>& volumes);
private:
- std::shared_ptr<AlsaMixer> getAlsaMixer(int card);
- std::map<int, std::shared_ptr<AlsaMixer>> getAlsaMixers();
+ std::shared_ptr<alsa::Mixer> getAlsaMixer(int card);
+ std::map<int, std::shared_ptr<alsa::Mixer>> getAlsaMixers();
std::mutex mLock;
// A map whose key is the card number and value is a shared pointer to corresponding
// AlsaMixer object.
- std::map<int, std::shared_ptr<AlsaMixer>> mMixerControls GUARDED_BY(mLock);
+ std::map<int, std::shared_ptr<alsa::Mixer>> mMixerControls GUARDED_BY(mLock);
};
} // namespace aidl::android::hardware::audio::core::usb
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.cpp b/audio/aidl/default/usb/UsbAlsaUtils.cpp
deleted file mode 100644
index 74d9c28..0000000
--- a/audio/aidl/default/usb/UsbAlsaUtils.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <map>
-#include <set>
-
-#include <Utils.h>
-#include <aidl/android/media/audio/common/AudioFormatType.h>
-#include <aidl/android/media/audio/common/PcmType.h>
-
-#include "UsbAlsaUtils.h"
-#include "core-impl/utils.h"
-
-using aidl::android::hardware::audio::common::getChannelCount;
-using aidl::android::media::audio::common::AudioChannelLayout;
-using aidl::android::media::audio::common::AudioFormatDescription;
-using aidl::android::media::audio::common::AudioFormatType;
-using aidl::android::media::audio::common::PcmType;
-
-namespace aidl::android::hardware::audio::core::usb {
-
-namespace {
-
-using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
-using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
-using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
-
-static const AudioChannelLayout INVALID_CHANNEL_LAYOUT =
- AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
-
-#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
- AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
-
-static const std::set<AudioChannelLayout> SUPPORTED_OUT_CHANNEL_LAYOUTS = {
- DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
- DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
- DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
- DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
- DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
-};
-
-static const std::set<AudioChannelLayout> SUPPORTED_IN_CHANNEL_LAYOUTS = {
- DEFINE_CHANNEL_LAYOUT_MASK(MONO),
- DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
-};
-
-#define DEFINE_CHANNEL_INDEX_MASK(n) \
- AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
-
-static const std::set<AudioChannelLayout> SUPPORTED_INDEX_CHANNEL_LAYOUTS = {
- DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), DEFINE_CHANNEL_INDEX_MASK(3),
- DEFINE_CHANNEL_INDEX_MASK(4), DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
- DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), DEFINE_CHANNEL_INDEX_MASK(9),
- DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
- DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15),
- DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
- DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21),
- DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
-};
-
-static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
- const std::set<AudioChannelLayout>& channelMasks) {
- AudioChannelCountToMaskMap channelMaskToCountMap;
- for (const auto& channelMask : channelMasks) {
- channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
- }
- return channelMaskToCountMap;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
- static const AudioChannelCountToMaskMap outLayouts =
- make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS);
- return outLayouts;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
- static const AudioChannelCountToMaskMap inLayouts =
- make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS);
- return inLayouts;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
- static const AudioChannelCountToMaskMap indexLayouts =
- make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS);
- return indexLayouts;
-}
-
-AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
- AudioFormatDescription result;
- result.type = type;
- return result;
-}
-
-AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
- auto result = make_AudioFormatDescription(AudioFormatType::PCM);
- result.pcm = pcm;
- return result;
-}
-
-const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
- static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
- {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
- {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
- {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
- {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
- {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
- {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
- };
- return formatDescToPcmFormatMap;
-}
-
-static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
- const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
- PcmFormatToAudioFormatDescMap result;
- for (const auto& formatPair : formatDescToPcmFormatMap) {
- result.emplace(formatPair.second, formatPair.first);
- }
- return result;
-}
-
-const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
- static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
- make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
- return pcmFormatToFormatDescMap;
-}
-
-} // namespace
-
-AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
- return findValueOrDefault(
- isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
- channelCount, INVALID_CHANNEL_LAYOUT);
-}
-
-AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
- return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
- INVALID_CHANNEL_LAYOUT);
-}
-
-unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
- switch (channelMask.getTag()) {
- case AudioChannelLayout::Tag::layoutMask: {
- return findKeyOrDefault(
- isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
- (unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/);
- }
- case AudioChannelLayout::Tag::indexMask: {
- return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
- (unsigned int)getChannelCount(channelMask),
- 0u /*defaultValue*/);
- }
- case AudioChannelLayout::Tag::none:
- case AudioChannelLayout::Tag::invalid:
- case AudioChannelLayout::Tag::voiceMask:
- default:
- return 0;
- }
-}
-
-AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
- return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
-}
-
-pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
- return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
-}
-
-} // namespace aidl::android::hardware::audio::core::usb
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.h b/audio/aidl/default/usb/UsbAlsaUtils.h
deleted file mode 100644
index 2d2f0f4..0000000
--- a/audio/aidl/default/usb/UsbAlsaUtils.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/media/audio/common/AudioChannelLayout.h>
-#include <aidl/android/media/audio/common/AudioFormatDescription.h>
-
-extern "C" {
-#include <tinyalsa/pcm.h>
-}
-
-namespace aidl::android::hardware::audio::core::usb {
-
-::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
- unsigned int channelCount, int isInput);
-::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
- unsigned int channelCount);
-unsigned int getChannelCountFromChannelMask(
- const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
-::aidl::android::media::audio::common::AudioFormatDescription
-legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
-pcm_format aidl2legacy_AudioFormatDescription_pcm_format(
- const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
-
-} // namespace aidl::android::hardware::audio::core::usb
\ No newline at end of file
diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp
index 1a7c3d4..0354e3c 100644
--- a/audio/aidl/vts/VtsHalAECTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp
@@ -51,7 +51,7 @@
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
- Parameter::Specific specific = getDefaultParamSpecific();
+ auto specific = getDefaultParamSpecific();
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
@@ -65,8 +65,13 @@
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
- Parameter::Specific getDefaultParamSpecific() {
- AcousticEchoCanceler aec = AcousticEchoCanceler::make<AcousticEchoCanceler::echoDelayUs>(0);
+ std::optional<Parameter::Specific> getDefaultParamSpecific() {
+ auto aec = AcousticEchoCanceler::make<AcousticEchoCanceler::echoDelayUs>(0);
+ if (!isParameterValid<AcousticEchoCanceler, Range::acousticEchoCanceler>(aec,
+ mDescriptor)) {
+ return std::nullopt;
+ }
+
Parameter::Specific specific =
Parameter::Specific::make<Parameter::Specific::acousticEchoCanceler>(aec);
return specific;
diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp
index bbb11fc..624d5d2 100644
--- a/audio/aidl/vts/VtsHalNSTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp
@@ -48,7 +48,7 @@
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
- Parameter::Specific specific = getDefaultParamSpecific();
+ std::optional<Parameter::Specific> specific = getDefaultParamSpecific();
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
@@ -62,9 +62,13 @@
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
- Parameter::Specific getDefaultParamSpecific() {
+ std::optional<Parameter::Specific> getDefaultParamSpecific() {
NoiseSuppression ns =
NoiseSuppression::make<NoiseSuppression::level>(NoiseSuppression::Level::MEDIUM);
+ if (!isParameterValid<NoiseSuppression, Range::noiseSuppression>(ns, mDescriptor)) {
+ return std::nullopt;
+ }
+
Parameter::Specific specific =
Parameter::Specific::make<Parameter::Specific::noiseSuppression>(ns);
return specific;
@@ -85,7 +89,9 @@
// validate parameter
Descriptor desc;
ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
- const binder_exception_t expected = EX_NONE;
+ const bool valid =
+ isParameterValid<NoiseSuppression, Range::noiseSuppression>(ns, desc);
+ const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
// set parameter
Parameter expectParam;
diff --git a/bluetooth/audio/aidl/vts/Android.bp b/bluetooth/audio/aidl/vts/Android.bp
index 0a2a89a..3e6953f 100644
--- a/bluetooth/audio/aidl/vts/Android.bp
+++ b/bluetooth/audio/aidl/vts/Android.bp
@@ -15,11 +15,14 @@
],
tidy_timeout_srcs: ["VtsHalBluetoothAudioTargetTest.cpp"],
srcs: ["VtsHalBluetoothAudioTargetTest.cpp"],
- shared_libs: [
+ static_libs: [
"android.hardware.audio.common-V1-ndk",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
+ "android.media.audio.common.types-V2-ndk",
+ ],
+ shared_libs: [
"libbase",
"libbinder_ndk",
"libcutils",
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index b3ca293..712f28a 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -31,6 +31,7 @@
"kernel_config_q_4.14",
"kernel_config_q_4.19",
],
+ core_hals: "only",
}
vintf_compatibility_matrix {
@@ -44,6 +45,7 @@
"kernel_config_r_4.19",
"kernel_config_r_5.4",
],
+ core_hals: "only",
}
vintf_compatibility_matrix {
@@ -57,6 +59,7 @@
"kernel_config_s_5.4",
"kernel_config_s_5.10",
],
+ core_hals: "only",
}
vintf_compatibility_matrix {
@@ -69,6 +72,7 @@
"kernel_config_t_5.10",
"kernel_config_t_5.15",
],
+ core_hals: "only",
}
vintf_compatibility_matrix {
@@ -81,6 +85,7 @@
"kernel_config_u_5.15",
"kernel_config_u_6.1",
],
+ core_hals: "only",
}
vintf_compatibility_matrix {
@@ -93,4 +98,5 @@
"kernel_config_v_5.15",
"kernel_config_v_6.1",
],
+ core_hals: "only",
}
diff --git a/compatibility_matrices/build/vintf_compatibility_matrix.go b/compatibility_matrices/build/vintf_compatibility_matrix.go
index c72cbde..4f342b2 100644
--- a/compatibility_matrices/build/vintf_compatibility_matrix.go
+++ b/compatibility_matrices/build/vintf_compatibility_matrix.go
@@ -35,10 +35,10 @@
pctx = android.NewPackageContext("android/vintf")
assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{
- Command: `${assembleVintfCmd} -i ${inputs} -o ${out}`,
+ Command: `${assembleVintfCmd} -i ${inputs} -o ${out} ${extraParams}`,
CommandDeps: []string{"${assembleVintfCmd}"},
Description: "assemble_vintf -i ${inputs}",
- }, "inputs")
+ }, "inputs", "extraParams")
xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{
Command: `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`,
@@ -64,6 +64,13 @@
// list of kernel_config modules to be combined to final output
Kernel_configs []string
+
+ // Default is "default" for compatibility matrices on /vendor
+ // and /odm, and "disallow" for compatibility matrices on /system,
+ // /product, and /system_ext.
+ // If value is "only", only android.* HALs are allowed. If value
+ // is "disallow", none of android.* HALs are allowed.
+ Core_hals *string
}
type vintfCompatibilityMatrixRule struct {
@@ -166,7 +173,8 @@
Implicits: inputPaths,
Output: g.genFile,
Args: map[string]string{
- "inputs": strings.Join(inputPaths.Strings(), ":"),
+ "inputs": strings.Join(inputPaths.Strings(), ":"),
+ "extraParams": strings.Join(g.getExtraParams(), " "),
},
})
g.generateValidateBuildAction(ctx, g.genFile, schema.Path())
@@ -191,3 +199,23 @@
},
}
}
+
+// Return extra parameters to assemble_vintf.
+func (g *vintfCompatibilityMatrixRule) getExtraParams() []string {
+ var extraParams []string
+
+ coreHalsStrategy := proptools.StringDefault(
+ g.properties.Core_hals,
+ g.defaultCoreHalsStrategy(),
+ )
+ extraParams = append(extraParams, "--core-hals", proptools.ShellEscape(coreHalsStrategy))
+ return extraParams
+}
+
+func (g *vintfCompatibilityMatrixRule) defaultCoreHalsStrategy() string {
+ // TODO(b/290408770): default to "disallow" for FCMs
+
+ // For Device (vendor, odm) compatibility matrix, default is
+ // to not check anything.
+ return "default"
+}
diff --git a/compatibility_matrices/bump.py b/compatibility_matrices/bump.py
new file mode 100755
index 0000000..88b7a42
--- /dev/null
+++ b/compatibility_matrices/bump.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+Creates the next compatibility matrix.
+
+Requires libvintf Level.h to be updated before executing this script.
+"""
+
+import argparse
+import os
+import pathlib
+import shutil
+import subprocess
+import textwrap
+
+
+def check_call(*args, **kwargs):
+ print(args)
+ subprocess.check_call(*args, **kwargs)
+
+
+def check_output(*args, **kwargs):
+ print(args)
+ return subprocess.check_output(*args, **kwargs)
+
+
+class Bump(object):
+
+ def __init__(self, cmdline_args):
+ self.top = pathlib.Path(os.environ["ANDROID_BUILD_TOP"])
+ self.interfaces_dir = self.top / "hardware/interfaces"
+
+ self.current_level = cmdline_args.current
+ self.current_module_name = f"framework_compatibility_matrix.{self.current_level}.xml"
+ self.current_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.current_level}.xml"
+
+ self.next_level = cmdline_args.next
+ self.next_module_name = f"framework_compatibility_matrix.{self.next_level}.xml"
+ self.next_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.next_level}.xml"
+
+ self.level_to_letter = self.get_level_to_letter_mapping()
+ print("Found level mapping in libvintf Level.h:", self.level_to_letter)
+
+ def run(self):
+ self.bump_kernel_configs()
+ self.copy_matrix()
+ self.edit_android_bp()
+ self.edit_android_mk()
+
+ def get_level_to_letter_mapping(self):
+ levels_file = self.top / "system/libvintf/include/vintf/Level.h"
+ with open(levels_file) as f:
+ lines = f.readlines()
+ pairs = [
+ line.split("=", maxsplit=2) for line in lines if "=" in line
+ ]
+ return {
+ level.strip().removesuffix(","): letter.strip()
+ for letter, level in pairs
+ }
+
+ def bump_kernel_configs(self):
+ check_call([
+ self.top / "kernel/configs/tools/bump.py",
+ self.level_to_letter[self.current_level].lower(),
+ self.level_to_letter[self.next_level].lower(),
+ ])
+
+ def copy_matrix(self):
+ shutil.copyfile(self.current_xml, self.next_xml)
+
+ def edit_android_bp(self):
+ android_bp = self.interfaces_dir / "compatibility_matrices/Android.bp"
+
+ with open(android_bp, "r+") as f:
+ if self.next_module_name not in f.read():
+ f.seek(0, 2) # end of file
+ f.write("\n")
+ f.write(
+ textwrap.dedent(f"""\
+ vintf_compatibility_matrix {{
+ name: "{self.next_module_name}",
+ }}
+ """))
+
+ next_kernel_configs = check_output(
+ """grep -rh name: | sed -E 's/^.*"(.*)".*/\\1/g'""",
+ cwd=self.top / "kernel/configs" /
+ self.level_to_letter[self.next_level].lower(),
+ text=True,
+ shell=True,
+ ).splitlines()
+ print(next_kernel_configs)
+
+ check_call([
+ "bpmodify", "-w", "-m", self.next_module_name, "-property", "stem",
+ "-str", self.next_xml.name, android_bp
+ ])
+
+ check_call([
+ "bpmodify", "-w", "-m", self.next_module_name, "-property", "srcs",
+ "-a",
+ self.next_xml.relative_to(android_bp.parent), android_bp
+ ])
+
+ check_call([
+ "bpmodify", "-w", "-m", self.next_module_name, "-property",
+ "kernel_configs", "-a", " ".join(next_kernel_configs), android_bp
+ ])
+
+ def edit_android_mk(self):
+ android_mk = self.interfaces_dir / "compatibility_matrices/Android.mk"
+ with open(android_mk) as f:
+ if self.next_module_name in f.read():
+ return
+ f.seek(0)
+ lines = f.readlines()
+ current_module_line_number = None
+ for line_number, line in enumerate(lines):
+ if self.current_module_name in line:
+ current_module_line_number = line_number
+ break
+ assert current_module_line_number is not None
+ lines.insert(current_module_line_number + 1,
+ f" {self.next_module_name} \\\n")
+ with open(android_mk, "w") as f:
+ f.write("".join(lines))
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("current",
+ type=str,
+ help="VINTF level of the current version (e.g. 9)")
+ parser.add_argument("next",
+ type=str,
+ help="VINTF level of the next version (e.g. 10)")
+ cmdline_args = parser.parse_args()
+
+ Bump(cmdline_args).run()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl
index e4d4cbe..607ceb3 100644
--- a/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl
+++ b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl
@@ -36,10 +36,9 @@
interface IThreadChip {
void open(in android.hardware.threadnetwork.IThreadChipCallback callback);
void close();
- void reset();
+ void hardwareReset();
void sendSpinelFrame(in byte[] frame);
const int ERROR_FAILED = 1;
- const int ERROR_INVALID_ARGS = 2;
- const int ERROR_NO_BUFS = 3;
- const int ERROR_BUSY = 4;
+ const int ERROR_NO_BUFS = 2;
+ const int ERROR_BUSY = 3;
}
diff --git a/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
index eebaa46..e695623 100644
--- a/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
+++ b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
@@ -30,19 +30,14 @@
const int ERROR_FAILED = 1;
/**
- * The invalid arguments.
- */
- const int ERROR_INVALID_ARGS = 2;
-
- /**
* Insufficient buffers available to send frames.
*/
- const int ERROR_NO_BUFS = 3;
+ const int ERROR_NO_BUFS = 2;
/**
* Service is busy and could not service the operation.
*/
- const int ERROR_BUSY = 4;
+ const int ERROR_BUSY = 3;
/**
* This method initializes the Thread HAL instance. If open completes
@@ -51,9 +46,10 @@
*
* @param callback A IThreadChipCallback callback instance.
*
+ * @throws EX_ILLEGAL_ARGUMENT if the callback handle is invalid (for example, it is null).
+ *
* @throws ServiceSpecificException with one of the following values:
* - ERROR_FAILED The interface cannot be opened due to an internal error.
- * - ERROR_INVALID_ARGS The callback handle is invalid (for example, it is null).
* - ERROR_BUSY This interface is in use.
*/
void open(in IThreadChipCallback callback);
@@ -64,11 +60,14 @@
void close();
/**
- * This method resets the Thread HAL internal state. The callback registered by
- * `open()` won’t be reset and the resource allocated by `open()` won’t be free.
+ * This method hardware resets the Thread radio chip via the physical reset pin.
+ * The callback registered by `open()` won’t be reset and the resource allocated
+ * by `open()` won’t be free.
+ *
+ * @throws EX_UNSUPPORTED_OPERATION if the Thread radio chip doesn't support the hardware reset.
*
*/
- void reset();
+ void hardwareReset();
/**
* This method sends a spinel frame to the Thread HAL.
diff --git a/threadnetwork/aidl/default/Android.bp b/threadnetwork/aidl/default/Android.bp
index 8b938d2..bcd5704 100644
--- a/threadnetwork/aidl/default/Android.bp
+++ b/threadnetwork/aidl/default/Android.bp
@@ -79,7 +79,6 @@
"fuzzer.cpp",
],
- required: ["ot-rcp"],
fuzz_config: {
cc: [
"zhanglongxia@google.com",
diff --git a/threadnetwork/aidl/default/fuzzer.cpp b/threadnetwork/aidl/default/fuzzer.cpp
index 512708d..fb6e548 100644
--- a/threadnetwork/aidl/default/fuzzer.cpp
+++ b/threadnetwork/aidl/default/fuzzer.cpp
@@ -22,7 +22,7 @@
using android::fuzzService;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- char url[] = "spinel+hdlc+forkpty:///vendor/bin/ot-rcp?forkpty-arg=2";
+ char url[] = "spinel+hdlc+null:///dev/null";
auto service = ndk::SharedRefBase::make<ThreadChip>(url);
fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
diff --git a/threadnetwork/aidl/default/thread_chip.cpp b/threadnetwork/aidl/default/thread_chip.cpp
index 94d1e93..3d38cb8 100644
--- a/threadnetwork/aidl/default/thread_chip.cpp
+++ b/threadnetwork/aidl/default/thread_chip.cpp
@@ -17,6 +17,8 @@
#include "thread_chip.hpp"
#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <utils/Log.h>
@@ -46,20 +48,36 @@
mSpinelInterface = std::make_shared<ot::Posix::HdlcInterface>(handleReceivedFrameJump, this,
mRxFrameBuffer);
} else {
- ALOGE("The protocol \"%s\" is not supported!", protocol);
- exit(1);
+ ALOGE("The protocol \"%s\" is not supported", protocol);
+ exit(EXIT_FAILURE);
}
CHECK_NE(mSpinelInterface, nullptr);
+
+ mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+ AIBinder_DeathRecipient_new(ThreadChip::onBinderDiedJump));
+ AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), ThreadChip::onBinderUnlinkedJump);
}
-void ThreadChip::clientDeathCallback(void* context) {
- reinterpret_cast<ThreadChip*>(context)->clientDeathCallback();
+ThreadChip::~ThreadChip() {
+ AIBinder_DeathRecipient_delete(mDeathRecipient.get());
}
-void ThreadChip::clientDeathCallback(void) {
- ALOGW("Thread Network HAL client is dead.");
- close();
+void ThreadChip::onBinderDiedJump(void* context) {
+ reinterpret_cast<ThreadChip*>(context)->onBinderDied();
+}
+
+void ThreadChip::onBinderDied(void) {
+ ALOGW("Thread Network HAL client is dead");
+}
+
+void ThreadChip::onBinderUnlinkedJump(void* context) {
+ reinterpret_cast<ThreadChip*>(context)->onBinderUnlinked();
+}
+
+void ThreadChip::onBinderUnlinked(void) {
+ ALOGW("ThreadChip binder is unlinked");
+ deinitChip();
}
void ThreadChip::handleReceivedFrameJump(void* context) {
@@ -76,75 +94,83 @@
}
ndk::ScopedAStatus ThreadChip::open(const std::shared_ptr<IThreadChipCallback>& in_callback) {
- ndk::ScopedAStatus status;
- AIBinder* binder;
+ ndk::ScopedAStatus status = initChip(in_callback);
- VerifyOrExit(mCallback == nullptr,
- status = errorStatus(ERROR_BUSY, "Interface is already opened"));
- VerifyOrExit(in_callback != nullptr,
- status = errorStatus(ERROR_INVALID_ARGS, "The callback is NULL"));
- binder = in_callback->asBinder().get();
- VerifyOrExit(binder != nullptr,
- status = errorStatus(ERROR_FAILED, "Failed to get the callback binder"));
- mBinderDeathRecipient = AIBinder_DeathRecipient_new(clientDeathCallback);
- VerifyOrExit(AIBinder_linkToDeath(binder, mBinderDeathRecipient, this) == STATUS_OK,
- status = errorStatus(ERROR_FAILED, "Failed to link the binder to death"));
- VerifyOrExit(mSpinelInterface->Init(mUrl) == OT_ERROR_NONE,
- status = errorStatus(ERROR_FAILED, "Failed to initialize the interface"));
-
- mCallback = in_callback;
- ot::Posix::Mainloop::Manager::Get().Add(*this);
- status = ndk::ScopedAStatus::ok();
-
-exit:
- if (!status.isOk()) {
- if (mBinderDeathRecipient != nullptr) {
- AIBinder_DeathRecipient_delete(mBinderDeathRecipient);
- mBinderDeathRecipient = nullptr;
- }
- ALOGW("Open failed, error: %s", status.getDescription().c_str());
+ if (status.isOk()) {
+ AIBinder_linkToDeath(in_callback->asBinder().get(), mDeathRecipient.get(), this);
+ ALOGI("Open IThreadChip successfully");
} else {
- ALOGI("open()");
+ ALOGW("Failed to open IThreadChip: %s", status.getDescription().c_str());
}
return status;
}
+ndk::ScopedAStatus ThreadChip::initChip(const std::shared_ptr<IThreadChipCallback>& in_callback) {
+ if (in_callback == nullptr) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ } else if (mCallback == nullptr) {
+ if (mSpinelInterface->Init(mUrl) != OT_ERROR_NONE) {
+ return errorStatus(ERROR_FAILED, "Failed to initialize the interface");
+ }
+
+ mCallback = in_callback;
+ ot::Posix::Mainloop::Manager::Get().Add(*this);
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return errorStatus(ERROR_BUSY, "Interface has been opened");
+ }
+}
+
ndk::ScopedAStatus ThreadChip::close() {
- VerifyOrExit(mCallback != nullptr);
- mCallback = nullptr;
- mSpinelInterface->Deinit();
+ ndk::ScopedAStatus status;
+ std::shared_ptr<IThreadChipCallback> callback = mCallback;
- ot::Posix::Mainloop::Manager::Get().Remove(*this);
+ status = deinitChip();
+ if (status.isOk()) {
+ if (callback != nullptr) {
+ AIBinder_unlinkToDeath(callback->asBinder().get(), mDeathRecipient.get(), this);
+ }
- AIBinder_DeathRecipient_delete(mBinderDeathRecipient);
- mBinderDeathRecipient = nullptr;
+ ALOGI("Close IThreadChip successfully");
+ } else {
+ ALOGW("Failed to close IThreadChip: %s", status.getDescription().c_str());
+ }
-exit:
- ALOGI("close()");
- return ndk::ScopedAStatus::ok();
+ return status;
+}
+
+ndk::ScopedAStatus ThreadChip::deinitChip() {
+ if (mCallback != nullptr) {
+ mSpinelInterface->Deinit();
+ ot::Posix::Mainloop::Manager::Get().Remove(*this);
+ mCallback = nullptr;
+ return ndk::ScopedAStatus::ok();
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus ThreadChip::sendSpinelFrame(const std::vector<uint8_t>& in_frame) {
ndk::ScopedAStatus status;
otError error;
- VerifyOrExit(mCallback != nullptr,
- status = errorStatus(ERROR_FAILED, "The interface is not open"));
-
- error = mSpinelInterface->SendFrame(reinterpret_cast<const uint8_t*>(in_frame.data()),
- in_frame.size());
- if (error == OT_ERROR_NONE) {
- status = ndk::ScopedAStatus::ok();
- } else if (error == OT_ERROR_NO_BUFS) {
- status = errorStatus(ERROR_NO_BUFS, "Insufficient buffer space to send");
- } else if (error == OT_ERROR_BUSY) {
- status = errorStatus(ERROR_BUSY, "The interface is busy");
+ if (mCallback == nullptr) {
+ status = errorStatus(ERROR_FAILED, "The interface is not open");
} else {
- status = errorStatus(ERROR_FAILED, "Failed to send the spinel frame");
+ error = mSpinelInterface->SendFrame(reinterpret_cast<const uint8_t*>(in_frame.data()),
+ in_frame.size());
+ if (error == OT_ERROR_NONE) {
+ status = ndk::ScopedAStatus::ok();
+ } else if (error == OT_ERROR_NO_BUFS) {
+ status = errorStatus(ERROR_NO_BUFS, "Insufficient buffer space to send");
+ } else if (error == OT_ERROR_BUSY) {
+ status = errorStatus(ERROR_BUSY, "The interface is busy");
+ } else {
+ status = errorStatus(ERROR_FAILED, "Failed to send the spinel frame");
+ }
}
-exit:
if (!status.isOk()) {
ALOGW("Send spinel frame failed, error: %s", status.getDescription().c_str());
}
@@ -152,8 +178,11 @@
return status;
}
-ndk::ScopedAStatus ThreadChip::reset() {
- mSpinelInterface->HardwareReset();
+ndk::ScopedAStatus ThreadChip::hardwareReset() {
+ if (mSpinelInterface->HardwareReset() == OT_ERROR_NOT_IMPLEMENTED) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
ALOGI("reset()");
return ndk::ScopedAStatus::ok();
}
diff --git a/threadnetwork/aidl/default/thread_chip.hpp b/threadnetwork/aidl/default/thread_chip.hpp
index 294190a..1ab6d54 100644
--- a/threadnetwork/aidl/default/thread_chip.hpp
+++ b/threadnetwork/aidl/default/thread_chip.hpp
@@ -22,6 +22,7 @@
#include "lib/spinel/spinel_interface.hpp"
#include "mainloop.hpp"
+#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
#include <utils/Mutex.h>
@@ -33,26 +34,31 @@
class ThreadChip : public BnThreadChip, ot::Posix::Mainloop::Source {
public:
ThreadChip(char* url);
+ ~ThreadChip();
ndk::ScopedAStatus open(const std::shared_ptr<IThreadChipCallback>& in_callback) override;
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus sendSpinelFrame(const std::vector<uint8_t>& in_frame) override;
- ndk::ScopedAStatus reset() override;
+ ndk::ScopedAStatus hardwareReset() override;
void Update(otSysMainloopContext& context) override;
void Process(const otSysMainloopContext& context) override;
private:
- static void clientDeathCallback(void* context);
- void clientDeathCallback(void);
+ static void onBinderDiedJump(void* context);
+ void onBinderDied(void);
+ static void onBinderUnlinkedJump(void* context);
+ void onBinderUnlinked(void);
static void handleReceivedFrameJump(void* context);
void handleReceivedFrame(void);
ndk::ScopedAStatus errorStatus(int32_t error, const char* message);
+ ndk::ScopedAStatus initChip(const std::shared_ptr<IThreadChipCallback>& in_callback);
+ ndk::ScopedAStatus deinitChip();
ot::Url::Url mUrl;
std::shared_ptr<ot::Spinel::SpinelInterface> mSpinelInterface;
ot::Spinel::SpinelInterface::RxFrameBuffer mRxFrameBuffer;
std::shared_ptr<IThreadChipCallback> mCallback;
- AIBinder_DeathRecipient* mBinderDeathRecipient;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
};
} // namespace threadnetwork
diff --git a/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp b/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
index 04c6dea..5925b54 100644
--- a/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
+++ b/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
@@ -91,7 +91,7 @@
ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
EXPECT_TRUE(thread_chip->open(callback).isOk());
- EXPECT_TRUE(thread_chip->reset().isOk());
+ EXPECT_TRUE(thread_chip->hardwareReset().isOk());
}
TEST_P(ThreadNetworkAidl, SendSpinelFrame) {