Loading audio/aidl/android/hardware/audio/core/IModule.aidl +13 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,19 @@ interface IModule { * device address is specified for a point-to-multipoint external device * connection. * * Since not all modules have a DSP that could perform sample rate and * format conversions, behavior related to mix port configurations may vary. * For modules with a DSP, mix ports can be pre-configured and have a fixed * set of audio profiles supported by the DSP. For modules without a DSP, * audio profiles of mix ports may change after connecting an external * device. The typical case is that the mix port has an empty set of * profiles when no external devices are connected, and after external * device connection it receives the same set of profiles as the device * ports that they can be routed to. The client will re-query current port * configurations using 'getAudioPorts'. All mix ports that can be routed to * the connected device port must have a non-empty set of audio profiles * after successful connection of an external device. * * Handling of a disconnect is done in a reverse order: * 1. Reset port configuration using the 'resetAudioPortConfig' method. * 2. Release the connected device port by calling the 'disconnectExternalDevice' Loading audio/aidl/common/include/Utils.h +6 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,12 @@ constexpr size_t getFrameSizeInBytes( return 0; } constexpr bool isDefaultAudioFormat( const ::aidl::android::media::audio::common::AudioFormatDescription& desc) { return desc.type == ::aidl::android::media::audio::common::AudioFormatType::DEFAULT && desc.pcm == ::aidl::android::media::audio::common::PcmType::DEFAULT; } constexpr bool isTelephonyDeviceType( ::aidl::android::media::audio::common::AudioDeviceType device) { return device == ::aidl::android::media::audio::common::AudioDeviceType::IN_TELEPHONY_RX || Loading audio/aidl/default/Module.cpp +66 −20 Original line number Diff line number Diff line Loading @@ -143,6 +143,21 @@ StreamOut::CreateInstance Module::getStreamOutCreator(Type type) { } } std::ostream& operator<<(std::ostream& os, Module::Type t) { switch (t) { case Module::Type::DEFAULT: os << "default"; break; case Module::Type::R_SUBMIX: os << "r_submix"; break; case Module::Type::USB: os << "usb"; break; } return os; } void Module::cleanUpPatch(int32_t patchId) { erase_all_values(mPatches, std::set<int32_t>{patchId}); } Loading Loading @@ -352,16 +367,17 @@ void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const Audio ndk::ScopedAStatus Module::setModuleDebug( const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) { LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString() LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString() << ", new flags: " << in_debug.toString(); if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections && !mConnectedDevicePorts.empty()) { LOG(ERROR) << __func__ << ": attempting to change device connections simulation " << "while having external devices connected"; LOG(ERROR) << __func__ << ": " << mType << ": attempting to change device connections simulation while having external " << "devices connected"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (in_debug.streamTransientStateDelayMs < 0) { LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: " LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: " << in_debug.streamTransientStateDelayMs; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } Loading Loading @@ -440,38 +456,45 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to " << connectedDevicePort.device.toString(); // Check if there is already a connected port with for the same external device. for (auto connectedPortId : mConnectedDevicePorts) { auto connectedPortIt = findById<AudioPort>(ports, connectedPortId); for (auto connectedPortPair : mConnectedDevicePorts) { auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first); if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device == connectedDevicePort.device) { LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString() << " is already connected at the device port id " << connectedPortId; << " is already connected at the device port id " << connectedPortPair.first; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } } if (!mDebug.simulateDeviceConnections) { // In a real HAL here we would attempt querying the profiles from the device. LOG(ERROR) << __func__ << ": failed to query supported device profiles"; // TODO: Check the return value when it is ready for actual devices. populateConnectedDevicePort(&connectedPort); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort); !status.isOk()) { return status; } connectedPort.id = ++getConfig().nextPortId; mConnectedDevicePorts.insert(connectedPort.id); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " << "connected port ID " << connectedPort.id; } else { auto& connectedProfiles = getConfig().connectedProfiles; if (auto connectedProfilesIt = connectedProfiles.find(templateId); connectedProfilesIt != connectedProfiles.end()) { connectedPort.profiles = connectedProfilesIt->second; } } if (connectedPort.profiles.empty()) { LOG(ERROR) << "Profiles of a connected port still empty after connecting external device " << connectedPort.toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } connectedPort.id = ++getConfig().nextPortId; auto [connectedPortsIt, _] = mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>())); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " << "connected port ID " << connectedPort.id; ports.push_back(connectedPort); onExternalDeviceConnectionChanged(connectedPort, true /*connected*/); *_aidl_return = std::move(connectedPort); std::vector<int32_t> routablePortIds; std::vector<AudioRoute> newRoutes; auto& routes = getConfig().routes; for (auto& r : routes) { Loading @@ -481,15 +504,30 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA newRoute.sinkPortId = connectedPort.id; newRoute.isExclusive = r.isExclusive; newRoutes.push_back(std::move(newRoute)); routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(), r.sourcePortIds.end()); } else { auto& srcs = r.sourcePortIds; if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) { srcs.push_back(connectedPort.id); routablePortIds.push_back(r.sinkPortId); } } } routes.insert(routes.end(), newRoutes.begin(), newRoutes.end()); // Note: this is a simplistic approach assuming that a mix port can only be populated // from a single device port. Implementing support for stuffing dynamic profiles with a superset // of all profiles from all routable dynamic device ports would be more involved. for (const auto mixPortId : routablePortIds) { auto portsIt = findById<AudioPort>(ports, mixPortId); if (portsIt != ports.end() && portsIt->profiles.empty()) { portsIt->profiles = connectedPort.profiles; connectedPortsIt->second.push_back(portsIt->id); } } *_aidl_return = std::move(connectedPort); return ndk::ScopedAStatus::ok(); } Loading @@ -504,7 +542,8 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mConnectedDevicePorts.count(in_portId) == 0) { auto connectedPortsIt = mConnectedDevicePorts.find(in_portId); if (connectedPortsIt == mConnectedDevicePorts.end()) { LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } Loading @@ -525,7 +564,6 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { } onExternalDeviceConnectionChanged(*portIt, false /*connected*/); ports.erase(portIt); mConnectedDevicePorts.erase(in_portId); LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released"; auto& routes = getConfig().routes; Loading @@ -540,6 +578,14 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { } } for (const auto mixPortId : connectedPortsIt->second) { auto mixPortIt = findById<AudioPort>(ports, mixPortId); if (mixPortIt != ports.end()) { mixPortIt->profiles = {}; } } mConnectedDevicePorts.erase(connectedPortsIt); return ndk::ScopedAStatus::ok(); } Loading audio/aidl/default/include/core-impl/Configuration.h +2 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,8 @@ struct Configuration { std::vector<::aidl::android::media::audio::common::AudioPort> ports; std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; // Port id -> List of profiles to use when the device port state is set to 'connected'. // Port id -> List of profiles to use when the device port state is set to 'connected' // in connection simulation mode. std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>> connectedProfiles; std::vector<AudioRoute> routes; Loading audio/aidl/default/include/core-impl/Module.h +4 −2 Original line number Diff line number Diff line Loading @@ -177,8 +177,10 @@ class Module : public BnModule { ChildInterface<IBluetooth> mBluetooth; ChildInterface<IBluetoothA2dp> mBluetoothA2dp; ChildInterface<IBluetoothLe> mBluetoothLe; // ids of ports created at runtime via 'connectExternalDevice'. std::set<int32_t> mConnectedDevicePorts; // ids of device ports created at runtime via 'connectExternalDevice'. // Also stores ids of mix ports with dynamic profiles which got populated from the connected // port. std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts; Streams mStreams; // Maps port ids and port config ids to patch ids. // Multimap because both ports and configs can be used by multiple patches. Loading Loading
audio/aidl/android/hardware/audio/core/IModule.aidl +13 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,19 @@ interface IModule { * device address is specified for a point-to-multipoint external device * connection. * * Since not all modules have a DSP that could perform sample rate and * format conversions, behavior related to mix port configurations may vary. * For modules with a DSP, mix ports can be pre-configured and have a fixed * set of audio profiles supported by the DSP. For modules without a DSP, * audio profiles of mix ports may change after connecting an external * device. The typical case is that the mix port has an empty set of * profiles when no external devices are connected, and after external * device connection it receives the same set of profiles as the device * ports that they can be routed to. The client will re-query current port * configurations using 'getAudioPorts'. All mix ports that can be routed to * the connected device port must have a non-empty set of audio profiles * after successful connection of an external device. * * Handling of a disconnect is done in a reverse order: * 1. Reset port configuration using the 'resetAudioPortConfig' method. * 2. Release the connected device port by calling the 'disconnectExternalDevice' Loading
audio/aidl/common/include/Utils.h +6 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,12 @@ constexpr size_t getFrameSizeInBytes( return 0; } constexpr bool isDefaultAudioFormat( const ::aidl::android::media::audio::common::AudioFormatDescription& desc) { return desc.type == ::aidl::android::media::audio::common::AudioFormatType::DEFAULT && desc.pcm == ::aidl::android::media::audio::common::PcmType::DEFAULT; } constexpr bool isTelephonyDeviceType( ::aidl::android::media::audio::common::AudioDeviceType device) { return device == ::aidl::android::media::audio::common::AudioDeviceType::IN_TELEPHONY_RX || Loading
audio/aidl/default/Module.cpp +66 −20 Original line number Diff line number Diff line Loading @@ -143,6 +143,21 @@ StreamOut::CreateInstance Module::getStreamOutCreator(Type type) { } } std::ostream& operator<<(std::ostream& os, Module::Type t) { switch (t) { case Module::Type::DEFAULT: os << "default"; break; case Module::Type::R_SUBMIX: os << "r_submix"; break; case Module::Type::USB: os << "usb"; break; } return os; } void Module::cleanUpPatch(int32_t patchId) { erase_all_values(mPatches, std::set<int32_t>{patchId}); } Loading Loading @@ -352,16 +367,17 @@ void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const Audio ndk::ScopedAStatus Module::setModuleDebug( const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) { LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString() LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString() << ", new flags: " << in_debug.toString(); if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections && !mConnectedDevicePorts.empty()) { LOG(ERROR) << __func__ << ": attempting to change device connections simulation " << "while having external devices connected"; LOG(ERROR) << __func__ << ": " << mType << ": attempting to change device connections simulation while having external " << "devices connected"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (in_debug.streamTransientStateDelayMs < 0) { LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: " LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: " << in_debug.streamTransientStateDelayMs; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } Loading Loading @@ -440,38 +456,45 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to " << connectedDevicePort.device.toString(); // Check if there is already a connected port with for the same external device. for (auto connectedPortId : mConnectedDevicePorts) { auto connectedPortIt = findById<AudioPort>(ports, connectedPortId); for (auto connectedPortPair : mConnectedDevicePorts) { auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first); if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device == connectedDevicePort.device) { LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString() << " is already connected at the device port id " << connectedPortId; << " is already connected at the device port id " << connectedPortPair.first; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } } if (!mDebug.simulateDeviceConnections) { // In a real HAL here we would attempt querying the profiles from the device. LOG(ERROR) << __func__ << ": failed to query supported device profiles"; // TODO: Check the return value when it is ready for actual devices. populateConnectedDevicePort(&connectedPort); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort); !status.isOk()) { return status; } connectedPort.id = ++getConfig().nextPortId; mConnectedDevicePorts.insert(connectedPort.id); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " << "connected port ID " << connectedPort.id; } else { auto& connectedProfiles = getConfig().connectedProfiles; if (auto connectedProfilesIt = connectedProfiles.find(templateId); connectedProfilesIt != connectedProfiles.end()) { connectedPort.profiles = connectedProfilesIt->second; } } if (connectedPort.profiles.empty()) { LOG(ERROR) << "Profiles of a connected port still empty after connecting external device " << connectedPort.toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } connectedPort.id = ++getConfig().nextPortId; auto [connectedPortsIt, _] = mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>())); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " << "connected port ID " << connectedPort.id; ports.push_back(connectedPort); onExternalDeviceConnectionChanged(connectedPort, true /*connected*/); *_aidl_return = std::move(connectedPort); std::vector<int32_t> routablePortIds; std::vector<AudioRoute> newRoutes; auto& routes = getConfig().routes; for (auto& r : routes) { Loading @@ -481,15 +504,30 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA newRoute.sinkPortId = connectedPort.id; newRoute.isExclusive = r.isExclusive; newRoutes.push_back(std::move(newRoute)); routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(), r.sourcePortIds.end()); } else { auto& srcs = r.sourcePortIds; if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) { srcs.push_back(connectedPort.id); routablePortIds.push_back(r.sinkPortId); } } } routes.insert(routes.end(), newRoutes.begin(), newRoutes.end()); // Note: this is a simplistic approach assuming that a mix port can only be populated // from a single device port. Implementing support for stuffing dynamic profiles with a superset // of all profiles from all routable dynamic device ports would be more involved. for (const auto mixPortId : routablePortIds) { auto portsIt = findById<AudioPort>(ports, mixPortId); if (portsIt != ports.end() && portsIt->profiles.empty()) { portsIt->profiles = connectedPort.profiles; connectedPortsIt->second.push_back(portsIt->id); } } *_aidl_return = std::move(connectedPort); return ndk::ScopedAStatus::ok(); } Loading @@ -504,7 +542,8 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mConnectedDevicePorts.count(in_portId) == 0) { auto connectedPortsIt = mConnectedDevicePorts.find(in_portId); if (connectedPortsIt == mConnectedDevicePorts.end()) { LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } Loading @@ -525,7 +564,6 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { } onExternalDeviceConnectionChanged(*portIt, false /*connected*/); ports.erase(portIt); mConnectedDevicePorts.erase(in_portId); LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released"; auto& routes = getConfig().routes; Loading @@ -540,6 +578,14 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { } } for (const auto mixPortId : connectedPortsIt->second) { auto mixPortIt = findById<AudioPort>(ports, mixPortId); if (mixPortIt != ports.end()) { mixPortIt->profiles = {}; } } mConnectedDevicePorts.erase(connectedPortsIt); return ndk::ScopedAStatus::ok(); } Loading
audio/aidl/default/include/core-impl/Configuration.h +2 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,8 @@ struct Configuration { std::vector<::aidl::android::media::audio::common::AudioPort> ports; std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; // Port id -> List of profiles to use when the device port state is set to 'connected'. // Port id -> List of profiles to use when the device port state is set to 'connected' // in connection simulation mode. std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>> connectedProfiles; std::vector<AudioRoute> routes; Loading
audio/aidl/default/include/core-impl/Module.h +4 −2 Original line number Diff line number Diff line Loading @@ -177,8 +177,10 @@ class Module : public BnModule { ChildInterface<IBluetooth> mBluetooth; ChildInterface<IBluetoothA2dp> mBluetoothA2dp; ChildInterface<IBluetoothLe> mBluetoothLe; // ids of ports created at runtime via 'connectExternalDevice'. std::set<int32_t> mConnectedDevicePorts; // ids of device ports created at runtime via 'connectExternalDevice'. // Also stores ids of mix ports with dynamic profiles which got populated from the connected // port. std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts; Streams mStreams; // Maps port ids and port config ids to patch ids. // Multimap because both ports and configs can be used by multiple patches. Loading