Merge "Enable Magnetometer sensor injection by AIDL flag" into main
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index 3e06595..81c99f7 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -4,6 +4,9 @@
"name": "VtsHalAudioCoreTargetTest"
},
{
+ "name": "audio_policy_config_xml_converter_tests"
+ },
+ {
"name": "VtsHalAudioEffectFactoryTargetTest"
},
{
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
index 818b18e..6b69845 100644
--- a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-in-async-sm.gv -o stream-in-async-sm.png
digraph stream_in_async_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=tomato] CLOSED;
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
index 805dc32..aa7af54 100644
--- a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-in-sm.gv -o stream-in-sm.png
digraph stream_in_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=tomato] CLOSED;
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
index 501dc01..a3f0de9 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-out-async-sm.gv -o stream-out-async-sm.png
digraph stream_out_async_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=lightgreen] IDLE; // buffer is empty
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
index 47e7fda..23fb5d9 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-out-sm.gv -o stream-out-sm.png
digraph stream_out_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=lightgreen] IDLE; // buffer is empty
diff --git a/audio/aidl/android/hardware/audio/effect/state.gv b/audio/aidl/android/hardware/audio/effect/state.gv
index c88521e..ce369ba 100644
--- a/audio/aidl/android/hardware/audio/effect/state.gv
+++ b/audio/aidl/android/hardware/audio/effect/state.gv
@@ -25,12 +25,12 @@
I -> INIT[label = "IFactory.createEffect" labelfontcolor = "navy"];
INIT -> F[label = "IFactory.destroyEffect"];
- INIT -> IDLE[label = "open()" labelfontcolor = "lime"];
- IDLE -> PROCESSING[label = "command(START"];
- PROCESSING -> IDLE[label = "command(STOP)\ncommand(RESET)"];
- IDLE -> INIT[label = "close()"];
+ INIT -> IDLE[label = "IEffect.open()" labelfontcolor = "lime"];
+ IDLE -> PROCESSING[label = "IEffect.command(START"];
+ PROCESSING -> IDLE[label = "IEffect.command(STOP)\nIEffect.command(RESET)"];
+ IDLE -> INIT[label = "IEffect.close()"];
- INIT -> INIT[label = "getState\ngetDescriptor"];
- IDLE -> IDLE[label = "getXXX\nsetParameter\ncommand(RESET)"];
- PROCESSING -> PROCESSING[label = "getXXX\nsetParameter"];
+ INIT -> INIT[label = "IEffect.getState\nIEffect.getDescriptor"];
+ IDLE -> IDLE[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor\nIEffect.command(RESET)"];
+ PROCESSING -> PROCESSING[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor"];
}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index e9294cf..4e583a4 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -73,18 +73,24 @@
"Configuration.cpp",
"EngineConfigXmlConverter.cpp",
"Module.cpp",
+ "ModulePrimary.cpp",
"SoundDose.cpp",
"Stream.cpp",
- "StreamStub.cpp",
+ "StreamSwitcher.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",
@@ -144,6 +150,50 @@
],
}
+cc_test {
+ name: "audio_policy_config_xml_converter_tests",
+ vendor_available: true,
+ defaults: [
+ "latest_android_media_audio_common_types_ndk_static",
+ "latest_android_hardware_audio_core_ndk_static",
+ ],
+ shared_libs: [
+ "libaudio_aidl_conversion_common_ndk",
+ "libaudioaidlcommon",
+ "libaudioutils",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libmedia_helper",
+ "libstagefright_foundation",
+ "libutils",
+ "libxml2",
+ ],
+ header_libs: [
+ "libaudio_system_headers",
+ "libaudioaidl_headers",
+ "libxsdc-utils",
+ ],
+ generated_sources: [
+ "audio_policy_configuration_aidl_default",
+ ],
+ generated_headers: [
+ "audio_policy_configuration_aidl_default",
+ ],
+ srcs: [
+ "AudioPolicyConfigXmlConverter.cpp",
+ "tests/AudioPolicyConfigXmlConverterTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ "-DBACKEND_NDK",
+ ],
+ test_suites: ["general-tests"],
+}
+
cc_defaults {
name: "aidlaudioeffectservice_defaults",
defaults: [
diff --git a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
index 2848d71..7452c8e 100644
--- a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
+++ b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
@@ -137,7 +137,7 @@
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS),
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS_HD),
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS_HD_MA),
- SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS_UHD),
+ SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS_UHD_P1),
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DTS_UHD_P2),
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_DOLBY_TRUEHD),
SIMPLE_FORMAT(MEDIA_MIMETYPE_AUDIO_EAC3_JOC),
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..b59bd7c 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();
}
@@ -675,7 +675,7 @@
nullptr, nullptr, &context));
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamIn> stream;
- RETURN_STATUS_IF_ERROR(createInputStream(in_args.sinkMetadata, std::move(context),
+ RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
mConfig->microphones, &stream));
StreamWrapper streamWrapper(stream);
if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
@@ -721,7 +721,7 @@
in_args.eventCallback, &context));
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamOut> stream;
- RETURN_STATUS_IF_ERROR(createOutputStream(in_args.sourceMetadata, std::move(context),
+ RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
in_args.offloadInfo, &stream));
StreamWrapper streamWrapper(stream);
if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
@@ -1129,7 +1129,7 @@
if (!mSoundDose) {
mSoundDose = ndk::SharedRefBase::make<sounddose::SoundDose>();
}
- *_aidl_return = mSoundDose.getPtr();
+ *_aidl_return = mSoundDose.getInstance();
LOG(DEBUG) << __func__ << ": returning instance of ISoundDose: " << _aidl_return->get();
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..29e8126
--- /dev/null
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of ITelephony: " << _aidl_return->get()->asBinder().get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModulePrimary::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
+ const std::vector<MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) {
+ return createStreamInstance<StreamInStub>(result, std::move(context), sinkMetadata,
+ microphones);
+}
+
+ndk::ScopedAStatus ModulePrimary::createOutputStream(
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
+ const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+ return createStreamInstance<StreamOutStub>(result, std::move(context), sourceMetadata,
+ offloadInfo);
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 251dea0..d2be48c 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -47,13 +47,19 @@
desc->reply = mReplyMQ->dupeDesc();
}
if (mDataMQ) {
- const size_t frameSize = getFrameSize();
- desc->frameSizeBytes = frameSize;
- desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
+ desc->frameSizeBytes = getFrameSize();
+ desc->bufferSizeFrames = getBufferSizeInFrames();
desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
}
}
+size_t StreamContext::getBufferSizeInFrames() const {
+ if (mDataMQ) {
+ return mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / getFrameSize();
+ }
+ return 0;
+}
+
size_t StreamContext::getFrameSize() const {
return getFrameSizeInBytes(mFormat, mChannelLayout);
}
@@ -85,17 +91,18 @@
}
std::string StreamWorkerCommonLogic::init() {
- if (mCommandMQ == nullptr) return "Command MQ is null";
- if (mReplyMQ == nullptr) return "Reply MQ is null";
- if (mDataMQ == nullptr) return "Data MQ is null";
- if (sizeof(DataBufferElement) != mDataMQ->getQuantumSize()) {
- return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
+ if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
+ if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ if (dataMQ == nullptr) return "Data MQ is null";
+ if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
+ return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
}
- mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
+ mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
if (mDataBuffer == nullptr) {
return "Failed to allocate data buffer for element count " +
- std::to_string(mDataMQ->getQuantumCount()) +
+ std::to_string(dataMQ->getQuantumCount()) +
", size in bytes: " + std::to_string(mDataBufferSize);
}
if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
@@ -108,12 +115,14 @@
bool isConnected) const {
reply->status = STATUS_OK;
if (isConnected) {
- reply->observable.frames = mFrameCount;
+ reply->observable.frames = mContext->getFrameCount();
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(
@@ -133,7 +142,7 @@
// TODO: Add a delay for transitions of async operations when/if they added.
StreamDescriptor::Command command{};
- if (!mCommandMQ->readBlocking(&command, 1)) {
+ if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -151,7 +160,7 @@
switch (command.getTag()) {
case Tag::halReservedExit:
if (const int32_t cookie = command.get<Tag::halReservedExit>();
- cookie == mInternalCommandCookie) {
+ cookie == mContext->getInternalCommandCookie()) {
mDriver->shutdown();
setClosed();
// This is an internal command, no need to reply.
@@ -269,7 +278,7 @@
}
reply.state = mState;
LOG(severity) << __func__ << ": writing reply " << reply.toString();
- if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -278,14 +287,16 @@
}
bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
- const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ const size_t byteCount = std::min({clientSize, dataMQ->availableToWrite(), mDataBufferSize});
const bool isConnected = mIsConnected;
+ const size_t frameSize = mContext->getFrameSize();
size_t actualFrameCount = 0;
bool fatal = false;
int32_t latency = Module::kLatencyMs;
if (isConnected) {
- if (::android::status_t status = mDriver->transfer(
- mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
+ if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
+ &actualFrameCount, &latency);
status != ::android::OK) {
fatal = true;
LOG(ERROR) << __func__ << ": read failed: " << status;
@@ -293,17 +304,16 @@
} else {
usleep(3000); // Simulate blocking transfer delay.
for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
- actualFrameCount = byteCount / mFrameSize;
+ actualFrameCount = byteCount / frameSize;
}
- const size_t actualByteCount = actualFrameCount * mFrameSize;
- if (bool success =
- actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
+ const size_t actualByteCount = actualFrameCount * frameSize;
+ if (bool success = actualByteCount > 0 ? dataMQ->write(&mDataBuffer[0], actualByteCount) : true;
success) {
LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
<< " succeeded; connected? " << isConnected;
// Frames are provided and counted regardless of connection status.
reply->fmqByteCount += actualByteCount;
- mFrameCount += actualFrameCount;
+ mContext->advanceFrameCount(actualFrameCount);
populateReply(reply, isConnected);
} else {
LOG(WARNING) << __func__ << ": writing of " << actualByteCount
@@ -322,7 +332,8 @@
if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - mTransientStateStart);
stateDurationMs >= mTransientStateDelayMs) {
- if (mAsyncCallback == nullptr) {
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+ if (asyncCallback == nullptr) {
// In blocking mode, mState can only be DRAINING.
mState = StreamDescriptor::State::IDLE;
} else {
@@ -330,13 +341,13 @@
// drain or transfer completion. In the stub, we switch unconditionally.
if (mState == StreamDescriptor::State::DRAINING) {
mState = StreamDescriptor::State::IDLE;
- ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
+ ndk::ScopedAStatus status = asyncCallback->onDrainReady();
if (!status.isOk()) {
LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
}
} else {
mState = StreamDescriptor::State::ACTIVE;
- ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
+ ndk::ScopedAStatus status = asyncCallback->onTransferReady();
if (!status.isOk()) {
LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
}
@@ -350,7 +361,7 @@
}
StreamDescriptor::Command command{};
- if (!mCommandMQ->readBlocking(&command, 1)) {
+ if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -369,7 +380,7 @@
switch (command.getTag()) {
case Tag::halReservedExit:
if (const int32_t cookie = command.get<Tag::halReservedExit>();
- cookie == mInternalCommandCookie) {
+ cookie == mContext->getInternalCommandCookie()) {
mDriver->shutdown();
setClosed();
// This is an internal command, no need to reply.
@@ -424,10 +435,11 @@
if (!write(fmqByteCount, &reply)) {
mState = StreamDescriptor::State::ERROR;
}
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
if (mState == StreamDescriptor::State::STANDBY ||
mState == StreamDescriptor::State::DRAIN_PAUSED ||
mState == StreamDescriptor::State::PAUSED) {
- if (mAsyncCallback == nullptr ||
+ if (asyncCallback == nullptr ||
mState != StreamDescriptor::State::DRAIN_PAUSED) {
mState = StreamDescriptor::State::PAUSED;
} else {
@@ -436,7 +448,7 @@
} else if (mState == StreamDescriptor::State::IDLE ||
mState == StreamDescriptor::State::DRAINING ||
mState == StreamDescriptor::State::ACTIVE) {
- if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
+ if (asyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
mState = StreamDescriptor::State::ACTIVE;
} else {
switchToTransientState(StreamDescriptor::State::TRANSFERRING);
@@ -458,7 +470,8 @@
if (::android::status_t status = mDriver->drain(mode);
status == ::android::OK) {
populateReply(&reply, mIsConnected);
- if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
+ if (mState == StreamDescriptor::State::ACTIVE &&
+ mContext->getForceSynchronousDrain()) {
mState = StreamDescriptor::State::IDLE;
} else {
switchToTransientState(StreamDescriptor::State::DRAINING);
@@ -533,7 +546,7 @@
}
reply.state = mState;
LOG(severity) << __func__ << ": writing reply " << reply.toString();
- if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -542,38 +555,40 @@
}
bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
- const size_t readByteCount = mDataMQ->availableToRead();
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ const size_t readByteCount = dataMQ->availableToRead();
+ const size_t frameSize = mContext->getFrameSize();
bool fatal = false;
int32_t latency = Module::kLatencyMs;
- if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
+ if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
const bool isConnected = mIsConnected;
LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
<< " succeeded; connected? " << isConnected;
// Amount of data that the HAL module is going to actually use.
size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
- if (byteCount >= mFrameSize && mForceTransientBurst) {
+ if (byteCount >= frameSize && mContext->getForceTransientBurst()) {
// In order to prevent the state machine from going to ACTIVE state,
// simulate partial write.
- byteCount -= mFrameSize;
+ byteCount -= frameSize;
}
size_t actualFrameCount = 0;
if (isConnected) {
if (::android::status_t status = mDriver->transfer(
- mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
+ mDataBuffer.get(), byteCount / frameSize, &actualFrameCount, &latency);
status != ::android::OK) {
fatal = true;
LOG(ERROR) << __func__ << ": write failed: " << status;
}
} else {
- if (mAsyncCallback == nullptr) {
+ if (mContext->getAsyncCallback() == nullptr) {
usleep(3000); // Simulate blocking transfer delay.
}
- actualFrameCount = byteCount / mFrameSize;
+ actualFrameCount = byteCount / frameSize;
}
- const size_t actualByteCount = actualFrameCount * mFrameSize;
+ const size_t actualByteCount = actualFrameCount * frameSize;
// Frames are consumed and counted regardless of the connection status.
reply->fmqByteCount += actualByteCount;
- mFrameCount += actualFrameCount;
+ mContext->advanceFrameCount(actualFrameCount);
populateReply(reply, isConnected);
} else {
LOG(WARNING) << __func__ << ": reading of " << readByteCount
@@ -595,18 +610,16 @@
ndk::ScopedAStatus StreamCommonImpl::initInstance(
const std::shared_ptr<StreamCommonInterface>& delegate) {
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
- mCommonBinder = mCommon->asBinder();
- AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
return mWorker->start() ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
std::shared_ptr<IStreamCommon>* _aidl_return) {
- if (mCommon == nullptr) {
+ if (!mCommon) {
LOG(FATAL) << __func__ << ": the common interface was not created";
}
- *_aidl_return = mCommon;
+ *_aidl_return = mCommon.getInstance();
LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
@@ -657,8 +670,7 @@
LOG(DEBUG) << __func__ << ": joining the worker thread...";
mWorker->stop();
LOG(DEBUG) << __func__ << ": worker thread joined";
- mContext.reset();
- mWorker->setClosed();
+ onClose(mWorker->setClosed());
return ndk::ScopedAStatus::ok();
} else {
LOG(ERROR) << __func__ << ": stream was already closed";
@@ -721,11 +733,15 @@
}
} // namespace
-StreamIn::StreamIn(const std::vector<MicrophoneInfo>& microphones)
- : mMicrophones(transformMicrophones(microphones)) {
+StreamIn::StreamIn(StreamContext&& context, const std::vector<MicrophoneInfo>& microphones)
+ : mContext(std::move(context)), mMicrophones(transformMicrophones(microphones)) {
LOG(DEBUG) << __func__;
}
+void StreamIn::defaultOnClose() {
+ mContext.reset();
+}
+
ndk::ScopedAStatus StreamIn::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
std::vector<MicrophoneDynamicInfo> result;
@@ -778,11 +794,15 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-StreamOut::StreamOut(const std::optional<AudioOffloadInfo>& offloadInfo)
- : mOffloadInfo(offloadInfo) {
+StreamOut::StreamOut(StreamContext&& context, const std::optional<AudioOffloadInfo>& offloadInfo)
+ : mContext(std::move(context)), mOffloadInfo(offloadInfo) {
LOG(DEBUG) << __func__;
}
+void StreamOut::defaultOnClose() {
+ mContext.reset();
+}
+
ndk::ScopedAStatus StreamOut::updateOffloadMetadata(
const AudioOffloadMetadata& in_offloadMetadata) {
LOG(DEBUG) << __func__;
diff --git a/audio/aidl/default/StreamSwitcher.cpp b/audio/aidl/default/StreamSwitcher.cpp
new file mode 100644
index 0000000..956f413
--- /dev/null
+++ b/audio/aidl/default/StreamSwitcher.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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_StreamSwitcher"
+
+#include <Utils.h>
+#include <android-base/logging.h>
+#include <error/expected_utils.h>
+
+#include "core-impl/StreamStub.h"
+#include "core-impl/StreamSwitcher.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::media::audio::common::AudioDevice;
+
+namespace aidl::android::hardware::audio::core {
+
+StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
+ : mMetadata(metadata), mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {}
+
+ndk::ScopedAStatus StreamSwitcher::closeCurrentStream(bool validateStreamState) {
+ if (!mStream) return ndk::ScopedAStatus::ok();
+ RETURN_STATUS_IF_ERROR(mStream->prepareToClose());
+ RETURN_STATUS_IF_ERROR(mStream->close());
+ if (validateStreamState && !isValidClosingStreamState(mStream->getStatePriorToClosing())) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mStream.reset();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::close() {
+ if (mStream != nullptr) {
+ auto status = closeCurrentStream(false /*validateStreamState*/);
+ // The actual state is irrelevant since only StreamSwitcher cares about it.
+ onClose(StreamDescriptor::State::STANDBY);
+ return status;
+ }
+ LOG(ERROR) << __func__ << ": stream was already closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+}
+
+ndk::ScopedAStatus StreamSwitcher::prepareToClose() {
+ if (mStream != nullptr) {
+ return mStream->prepareToClose();
+ }
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+}
+
+ndk::ScopedAStatus StreamSwitcher::updateHwAvSyncId(int32_t in_hwAvSyncId) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ RETURN_STATUS_IF_ERROR(mStream->updateHwAvSyncId(in_hwAvSyncId));
+ mHwAvSyncId = in_hwAvSyncId;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mIsStubStream) {
+ LOG(ERROR) << __func__ << ": the stream is not connected";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return mStream->getVendorParameters(in_ids, _aidl_return);
+}
+
+ndk::ScopedAStatus StreamSwitcher::setVendorParameters(
+ const std::vector<VendorParameter>& in_parameters, bool in_async) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mIsStubStream) {
+ mMissedParameters.emplace_back(in_parameters, in_async);
+ return ndk::ScopedAStatus::ok();
+ }
+ return mStream->setVendorParameters(in_parameters, in_async);
+}
+
+ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr<IEffect>& in_effect) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (!mIsStubStream) {
+ RETURN_STATUS_IF_ERROR(mStream->addEffect(in_effect));
+ }
+ mEffects.push_back(in_effect);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr<IEffect>& in_effect) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ for (auto it = mEffects.begin(); it != mEffects.end();) {
+ if ((*it)->asBinder() == in_effect->asBinder()) {
+ it = mEffects.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon(
+ std::shared_ptr<IStreamCommon>* _aidl_return) {
+ if (!mCommon) {
+ LOG(FATAL) << __func__ << ": the common interface was not created";
+ }
+ *_aidl_return = mCommon.getInstance();
+ LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mMetadata = metadata;
+ return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::initInstance(
+ const std::shared_ptr<StreamCommonInterface>& delegate) {
+ mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
+ // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
+ return mStream->initInstance(nullptr);
+}
+
+const StreamContext& StreamSwitcher::getContext() const {
+ return *mContext;
+}
+
+bool StreamSwitcher::isClosed() const {
+ return mStream == nullptr || mStream->isClosed();
+}
+
+const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const {
+ return mStream->getConnectedDevices();
+}
+
+ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDevice>& devices) {
+ LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
+ if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok();
+ const DeviceSwitchBehavior behavior = switchCurrentStream(devices);
+ if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ } else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) {
+ // This is an error in the extending class.
+ LOG(FATAL) << __func__
+ << ": switching to stub stream with connected devices is not allowed";
+ }
+ if (behavior == USE_CURRENT_STREAM) {
+ mIsStubStream = false;
+ } else {
+ LOG(DEBUG) << __func__ << ": connected devices changed, switching stream";
+ // Two streams can't be opened for the same context, thus we always need to close
+ // the current one before creating a new one.
+ RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/));
+ if (behavior == CREATE_NEW_STREAM) {
+ mStream = createNewStream(devices, mContext, mMetadata);
+ mIsStubStream = false;
+ } else { // SWITCH_TO_STUB_STREAM
+ mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
+ mIsStubStream = true;
+ }
+ // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
+ if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) {
+ // Need to close the current failed stream, and report an error.
+ // Since we can't operate without a stream implementation, put a stub in.
+ RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/));
+ mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
+ (void)mStream->initInstance(nullptr);
+ (void)mStream->setConnectedDevices(devices);
+ return status;
+ }
+ }
+ RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices));
+ if (behavior == CREATE_NEW_STREAM) {
+ // These updates are less critical, only log warning on failure.
+ if (mHwAvSyncId.has_value()) {
+ if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) {
+ LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: "
+ << status.getDescription();
+ }
+ }
+ for (const auto& vndParam : mMissedParameters) {
+ if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second);
+ !status.isOk()) {
+ LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: "
+ << status.getDescription();
+ }
+ }
+ mMissedParameters.clear();
+ for (const auto& effect : mEffects) {
+ if (auto status = mStream->addEffect(effect); !status.isOk()) {
+ LOG(WARNING) << __func__ << ": error while adding effect for a new stream: "
+ << status.getDescription();
+ }
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
new file mode 100644
index 0000000..126c033
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 <algorithm>
+#include <cmath>
+
+#define LOG_TAG "AHAL_AlsaMixer"
+#include <android-base/logging.h>
+#include <android/binder_status.h>
+
+#include "Mixer.h"
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+// 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}}},
+ {Mixer::MIC_SWITCH, {{"Capture Switch", MIXER_CTL_TYPE_BOOL}}},
+ {Mixer::MIC_GAIN, {{"Capture Volume", MIXER_CTL_TYPE_INT}}}};
+
+// static
+Mixer::Controls Mixer::initializeMixerControls(struct mixer* mixer) {
+ if (mixer == nullptr) return {};
+ Controls 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, ctl);
+ if (!mixerCtlNames.empty()) {
+ mixerCtlNames += ",";
+ }
+ mixerCtlNames += ctlName;
+ break;
+ }
+ }
+ }
+ LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
+ return mixerControls;
+}
+
+std::ostream& operator<<(std::ostream& s, Mixer::Control c) {
+ switch (c) {
+ case Mixer::Control::MASTER_SWITCH:
+ s << "master mute";
+ break;
+ case Mixer::Control::MASTER_VOLUME:
+ s << "master volume";
+ break;
+ case Mixer::Control::HW_VOLUME:
+ s << "volume";
+ break;
+ case Mixer::Control::MIC_SWITCH:
+ s << "mic mute";
+ break;
+ case Mixer::Control::MIC_GAIN:
+ s << "mic gain";
+ break;
+ }
+ return s;
+}
+
+Mixer::Mixer(int card) : mMixer(mixer_open(card)), mMixerControls(initializeMixerControls(mMixer)) {
+ if (!isValid()) {
+ PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
+ }
+}
+
+Mixer::~Mixer() {
+ if (isValid()) {
+ std::lock_guard l(mMixerAccess);
+ mixer_close(mMixer);
+ }
+}
+
+ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
+ return setMixerControlMute(MASTER_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
+ return setMixerControlVolume(MASTER_VOLUME, volume);
+}
+
+ndk::ScopedAStatus Mixer::setMicGain(float gain) {
+ return setMixerControlVolume(MIC_GAIN, gain);
+}
+
+ndk::ScopedAStatus Mixer::setMicMute(bool muted) {
+ return setMixerControlMute(MIC_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto it = mMixerControls.find(Mixer::HW_VOLUME);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ std::vector<int> percents;
+ std::transform(
+ volumes.begin(), volumes.end(), std::back_inserter(percents),
+ [](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); });
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlPercent(it->second, percents); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto it = mMixerControls.find(ctl);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto it = mMixerControls.find(ctl);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ volume = std::clamp(volume, 0.0f, 1.0f);
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
+int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0);
+ error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
+int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
+} // 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..78728c2
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <iostream>
+#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 Mixer {
+ public:
+ explicit Mixer(int card);
+ ~Mixer();
+
+ bool isValid() const { return mMixer != nullptr; }
+
+ ndk::ScopedAStatus setMasterMute(bool muted);
+ ndk::ScopedAStatus setMasterVolume(float volume);
+ ndk::ScopedAStatus setMicGain(float gain);
+ ndk::ScopedAStatus setMicMute(bool muted);
+ ndk::ScopedAStatus setVolumes(const std::vector<float>& volumes);
+
+ private:
+ enum Control {
+ MASTER_SWITCH,
+ MASTER_VOLUME,
+ HW_VOLUME,
+ MIC_SWITCH,
+ MIC_GAIN,
+ };
+ using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
+ using Controls = std::map<Control, struct mixer_ctl*>;
+
+ friend std::ostream& operator<<(std::ostream&, Control);
+ static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
+ static Controls initializeMixerControls(struct mixer* mixer);
+
+ ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted);
+ ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume);
+
+ int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess);
+ int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents)
+ REQUIRES(mMixerAccess);
+ int setMixerControlValue(struct mixer_ctl* ctl, int value) REQUIRES(mMixerAccess);
+
+ // Since ALSA functions do not use internal locking, enforce thread safety at our level.
+ std::mutex mMixerAccess;
+ // The mixer object is owned by ALSA and will be released when the mixer is closed.
+ struct mixer* const mMixer;
+ // `mMixerControls` will only be initialized in constructor. After that, it wil only be
+ // read but not be modified. Each mixer_ctl object is owned by ALSA, it's life span is
+ // the same as of the mixer itself.
+ const Controls 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..00a7a84
--- /dev/null
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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(StreamContext* context, const Metadata& metadata, int readWriteRetries)
+ : StreamCommonImpl(context, metadata),
+ mFrameSizeBytes(getContext().getFrameSize()),
+ mIsInput(isInput(metadata)),
+ mConfig(alsa::getPcmConfig(getContext(), mIsInput)),
+ mReadWriteRetries(readWriteRetries) {}
+
+::android::status_t StreamAlsa::init() {
+ return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
+}
+
+::android::status_t StreamAlsa::drain(StreamDescriptor::DrainMode) {
+ usleep(1000);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::flush() {
+ usleep(1000);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::pause() {
+ usleep(1000);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::standby() {
+ mAlsaDeviceProxies.clear();
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::start() {
+ decltype(mAlsaDeviceProxies) alsaDeviceProxies;
+ for (const auto& device : getDeviceProfiles()) {
+ alsa::DeviceProxy proxy;
+ if (device.isExternal) {
+ // Always ask alsa configure as required since the configuration should be supported
+ // by the connected device. That is guaranteed by `setAudioPortConfig` and
+ // `setAudioPatch`.
+ proxy = alsa::openProxyForExternalDevice(
+ device, const_cast<struct pcm_config*>(&mConfig.value()),
+ true /*require_exact_match*/);
+ } else {
+ proxy = alsa::openProxyForAttachedDevice(
+ device, const_cast<struct pcm_config*>(&mConfig.value()),
+ getContext().getBufferSizeInFrames());
+ }
+ if (!proxy) {
+ return ::android::NO_INIT;
+ }
+ 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_with_retries(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer,
+ mReadWriteRetries);
+ maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
+ } else {
+ for (auto& proxy : mAlsaDeviceProxies) {
+ proxy_write_with_retries(proxy.get(), buffer, bytesToTransfer, mReadWriteRetries);
+ 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..20f7797
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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,
+ .isExternal = !audioDevice.type.connection.empty()};
+}
+
+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;
+ }
+ });
+}
+
+DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, size_t bufferFrameCount) {
+ if (deviceProfile.isExternal) {
+ LOG(FATAL) << __func__ << ": called for an external device, address=" << deviceProfile;
+ }
+ alsa_device_profile profile;
+ profile_init(&profile, deviceProfile.direction);
+ profile.card = deviceProfile.card;
+ profile.device = deviceProfile.device;
+ if (!profile_fill_builtin_device_info(&profile, pcmConfig, bufferFrameCount)) {
+ LOG(FATAL) << __func__ << ": failed to init for built-in device, address=" << deviceProfile;
+ }
+ auto proxy = makeDeviceProxy();
+ if (int err = proxy_prepare_from_default_config(proxy.get(), &profile); err != 0) {
+ LOG(FATAL) << __func__ << ": fail to prepare for device address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ return proxy;
+}
+
+DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, bool requireExactMatch) {
+ if (!deviceProfile.isExternal) {
+ LOG(FATAL) << __func__ << ": called for an attached device, address=" << deviceProfile;
+ }
+ auto profile = readAlsaDeviceInfo(deviceProfile);
+ if (!profile.has_value()) {
+ LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile;
+ return nullptr;
+ }
+ auto proxy = makeDeviceProxy();
+ if (int err = proxy_prepare(proxy.get(), &profile.value(), pcmConfig, requireExactMatch);
+ err != 0) {
+ LOG(ERROR) << __func__ << ": fail to prepare for device address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ return 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..615e657
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.h
@@ -0,0 +1,75 @@
+/*
+ * 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 */
+ bool isExternal;
+};
+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();
+DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, size_t bufferFrameCount);
+DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, bool requireExactMatch);
+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/AudioPolicyConfigXmlConverter.h b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
index 94501a8..090d585 100644
--- a/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
+++ b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
@@ -38,9 +38,10 @@
const ::aidl::android::media::audio::common::AudioHalEngineConfig& getAidlEngineConfig();
const SurroundSoundConfig& getSurroundSoundConfig();
- private:
+ // Public for testing purposes.
static const SurroundSoundConfig& getDefaultSurroundSoundConfig();
+ private:
const std::optional<::android::audio::policy::configuration::AudioPolicyConfiguration>&
getXsdcConfig() const {
return mConverter.getXsdcConfig();
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..2421b59
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ChildInterface.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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);
+ return *this;
+ }
+ explicit operator bool() const { return !!this->first; }
+ C& operator*() const { return *(this->first); }
+ C* operator->() const { return this->first; }
+ // Use 'getInstance' when returning the interface instance.
+ std::shared_ptr<C> getInstance() {
+ if (this->second.get() == nullptr) {
+ this->second = this->first->asBinder();
+ AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
+ ANDROID_PRIORITY_AUDIO);
+ }
+ 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..539221d 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;
@@ -181,16 +159,16 @@
// The following virtual functions are intended for vendor extension via inheritance.
virtual ndk::ScopedAStatus createInputStream(
+ StreamContext&& context,
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 ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
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..6264237
--- /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(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) override;
+ ndk::ScopedAStatus createOutputStream(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ 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..e87be3d 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
@@ -33,13 +33,13 @@
// Module interfaces
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
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 ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
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..4f77161
--- /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(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) override;
+ ndk::ScopedAStatus createOutputStream(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ 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..a296b8c 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
@@ -33,13 +33,13 @@
// Module interfaces
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
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 ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index aaf5860..fa2b760 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -43,6 +43,7 @@
#include <system/thread_defs.h>
#include <utils/Errors.h>
+#include "core-impl/ChildInterface.h"
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
@@ -65,7 +66,8 @@
DataMQ;
// Ensure that this value is not used by any of StreamDescriptor.State enums
- static constexpr int32_t STATE_CLOSED = -1;
+ static constexpr StreamDescriptor::State STATE_CLOSED =
+ static_cast<StreamDescriptor::State>(-1);
struct DebugParameters {
// An extra delay for transient states, in ms.
@@ -112,7 +114,8 @@
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(std::move(other.mAsyncCallback)),
mOutEventCallback(std::move(other.mOutEventCallback)),
- mDebugParameters(std::move(other.mDebugParameters)) {}
+ mDebugParameters(std::move(other.mDebugParameters)),
+ mFrameCount(other.mFrameCount) {}
StreamContext& operator=(StreamContext&& other) {
mCommandMQ = std::move(other.mCommandMQ);
mInternalCommandCookie = other.mInternalCommandCookie;
@@ -127,11 +130,13 @@
mAsyncCallback = std::move(other.mAsyncCallback);
mOutEventCallback = std::move(other.mOutEventCallback);
mDebugParameters = std::move(other.mDebugParameters);
+ mFrameCount = other.mFrameCount;
return *this;
}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
+ size_t getBufferSizeInFrames() const;
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayout() const {
return mChannelLayout;
}
@@ -154,7 +159,12 @@
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
int getSampleRate() const { return mSampleRate; }
bool isValid() const;
+ // 'reset' is called on a Binder thread when closing the stream. Does not use
+ // locking because it only cleans MQ pointers which were also set on the Binder thread.
void reset();
+ // 'advanceFrameCount' and 'getFrameCount' are only called on the worker thread.
+ long advanceFrameCount(size_t increase) { return mFrameCount += increase; }
+ long getFrameCount() const { return mFrameCount; }
private:
std::unique_ptr<CommandMQ> mCommandMQ;
@@ -170,6 +180,7 @@
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
DebugParameters mDebugParameters;
+ long mFrameCount = 0;
};
// This interface provides operations of the stream which are executed on the worker thread.
@@ -184,31 +195,34 @@
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.
};
class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
public:
- bool isClosed() const {
- return static_cast<int32_t>(mState.load()) == StreamContext::STATE_CLOSED;
+ bool isClosed() const { return mState == StreamContext::STATE_CLOSED; }
+ StreamDescriptor::State setClosed() {
+ auto prevState = mState.exchange(StreamContext::STATE_CLOSED);
+ if (prevState != StreamContext::STATE_CLOSED) {
+ mStatePriorToClosing = prevState;
+ }
+ return mStatePriorToClosing;
}
- void setClosed() { mState = static_cast<StreamDescriptor::State>(StreamContext::STATE_CLOSED); }
void setIsConnected(bool connected) { mIsConnected = connected; }
protected:
using DataBufferElement = int8_t;
- StreamWorkerCommonLogic(const StreamContext& context, DriverInterface* driver)
- : mDriver(driver),
- mInternalCommandCookie(context.getInternalCommandCookie()),
- mFrameSize(context.getFrameSize()),
- mCommandMQ(context.getCommandMQ()),
- mReplyMQ(context.getReplyMQ()),
- mDataMQ(context.getDataMQ()),
- mAsyncCallback(context.getAsyncCallback()),
- mTransientStateDelayMs(context.getTransientStateDelayMs()),
- mForceTransientBurst(context.getForceTransientBurst()),
- mForceSynchronousDrain(context.getForceSynchronousDrain()) {}
+ StreamWorkerCommonLogic(StreamContext* context, DriverInterface* driver)
+ : mContext(context),
+ mDriver(driver),
+ mTransientStateDelayMs(context->getTransientStateDelayMs()) {}
std::string init() override;
void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
void populateReplyWrongState(StreamDescriptor::Reply* reply,
@@ -218,38 +232,35 @@
mTransientStateStart = std::chrono::steady_clock::now();
}
+ // The context is only used for reading, except for updating the frame count,
+ // which happens on the worker thread only.
+ StreamContext* const mContext;
DriverInterface* const mDriver;
+ // This is the state the stream was in before being closed. It is retrieved by the main
+ // thread after joining the worker thread.
+ StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY;
// Atomic fields are used both by the main and worker threads.
std::atomic<bool> mIsConnected = false;
static_assert(std::atomic<StreamDescriptor::State>::is_always_lock_free);
std::atomic<StreamDescriptor::State> mState = StreamDescriptor::State::STANDBY;
- // All fields are used on the worker thread only.
- const int mInternalCommandCookie;
- const size_t mFrameSize;
- StreamContext::CommandMQ* const mCommandMQ;
- StreamContext::ReplyMQ* const mReplyMQ;
- StreamContext::DataMQ* const mDataMQ;
- std::shared_ptr<IStreamCallback> mAsyncCallback;
+ // All fields below are used on the worker thread only.
const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
- const bool mForceTransientBurst;
- const bool mForceSynchronousDrain;
// We use an array and the "size" field instead of a vector to be able to detect
// memory allocation issues.
std::unique_ptr<DataBufferElement[]> mDataBuffer;
size_t mDataBufferSize;
- long mFrameCount = 0;
};
// This interface is used to decouple stream implementations from a concrete StreamWorker
// implementation.
struct StreamWorkerInterface {
- using CreateInstance = std::function<StreamWorkerInterface*(const StreamContext& context,
- DriverInterface* driver)>;
+ using CreateInstance =
+ std::function<StreamWorkerInterface*(StreamContext* context, DriverInterface* driver)>;
virtual ~StreamWorkerInterface() = default;
virtual bool isClosed() const = 0;
virtual void setIsConnected(bool isConnected) = 0;
- virtual void setClosed() = 0;
+ virtual StreamDescriptor::State setClosed() = 0;
virtual bool start() = 0;
virtual void stop() = 0;
};
@@ -260,11 +271,11 @@
using WorkerImpl = ::android::hardware::audio::common::StreamWorker<WorkerLogic>;
public:
- StreamWorkerImpl(const StreamContext& context, DriverInterface* driver)
+ StreamWorkerImpl(StreamContext* context, DriverInterface* driver)
: WorkerImpl(context, driver) {}
bool isClosed() const override { return WorkerImpl::isClosed(); }
void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
- void setClosed() override { WorkerImpl::setClosed(); }
+ StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
bool start() override {
return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_AUDIO);
}
@@ -274,7 +285,7 @@
class StreamInWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
- StreamInWorkerLogic(const StreamContext& context, DriverInterface* driver)
+ StreamInWorkerLogic(StreamContext* context, DriverInterface* driver)
: StreamWorkerCommonLogic(context, driver) {}
protected:
@@ -288,8 +299,9 @@
class StreamOutWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
- StreamOutWorkerLogic(const StreamContext& context, DriverInterface* driver)
- : StreamWorkerCommonLogic(context, driver), mEventCallback(context.getOutEventCallback()) {}
+ StreamOutWorkerLogic(StreamContext* context, DriverInterface* driver)
+ : StreamWorkerCommonLogic(context, driver),
+ mEventCallback(context->getOutEventCallback()) {}
protected:
Status cycle() override;
@@ -403,16 +415,17 @@
};
// The implementation of DriverInterface must be provided by each concrete stream implementation.
+// Note that StreamCommonImpl does not own the context. This is to support swapping on the fly
+// implementations of the stream while keeping the same IStreamIn/Out instance. It's that instance
+// who must be owner of the context.
class StreamCommonImpl : virtual public StreamCommonInterface, virtual public DriverInterface {
public:
- StreamCommonImpl(const Metadata& metadata, StreamContext&& context,
+ StreamCommonImpl(StreamContext* context, const Metadata& metadata,
const StreamWorkerInterface::CreateInstance& createWorker)
- : mMetadata(metadata),
- mContext(std::move(context)),
- mWorker(createWorker(mContext, this)) {}
- StreamCommonImpl(const Metadata& metadata, StreamContext&& context)
+ : mContext(*context), mMetadata(metadata), mWorker(createWorker(context, this)) {}
+ StreamCommonImpl(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(
- metadata, std::move(context),
+ context, metadata,
isInput(metadata) ? getDefaultInWorkerCreator() : getDefaultOutWorkerCreator()) {}
~StreamCommonImpl();
@@ -444,23 +457,23 @@
protected:
static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
- return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+ return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamInWorker(ctx, driver);
};
}
static StreamWorkerInterface::CreateInstance getDefaultOutWorkerCreator() {
- return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+ return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamOutWorker(ctx, driver);
};
}
+ virtual void onClose(StreamDescriptor::State statePriorToClosing) = 0;
void stopWorker();
+ const StreamContext& mContext;
Metadata mMetadata;
- StreamContext mContext;
std::unique_ptr<StreamWorkerInterface> mWorker;
- std::shared_ptr<StreamCommonDelegator> mCommon;
- ndk::SpAIBinder mCommonBinder;
+ ChildInterface<StreamCommonDelegator> mCommon;
ConnectedDevices mConnectedDevices;
};
@@ -468,6 +481,8 @@
// concrete input/output stream implementations.
class StreamIn : virtual public StreamCommonInterface, public BnStreamIn {
protected:
+ void defaultOnClose();
+
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
@@ -487,14 +502,17 @@
friend class ndk::SharedRefBase;
- explicit StreamIn(
- const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+ StreamIn(StreamContext&& context,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+ StreamContext mContext;
const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
};
class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
protected:
+ void defaultOnClose();
+
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
@@ -528,10 +546,12 @@
friend class ndk::SharedRefBase;
- explicit StreamOut(const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
- offloadInfo);
+ StreamOut(StreamContext&& context,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo);
- std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
+ StreamContext mContext;
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
std::optional<::aidl::android::hardware::audio::common::AudioOffloadMetadata> mOffloadMetadata;
};
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..f98a922
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -0,0 +1,59 @@
+/*
+ * 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(StreamContext* context, const Metadata& metadata, int readWriteRetries);
+ // 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;
+ ::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;
+ const int mReadWriteRetries;
+ // 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..b39583e 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -29,7 +29,7 @@
class StreamRemoteSubmix : public StreamCommonImpl {
public:
- StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context);
+ StreamRemoteSubmix(StreamContext* context, const Metadata& metadata);
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -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.
@@ -71,28 +72,32 @@
static constexpr int kReadAttemptSleepUs = 5000;
};
-class StreamInRemoteSubmix final : public StreamRemoteSubmix, public StreamIn {
+class StreamInRemoteSubmix final : public StreamIn, public StreamRemoteSubmix {
public:
friend class ndk::SharedRefBase;
StreamInRemoteSubmix(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
-class StreamOutRemoteSubmix final : public StreamRemoteSubmix, public StreamOut {
+class StreamOutRemoteSubmix final : public StreamOut, public StreamRemoteSubmix {
public:
friend class ndk::SharedRefBase;
StreamOutRemoteSubmix(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamStub.h b/audio/aidl/default/include/core-impl/StreamStub.h
index 6b1b2dd..a8a3fc4 100644
--- a/audio/aidl/default/include/core-impl/StreamStub.h
+++ b/audio/aidl/default/include/core-impl/StreamStub.h
@@ -22,7 +22,7 @@
class StreamStub : public StreamCommonImpl {
public:
- StreamStub(const Metadata& metadata, StreamContext&& context);
+ StreamStub(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -43,22 +43,28 @@
bool mIsStandby = true; // Used for validating the state machine logic.
};
-class StreamInStub final : public StreamStub, public StreamIn {
+class StreamInStub final : public StreamIn, public StreamStub {
public:
friend class ndk::SharedRefBase;
StreamInStub(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
-class StreamOutStub final : public StreamStub, public StreamOut {
+class StreamOutStub final : public StreamOut, public StreamStub {
public:
friend class ndk::SharedRefBase;
- StreamOutStub(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ StreamOutStub(StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamSwitcher.h b/audio/aidl/default/include/core-impl/StreamSwitcher.h
new file mode 100644
index 0000000..e462481
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamSwitcher.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// 'StreamSwitcher' is implementation of 'StreamCommonInterface' which allows
+// dynamically switching the underlying stream implementation based on currently
+// connected devices. This is achieved by replacing inheritance from
+// 'StreamCommonImpl' with owning an instance of it. StreamSwitcher must be
+// extended in order to supply the logic for choosing the stream
+// implementation. When there are no connected devices, for instance, upon the
+// creation, the StreamSwitcher engages an instance of a stub stream in order to
+// keep serving requests coming via 'StreamDescriptor'.
+//
+// StreamSwitcher implements the 'IStreamCommon' interface directly, with
+// necessary delegation to the current stream implementation. While the stub
+// stream is engaged, any requests made via 'IStreamCommon' (parameters, effects
+// setting, etc) are postponed and only delivered on device connection change
+// to the "real" stream implementation provided by the extending class. This is why
+// the behavior of StreamSwitcher in the "stub" state is not identical to behavior
+// of 'StreamStub'. It can become a full substitute for 'StreamStub' once
+// device connection change event occurs and the extending class returns
+// 'LEAVE_CURRENT_STREAM' from 'switchCurrentStream' method.
+//
+// There is a natural limitation that the current stream implementation may only
+// be switched when the stream is in the 'STANDBY' state. Thus, when the event
+// to switch the stream occurs, the current stream is stopped and joined, and
+// its last state is validated. Since the change of the set of connected devices
+// normally occurs on patch updates, if the stream was not in standby, this is
+// reported to the caller of 'IModule.setAudioPatch' as the 'EX_ILLEGAL_STATE'
+// error.
+//
+// The simplest use case, when the implementor just needs to emulate the legacy HAL API
+// behavior of receiving the connected devices upon stream creation, the implementation
+// of the extending class can look as follows. We assume that 'StreamLegacy' implementation
+// is the one requiring to know connected devices on creation:
+//
+// class StreamLegacy : public StreamCommonImpl {
+// public:
+// StreamLegacy(StreamContext* context, const Metadata& metadata,
+// const std::vector<AudioDevice>& devices);
+// };
+//
+// class StreamOutLegacy final : public StreamOut, public StreamSwitcher {
+// public:
+// StreamOutLegacy(StreamContext&& context, metatadata etc.)
+// private:
+// DeviceSwitchBehavior switchCurrentStream(const std::vector<AudioDevice>&) override {
+// // This implementation effectively postpones stream creation until
+// // receiving the first call to 'setConnectedDevices' with a non-empty list.
+// return isStubStream() ? DeviceSwitchBehavior::CREATE_NEW_STREAM :
+// DeviceSwitchBehavior::USE_CURRENT_STREAM;
+// }
+// std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
+// const std::vector<AudioDevice>& devices,
+// StreamContext* context, const Metadata& metadata) override {
+// return std::unique_ptr<StreamCommonInterfaceEx>(new InnerStreamWrapper<StreamLegacy>(
+// context, metadata, devices));
+// }
+// void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+// }
+//
+
+class StreamCommonInterfaceEx : virtual public StreamCommonInterface {
+ public:
+ virtual StreamDescriptor::State getStatePriorToClosing() const = 0;
+};
+
+template <typename T>
+class InnerStreamWrapper : public T, public StreamCommonInterfaceEx {
+ public:
+ InnerStreamWrapper(StreamContext* context, const Metadata& metadata) : T(context, metadata) {}
+ StreamDescriptor::State getStatePriorToClosing() const override { return mStatePriorToClosing; }
+
+ private:
+ // Do not need to do anything on close notification from the inner stream
+ // because StreamSwitcher handles IStreamCommon::close by itself.
+ void onClose(StreamDescriptor::State statePriorToClosing) override {
+ mStatePriorToClosing = statePriorToClosing;
+ }
+
+ StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY;
+};
+
+class StreamSwitcher : virtual public StreamCommonInterface {
+ public:
+ StreamSwitcher(StreamContext* context, const Metadata& metadata);
+
+ ndk::ScopedAStatus close() override;
+ ndk::ScopedAStatus prepareToClose() override;
+ ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override;
+ ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) override;
+ ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+ bool in_async) override;
+ ndk::ScopedAStatus addEffect(
+ const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
+ override;
+ ndk::ScopedAStatus removeEffect(
+ const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
+ override;
+
+ ndk::ScopedAStatus getStreamCommonCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override;
+ ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
+
+ ndk::ScopedAStatus initInstance(
+ const std::shared_ptr<StreamCommonInterface>& delegate) override;
+ const StreamContext& getContext() const override;
+ bool isClosed() const override;
+ const ConnectedDevices& getConnectedDevices() const override;
+ ndk::ScopedAStatus setConnectedDevices(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
+ override;
+
+ protected:
+ // Since switching a stream requires closing down the current stream, StreamSwitcher
+ // asks the extending class its intent on the connected devices change.
+ enum DeviceSwitchBehavior {
+ // Continue using the current stream implementation. If it's the stub implementation,
+ // StreamSwitcher starts treating the stub stream as a "real" implementation,
+ // without effectively closing it and starting again.
+ USE_CURRENT_STREAM,
+ // This is the normal case when the extending class provides a "real" implementation
+ // which is not a stub implementation.
+ CREATE_NEW_STREAM,
+ // This is the case when the extending class wants to revert back to the initial
+ // condition of using a stub stream provided by the StreamSwitcher. This behavior
+ // is only allowed when the list of connected devices is empty.
+ SWITCH_TO_STUB_STREAM,
+ // Use when the set of devices is not supported by the extending class. This returns
+ // 'EX_UNSUPPORTED_OPERATION' from 'setConnectedDevices'.
+ UNSUPPORTED_DEVICES,
+ };
+ // StreamSwitcher will call these methods from 'setConnectedDevices'. If the switch behavior
+ // is 'CREATE_NEW_STREAM', the 'createwNewStream' function will be called (with the same
+ // device vector) for obtaining a new stream implementation, assuming that closing
+ // the current stream was a success.
+ virtual DeviceSwitchBehavior switchCurrentStream(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
+ virtual std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
+ StreamContext* context, const Metadata& metadata) = 0;
+ virtual void onClose(StreamDescriptor::State streamPriorToClosing) = 0;
+
+ bool isStubStream() const { return mIsStubStream; }
+ StreamCommonInterfaceEx* getCurrentStream() const { return mStream.get(); }
+
+ private:
+ using VndParam = std::pair<std::vector<VendorParameter>, bool /*isAsync*/>;
+
+ static constexpr bool isValidClosingStreamState(StreamDescriptor::State state) {
+ return state == StreamDescriptor::State::STANDBY || state == StreamDescriptor::State::ERROR;
+ }
+
+ ndk::ScopedAStatus closeCurrentStream(bool validateStreamState);
+
+ // StreamSwitcher does not own the context.
+ StreamContext* mContext;
+ Metadata mMetadata;
+ ChildInterface<StreamCommonDelegator> mCommon;
+ // The current stream.
+ std::unique_ptr<StreamCommonInterfaceEx> mStream;
+ // Indicates whether 'mCurrentStream' is a stub stream implementation
+ // maintained by StreamSwitcher until the extending class provides a "real"
+ // implementation. The invariant of this state is that there are no connected
+ // devices.
+ bool mIsStubStream = true;
+ // Storage for the data from commands received via 'IStreamCommon'.
+ std::optional<int32_t> mHwAvSyncId;
+ std::vector<VndParam> mMissedParameters;
+ std::vector<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>> mEffects;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index 8c40782..74e30ff 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -17,84 +17,62 @@
#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);
+ StreamUsb(StreamContext* context, const Metadata& metadata);
// 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 {
+class StreamInUsb final : public StreamIn, public StreamUsb {
public:
friend class ndk::SharedRefBase;
StreamInUsb(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
-class StreamOutUsb final : public StreamUsb, public StreamOut {
+class StreamOutUsb final : public StreamOut, public StreamUsb {
public:
friend class ndk::SharedRefBase;
- StreamOutUsb(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ StreamOutUsb(StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
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/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
index 2b79f51..9be7837 100644
--- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
@@ -56,16 +56,16 @@
}
ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream(
- const SinkMetadata& sinkMetadata, StreamContext&& context,
+ StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInRemoteSubmix>(result, sinkMetadata, std::move(context),
+ return createStreamInstance<StreamInRemoteSubmix>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream(
- const SourceMetadata& sourceMetadata, StreamContext&& context,
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutRemoteSubmix>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutRemoteSubmix>(result, std::move(context), sourceMetadata,
offloadInfo);
}
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 5af0d91..9537ebc 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -29,14 +29,14 @@
namespace aidl::android::hardware::audio::core {
-StreamRemoteSubmix::StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
- mPortId(context.getPortId()),
+StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata),
+ mPortId(context->getPortId()),
mIsInput(isInput(metadata)) {
- mStreamConfig.frameSize = context.getFrameSize();
- mStreamConfig.format = context.getFormat();
- mStreamConfig.channelLayout = context.getChannelLayout();
- mStreamConfig.sampleRate = context.getSampleRate();
+ mStreamConfig.frameSize = context->getFrameSize();
+ mStreamConfig.format = context->getFormat();
+ mStreamConfig.channelLayout = context->getChannelLayout();
+ mStreamConfig.sampleRate = context->getSampleRate();
}
std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
@@ -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;
@@ -332,10 +353,11 @@
return ::android::OK;
}
-StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+StreamInRemoteSubmix::StreamInRemoteSubmix(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones),
+ StreamRemoteSubmix(&(StreamIn::mContext), sinkMetadata) {}
ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
@@ -344,9 +366,10 @@
return ndk::ScopedAStatus::ok();
}
-StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
- StreamContext&& context,
+StreamOutRemoteSubmix::StreamOutRemoteSubmix(StreamContext&& context,
+ const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
+ : StreamOut(std::move(context), offloadInfo),
+ StreamRemoteSubmix(&(StreamOut::mContext), sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
index 8f5b8cb..ddac64d 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.cpp
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -27,7 +27,7 @@
namespace aidl::android::hardware::audio::core::r_submix {
// Verify a submix input or output stream can be opened.
-bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig streamConfig) {
+bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) {
// If the stream is already open, don't open it again.
// ENABLE_LEGACY_INPUT_OPEN is default behaviour
if (!isInput && isStreamOutOpen()) {
@@ -43,7 +43,7 @@
// Compare this stream config with existing pipe config, returning false if they do *not*
// match, true otherwise.
-bool SubmixRoute::isStreamConfigCompatible(const AudioConfig streamConfig) {
+bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) {
if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
<< streamConfig.channelLayout.toString()
@@ -126,7 +126,7 @@
// If SubmixRoute doesn't exist for a port, create a pipe for the submix audio device of size
// buffer_size_frames and store config of the submix audio device.
-::android::status_t SubmixRoute::createPipe(const AudioConfig streamConfig) {
+::android::status_t SubmixRoute::createPipe(const AudioConfig& streamConfig) {
const int channelCount = getChannelCount(streamConfig.channelLayout);
const audio_format_t audioFormat = VALUE_OR_RETURN_STATUS(
aidl2legacy_AudioFormatDescription_audio_format_t(streamConfig.format));
@@ -201,9 +201,9 @@
if (isInput) {
mStreamInStandby = true;
- } else {
+ } else if (!mStreamOutStandby) {
mStreamOutStandby = true;
- mStreamOutStandbyTransition = !mStreamOutStandbyTransition;
+ mStreamOutStandbyTransition = true;
}
}
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 5f7ea75..1a98df2 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -93,9 +93,9 @@
return mSource;
}
- bool isStreamConfigValid(bool isInput, const AudioConfig streamConfig);
+ bool isStreamConfigValid(bool isInput, const AudioConfig& streamConfig);
void closeStream(bool isInput);
- ::android::status_t createPipe(const AudioConfig streamConfig);
+ ::android::status_t createPipe(const AudioConfig& streamConfig);
void exitStandby(bool isInput);
bool hasAtleastOneStreamOpen();
int notifyReadError();
@@ -107,7 +107,7 @@
long updateReadCounterFrames(size_t frameCount);
private:
- bool isStreamConfigCompatible(const AudioConfig streamConfig);
+ bool isStreamConfigCompatible(const AudioConfig& streamConfig);
std::mutex mLock;
diff --git a/audio/aidl/default/stub/ModuleStub.cpp b/audio/aidl/default/stub/ModuleStub.cpp
new file mode 100644
index 0000000..9f6e0b4
--- /dev/null
+++ b/audio/aidl/default/stub/ModuleStub.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of IBluetooth: " << _aidl_return->get()->asBinder().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.getInstance();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: "
+ << _aidl_return->get()->asBinder().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.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of IBluetoothLe: " << _aidl_return->get()->asBinder().get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
+ const std::vector<MicrophoneInfo>& microphones,
+ std::shared_ptr<StreamIn>* result) {
+ return createStreamInstance<StreamInStub>(result, std::move(context), sinkMetadata,
+ microphones);
+}
+
+ndk::ScopedAStatus ModuleStub::createOutputStream(
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
+ const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+ return createStreamInstance<StreamOutStub>(result, std::move(context), sourceMetadata,
+ offloadInfo);
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
similarity index 88%
rename from audio/aidl/default/StreamStub.cpp
rename to audio/aidl/default/stub/StreamStub.cpp
index 2dcf4d4..66f4605 100644
--- a/audio/aidl/default/StreamStub.cpp
+++ b/audio/aidl/default/stub/StreamStub.cpp
@@ -31,8 +31,8 @@
namespace aidl::android::hardware::audio::core {
-StreamStub::StreamStub(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
+StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata),
mFrameSizeBytes(getContext().getFrameSize()),
mSampleRate(getContext().getSampleRate()),
mIsAsynchronous(!!getContext().getAsyncCallback()),
@@ -118,12 +118,13 @@
mIsInitialized = false;
}
-StreamInStub::StreamInStub(const SinkMetadata& sinkMetadata, StreamContext&& context,
+StreamInStub::StreamInStub(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamStub(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones), StreamStub(&(StreamIn::mContext), sinkMetadata) {}
-StreamOutStub::StreamOutStub(const SourceMetadata& sourceMetadata, StreamContext&& context,
+StreamOutStub::StreamOutStub(StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamStub(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
+ : StreamOut(std::move(context), offloadInfo),
+ StreamStub(&(StreamOut::mContext), sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/tests/AudioPolicyConfigXmlConverterTest.cpp b/audio/aidl/default/tests/AudioPolicyConfigXmlConverterTest.cpp
new file mode 100644
index 0000000..572bc5a
--- /dev/null
+++ b/audio/aidl/default/tests/AudioPolicyConfigXmlConverterTest.cpp
@@ -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.
+ */
+
+// #include <memory>
+// #include <string>
+// #include <vector>
+
+#include <android-base/macros.h>
+#include <gtest/gtest.h>
+#define LOG_TAG "AudioPolicyConfigXmlConverterTest"
+#include <log/log.h>
+
+#include <core-impl/AudioPolicyConfigXmlConverter.h>
+#include <media/AidlConversionCppNdk.h>
+
+using aidl::android::hardware::audio::core::internal::AudioPolicyConfigXmlConverter;
+using aidl::android::media::audio::common::AudioFormatDescription;
+
+namespace {
+
+void ValidateAudioFormatDescription(const AudioFormatDescription& format) {
+ auto conv = ::aidl::android::aidl2legacy_AudioFormatDescription_audio_format_t(format);
+ ASSERT_TRUE(conv.ok()) << format.toString();
+}
+
+} // namespace
+
+TEST(AudioPolicyConfigXmlConverterTest, DefaultSurroundSoundConfigIsValid) {
+ auto config = AudioPolicyConfigXmlConverter::getDefaultSurroundSoundConfig();
+ for (const auto& family : config.formatFamilies) {
+ EXPECT_NO_FATAL_FAILURE(ValidateAudioFormatDescription(family.primaryFormat));
+ SCOPED_TRACE(family.primaryFormat.toString());
+ for (const auto& sub : family.subFormats) {
+ EXPECT_NO_FATAL_FAILURE(ValidateAudioFormatDescription(sub));
+ }
+ }
+}
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 5c9d477..f926e09 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
@@ -102,75 +68,31 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-ndk::ScopedAStatus ModuleUsb::createInputStream(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModuleUsb::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInUsb>(result, sinkMetadata, std::move(context), microphones);
+ return createStreamInstance<StreamInUsb>(result, std::move(context), sinkMetadata, microphones);
}
-ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModuleUsb::createOutputStream(StreamContext&& context,
+ const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo,
std::shared_ptr<StreamOut>* result) {
if (offloadInfo.has_value()) {
LOG(ERROR) << __func__ << ": offload is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- return createStreamInstance<StreamOutUsb>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutUsb>(result, std::move(context), sourceMetadata,
offloadInfo);
}
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..9684a87 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;
-}
+StreamUsb::StreamUsb(StreamContext* context, const Metadata& metadata)
+ : StreamAlsa(context, metadata, 1 /*readWriteRetries*/) {}
ndk::ScopedAStatus StreamUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
@@ -89,122 +45,47 @@
<< ") 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);
}
+ RETURN_STATUS_IF_ERROR(setConnectedDevices(connectedDevices));
std::lock_guard guard(mLock);
- RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+ mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
mConnectedDevicesUpdated.store(true, std::memory_order_release);
return ndk::ScopedAStatus::ok();
}
-::android::status_t StreamUsb::drain(StreamDescriptor::DrainMode) {
- usleep(1000);
- return ::android::OK;
-}
-
-::android::status_t StreamUsb::flush() {
- usleep(1000);
- return ::android::OK;
-}
-
-::android::status_t StreamUsb::pause() {
- usleep(1000);
- return ::android::OK;
-}
-
::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,
+StreamInUsb::StreamInUsb(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamUsb(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones), StreamUsb(&(StreamIn::mContext), sinkMetadata) {}
ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
@@ -212,11 +93,11 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
+StreamOutUsb::StreamOutUsb(StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
- mChannelCount = getChannelCount(getContext().getChannelLayout());
-}
+ : StreamOut(std::move(context), offloadInfo),
+ StreamUsb(&(StreamOut::mContext), sourceMetadata),
+ mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
@@ -224,17 +105,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..0a49446 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;
@@ -165,12 +33,10 @@
bool connected) {
LOG(DEBUG) << __func__ << ": card=" << card << ", connected=" << connected;
if (connected) {
- struct mixer* mixer = mixer_open(card);
- if (mixer == nullptr) {
- PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
+ auto alsaMixer = std::make_shared<alsa::Mixer>(card);
+ if (!alsaMixer->isValid()) {
return;
}
- auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
@@ -209,7 +75,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 +84,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/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
index 141efc1..62046f3 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
@@ -18,6 +18,7 @@
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
+#include <condition_variable>
#include <chrono>
#include <memory>
@@ -28,6 +29,8 @@
namespace automotive {
namespace vehicle {
+using ::android::base::ScopedLockAssertion;
+
class RecurrentTimerTest : public testing::Test {
public:
std::shared_ptr<RecurrentTimer::Callback> getCallback(size_t token) {
@@ -35,6 +38,15 @@
std::scoped_lock<std::mutex> lockGuard(mLock);
mCallbacks.push_back(token);
+ mCond.notify_all();
+ });
+ }
+
+ bool waitForCalledCallbacks(size_t count, size_t timeoutInMs) {
+ std::unique_lock<std::mutex> uniqueLock(mLock);
+ return mCond.wait_for(uniqueLock, std::chrono::milliseconds(timeoutInMs), [this, count] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mCallbacks.size() >= count;
});
}
@@ -54,6 +66,7 @@
}
private:
+ std::condition_variable mCond;
std::mutex mLock;
std::vector<size_t> mCallbacks GUARDED_BY(mLock);
};
@@ -66,12 +79,11 @@
auto action = getCallback(0);
timer.registerTimerCallback(interval, action);
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ // Should only takes 1s, use 5s as timeout to be safe.
+ ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
+ << "Not enough callbacks called before timeout";
timer.unregisterTimerCallback(action);
-
- // Theoretically trigger 10 times, but check for at least 9 times to be stable.
- ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
}
TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
@@ -92,10 +104,11 @@
timer.registerTimerCallback(interval, action);
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ // Should only takes 1s, use 5s as timeout to be safe.
+ ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
+ << "Not enough callbacks called before timeout";
- // Theoretically trigger 10 times, but check for at least 9 times to be stable.
- ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
+ timer.unregisterTimerCallback(action);
}
TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
@@ -114,7 +127,9 @@
std::this_thread::sleep_for(std::chrono::milliseconds(200));
- ASSERT_TRUE(getCalledCallbacks().empty());
+ // Should be 0, but in rare cases there might be 1 events in the queue while the timer is
+ // being destroyed.
+ ASSERT_LE(getCalledCallbacks().size(), 1u);
}
TEST_F(RecurrentTimerTest, testRegisterMultipleCallbacks) {
@@ -132,7 +147,11 @@
auto action3 = getCallback(3);
timer.registerTimerCallback(interval3, action3);
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ // In 1s, we should generate 10 + 20 + 33 = 63 events.
+ // Here we are waiting for more events to make sure we receive enough events for each actions.
+ // Use 5s as timeout to be safe.
+ ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 70u, /* timeoutInMs= */ 5000))
+ << "Not enough callbacks called before timeout";
timer.unregisterTimerCallback(action1);
timer.unregisterTimerCallback(action2);
@@ -152,20 +171,18 @@
action3Count++;
}
}
- // Theoretically trigger 10 times, but check for at least 9 times to be stable.
- ASSERT_GE(action1Count, static_cast<size_t>(9));
- // Theoretically trigger 20 times, but check for at least 15 times to be stable.
- ASSERT_GE(action2Count, static_cast<size_t>(15));
- // Theoretically trigger 33 times, but check for at least 25 times to be stable.
- ASSERT_GE(action3Count, static_cast<size_t>(25));
+
+ ASSERT_GE(action1Count, static_cast<size_t>(10));
+ ASSERT_GE(action2Count, static_cast<size_t>(20));
+ ASSERT_GE(action3Count, static_cast<size_t>(33));
}
TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
RecurrentTimer timer;
- // 0.02s
- int64_t interval1 = 20000000;
- // 0.01s
- int64_t interval2 = 10000000;
+ // 0.2s
+ int64_t interval1 = 200'000'000;
+ // 0.1s
+ int64_t interval2 = 100'000'000;
auto action = getCallback(0);
for (int i = 0; i < 10; i++) {
@@ -175,10 +192,9 @@
clearCalledCallbacks();
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
-
- // Theoretically trigger 10 times, but check for at least 9 times to be stable.
- ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
+ // Should only takes 1s, use 5s as timeout to be safe.
+ ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
+ << "Not enough callbacks called before timeout";
timer.unregisterTimerCallback(action);
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
index e5222a7..24eb4d0 100644
--- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -222,6 +222,8 @@
int wait_for_completed_packets_event(uint16_t handle);
void send_and_wait_for_cmd_complete(std::unique_ptr<CommandBuilder> cmd,
std::vector<uint8_t>& cmd_complete);
+ void reassemble_sco_loopback_pkt(std::vector<uint8_t>& scoPackets,
+ size_t size);
// A simple test implementation of BluetoothHciCallbacks.
class BluetoothHciCallbacks
@@ -569,6 +571,11 @@
ASSERT_TRUE(
sco_queue.tryPopWithTimeout(sco_loopback, kWaitForScoDataTimeout));
+ if (sco_loopback.size() < size) {
+ // The packets may have been split for USB. Reassemble before checking.
+ reassemble_sco_loopback_pkt(sco_loopback, size);
+ }
+
ASSERT_EQ(sco_packet, sco_loopback);
}
logger.setTotalBytes(num_packets * size * 2);
@@ -703,6 +710,22 @@
wait_for_command_complete_event(view.GetOpCode(), cmd_complete));
}
+// Handle the loopback packet.
+void BluetoothAidlTest::reassemble_sco_loopback_pkt(std::vector<uint8_t>& scoPackets,
+ size_t size) {
+ std::vector<uint8_t> sco_packet_whole;
+ sco_packet_whole.assign(scoPackets.begin(), scoPackets.end());
+ while (size + 3 > sco_packet_whole.size()) {
+ std::vector<uint8_t> sco_packets;
+ ASSERT_TRUE(
+ sco_queue.tryPopWithTimeout(sco_packets, kWaitForScoDataTimeout));
+ sco_packet_whole.insert(sco_packet_whole.end(), sco_packets.begin() + 3,
+ sco_packets.end());
+ }
+ scoPackets.assign(sco_packet_whole.begin(), sco_packet_whole.end());
+ scoPackets[2] = size;
+}
+
// Empty test: Initialize()/Close() are called in SetUp()/TearDown().
TEST_P(BluetoothAidlTest, InitializeAndClose) {}
diff --git a/bluetooth/audio/OWNERS b/bluetooth/audio/OWNERS
index a8e9bda..f3657ca 100644
--- a/bluetooth/audio/OWNERS
+++ b/bluetooth/audio/OWNERS
@@ -1,4 +1,6 @@
+# Bug component: 27441
+
include platform/packages/modules/Bluetooth:/OWNERS
-cheneyni@google.com
aliceypkuo@google.com
+quocbaodo@google.com
diff --git a/bluetooth/audio/aidl/vts/Android.bp b/bluetooth/audio/aidl/vts/Android.bp
index e03fb58..3e6953f 100644
--- a/bluetooth/audio/aidl/vts/Android.bp
+++ b/bluetooth/audio/aidl/vts/Android.bp
@@ -15,16 +15,20 @@
],
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",
"libfmq",
],
+ test_config: "VtsHalBluetoothAudioTargetTest.xml",
test_suites: [
"general-tests",
"vts",
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.xml b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.xml
new file mode 100644
index 0000000..7b02685
--- /dev/null
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalBluetoothAudioTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
+ <!-- Skips test module if ro.product.first_api_level < 33. -->
+ <option name="min-api-level" value="33" />
+ </object>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalBluetoothAudioTargetTest->/data/local/tmp/VtsHalBluetoothAudioTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalBluetoothAudioTargetTest" />
+ </test>
+</configuration>
diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp
old mode 100755
new mode 100644
index 3c16d57..6ce5681
--- a/cas/aidl/default/Android.bp
+++ b/cas/aidl/default/Android.bp
@@ -68,6 +68,7 @@
defaults: ["cas_service_example_defaults"],
init_rc: ["cas-default-lazy.rc"],
cflags: ["-DLAZY_SERVICE"],
+ overrides: ["android.hardware.cas-service.example"],
}
cc_fuzz {
diff --git a/cas/aidl/default/CasImpl.cpp b/cas/aidl/default/CasImpl.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/CasImpl.h b/cas/aidl/default/CasImpl.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/DescramblerImpl.cpp b/cas/aidl/default/DescramblerImpl.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/DescramblerImpl.h b/cas/aidl/default/DescramblerImpl.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/FactoryLoader.h b/cas/aidl/default/FactoryLoader.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/MediaCasService.cpp b/cas/aidl/default/MediaCasService.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/MediaCasService.h b/cas/aidl/default/MediaCasService.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/SharedLibrary.cpp b/cas/aidl/default/SharedLibrary.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/SharedLibrary.h b/cas/aidl/default/SharedLibrary.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/TypeConvert.cpp b/cas/aidl/default/TypeConvert.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/TypeConvert.h b/cas/aidl/default/TypeConvert.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/android.hardware.cas-service.xml b/cas/aidl/default/android.hardware.cas-service.xml
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/cas-default-lazy.rc b/cas/aidl/default/cas-default-lazy.rc
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/cas-default.rc b/cas/aidl/default/cas-default.rc
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/fuzzer.cpp b/cas/aidl/default/fuzzer.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp
old mode 100755
new mode 100644
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/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index b9fb3f4..bb7637a 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -281,9 +281,15 @@
<version>1.0</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
@@ -361,6 +367,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index b374c8c..dad1558 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -319,11 +319,21 @@
<version>1.0-1</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
@@ -399,6 +409,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
index 40ae655..23f634d 100644
--- a/compatibility_matrices/compatibility_matrix.6.xml
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -368,11 +368,21 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
@@ -454,6 +464,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
diff --git a/compatibility_matrices/compatibility_matrix.7.xml b/compatibility_matrices/compatibility_matrix.7.xml
index e5ef954..33c3148 100644
--- a/compatibility_matrices/compatibility_matrix.7.xml
+++ b/compatibility_matrices/compatibility_matrix.7.xml
@@ -430,11 +430,21 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
@@ -582,6 +592,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index 1c2cd50..387648c 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -433,10 +433,20 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.media.c2</name>
<version>1</version>
@@ -585,6 +595,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 60bb2ab..f616bdb 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -330,9 +330,6 @@
<!-- Either the native or the HIDL mapper HAL must exist on the device -->
<hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
- <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
- <version>2.1</version>
- <version>3.0</version>
<version>4.0</version>
<interface>
<name>IMapper</name>
@@ -425,10 +422,20 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.media.c2</name>
<version>1</version>
@@ -577,6 +584,7 @@
<interface>
<name>ISap</name>
<instance>slot1</instance>
+ <instance>slot2</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
@@ -741,7 +749,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.weaver</name>
- <version>1</version>
+ <version>2</version>
<interface>
<name>IWeaver</name>
<instance>default</instance>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index b17c0e2..ccce449 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <functional>
#include <string>
#include <vector>
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
index 50aa2b7..7194ebe 100644
--- a/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/BufferDescriptorInfo.aidl
@@ -23,7 +23,14 @@
@VintfStability
parcelable BufferDescriptorInfo {
/**
- * The name of the buffer in ASCII. Useful for debugging/tracing.
+ * The name of the buffer in null-terminated ASCII. Useful for debugging/tracing.
+ *
+ * NOTE: While a well behaved client will ensure it passes a null-terminated string
+ * within the 128-byte limit, the IAllocator service implementation should be
+ * be defensive against malformed input. As such, it is recommended that
+ * IAllocator implementations proactively do `name[127] = 0` upon receiving
+ * an allocation request to enusre that the string is definitely
+ * null-terminated regardless of what the client sent.
*/
byte[128] name;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
index ba6fe97..18b219c 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
@@ -40,14 +40,12 @@
/**
* The stage in which dimming operations should be performed when compositing
* the client target.
+ *
* Note that with a COLORIMETRIC RenderIntent, DimmingSpace must be LINEAR. That is, dimming
- * is defined to occur in linear space.
- * However, some composer implementations may, with other vendor-defined RenderIntents,
- * configure their hardware such as image quality adjustments is intended to occur after
- * composition. In this scenario, if the dimming operation were applied in linear space,
- * then the resulting dimming operation may comepl those image quality adjustments to
- * incorrectly alter the gamma curve. To avoid this issue, those implementations must opt to
- * dim in gamma space.
+ * is defined to occur in linear space. However, some composer implementations may, with
+ * other vendor-defined RenderIntents, apply certain image quality adjustments that are
+ * sensitive to gamma shift when dimming in linear space. To avoid this issue, those
+ * implementations must opt to dim in gamma space.
*/
DimmingStage dimmingStage;
}
diff --git a/health/aidl/default/Health.cpp b/health/aidl/default/Health.cpp
index f401643..1d8cc13 100644
--- a/health/aidl/default/Health.cpp
+++ b/health/aidl/default/Health.cpp
@@ -272,7 +272,11 @@
{
std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
- callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
+ auto linked_callback_result = LinkedCallback::Make(ref<Health>(), callback);
+ if (!linked_callback_result.ok()) {
+ return ndk::ScopedAStatus::fromStatus(-linked_callback_result.error().code());
+ }
+ callbacks_.emplace_back(std::move(*linked_callback_result));
// unlock
}
diff --git a/health/aidl/default/LinkedCallback.cpp b/health/aidl/default/LinkedCallback.cpp
index 2985ffe..26e99f9 100644
--- a/health/aidl/default/LinkedCallback.cpp
+++ b/health/aidl/default/LinkedCallback.cpp
@@ -24,7 +24,7 @@
namespace aidl::android::hardware::health {
-std::unique_ptr<LinkedCallback> LinkedCallback::Make(
+::android::base::Result<std::unique_ptr<LinkedCallback>> LinkedCallback::Make(
std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback) {
std::unique_ptr<LinkedCallback> ret(new LinkedCallback());
binder_status_t linkRet =
@@ -32,7 +32,7 @@
reinterpret_cast<void*>(ret.get()));
if (linkRet != ::STATUS_OK) {
LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
- return nullptr;
+ return ::android::base::Error(-linkRet);
}
ret->service_ = service;
ret->callback_ = std::move(callback);
diff --git a/health/aidl/default/LinkedCallback.h b/health/aidl/default/LinkedCallback.h
index 82490a7..da494c9 100644
--- a/health/aidl/default/LinkedCallback.h
+++ b/health/aidl/default/LinkedCallback.h
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/health/IHealthInfoCallback.h>
#include <android-base/macros.h>
+#include <android-base/result.h>
#include <android/binder_auto_utils.h>
#include <health-impl/Health.h>
@@ -34,8 +35,8 @@
// service->death_reciepient() should be from CreateDeathRecipient().
// Not using a strong reference to |service| to avoid circular reference. The lifetime
// of |service| must be longer than this LinkedCallback object.
- static std::unique_ptr<LinkedCallback> Make(std::shared_ptr<Health> service,
- std::shared_ptr<IHealthInfoCallback> callback);
+ static ::android::base::Result<std::unique_ptr<LinkedCallback>> Make(
+ std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback);
// Automatically unlinkToDeath upon destruction. So, it is always safe to reinterpret_cast
// the cookie back to the LinkedCallback object.
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index 54b2740..fc3066e 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <functional>
#include <memory>
#include <mutex>
#include <vector>
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 0499079..8a8eaa4 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -89,6 +89,29 @@
return imei;
}
+// Use `ro.product.<property>_for_attestation` property for attestation if it is present else
+// fallback to use `ro.product.vendor.<property>` if it is present else fallback to
+// `ro.product.<property>`. Similar logic can be seen in Java method `getVendorDeviceIdProperty`
+// in frameworks/base/core/java/android/os/Build.java.
+template <Tag tag>
+void add_attestation_id(AuthorizationSetBuilder* attestation_id_tags,
+ TypedTag<TagType::BYTES, tag> tag_type, const char* prop) {
+ ::android::String8 prop_name =
+ ::android::String8::format("ro.product.%s_for_attestation", prop);
+ std::string prop_value = ::android::base::GetProperty(prop_name.string(), /* default= */ "");
+ if (!prop_value.empty()) {
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ } else {
+ prop_name = ::android::String8::format("ro.product.vendor.%s", prop);
+ prop_value = ::android::base::GetProperty(prop_name.string(), /* default= */ "");
+ if (!prop_value.empty()) {
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ } else {
+ prop_name = ::android::String8::format("ro.product.%s", prop);
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ }
+ }
+}
} // namespace
class AttestKeyTest : public KeyMintAidlTestBase {
@@ -798,11 +821,6 @@
}
TEST_P(AttestKeyTest, EcdsaAttestationID) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
// Create attestation key.
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
@@ -822,39 +840,12 @@
// Collection of valid attestation ID tags.
auto attestation_id_tags = AuthorizationSetBuilder();
- // Use ro.product.brand_for_attestation property for attestation if it is present else fallback
- // to ro.product.brand
- std::string prop_value =
- ::android::base::GetProperty("ro.product.brand_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND,
- "ro.product.brand_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
- }
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
- // Use ro.product.name_for_attestation property for attestation if it is present else fallback
- // to ro.product.name
- prop_value = ::android::base::GetProperty("ro.product.name_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT,
- "ro.product.name_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
- }
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "brand");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "device");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "name");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "model");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
- "ro.product.manufacturer");
- // Use ro.product.model_for_attestation property for attestation if it is present else fallback
- // to ro.product.model
- prop_value =
- ::android::base::GetProperty("ro.product.model_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL,
- "ro.product.model_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
- }
string imei = get_imei(0);
if (!imei.empty()) {
@@ -955,12 +946,6 @@
}
TEST_P(AttestKeyTest, SecondIMEIAttestationIDSuccess) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
-
// Skip the test if there is no second IMEI exists.
string second_imei = get_imei(1);
if (second_imei.empty()) {
@@ -1029,12 +1014,6 @@
}
TEST_P(AttestKeyTest, MultipleIMEIAttestationIDSuccess) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
-
// Skip the test if there is no first IMEI exists.
string imei = get_imei(0);
if (imei.empty()) {
diff --git a/security/rkp/README.md b/security/rkp/README.md
index f8e1d5e..8cd1582 100644
--- a/security/rkp/README.md
+++ b/security/rkp/README.md
@@ -52,7 +52,7 @@
* Degenerate DICE (Phase 1): A TEE root of trust key pair is used to sign
certificate requests; a single self-signed certificate signifies this phase.
* DICE (Phase 2): A hardware root of trust key pair is only accessible to ROM
- code; the boot process follows the [Android Profile for
+ or ROM extension code; the boot process follows the [Android Profile for
DICE](#android-profile-for-dice).
* SoC vendor certified DICE (Phase 3): This is identical to Phase 2, except the
SoC vendor also does the UDS\_pub extraction or certification in their
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) {
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl
new file mode 100644
index 0000000..efafa32
--- /dev/null
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.uwb.fira_android;
+@Backing(type="byte") @VintfStability
+enum UwbVendorDataPacketFormat {
+ RADAR_DATA_MESSAGE = 0xF,
+}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index 34bc4ec..1506406 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -39,5 +39,4 @@
ANDROID_RANGE_DIAGNOSTICS = 0x2,
RADAR_SET_APP_CONFIG = 0x11,
RADAR_GET_APP_CONFIG = 0x12,
- RADAR_DATA_NTF = 0x13,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl
new file mode 100644
index 0000000..7a1b033
--- /dev/null
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorDataPacketFormat.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.uwb.fira_android;
+
+/**
+ * Android specific vendor DPF (Data Packet Format) should be defined here.
+ */
+@VintfStability
+@Backing(type="byte")
+enum UwbVendorDataPacketFormat {
+ // Used to send radar data messages from UWBS to the host.
+ RADAR_DATA_MESSAGE = 0xF,
+}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index 203b940..95c10a8 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -46,6 +46,4 @@
RADAR_SET_APP_CONFIG = 0x11,
// Used to get application configurations for radar session.
RADAR_GET_APP_CONFIG = 0x12,
- // Used to report radar data for certain radar data types.
- RADAR_DATA_NTF = 0x13,
}
diff --git a/weaver/1.0/vts/functional/Android.bp b/weaver/1.0/vts/functional/Android.bp
deleted file mode 100644
index cc1d284..0000000
--- a/weaver/1.0/vts/functional/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "hardware_interfaces_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["hardware_interfaces_license"],
-}
-
-cc_test {
- name: "VtsHalWeaverV1_0TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: ["VtsHalWeaverV1_0TargetTest.cpp"],
- static_libs: ["android.hardware.weaver@1.0"],
- test_suites: ["general-tests", "vts"],
-}
diff --git a/weaver/1.0/vts/functional/OWNERS b/weaver/1.0/vts/functional/OWNERS
deleted file mode 100644
index ec8c304..0000000
--- a/weaver/1.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 186411
-chengyouho@google.com
-frankwoo@google.com
diff --git a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
deleted file mode 100644
index 66465a9..0000000
--- a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/hardware/weaver/1.0/IWeaver.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-#include <limits>
-
-using ::android::hardware::weaver::V1_0::IWeaver;
-using ::android::hardware::weaver::V1_0::WeaverConfig;
-using ::android::hardware::weaver::V1_0::WeaverReadStatus;
-using ::android::hardware::weaver::V1_0::WeaverReadResponse;
-using ::android::hardware::weaver::V1_0::WeaverStatus;
-using ::android::hardware::Return;
-using ::android::sp;
-
-const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
-const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
-
-struct WeaverHidlTest : public ::testing::TestWithParam<std::string> {
- virtual void SetUp() override {
- weaver = IWeaver::getService(GetParam());
- ASSERT_NE(weaver, nullptr);
- }
-
- virtual void TearDown() override {}
-
- sp<IWeaver> weaver;
-};
-
-/*
- * Checks config values are suitably large
- */
-TEST_P(WeaverHidlTest, GetConfig) {
- WeaverStatus status;
- WeaverConfig config;
-
- bool callbackCalled = false;
- auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- EXPECT_GE(config.slots, 16u);
- EXPECT_GE(config.keySize, 16u);
- EXPECT_GE(config.valueSize, 16u);
-}
-
-/*
- * Gets the config twice and checks they are the same
- */
-TEST_P(WeaverHidlTest, GettingConfigMultipleTimesGivesSameResult) {
- WeaverConfig config1;
- WeaverConfig config2;
-
- WeaverStatus status;
- bool callbackCalled = false;
- auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config1 = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- callbackCalled = false;
- ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config2 = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- EXPECT_EQ(config1, config2);
-}
-
-/*
- * Gets the number of slots from the config and writes a key and value to the last one
- */
-TEST_P(WeaverHidlTest, WriteToLastSlot) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- const uint32_t lastSlot = config.slots - 1;
- const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::OK);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with the same key and receives the value that was previously written
- */
-TEST_P(WeaverHidlTest, WriteFollowedByReadGivesTheSameValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ret, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(readRet.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverReadStatus::OK);
- EXPECT_EQ(readValue, VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Overwrites the slot with a new key and value
- * Reads the slot with the new key and receives the new value
- */
-TEST_P(WeaverHidlTest, OverwritingSlotUpdatesTheValue) {
- constexpr uint32_t slotId = 0;
- const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
- ASSERT_TRUE(initialWriteRet.isOk());
- ASSERT_EQ(initialWriteRet, WeaverStatus::OK);
-
- const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
- ASSERT_TRUE(overwriteRet.isOk());
- ASSERT_EQ(overwriteRet, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(readRet.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverReadStatus::OK);
- EXPECT_EQ(readValue, OTHER_VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with a different key so does not receive the value
- */
-TEST_P(WeaverHidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ret, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- const auto readRet =
- weaver->read(slotId, WRONG_KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(status, WeaverReadStatus::INCORRECT_KEY);
- EXPECT_TRUE(readValue.empty());
-}
-
-/*
- * Writing to an invalid slot fails
- */
-TEST_P(WeaverHidlTest, WritingToInvalidSlotFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- const auto writeRet = weaver->write(config.slots, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Reading from an invalid slot fails rather than incorrect key
- */
-TEST_P(WeaverHidlTest, ReadingFromInvalidSlotFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- bool callbackCalled = false;
- WeaverReadStatus readStatus;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(config.slots, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- readStatus = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writing a key that is too large fails
- */
-TEST_P(WeaverHidlTest, WriteWithTooLargeKeyFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, bigKey, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Writing a value that is too large fails
- */
-TEST_P(WeaverHidlTest, WriteWithTooLargeValueFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigValue(config.valueSize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, KEY, bigValue);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Reading with a key that is loo large fails
- */
-TEST_P(WeaverHidlTest, ReadWithTooLargeKeyFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- bool callbackCalled = false;
- WeaverReadStatus readStatus;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(slotId, bigKey, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- readStatus = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverHidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, WeaverHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IWeaver::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/weaver/OWNERS b/weaver/OWNERS
new file mode 100644
index 0000000..7e579f6
--- /dev/null
+++ b/weaver/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1081729
+ebiggers@google.com
+paulcrowley@google.com
+swillden@google.com
+wfrichar@google.com
+chengyouho@google.com
diff --git a/weaver/aidl/Android.bp b/weaver/aidl/Android.bp
index caa92aa..74cec99 100644
--- a/weaver/aidl/Android.bp
+++ b/weaver/aidl/Android.bp
@@ -17,5 +17,10 @@
platform_apis: true,
},
},
- versions: ["1"],
+ versions_with_info: [
+ {
+ version: "1",
+ imports: [],
+ },
+ ],
}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
index 47ee4c8..96e528f 100644
--- a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,4 +36,5 @@
parcelable WeaverReadResponse {
long timeout;
byte[] value;
+ android.hardware.weaver.WeaverReadStatus status = android.hardware.weaver.WeaverReadStatus.FAILED;
}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadStatus.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadStatus.aidl
new file mode 100644
index 0000000..fce9758
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadStatus.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@Backing(type="int") @VintfStability
+enum WeaverReadStatus {
+ OK = 0,
+ FAILED = 1,
+ INCORRECT_KEY = 2,
+ THROTTLE = 3,
+}
diff --git a/weaver/aidl/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
index f51034a..ae816ef 100644
--- a/weaver/aidl/android/hardware/weaver/IWeaver.aidl
+++ b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
@@ -20,8 +20,8 @@
import android.hardware.weaver.WeaverReadResponse;
/**
- * Weaver provides secure storage of secret values that may only be read if the
- * corresponding key has been presented.
+ * Weaver provides secure persistent storage of secret values that may only be
+ * read if the corresponding key has been presented.
*
* The storage must be secure as the device's user authentication and encryption
* relies on the security of these values. The cardinality of the domains of the
@@ -76,7 +76,8 @@
WeaverReadResponse read(in int slotId, in byte[] key);
/**
- * Overwrites the identified slot with the provided key and value.
+ * Overwrites the identified slot with the provided key and value, rendering
+ * the previous contents of the slot permanently unrecoverable.
*
* The new values are written regardless of the current state of the slot in
* order to remain idempotent.
diff --git a/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
index ec006e8..17ea718 100644
--- a/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
+++ b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +16,22 @@
package android.hardware.weaver;
+import android.hardware.weaver.WeaverReadStatus;
+
@VintfStability
parcelable WeaverReadResponse {
/**
- * The time to wait, in milliseconds, before making the next request.
+ * The time to wait, in milliseconds, before making the next request,
+ * must be greater than or equal to zero and less than INT_MAX.
*/
long timeout;
/**
* The value read from the slot or empty if the value was not read.
*/
byte[] value;
+ /**
+ * Status from WeaverReadStatus
+ */
+ WeaverReadStatus status = WeaverReadStatus.FAILED;
}
diff --git a/weaver/aidl/android/hardware/weaver/WeaverReadStatus.aidl b/weaver/aidl/android/hardware/weaver/WeaverReadStatus.aidl
new file mode 100644
index 0000000..36e731f
--- /dev/null
+++ b/weaver/aidl/android/hardware/weaver/WeaverReadStatus.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.weaver;
+
+@VintfStability
+@Backing(type="int")
+enum WeaverReadStatus {
+ OK,
+ FAILED,
+ INCORRECT_KEY,
+ THROTTLE,
+}
diff --git a/weaver/aidl/default/Android.bp b/weaver/aidl/default/Android.bp
index 70d9171..494cb1b 100644
--- a/weaver/aidl/default/Android.bp
+++ b/weaver/aidl/default/Android.bp
@@ -34,7 +34,7 @@
"Weaver.cpp",
],
shared_libs: [
- "android.hardware.weaver-V1-ndk",
+ "android.hardware.weaver-V2-ndk",
"libbase",
"libbinder_ndk",
],
diff --git a/weaver/aidl/default/Weaver.cpp b/weaver/aidl/default/Weaver.cpp
index 6b77924..c9ffe85 100644
--- a/weaver/aidl/default/Weaver.cpp
+++ b/weaver/aidl/default/Weaver.cpp
@@ -37,18 +37,19 @@
}
::ndk::ScopedAStatus Weaver::read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* out_response) {
+ using ::aidl::android::hardware::weaver::WeaverReadStatus;
if (in_slotId > 15 || in_key.size() > 16) {
- *out_response = {0, {}};
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(Weaver::STATUS_FAILED));
+ *out_response = {0, {}, WeaverReadStatus::FAILED};
+ return ndk::ScopedAStatus::ok();
}
if (slot_array[in_slotId].key != in_key) {
- *out_response = {0, {}};
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(Weaver::STATUS_INCORRECT_KEY));
+ *out_response = {0, {}, WeaverReadStatus::INCORRECT_KEY};
+ return ndk::ScopedAStatus::ok();
}
- *out_response = {0, slot_array[in_slotId].value};
+ *out_response = {0, slot_array[in_slotId].value, WeaverReadStatus::OK};
return ::ndk::ScopedAStatus::ok();
}
diff --git a/weaver/aidl/default/android.hardware.weaver-service.example.xml b/weaver/aidl/default/android.hardware.weaver-service.example.xml
index ed291cd..bfe4396 100644
--- a/weaver/aidl/default/android.hardware.weaver-service.example.xml
+++ b/weaver/aidl/default/android.hardware.weaver-service.example.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.weaver</name>
- <version>1</version>
+ <version>2</version>
<interface>
<name>IWeaver</name>
<instance>default</instance>
diff --git a/weaver/aidl/vts/OWNERS b/weaver/aidl/vts/OWNERS
deleted file mode 100644
index 40d95e4..0000000
--- a/weaver/aidl/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chengyouho@google.com
-frankwoo@google.com
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
deleted file mode 100644
index 878c762..0000000
--- a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2020 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 <aidl/Gtest.h>
-#include <aidl/Vintf.h>
-
-#include <aidl/android/hardware/weaver/IWeaver.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-#include <limits>
-
-using ::aidl::android::hardware::weaver::IWeaver;
-using ::aidl::android::hardware::weaver::WeaverConfig;
-using ::aidl::android::hardware::weaver::WeaverReadResponse;
-
-using ::ndk::SpAIBinder;
-
-const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
-const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
-
-struct WeaverAidlTest : public ::testing::TestWithParam<std::string> {
- virtual void SetUp() override {
- weaver = IWeaver::fromBinder(
- SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
- ASSERT_NE(weaver, nullptr);
- }
-
- virtual void TearDown() override {}
-
- std::shared_ptr<IWeaver> weaver;
-};
-
-/*
- * Checks config values are suitably large
- */
-TEST_P(WeaverAidlTest, GetConfig) {
- WeaverConfig config;
-
- auto ret = weaver->getConfig(&config);
-
- ASSERT_TRUE(ret.isOk());
-
- EXPECT_GE(config.slots, 16u);
- EXPECT_GE(config.keySize, 16u);
- EXPECT_GE(config.valueSize, 16u);
-}
-
-/*
- * Gets the config twice and checks they are the same
- */
-TEST_P(WeaverAidlTest, GettingConfigMultipleTimesGivesSameResult) {
- WeaverConfig config1;
- WeaverConfig config2;
-
- auto ret = weaver->getConfig(&config1);
- ASSERT_TRUE(ret.isOk());
-
- ret = weaver->getConfig(&config2);
- ASSERT_TRUE(ret.isOk());
-
- EXPECT_EQ(config1, config2);
-}
-
-/*
- * Gets the number of slots from the config and writes a key and value to the last one
- */
-TEST_P(WeaverAidlTest, WriteToLastSlot) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
-
- ASSERT_TRUE(configRet.isOk());
-
- const uint32_t lastSlot = config.slots - 1;
- const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with the same key and receives the value that was previously written
- */
-TEST_P(WeaverAidlTest, WriteFollowedByReadGivesTheSameValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_EQ(readValue, VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Overwrites the slot with a new key and value
- * Reads the slot with the new key and receives the new value
- */
-TEST_P(WeaverAidlTest, OverwritingSlotUpdatesTheValue) {
- constexpr uint32_t slotId = 0;
- const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
- ASSERT_TRUE(initialWriteRet.isOk());
-
- const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
- ASSERT_TRUE(overwriteRet.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_EQ(readValue, OTHER_VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with a different key so does not receive the value
- */
-TEST_P(WeaverAidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- const auto readRet =
- weaver->read(slotId, WRONG_KEY, &response);
-
- readValue = response.value;
-
- ASSERT_FALSE(readRet.isOk());
- ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
- ASSERT_EQ(IWeaver::STATUS_INCORRECT_KEY, readRet.getServiceSpecificError());
- EXPECT_TRUE(readValue.empty());
-}
-
-/*
- * Writing to an invalid slot fails
- */
-TEST_P(WeaverAidlTest, WritingToInvalidSlotFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- const auto writeRet = weaver->write(config.slots, KEY, VALUE);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Reading from an invalid slot fails rather than incorrect key
- */
-TEST_P(WeaverAidlTest, ReadingFromInvalidSlotFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(config.slots, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
-
- ASSERT_FALSE(readRet.isOk());
- ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
- ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writing a key that is too large fails
- */
-TEST_P(WeaverAidlTest, WriteWithTooLargeKeyFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, bigKey, VALUE);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Writing a value that is too large fails
- */
-TEST_P(WeaverAidlTest, WriteWithTooLargeValueFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigValue(config.valueSize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, KEY, bigValue);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Reading with a key that is loo large fails
- */
-TEST_P(WeaverAidlTest, ReadWithTooLargeKeyFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(slotId, bigKey, &response);
-
- readValue = response.value;
- timeout = response.timeout;
-
- ASSERT_FALSE(readRet.isOk());
- ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
- ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverAidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, WeaverAidlTest,
- testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor)),
- android::PrintInstanceNameToString);
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- ABinderProcess_setThreadPoolMaxThreadCount(1);
- ABinderProcess_startThreadPool();
- return RUN_ALL_TESTS();
-}
diff --git a/weaver/aidl/vts/Android.bp b/weaver/vts/Android.bp
similarity index 92%
rename from weaver/aidl/vts/Android.bp
rename to weaver/vts/Android.bp
index cf1661c..ee03b28 100644
--- a/weaver/aidl/vts/Android.bp
+++ b/weaver/vts/Android.bp
@@ -34,7 +34,10 @@
"libbinder_ndk",
"libbase",
],
- static_libs: ["android.hardware.weaver-V1-ndk"],
+ static_libs: [
+ "android.hardware.weaver-V2-ndk",
+ "android.hardware.weaver@1.0",
+ ],
test_suites: [
"general-tests",
"vts",
diff --git a/weaver/vts/VtsHalWeaverTargetTest.cpp b/weaver/vts/VtsHalWeaverTargetTest.cpp
new file mode 100644
index 0000000..754d467
--- /dev/null
+++ b/weaver/vts/VtsHalWeaverTargetTest.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2020 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/weaver/IWeaver.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <limits>
+
+using ::aidl::android::hardware::weaver::IWeaver;
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+using ::aidl::android::hardware::weaver::WeaverReadStatus;
+
+using HidlIWeaver = ::android::hardware::weaver::V1_0::IWeaver;
+using HidlWeaverConfig = ::android::hardware::weaver::V1_0::WeaverConfig;
+using HidlWeaverReadStatus = ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using HidlWeaverReadResponse = ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using HidlWeaverStatus = ::android::hardware::weaver::V1_0::WeaverStatus;
+
+const std::string kSlotMapFile = "/metadata/password_slots/slot_map";
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
+
+class WeaverAdapter {
+ public:
+ virtual ~WeaverAdapter() {}
+ virtual bool isReady() = 0;
+ virtual ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) = 0;
+ virtual ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) = 0;
+ virtual ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) = 0;
+};
+
+class WeaverAidlAdapter : public WeaverAdapter {
+ public:
+ WeaverAidlAdapter(const std::string& param)
+ : aidl_weaver_(IWeaver::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_waitForService(param.c_str())))) {}
+ ~WeaverAidlAdapter() {}
+
+ bool isReady() { return aidl_weaver_ != nullptr; }
+
+ ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) {
+ return aidl_weaver_->getConfig(_aidl_return);
+ }
+
+ ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) {
+ return aidl_weaver_->read(in_slotId, in_key, _aidl_return);
+ }
+
+ ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) {
+ return aidl_weaver_->write(in_slotId, in_key, in_value);
+ }
+
+ private:
+ std::shared_ptr<IWeaver> aidl_weaver_;
+};
+
+class WeaverHidlAdapter : public WeaverAdapter {
+ public:
+ WeaverHidlAdapter(const std::string& param) : hidl_weaver_(HidlIWeaver::getService(param)) {}
+ ~WeaverHidlAdapter() {}
+
+ bool isReady() { return hidl_weaver_ != nullptr; }
+
+ ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) {
+ bool callbackCalled = false;
+ HidlWeaverStatus status;
+ HidlWeaverConfig config;
+ auto ret = hidl_weaver_->getConfig([&](HidlWeaverStatus s, HidlWeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ config = c;
+ });
+ if (!ret.isOk() || !callbackCalled || status != HidlWeaverStatus::OK) {
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ _aidl_return->slots = config.slots;
+ _aidl_return->keySize = config.keySize;
+ _aidl_return->valueSize = config.valueSize;
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) {
+ bool callbackCalled = false;
+ HidlWeaverReadStatus status;
+ std::vector<uint8_t> value;
+ uint32_t timeout;
+ auto ret = hidl_weaver_->read(in_slotId, in_key,
+ [&](HidlWeaverReadStatus s, HidlWeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ value = r.value;
+ timeout = r.timeout;
+ });
+ if (!ret.isOk() || !callbackCalled) {
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ switch (status) {
+ case HidlWeaverReadStatus::OK:
+ _aidl_return->status = WeaverReadStatus::OK;
+ break;
+ case HidlWeaverReadStatus::FAILED:
+ _aidl_return->status = WeaverReadStatus::FAILED;
+ break;
+ case HidlWeaverReadStatus::INCORRECT_KEY:
+ _aidl_return->status = WeaverReadStatus::INCORRECT_KEY;
+ break;
+ case HidlWeaverReadStatus::THROTTLE:
+ _aidl_return->status = WeaverReadStatus::THROTTLE;
+ break;
+ default:
+ ADD_FAILURE() << "Unknown HIDL read status: " << static_cast<uint32_t>(status);
+ _aidl_return->status = WeaverReadStatus::FAILED;
+ break;
+ }
+ _aidl_return->value = value;
+ _aidl_return->timeout = timeout;
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) {
+ auto status = hidl_weaver_->write(in_slotId, in_key, in_value);
+ switch (status) {
+ case HidlWeaverStatus::OK:
+ return ::ndk::ScopedAStatus::ok();
+ case HidlWeaverStatus::FAILED:
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ default:
+ ADD_FAILURE() << "Unknown HIDL write status: " << status.description();
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ }
+
+ private:
+ android::sp<HidlIWeaver> hidl_weaver_;
+};
+
+class WeaverTest : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
+ protected:
+ void SetUp() override;
+ void TearDown() override {}
+ void FindFreeSlots();
+
+ std::unique_ptr<WeaverAdapter> weaver_;
+ WeaverConfig config_;
+ uint32_t first_free_slot_;
+ uint32_t last_free_slot_;
+};
+
+void WeaverTest::SetUp() {
+ std::string api, instance_name;
+ std::tie(api, instance_name) = GetParam();
+ if (api == "hidl") {
+ weaver_.reset(new WeaverHidlAdapter(instance_name));
+ } else if (api == "aidl") {
+ weaver_.reset(new WeaverAidlAdapter(instance_name));
+ } else {
+ FAIL() << "Bad test parameterization";
+ }
+ ASSERT_TRUE(weaver_->isReady());
+
+ auto ret = weaver_->getConfig(&config_);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_GT(config_.slots, 0);
+ GTEST_LOG_(INFO) << "WeaverConfig: slots=" << config_.slots << ", keySize=" << config_.keySize
+ << ", valueSize=" << config_.valueSize;
+
+ FindFreeSlots();
+ GTEST_LOG_(INFO) << "First free slot is " << first_free_slot_ << ", last free slot is "
+ << last_free_slot_;
+}
+
+void WeaverTest::FindFreeSlots() {
+ // Determine which Weaver slots are in use by the system. These slots can't be used by the test.
+ std::set<uint32_t> used_slots;
+ if (access(kSlotMapFile.c_str(), F_OK) == 0) {
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(kSlotMapFile, &contents))
+ << "Failed to read " << kSlotMapFile;
+ for (const auto& line : android::base::Split(contents, "\n")) {
+ auto trimmed_line = android::base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line[0] == '\0') continue;
+ auto slot_and_user = android::base::Split(trimmed_line, "=");
+ uint32_t slot;
+ ASSERT_TRUE(slot_and_user.size() == 2 &&
+ android::base::ParseUint(slot_and_user[0], &slot))
+ << "Error parsing " << kSlotMapFile << " at \"" << line << "\"";
+ GTEST_LOG_(INFO) << "Slot " << slot << " is in use by " << slot_and_user[1];
+ ASSERT_LT(slot, config_.slots);
+ used_slots.insert(slot);
+ }
+ }
+ // Starting in Android 14, the system will always use at least one Weaver slot if Weaver is
+ // supported at all. Make sure we saw at least one.
+ // TODO: uncomment after Android 14 is merged into AOSP
+ // ASSERT_FALSE(used_slots.empty())
+ //<< "Could not determine which Weaver slots are in use by the system";
+
+ // Find the first free slot.
+ int found = 0;
+ for (uint32_t i = 0; i < config_.slots; i++) {
+ if (used_slots.find(i) == used_slots.end()) {
+ first_free_slot_ = i;
+ found++;
+ break;
+ }
+ }
+ // Find the last free slot.
+ for (uint32_t i = config_.slots; i > 0; i--) {
+ if (used_slots.find(i - 1) == used_slots.end()) {
+ last_free_slot_ = i - 1;
+ found++;
+ break;
+ }
+ }
+ ASSERT_EQ(found, 2) << "All Weaver slots are already in use by the system";
+}
+
+/*
+ * Checks config values are suitably large
+ */
+TEST_P(WeaverTest, GetConfig) {
+ EXPECT_GE(config_.slots, 16u);
+ EXPECT_GE(config_.keySize, 16u);
+ EXPECT_GE(config_.valueSize, 16u);
+}
+
+/*
+ * Gets the config twice and checks they are the same
+ */
+TEST_P(WeaverTest, GettingConfigMultipleTimesGivesSameResult) {
+ WeaverConfig config2;
+
+ auto ret = weaver_->getConfig(&config2);
+ ASSERT_TRUE(ret.isOk());
+
+ EXPECT_EQ(config_, config2);
+}
+
+/*
+ * Writes a key and value to the last free slot
+ */
+TEST_P(WeaverTest, WriteToLastSlot) {
+ const auto writeRet = weaver_->write(last_free_slot_, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with the same key and receives the value that was previously written
+ */
+TEST_P(WeaverTest, WriteFollowedByReadGivesTheSameValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto ret = weaver_->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(response.value, VALUE);
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::OK);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Overwrites the slot with a new key and value
+ * Reads the slot with the new key and receives the new value
+ */
+TEST_P(WeaverTest, OverwritingSlotUpdatesTheValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto initialWriteRet = weaver_->write(slotId, WRONG_KEY, VALUE);
+ ASSERT_TRUE(initialWriteRet.isOk());
+
+ const auto overwriteRet = weaver_->write(slotId, KEY, OTHER_VALUE);
+ ASSERT_TRUE(overwriteRet.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(response.value, OTHER_VALUE);
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::OK);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with a different key so does not receive the value
+ */
+TEST_P(WeaverTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto writeRet = weaver_->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, WRONG_KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.status, WeaverReadStatus::INCORRECT_KEY);
+}
+
+/*
+ * Writing to an invalid slot fails
+ */
+TEST_P(WeaverTest, WritingToInvalidSlotFails) {
+ if (config_.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ const auto writeRet = weaver_->write(config_.slots, KEY, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading from an invalid slot fails rather than incorrect key
+ */
+TEST_P(WeaverTest, ReadingFromInvalidSlotFails) {
+ if (config_.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(config_.slots, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::FAILED);
+}
+
+/*
+ * Writing a key that is too large fails
+ */
+TEST_P(WeaverTest, WriteWithTooLargeKeyFails) {
+ std::vector<uint8_t> bigKey(config_.keySize + 1);
+
+ const auto writeRet = weaver_->write(first_free_slot_, bigKey, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Writing a value that is too large fails
+ */
+TEST_P(WeaverTest, WriteWithTooLargeValueFails) {
+ std::vector<uint8_t> bigValue(config_.valueSize + 1);
+
+ const auto writeRet = weaver_->write(first_free_slot_, KEY, bigValue);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading with a key that is too large fails
+ */
+TEST_P(WeaverTest, ReadWithTooLargeKeyFails) {
+ std::vector<uint8_t> bigKey(config_.keySize + 1);
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(first_free_slot_, bigKey, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::FAILED);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverTest);
+
+// Instantiate the test for each HIDL Weaver service.
+INSTANTIATE_TEST_SUITE_P(
+ PerHidlInstance, WeaverTest,
+ testing::Combine(testing::Values("hidl"),
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ HidlIWeaver::descriptor))),
+ [](const testing::TestParamInfo<std::tuple<std::string, std::string>>& info) {
+ return android::hardware::PrintInstanceNameToString(
+ testing::TestParamInfo<std::string>{std::get<1>(info.param), info.index});
+ });
+
+// Instantiate the test for each AIDL Weaver service.
+INSTANTIATE_TEST_SUITE_P(
+ PerAidlInstance, WeaverTest,
+ testing::Combine(testing::Values("aidl"),
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor))),
+ [](const testing::TestParamInfo<std::tuple<std::string, std::string>>& info) {
+ // This name_generator makes the instance name be included in the test case names, e.g.
+ // "PerAidlInstance/WeaverTest#GetConfig/0_android_hardware_weaver_IWeaver_default"
+ // instead of "PerAidlInstance/WeaverTest#GetConfig/0".
+ return android::PrintInstanceNameToString(
+ testing::TestParamInfo<std::string>{std::get<1>(info.param), info.index});
+ });
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}