/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define LOG_TAG "CoreAudioHalAidlTest" #include #include #include #include #include #include #include #include #include #include namespace { using ::aidl::android::hardware::audio::core::AudioPatch; using ::aidl::android::hardware::audio::core::AudioRoute; using ::aidl::android::hardware::audio::core::VendorParameter; using ::aidl::android::media::audio::common::AudioChannelLayout; using ::aidl::android::media::audio::common::AudioConfig; using ::aidl::android::media::audio::common::AudioDevice; using ::aidl::android::media::audio::common::AudioDeviceDescription; using ::aidl::android::media::audio::common::AudioDeviceType; using ::aidl::android::media::audio::common::AudioFormatDescription; using ::aidl::android::media::audio::common::AudioFormatType; using ::aidl::android::media::audio::common::AudioGainConfig; using ::aidl::android::media::audio::common::AudioGainMode; using ::aidl::android::media::audio::common::AudioIoFlags; using ::aidl::android::media::audio::common::AudioPort; using ::aidl::android::media::audio::common::AudioPortConfig; using ::aidl::android::media::audio::common::AudioPortDeviceExt; using ::aidl::android::media::audio::common::AudioPortExt; using ::aidl::android::media::audio::common::AudioPortMixExt; using ::aidl::android::media::audio::common::AudioProfile; using ::aidl::android::media::audio::common::AudioSource; using ::aidl::android::media::audio::common::PcmType; class VendorParameterMock { public: const std::vector& getRetrievedParameterIds() const { return mGetParameterIds; } const std::vector& getAsyncParameters() const { return mAsyncParameters; } const std::vector& getSyncParameters() const { return mSyncParameters; } protected: ndk::ScopedAStatus getVendorParametersImpl(const std::vector& in_parameterIds) { mGetParameterIds.insert(mGetParameterIds.end(), in_parameterIds.begin(), in_parameterIds.end()); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setVendorParametersImpl(const std::vector& in_parameters, bool async) { if (async) { mAsyncParameters.insert(mAsyncParameters.end(), in_parameters.begin(), in_parameters.end()); } else { mSyncParameters.insert(mSyncParameters.end(), in_parameters.begin(), in_parameters.end()); } return ndk::ScopedAStatus::ok(); } private: std::vector mGetParameterIds; std::vector mAsyncParameters; std::vector mSyncParameters; }; struct Configuration { std::vector ports; std::vector portConfigs; std::vector routes; std::vector patches; int32_t nextPortId = 1; int32_t nextPatchId = 1; }; void fillProfile(AudioProfile* profile, const std::vector& channelLayouts, const std::vector& sampleRates) { for (auto layout : channelLayouts) { profile->channelMasks.push_back( AudioChannelLayout::make(layout)); } profile->sampleRates.insert(profile->sampleRates.end(), sampleRates.begin(), sampleRates.end()); } AudioProfile createProfile(PcmType pcmType, const std::vector& channelLayouts, const std::vector& sampleRates) { AudioProfile profile; profile.format.type = AudioFormatType::PCM; profile.format.pcm = pcmType; fillProfile(&profile, channelLayouts, sampleRates); return profile; } AudioPortExt createPortDeviceExt(AudioDeviceType devType, int32_t flags, std::string connection = "") { AudioPortDeviceExt deviceExt; deviceExt.device.type.type = devType; if (devType == AudioDeviceType::IN_MICROPHONE && connection.empty()) { deviceExt.device.address = "bottom"; } else if (devType == AudioDeviceType::IN_MICROPHONE_BACK && connection.empty()) { deviceExt.device.address = "back"; } deviceExt.device.type.connection = std::move(connection); deviceExt.flags = flags; return AudioPortExt::make(deviceExt); } AudioPortExt createPortMixExt(int32_t maxOpenStreamCount, int32_t maxActiveStreamCount) { AudioPortMixExt mixExt; mixExt.maxOpenStreamCount = maxOpenStreamCount; mixExt.maxActiveStreamCount = maxActiveStreamCount; return AudioPortExt::make(mixExt); } AudioPort createPort(int32_t id, const std::string& name, int32_t flags, bool isInput, const AudioPortExt& ext) { AudioPort port; port.id = id; port.name = name; port.flags = isInput ? AudioIoFlags::make(flags) : AudioIoFlags::make(flags); port.ext = ext; return port; } AudioRoute createRoute(const std::vector& sources, const AudioPort& sink) { AudioRoute route; route.sinkPortId = sink.id; std::transform(sources.begin(), sources.end(), std::back_inserter(route.sourcePortIds), [](const auto& port) { return port.id; }); return route; } template auto findById(std::vector& v, int32_t id) { return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; }); } Configuration getTestConfiguration() { const std::vector standardPcmAudioProfiles = { createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})}; Configuration c; AudioPort micInDevice = createPort(c.nextPortId++, "Built-In Mic", 0, true, createPortDeviceExt(AudioDeviceType::IN_MICROPHONE, 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); micInDevice.profiles = standardPcmAudioProfiles; c.ports.push_back(micInDevice); AudioPort micInBackDevice = createPort(c.nextPortId++, "Built-In Back Mic", 0, true, createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0)); micInDevice.profiles = standardPcmAudioProfiles; c.ports.push_back(micInBackDevice); AudioPort primaryInMix = createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 1)); primaryInMix.profiles = standardPcmAudioProfiles; c.ports.push_back(primaryInMix); AudioPort speakerOutDevice = createPort(c.nextPortId++, "Speaker", 0, false, createPortDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)); speakerOutDevice.profiles = standardPcmAudioProfiles; c.ports.push_back(speakerOutDevice); AudioPort btOutDevice = createPort(c.nextPortId++, "BT A2DP Out", 0, false, createPortDeviceExt(AudioDeviceType::OUT_DEVICE, 0, AudioDeviceDescription::CONNECTION_BT_A2DP)); btOutDevice.profiles = standardPcmAudioProfiles; c.ports.push_back(btOutDevice); AudioPort btOutMix = createPort(c.nextPortId++, "a2dp output", 0, false, createPortMixExt(1, 1)); btOutMix.profiles = standardPcmAudioProfiles; c.ports.push_back(btOutMix); c.routes.push_back(createRoute({micInDevice, micInBackDevice}, primaryInMix)); c.routes.push_back(createRoute({btOutMix}, btOutDevice)); return c; } class ModuleMock : public ::aidl::android::hardware::audio::core::BnModule, public VendorParameterMock { public: ModuleMock() = default; explicit ModuleMock(const Configuration& config) : mConfig(config) {} bool isScreenTurnedOn() const { return mIsScreenTurnedOn; } ScreenRotation getScreenRotation() const { return mScreenRotation; } std::vector getPatches() { std::vector result; getAudioPatches(&result); return result; } std::optional getPortConfig(int32_t id) { auto iter = findById(mConfig.portConfigs, id); if (iter != mConfig.portConfigs.end()) { return *iter; } return std::nullopt; } private: ndk::ScopedAStatus setModuleDebug( const ::aidl::android::hardware::audio::core::ModuleDebug&) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getTelephony( std::shared_ptr<::aidl::android::hardware::audio::core::ITelephony>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getBluetooth( std::shared_ptr<::aidl::android::hardware::audio::core::IBluetooth>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getBluetoothA2dp( std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothA2dp>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getBluetoothLe( std::shared_ptr<::aidl::android::hardware::audio::core::IBluetoothLe>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus connectExternalDevice( const ::aidl::android::media::audio::common::AudioPort& portIdAndData, ::aidl::android::media::audio::common::AudioPort* port) override { auto src = portIdAndData; // Make a copy to mimic RPC behavior. auto iter = findById(mConfig.ports, src.id); if (iter == mConfig.ports.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *port = *iter; port->ext = src.ext; port->id = mConfig.nextPortId++; ALOGD("%s: returning %s", __func__, port->toString().c_str()); mConfig.ports.push_back(*port); std::vector newRoutes; for (auto& r : mConfig.routes) { if (r.sinkPortId == src.id) { newRoutes.push_back(AudioRoute{.sourcePortIds = r.sourcePortIds, .sinkPortId = port->id, .isExclusive = r.isExclusive}); } else if (std::find(r.sourcePortIds.begin(), r.sourcePortIds.end(), src.id) != r.sourcePortIds.end()) { r.sourcePortIds.push_back(port->id); } } mConfig.routes.insert(mConfig.routes.end(), newRoutes.begin(), newRoutes.end()); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus disconnectExternalDevice(int32_t portId) override { auto iter = findById(mConfig.ports, portId); if (iter == mConfig.ports.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } mConfig.ports.erase(iter); for (auto it = mConfig.routes.begin(); it != mConfig.routes.end();) { if (it->sinkPortId == portId) { it = mConfig.routes.erase(it); } else { if (auto srcIt = std::find(it->sourcePortIds.begin(), it->sourcePortIds.end(), portId); srcIt != it->sourcePortIds.end()) { it->sourcePortIds.erase(srcIt); } ++it; } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioPatches( std::vector<::aidl::android::hardware::audio::core::AudioPatch>* patches) override { *patches = mConfig.patches; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioPort( int32_t portId, ::aidl::android::media::audio::common::AudioPort* port) override { auto iter = findById(mConfig.ports, portId); if (iter == mConfig.ports.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *port = *iter; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioPortConfigs( std::vector<::aidl::android::media::audio::common::AudioPortConfig>* configs) override { *configs = mConfig.portConfigs; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioPorts( std::vector<::aidl::android::media::audio::common::AudioPort>* ports) override { *ports = mConfig.ports; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioRoutes( std::vector<::aidl::android::hardware::audio::core::AudioRoute>* routes) override { *routes = mConfig.routes; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAudioRoutesForAudioPort( int32_t portId, std::vector<::aidl::android::hardware::audio::core::AudioRoute>* routes) override { for (auto& r : mConfig.routes) { const auto& srcs = r.sourcePortIds; if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) { routes->push_back(r); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus openInputStream(const OpenInputStreamArguments&, OpenInputStreamReturn*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus openOutputStream(const OpenOutputStreamArguments&, OpenOutputStreamReturn*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getSupportedPlaybackRateFactors(SupportedPlaybackRateFactors*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setAudioPatch( const ::aidl::android::hardware::audio::core::AudioPatch& requested, ::aidl::android::hardware::audio::core::AudioPatch* patch) override { if (requested.id == 0) { *patch = requested; patch->id = mConfig.nextPatchId++; mConfig.patches.push_back(*patch); ALOGD("%s: returning %s", __func__, patch->toString().c_str()); } else { auto iter = findById(mConfig.patches, requested.id); if (iter == mConfig.patches.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *iter = *patch = requested; ALOGD("%s: updated %s", __func__, patch->toString().c_str()); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setAudioPortConfig( const ::aidl::android::media::audio::common::AudioPortConfig& requested, ::aidl::android::media::audio::common::AudioPortConfig* config, bool* applied) override { *applied = false; auto src = requested; // Make a copy to mimic RPC behavior. if (src.id == 0) { *config = src; if (config->ext.getTag() == AudioPortExt::unspecified) { auto iter = findById(mConfig.ports, src.portId); if (iter == mConfig.ports.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } config->ext = iter->ext; } config->id = mConfig.nextPortId++; mConfig.portConfigs.push_back(*config); ALOGD("%s: returning %s", __func__, config->toString().c_str()); } else { auto iter = findById(mConfig.portConfigs, src.id); if (iter == mConfig.portConfigs.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } *iter = *config = src; ALOGD("%s: updated %s", __func__, config->toString().c_str()); } *applied = true; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus resetAudioPatch(int32_t patchId) override { auto iter = findById(mConfig.patches, patchId); if (iter == mConfig.patches.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } mConfig.patches.erase(iter); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus resetAudioPortConfig(int32_t portConfigId) override { auto iter = findById(mConfig.portConfigs, portConfigId); if (iter == mConfig.portConfigs.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } mConfig.portConfigs.erase(iter); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getMasterMute(bool*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setMasterMute(bool) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getMasterVolume(float*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setMasterVolume(float) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getMicMute(bool*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setMicMute(bool) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getMicrophones( std::vector<::aidl::android::media::audio::common::MicrophoneInfo>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus updateAudioMode(::aidl::android::media::audio::common::AudioMode) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus updateScreenRotation(ScreenRotation in_rotation) override { mScreenRotation = in_rotation; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus updateScreenState(bool in_isTurnedOn) override { mIsScreenTurnedOn = in_isTurnedOn; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getSoundDose( std::shared_ptr<::aidl::android::hardware::audio::core::sounddose::ISoundDose>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus generateHwAvSyncId(int32_t*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getVendorParameters(const std::vector& in_parameterIds, std::vector*) override { return getVendorParametersImpl(in_parameterIds); } ndk::ScopedAStatus setVendorParameters(const std::vector& in_parameters, bool async) override { return setVendorParametersImpl(in_parameters, async); } ndk::ScopedAStatus addDeviceEffect( int32_t, const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus removeDeviceEffect( int32_t, const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getMmapPolicyInfos( ::aidl::android::media::audio::common::AudioMMapPolicyType, std::vector<::aidl::android::media::audio::common::AudioMMapPolicyInfo>*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus supportsVariableLatency(bool*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t) override { return ndk::ScopedAStatus::ok(); } Configuration mConfig; bool mIsScreenTurnedOn = false; ScreenRotation mScreenRotation = ScreenRotation::DEG_0; }; class StreamCommonMock : public ::aidl::android::hardware::audio::core::BnStreamCommon, public VendorParameterMock { ndk::ScopedAStatus close() override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus prepareToClose() override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus updateHwAvSyncId(int32_t) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus getVendorParameters(const std::vector& in_parameterIds, std::vector*) override { return getVendorParametersImpl(in_parameterIds); } ndk::ScopedAStatus setVendorParameters(const std::vector& in_parameters, bool async) override { return setVendorParametersImpl(in_parameters, async); } ndk::ScopedAStatus addEffect( const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus removeEffect( const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&) override { return ndk::ScopedAStatus::ok(); } }; VendorParameter makeVendorParameter(const std::string& id, int value) { VendorParameter result{.id = id}; // Note: in real life, a parcelable type defined by vendor must be used, // here we use Int just for test purposes. ::aidl::android::media::audio::common::Int vendorValue{.value = value}; result.ext.setParcelable(std::move(vendorValue)); return result; } android::status_t parseVendorParameter(const VendorParameter& param, int* value) { std::optional<::aidl::android::media::audio::common::Int> vendorValue; RETURN_STATUS_IF_ERROR(param.ext.getParcelable(&vendorValue)); if (!vendorValue.has_value()) return android::BAD_VALUE; *value = vendorValue.value().value; return android::OK; } class TestHalAdapterVendorExtension : public ::aidl::android::media::audio::BnHalAdapterVendorExtension { public: static const std::string kLegacyParameterKey; static const std::string kLegacyAsyncParameterKey; static const std::string kModuleVendorParameterId; static const std::string kStreamVendorParameterId; private: ndk::ScopedAStatus parseVendorParameterIds(ParameterScope in_scope, const std::string& in_rawKeys, std::vector* _aidl_return) override { android::AudioParameter keys(android::String8(in_rawKeys.c_str())); for (size_t i = 0; i < keys.size(); ++i) { android::String8 key; if (android::status_t status = keys.getAt(i, key); status != android::OK) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } switch (in_scope) { case ParameterScope::MODULE: if (key == android::String8(kLegacyParameterKey.c_str()) || key == android::String8(kLegacyAsyncParameterKey.c_str())) { _aidl_return->push_back(kModuleVendorParameterId); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } break; case ParameterScope::STREAM: if (key == android::String8(kLegacyParameterKey.c_str()) || key == android::String8(kLegacyAsyncParameterKey.c_str())) { _aidl_return->push_back(kStreamVendorParameterId); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } break; } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus parseVendorParameters( ParameterScope in_scope, const std::string& in_rawKeysAndValues, std::vector* out_syncParameters, std::vector* out_asyncParameters) override { android::AudioParameter legacy(android::String8(in_rawKeysAndValues.c_str())); for (size_t i = 0; i < legacy.size(); ++i) { android::String8 key; if (android::status_t status = legacy.getAt(i, key); status != android::OK) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } int value; if (android::status_t status = legacy.getInt(key, value); status != android::OK) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } std::string parameterId; switch (in_scope) { case ParameterScope::MODULE: parameterId = kModuleVendorParameterId; break; case ParameterScope::STREAM: parameterId = kStreamVendorParameterId; break; } if (key == android::String8(kLegacyParameterKey.c_str())) { out_syncParameters->push_back(makeVendorParameter(parameterId, value)); } else if (key == android::String8(kLegacyAsyncParameterKey.c_str())) { out_asyncParameters->push_back(makeVendorParameter(parameterId, value)); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus parseBluetoothA2dpReconfigureOffload( const std::string&, std::vector*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus parseBluetoothLeReconfigureOffload(const std::string&, std::vector*) override { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus processVendorParameters(ParameterScope in_scope, const std::vector& in_parameters, std::string* _aidl_return) override { android::AudioParameter legacy; for (const auto& vendorParam : in_parameters) { if ((in_scope == ParameterScope::MODULE && vendorParam.id == kModuleVendorParameterId) || (in_scope == ParameterScope::STREAM && vendorParam.id == kStreamVendorParameterId)) { int value; if (android::status_t status = parseVendorParameter(vendorParam, &value); status != android::OK) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } legacy.addInt(android::String8(kLegacyParameterKey.c_str()), value); } } *_aidl_return = legacy.toString().c_str(); return ndk::ScopedAStatus::ok(); } }; const std::string TestHalAdapterVendorExtension::kLegacyParameterKey = "aosp_test_param"; const std::string TestHalAdapterVendorExtension::kLegacyAsyncParameterKey = "aosp_test_param_async"; // Note: in real life, there is no need to explicitly separate "module" and "stream" // parameters, here it's done just for test purposes. const std::string TestHalAdapterVendorExtension::kModuleVendorParameterId = "aosp.test.module.parameter"; const std::string TestHalAdapterVendorExtension::kStreamVendorParameterId = "aosp.test.stream.parameter"; android::String8 createParameterString(const std::string& key, const std::string& value) { android::AudioParameter params; params.add(android::String8(key.c_str()), android::String8(value.c_str())); return params.toString(); } android::String8 createParameterString(const std::string& key, int value) { android::AudioParameter params; params.addInt(android::String8(key.c_str()), value); return params.toString(); } template struct mf_traits {}; template struct mf_traits { using member_type = U; }; } // namespace // Provide value printers for types generated from AIDL // They need to be in the same namespace as the types we intend to print namespace aidl::android::hardware::audio::core { template std::enable_if_t::member_type>, std::ostream&> operator<<(std::ostream& os, const P& p) { return os << p.toString(); } template std::enable_if_t, std::ostream&> operator<<(std::ostream& os, const E& e) { return os << toString(e); } } // namespace aidl::android::hardware::audio::core namespace aidl::android::media::audio::common { template std::enable_if_t::member_type>, std::ostream&> operator<<(std::ostream& os, const P& p) { return os << p.toString(); } template std::enable_if_t, std::ostream&> operator<<(std::ostream& os, const E& e) { return os << toString(e); } } // namespace aidl::android::media::audio::common using namespace android; namespace { class StreamHalMock : public virtual StreamHalInterface { public: StreamHalMock() = default; ~StreamHalMock() override = default; status_t getBufferSize(size_t*) override { return OK; } status_t getAudioProperties(audio_config_base_t*) override { return OK; } status_t setParameters(const String8&) override { return OK; } status_t getParameters(const String8&, String8*) override { return OK; } status_t getFrameSize(size_t*) override { return OK; } status_t addEffect(sp) override { return OK; } status_t removeEffect(sp) override { return OK; } status_t standby() override { return OK; } status_t dump(int, const Vector&) override { return OK; } status_t start() override { return OK; } status_t stop() override { return OK; } status_t createMmapBuffer(int32_t, struct audio_mmap_buffer_info*) override { return OK; } status_t getMmapPosition(struct audio_mmap_position*) override { return OK; } status_t setHalThreadPriority(int) override { return OK; } status_t legacyCreateAudioPatch(const struct audio_port_config&, std::optional, audio_devices_t) override { return OK; } status_t legacyReleaseAudioPatch() override { return OK; } }; } // namespace class DeviceHalAidlTest : public testing::Test { public: void SetUp() override { mModule = ndk::SharedRefBase::make(); mDevice = sp::make("test", mModule, nullptr /*vext*/); } void TearDown() override { mDevice.clear(); mModule.reset(); } protected: std::shared_ptr mModule; sp mDevice; }; TEST_F(DeviceHalAidlTest, ScreenState) { EXPECT_FALSE(mModule->isScreenTurnedOn()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString(AudioParameter::keyScreenState, AudioParameter::valueOn))); EXPECT_TRUE(mModule->isScreenTurnedOn()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString(AudioParameter::keyScreenState, AudioParameter::valueOff))); EXPECT_FALSE(mModule->isScreenTurnedOn()); // The adaptation layer only logs a warning. EXPECT_EQ(OK, mDevice->setParameters( createParameterString(AudioParameter::keyScreenState, "blah"))); EXPECT_FALSE(mModule->isScreenTurnedOn()); } TEST_F(DeviceHalAidlTest, ScreenRotation) { using ScreenRotation = ::aidl::android::hardware::audio::core::IModule::ScreenRotation; EXPECT_EQ(ScreenRotation::DEG_0, mModule->getScreenRotation()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString(AudioParameter::keyScreenRotation, 90))); EXPECT_EQ(ScreenRotation::DEG_90, mModule->getScreenRotation()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString(AudioParameter::keyScreenRotation, 0))); EXPECT_EQ(ScreenRotation::DEG_0, mModule->getScreenRotation()); // The adaptation layer only logs a warning. EXPECT_EQ(OK, mDevice->setParameters(createParameterString(AudioParameter::keyScreenRotation, 42))); EXPECT_EQ(ScreenRotation::DEG_0, mModule->getScreenRotation()); } class DeviceHalAidlVendorParametersTest : public testing::Test { public: void SetUp() override { mModule = ndk::SharedRefBase::make(); mVendorExt = ndk::SharedRefBase::make(); mDevice = sp::make("test", mModule, mVendorExt); } void TearDown() override { mDevice.clear(); mVendorExt.reset(); mModule.reset(); } protected: std::shared_ptr mModule; std::shared_ptr mVendorExt; sp mDevice; }; TEST_F(DeviceHalAidlVendorParametersTest, GetVendorParameter) { EXPECT_EQ(0UL, mModule->getRetrievedParameterIds().size()); String8 values; EXPECT_EQ(OK, mDevice->getParameters( String8(TestHalAdapterVendorExtension::kLegacyParameterKey.c_str()), &values)); EXPECT_EQ(1UL, mModule->getRetrievedParameterIds().size()); if (mModule->getRetrievedParameterIds().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kModuleVendorParameterId, mModule->getRetrievedParameterIds()[0]); } } TEST_F(DeviceHalAidlVendorParametersTest, SetVendorParameter) { EXPECT_EQ(0UL, mModule->getAsyncParameters().size()); EXPECT_EQ(0UL, mModule->getSyncParameters().size()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString( TestHalAdapterVendorExtension::kLegacyParameterKey, 42))); EXPECT_EQ(0UL, mModule->getAsyncParameters().size()); EXPECT_EQ(1UL, mModule->getSyncParameters().size()); EXPECT_EQ(OK, mDevice->setParameters(createParameterString( TestHalAdapterVendorExtension::kLegacyAsyncParameterKey, 43))); EXPECT_EQ(1UL, mModule->getAsyncParameters().size()); EXPECT_EQ(1UL, mModule->getSyncParameters().size()); if (mModule->getSyncParameters().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kModuleVendorParameterId, mModule->getSyncParameters()[0].id); int value{}; EXPECT_EQ(android::OK, parseVendorParameter(mModule->getSyncParameters()[0], &value)); EXPECT_EQ(42, value); } if (mModule->getAsyncParameters().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kModuleVendorParameterId, mModule->getAsyncParameters()[0].id); int value{}; EXPECT_EQ(android::OK, parseVendorParameter(mModule->getAsyncParameters()[0], &value)); EXPECT_EQ(43, value); } } TEST_F(DeviceHalAidlVendorParametersTest, SetInvalidVendorParameters) { android::AudioParameter legacy; legacy.addInt(android::String8(TestHalAdapterVendorExtension::kLegacyParameterKey.c_str()), 42); legacy.addInt(android::String8(TestHalAdapterVendorExtension::kLegacyAsyncParameterKey.c_str()), 43); legacy.addInt(android::String8("random_name"), 44); EXPECT_EQ(0UL, mModule->getAsyncParameters().size()); EXPECT_EQ(0UL, mModule->getSyncParameters().size()); // TestHalAdapterVendorExtension throws an error for unknown parameters. EXPECT_EQ(android::BAD_VALUE, mDevice->setParameters(legacy.toString())); EXPECT_EQ(0UL, mModule->getAsyncParameters().size()); EXPECT_EQ(0UL, mModule->getSyncParameters().size()); } class StreamHalAidlVendorParametersTest : public testing::Test { public: void SetUp() override { mStreamCommon = ndk::SharedRefBase::make(); mVendorExt = ndk::SharedRefBase::make(); struct audio_config config = AUDIO_CONFIG_INITIALIZER; ::aidl::android::hardware::audio::core::StreamDescriptor descriptor; mStream = sp::make("test", false /*isInput*/, config, 0 /*nominalLatency*/, StreamContextAidl(descriptor, false /*isAsynchronous*/), mStreamCommon, mVendorExt); } void TearDown() override { mStream.clear(); mVendorExt.reset(); mStreamCommon.reset(); } protected: std::shared_ptr mStreamCommon; std::shared_ptr mVendorExt; sp mStream; }; TEST_F(StreamHalAidlVendorParametersTest, GetVendorParameter) { EXPECT_EQ(0UL, mStreamCommon->getRetrievedParameterIds().size()); String8 values; EXPECT_EQ(OK, mStream->getParameters( String8(TestHalAdapterVendorExtension::kLegacyParameterKey.c_str()), &values)); EXPECT_EQ(1UL, mStreamCommon->getRetrievedParameterIds().size()); if (mStreamCommon->getRetrievedParameterIds().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kStreamVendorParameterId, mStreamCommon->getRetrievedParameterIds()[0]); } } TEST_F(StreamHalAidlVendorParametersTest, SetVendorParameter) { EXPECT_EQ(0UL, mStreamCommon->getAsyncParameters().size()); EXPECT_EQ(0UL, mStreamCommon->getSyncParameters().size()); EXPECT_EQ(OK, mStream->setParameters(createParameterString( TestHalAdapterVendorExtension::kLegacyParameterKey, 42))); EXPECT_EQ(0UL, mStreamCommon->getAsyncParameters().size()); EXPECT_EQ(1UL, mStreamCommon->getSyncParameters().size()); EXPECT_EQ(OK, mStream->setParameters(createParameterString( TestHalAdapterVendorExtension::kLegacyAsyncParameterKey, 43))); EXPECT_EQ(1UL, mStreamCommon->getAsyncParameters().size()); EXPECT_EQ(1UL, mStreamCommon->getSyncParameters().size()); if (mStreamCommon->getSyncParameters().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kStreamVendorParameterId, mStreamCommon->getSyncParameters()[0].id); int value{}; EXPECT_EQ(android::OK, parseVendorParameter(mStreamCommon->getSyncParameters()[0], &value)); EXPECT_EQ(42, value); } if (mStreamCommon->getAsyncParameters().size() >= 1) { EXPECT_EQ(TestHalAdapterVendorExtension::kStreamVendorParameterId, mStreamCommon->getAsyncParameters()[0].id); int value{}; EXPECT_EQ(android::OK, parseVendorParameter(mStreamCommon->getAsyncParameters()[0], &value)); EXPECT_EQ(43, value); } } TEST_F(StreamHalAidlVendorParametersTest, SetInvalidVendorParameters) { android::AudioParameter legacy; legacy.addInt(android::String8(TestHalAdapterVendorExtension::kLegacyParameterKey.c_str()), 42); legacy.addInt(android::String8(TestHalAdapterVendorExtension::kLegacyAsyncParameterKey.c_str()), 43); legacy.addInt(android::String8("random_name"), 44); EXPECT_EQ(0UL, mStreamCommon->getAsyncParameters().size()); EXPECT_EQ(0UL, mStreamCommon->getSyncParameters().size()); // TestHalAdapterVendorExtension throws an error for unknown parameters. EXPECT_EQ(android::BAD_VALUE, mStream->setParameters(legacy.toString())); EXPECT_EQ(0UL, mStreamCommon->getAsyncParameters().size()); EXPECT_EQ(0UL, mStreamCommon->getSyncParameters().size()); } class Hal2AidlMapperTest : public testing::Test { public: void SetUp() override { mModule = ndk::SharedRefBase::make(getTestConfiguration()); mMapper = std::make_unique("test", mModule); ASSERT_EQ(OK, mMapper->initialize()); mConnectedPort.ext = createPortDeviceExt(AudioDeviceType::OUT_DEVICE, 0, AudioDeviceDescription::CONNECTION_BT_A2DP); mConnectedPort.ext.get().device.address = "00:11:22:33:44:55"; ASSERT_EQ(OK, mMapper->setDevicePortConnectedState(mConnectedPort, true /*connected*/)); std::mutex mutex; // Only needed for cleanups. auto mapperAccessor = std::make_unique>(*mMapper, mutex); Hal2AidlMapper::Cleanups cleanups(*mapperAccessor); AudioConfig config; config.base.channelMask = AudioChannelLayout::make( AudioChannelLayout::LAYOUT_STEREO); config.base.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT}; config.base.sampleRate = 48000; ASSERT_EQ(OK, mMapper->prepareToOpenStream( 42 /*ioHandle*/, mConnectedPort.ext.get().device, AudioIoFlags::make(0), AudioSource::DEFAULT, &cleanups, &config, &mMixPortConfig, &mPatch)); cleanups.disarmAll(); ASSERT_NE(0, mPatch.id); ASSERT_NE(0, mMixPortConfig.id); mStream = sp::make(); mMapper->addStream(mStream, mMixPortConfig.id, mPatch.id); ASSERT_EQ(OK, mMapper->findPortConfig(mConnectedPort.ext.get().device, &mDevicePortConfig)); ASSERT_EQ(1UL, mPatch.sourcePortConfigIds.size()); ASSERT_EQ(mMixPortConfig.id, mPatch.sourcePortConfigIds[0]); ASSERT_EQ(1UL, mPatch.sinkPortConfigIds.size()); ASSERT_EQ(mDevicePortConfig.id, mPatch.sinkPortConfigIds[0]); } void TearDown() override { mStream.clear(); mMapper.reset(); mModule.reset(); } protected: void CloseDisconnectImpl() { mStream.clear(); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); } void ConnectAnotherDevice() { mConnectedPort.ext.get().device.address = "00:11:22:33:44:66"; ASSERT_EQ(OK, mMapper->setDevicePortConnectedState(mConnectedPort, true /*connected*/)); } void CreateFwkPatch(int32_t* patchId) { std::mutex mutex; // Only needed for cleanups. auto mapperAccessor = std::make_unique>(*mMapper, mutex); Hal2AidlMapper::Cleanups cleanups(*mapperAccessor); ASSERT_EQ(OK, mMapper->createOrUpdatePatch({mMixPortConfig}, {mDevicePortConfig}, patchId, &cleanups)); cleanups.disarmAll(); } void DisconnectDevice() { ASSERT_EQ(OK, mMapper->prepareToDisconnectExternalDevice(mConnectedPort)); ASSERT_EQ(OK, mMapper->setDevicePortConnectedState(mConnectedPort, false /*connected*/)); } void ReleaseFwkOnlyPatch(int32_t patchId) { // The patch only exists for the framework. EXPECT_EQ(patchId, mMapper->findFwkPatch(patchId)); ASSERT_EQ(BAD_VALUE, mMapper->releaseAudioPatch(patchId)); mMapper->eraseFwkPatch(patchId); // The patch is now erased. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); } std::shared_ptr mModule; std::unique_ptr mMapper; AudioPort mConnectedPort; AudioPortConfig mMixPortConfig; AudioPortConfig mDevicePortConfig; AudioPatch mPatch; sp mStream; }; /** * External device connections and patches tests diagram. * * [Connect device] -> [Create Stream] * |-> [ (1) Close Stream] -> [Disconnect Device] * |-> [ (2) Disconnect Device] * | |-> [ (3) Close Stream] * | \-> [ (4) Connect Another Device] * | |-> (1) * | |-> (2) -> (3) * | \-> (5) -> (7) * \-> [ (5) Create/Update Fwk Patch] * |-> [(6) Release Fwk Patch] * | |-> (1) * | \-> (2) (including reconnection) * \-> [(7) Disconnect Device] * |-> [Release Fwk Patch] -> [Close Stream] * \-> (4) -> (5) -> (6) -> (1) * * Note that the test (acting on behalf of DeviceHalAidl) is responsible * for calling `eraseFwkPatch` and `updateFwkPatch` when needed. */ // (1) TEST_F(Hal2AidlMapperTest, CloseDisconnect) { ASSERT_NO_FATAL_FAILURE(CloseDisconnectImpl()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); } // (2) -> (3) TEST_F(Hal2AidlMapperTest, DisconnectClose) { ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); mStream.clear(); } // (2) -> (4) -> (1) TEST_F(Hal2AidlMapperTest, DisconnectConnectCloseDisconnect) { ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(ConnectAnotherDevice()); ASSERT_NO_FATAL_FAILURE(CloseDisconnectImpl()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); } // (2) -> (4) -> (2) -> (3) TEST_F(Hal2AidlMapperTest, DisconnectConnectDisconnectClose) { ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(ConnectAnotherDevice()); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); mStream.clear(); } // (5) -> (6) -> (1) TEST_F(Hal2AidlMapperTest, CreateFwkPatchReleaseCloseDisconnect) { int32_t patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&patchId)); // Must be the patch created during stream opening. ASSERT_EQ(mPatch.id, patchId); // The patch was not reset by HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_EQ(OK, mMapper->releaseAudioPatch(patchId)); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); ASSERT_NO_FATAL_FAILURE(CloseDisconnectImpl()); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); } // (5) -> (6) -> (2) -> (3) TEST_F(Hal2AidlMapperTest, CreateFwkPatchReleaseDisconnectClose) { int32_t patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&patchId)); // Must be the patch created during stream opening. ASSERT_EQ(mPatch.id, patchId); // The patch was not reset by HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_EQ(OK, mMapper->releaseAudioPatch(patchId)); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); mStream.clear(); } // (5) -> (6) -> (2) -> (4) -> (2) -> (3) TEST_F(Hal2AidlMapperTest, CreateFwkPatchReleaseDisconnectConnectDisconnectClose) { int32_t patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&patchId)); // Must be the patch created during stream opening. ASSERT_EQ(mPatch.id, patchId); // The patch was not reset by HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_EQ(OK, mMapper->releaseAudioPatch(patchId)); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(ConnectAnotherDevice()); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); mStream.clear(); } // (5) -> (7) -> Release -> Close TEST_F(Hal2AidlMapperTest, CreateFwkPatchDisconnectReleaseClose) { int32_t patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&patchId)); // Must be the patch created during stream opening. ASSERT_EQ(mPatch.id, patchId); // The patch was not reset by HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); ASSERT_NO_FATAL_FAILURE(ReleaseFwkOnlyPatch(patchId)); mStream.clear(); EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); } // (5) -> (7) -> (4) -> (5) -> (6) -> (1) TEST_F(Hal2AidlMapperTest, CreateFwkPatchDisconnectConnectUpdateReleaseCloseDisconnect) { int32_t patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&patchId)); // Must be the patch created during stream opening. ASSERT_EQ(mPatch.id, patchId); // The patch was not reset by HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch now only exists for the framework. EXPECT_EQ(mPatch.id, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(ConnectAnotherDevice()); // Change the device address locally, for patch update. mDevicePortConfig.ext.get().device.address = mConnectedPort.ext.get().device.address; int32_t newPatchId = patchId; ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&newPatchId)); EXPECT_NE(patchId, newPatchId); mMapper->updateFwkPatch(patchId, newPatchId); EXPECT_EQ(newPatchId, mMapper->findFwkPatch(patchId)); // Just in case, check that HAL patch ID is not listed as a fwk patch. EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); // Verify that device port config was updated. ASSERT_EQ(OK, mMapper->findPortConfig(mConnectedPort.ext.get().device, &mDevicePortConfig)); ASSERT_EQ(OK, mMapper->releaseAudioPatch(newPatchId)); // The patch does not exist both for the fwk and the HAL, must not be listed under fwkPatches. EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); // Just in case, check that HAL patch ID is not listed. EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); ASSERT_NO_FATAL_FAILURE(CloseDisconnectImpl()); EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); EXPECT_EQ(0, mMapper->findFwkPatch(patchId)); EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); } // (2) -> (4) -> (5) -> (7) -> Release -> Close TEST_F(Hal2AidlMapperTest, DisconnectConnectCreateFwkPatchDisconnectReleaseClose) { const int32_t patchId = mPatch.id; ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); // The patch is owned by HAL, must not be listed under fwkPatches after disconnection. EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); ASSERT_NO_FATAL_FAILURE(ConnectAnotherDevice()); // Change the device address locally, for patch update. mDevicePortConfig.ext.get().device.address = mConnectedPort.ext.get().device.address; int32_t newPatchId = 0; // Use 0 since the fwk does not know about the HAL patch. EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); ASSERT_NO_FATAL_FAILURE(CreateFwkPatch(&newPatchId)); EXPECT_NE(0, newPatchId); EXPECT_NE(patchId, newPatchId); // Just in case, check that HAL patch ID is not listed as a fwk patch. EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); // Verify that device port config was updated. ASSERT_EQ(OK, mMapper->findPortConfig(mConnectedPort.ext.get().device, &mDevicePortConfig)); ASSERT_NO_FATAL_FAILURE(DisconnectDevice()); ASSERT_NO_FATAL_FAILURE(ReleaseFwkOnlyPatch(newPatchId)); mStream.clear(); EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id)); EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId)); } TEST_F(Hal2AidlMapperTest, ChangeTransientPatchDevice) { std::mutex mutex; // Only needed for cleanups. auto mapperAccessor = std::make_unique>(*mMapper, mutex); Hal2AidlMapper::Cleanups cleanups(*mapperAccessor); AudioConfig config; config.base.channelMask = AudioChannelLayout::make( AudioChannelLayout::LAYOUT_STEREO); config.base.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT}; config.base.sampleRate = 48000; AudioDevice defaultDevice; defaultDevice.type.type = AudioDeviceType::IN_DEFAULT; AudioPortConfig mixPortConfig; AudioPatch transientPatch; ASSERT_EQ(OK, mMapper->prepareToOpenStream(43 /*ioHandle*/, defaultDevice, AudioIoFlags::make(0), AudioSource::DEFAULT, &cleanups, &config, &mixPortConfig, &transientPatch)); cleanups.disarmAll(); ASSERT_NE(0, transientPatch.id); ASSERT_NE(0, mixPortConfig.id); sp stream = sp::make(); mMapper->addStream(stream, mixPortConfig.id, transientPatch.id); AudioPatch patch{}; int32_t patchId; AudioPortConfig backMicPortConfig; backMicPortConfig.channelMask = config.base.channelMask; backMicPortConfig.format = config.base.format; backMicPortConfig.sampleRate = aidl::android::media::audio::common::Int{config.base.sampleRate}; backMicPortConfig.flags = AudioIoFlags::make(0); backMicPortConfig.ext = createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0); ASSERT_EQ(OK, mMapper->createOrUpdatePatch({backMicPortConfig}, {mixPortConfig}, &patchId, &cleanups)); cleanups.disarmAll(); ASSERT_EQ(android::OK, mMapper->findPortConfig(backMicPortConfig.ext.get().device, &backMicPortConfig)); EXPECT_NE(0, backMicPortConfig.id); EXPECT_EQ(transientPatch.id, patchId); auto patches = mModule->getPatches(); auto patchIt = findById(patches, patchId); ASSERT_NE(patchIt, patches.end()); EXPECT_EQ(std::vector{backMicPortConfig.id}, patchIt->sourcePortConfigIds); EXPECT_EQ(std::vector{mixPortConfig.id}, patchIt->sinkPortConfigIds); } TEST_F(Hal2AidlMapperTest, SetAudioPortConfigGainChangeExistingPortConfig) { // First set config, then update gain. AudioPortConfig speakerPortConfig; speakerPortConfig.ext = createPortDeviceExt(AudioDeviceType::OUT_SPEAKER, 0); speakerPortConfig.channelMask = AudioChannelLayout::make( AudioChannelLayout::LAYOUT_STEREO); speakerPortConfig.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT}; speakerPortConfig.sampleRate = ::aidl::android::media::audio::common::Int(48000); AudioPortConfig resultingPortConfig; ASSERT_EQ(OK, mMapper->setPortConfig(speakerPortConfig, std::set(), &resultingPortConfig)); EXPECT_NE(0, resultingPortConfig.id); EXPECT_NE(0, resultingPortConfig.portId); AudioPortConfig gainUpdate; gainUpdate.ext = createPortDeviceExt(AudioDeviceType::OUT_SPEAKER, 0); AudioGainConfig gainConfig{.index = -1, .mode = 1 << static_cast(AudioGainMode::JOINT), .channelMask = AudioChannelLayout{}, .values = std::vector{-3200}, .rampDurationMs = 0}; gainUpdate.gain = gainConfig; AudioPortConfig resultingGainUpdate; ASSERT_EQ(OK, mMapper->setPortConfig(gainUpdate, std::set(), &resultingGainUpdate)); EXPECT_EQ(resultingPortConfig.id, resultingGainUpdate.id); auto updatedPortConfig = mModule->getPortConfig(resultingGainUpdate.id); ASSERT_TRUE(updatedPortConfig.has_value()); ASSERT_TRUE(updatedPortConfig->gain.has_value()); EXPECT_EQ(gainConfig, updatedPortConfig->gain); } TEST_F(Hal2AidlMapperTest, SetAudioPortConfigGainChangeFromScratch) { // Set gain as the first operation, the HAL should suggest the rest of the configuration. AudioPortConfig gainSet; gainSet.ext = createPortDeviceExt(AudioDeviceType::OUT_SPEAKER, 0); AudioGainConfig gainConfig{.index = -1, .mode = 1 << static_cast(AudioGainMode::JOINT), .channelMask = AudioChannelLayout{}, .values = std::vector{-3200}, .rampDurationMs = 0}; gainSet.gain = gainConfig; AudioPortConfig resultingPortConfig; ASSERT_EQ(OK, mMapper->setPortConfig(gainSet, std::set(), &resultingPortConfig)); EXPECT_NE(0, resultingPortConfig.id); EXPECT_NE(0, resultingPortConfig.portId); auto portConfig = mModule->getPortConfig(resultingPortConfig.id); ASSERT_TRUE(portConfig.has_value()); ASSERT_TRUE(portConfig->gain.has_value()); EXPECT_EQ(gainConfig, portConfig->gain); }