Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2023 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "AHAL_ModuleBluetooth" |
| 18 | |
| 19 | #include <android-base/logging.h> |
| 20 | |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 21 | #include "BluetoothAudioSession.h" |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 22 | #include "core-impl/ModuleBluetooth.h" |
| 23 | #include "core-impl/StreamBluetooth.h" |
| 24 | |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 25 | using aidl::android::hardware::audio::common::SinkMetadata; |
| 26 | using aidl::android::hardware::audio::common::SourceMetadata; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 27 | using aidl::android::hardware::bluetooth::audio::ChannelMode; |
| 28 | using aidl::android::hardware::bluetooth::audio::PcmConfiguration; |
| 29 | using aidl::android::media::audio::common::AudioChannelLayout; |
| 30 | using aidl::android::media::audio::common::AudioConfigBase; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 31 | using aidl::android::media::audio::common::AudioDeviceDescription; |
| 32 | using aidl::android::media::audio::common::AudioDeviceType; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 33 | using aidl::android::media::audio::common::AudioFormatDescription; |
| 34 | using aidl::android::media::audio::common::AudioFormatType; |
| 35 | using aidl::android::media::audio::common::AudioIoFlags; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 36 | using aidl::android::media::audio::common::AudioOffloadInfo; |
| 37 | using aidl::android::media::audio::common::AudioPort; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 38 | using aidl::android::media::audio::common::AudioPortConfig; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 39 | using aidl::android::media::audio::common::AudioPortExt; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 40 | using aidl::android::media::audio::common::AudioProfile; |
| 41 | using aidl::android::media::audio::common::Int; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 42 | using aidl::android::media::audio::common::MicrophoneInfo; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 43 | using aidl::android::media::audio::common::PcmType; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 44 | using android::bluetooth::audio::aidl::BluetoothAudioPortAidl; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 45 | using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn; |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 46 | using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut; |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 47 | |
Devin Moore | 0b2b93d | 2023-11-20 22:10:21 +0000 | [diff] [blame] | 48 | // TODO(b/312265159) bluetooth audio should be in its own process |
| 49 | // Remove this and the shared_libs when that happens |
| 50 | extern "C" binder_status_t createIBluetoothAudioProviderFactory(); |
| 51 | |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 52 | namespace aidl::android::hardware::audio::core { |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 53 | |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 54 | namespace { |
| 55 | |
| 56 | PcmType pcmTypeFromBitsPerSample(int8_t bitsPerSample) { |
| 57 | if (bitsPerSample == 8) |
| 58 | return PcmType::UINT_8_BIT; |
| 59 | else if (bitsPerSample == 16) |
| 60 | return PcmType::INT_16_BIT; |
| 61 | else if (bitsPerSample == 24) |
| 62 | return PcmType::INT_24_BIT; |
| 63 | else if (bitsPerSample == 32) |
| 64 | return PcmType::INT_32_BIT; |
| 65 | ALOGE("Unsupported bitsPerSample: %d", bitsPerSample); |
| 66 | return PcmType::DEFAULT; |
| 67 | } |
| 68 | |
| 69 | AudioChannelLayout channelLayoutFromChannelMode(ChannelMode mode) { |
| 70 | if (mode == ChannelMode::MONO) { |
| 71 | return AudioChannelLayout::make<AudioChannelLayout::layoutMask>( |
| 72 | AudioChannelLayout::LAYOUT_MONO); |
| 73 | } else if (mode == ChannelMode::STEREO || mode == ChannelMode::DUALMONO) { |
| 74 | return AudioChannelLayout::make<AudioChannelLayout::layoutMask>( |
| 75 | AudioChannelLayout::LAYOUT_STEREO); |
| 76 | } |
| 77 | ALOGE("Unsupported channel mode: %s", toString(mode).c_str()); |
| 78 | return AudioChannelLayout{}; |
| 79 | } |
| 80 | |
| 81 | } // namespace |
| 82 | |
Devin Moore | 0b2b93d | 2023-11-20 22:10:21 +0000 | [diff] [blame] | 83 | ModuleBluetooth::ModuleBluetooth(std::unique_ptr<Module::Configuration>&& config) |
| 84 | : Module(Type::BLUETOOTH, std::move(config)) { |
| 85 | // TODO(b/312265159) bluetooth audio should be in its own process |
| 86 | // Remove this and the shared_libs when that happens |
| 87 | binder_status_t status = createIBluetoothAudioProviderFactory(); |
| 88 | if (status != STATUS_OK) { |
| 89 | LOG(ERROR) << "Failed to create bluetooth audio provider factory. Status: " |
| 90 | << ::android::statusToString(status); |
| 91 | } |
| 92 | } |
| 93 | |
Ram Mohan | 18f0d51 | 2023-07-01 00:47:09 +0530 | [diff] [blame] | 94 | ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp( |
| 95 | std::shared_ptr<IBluetoothA2dp>* _aidl_return) { |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 96 | *_aidl_return = getBtA2dp().getInstance(); |
Ram Mohan | 18f0d51 | 2023-07-01 00:47:09 +0530 | [diff] [blame] | 97 | LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get(); |
| 98 | return ndk::ScopedAStatus::ok(); |
| 99 | } |
| 100 | |
| 101 | ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) { |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 102 | *_aidl_return = getBtLe().getInstance(); |
| 103 | LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get(); |
| 104 | return ndk::ScopedAStatus::ok(); |
| 105 | } |
| 106 | |
| 107 | ChildInterface<BluetoothA2dp>& ModuleBluetooth::getBtA2dp() { |
| 108 | if (!mBluetoothA2dp) { |
| 109 | auto handle = ndk::SharedRefBase::make<BluetoothA2dp>(); |
| 110 | handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this)); |
| 111 | mBluetoothA2dp = handle; |
| 112 | } |
| 113 | return mBluetoothA2dp; |
| 114 | } |
| 115 | |
| 116 | ChildInterface<BluetoothLe>& ModuleBluetooth::getBtLe() { |
Ram Mohan | 18f0d51 | 2023-07-01 00:47:09 +0530 | [diff] [blame] | 117 | if (!mBluetoothLe) { |
| 118 | auto handle = ndk::SharedRefBase::make<BluetoothLe>(); |
| 119 | handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this)); |
| 120 | mBluetoothLe = handle; |
| 121 | } |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 122 | return mBluetoothLe; |
Ram Mohan | 18f0d51 | 2023-07-01 00:47:09 +0530 | [diff] [blame] | 123 | } |
| 124 | |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 125 | ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() { |
| 126 | return std::make_tuple(std::weak_ptr<IBluetooth>(), getBtA2dp().getPtr(), getBtLe().getPtr()); |
Ram Mohan | 18f0d51 | 2023-07-01 00:47:09 +0530 | [diff] [blame] | 127 | } |
| 128 | |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 129 | ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) { |
| 130 | LOG(DEBUG) << __func__ << ": is not supported"; |
| 131 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 132 | } |
| 133 | |
| 134 | ndk::ScopedAStatus ModuleBluetooth::setMicMute(bool in_mute __unused) { |
| 135 | LOG(DEBUG) << __func__ << ": is not supported"; |
| 136 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 137 | } |
| 138 | |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 139 | ndk::ScopedAStatus ModuleBluetooth::setAudioPortConfig(const AudioPortConfig& in_requested, |
| 140 | AudioPortConfig* out_suggested, |
| 141 | bool* _aidl_return) { |
| 142 | auto fillConfig = [this](const AudioPort& port, AudioPortConfig* config) { |
| 143 | if (port.ext.getTag() == AudioPortExt::device) { |
| 144 | CachedProxy proxy; |
| 145 | auto status = findOrCreateProxy(port, proxy); |
| 146 | if (status.isOk()) { |
| 147 | const auto& pcmConfig = proxy.pcmConfig; |
| 148 | LOG(DEBUG) << "setAudioPortConfig: suggesting port config from " |
| 149 | << pcmConfig.toString(); |
| 150 | const auto pcmType = pcmTypeFromBitsPerSample(pcmConfig.bitsPerSample); |
| 151 | const auto channelMask = channelLayoutFromChannelMode(pcmConfig.channelMode); |
| 152 | if (pcmType != PcmType::DEFAULT && channelMask != AudioChannelLayout{}) { |
| 153 | config->format = |
| 154 | AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}; |
| 155 | config->channelMask = channelMask; |
| 156 | config->sampleRate = Int{.value = pcmConfig.sampleRateHz}; |
| 157 | config->flags = port.flags; |
| 158 | config->ext = port.ext; |
| 159 | return true; |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | return generateDefaultPortConfig(port, config); |
| 164 | }; |
| 165 | return Module::setAudioPortConfigImpl(in_requested, fillConfig, out_suggested, _aidl_return); |
| 166 | } |
| 167 | |
| 168 | ndk::ScopedAStatus ModuleBluetooth::checkAudioPatchEndpointsMatch( |
| 169 | const std::vector<AudioPortConfig*>& sources, const std::vector<AudioPortConfig*>& sinks) { |
| 170 | // Both sources and sinks must be non-empty, this is guaranteed by 'setAudioPatch'. |
| 171 | const bool isInput = sources[0]->ext.getTag() == AudioPortExt::device; |
| 172 | const int32_t devicePortId = isInput ? sources[0]->portId : sinks[0]->portId; |
| 173 | const auto proxyIt = mProxies.find(devicePortId); |
| 174 | if (proxyIt == mProxies.end()) return ndk::ScopedAStatus::ok(); |
| 175 | const auto& pcmConfig = proxyIt->second.pcmConfig; |
| 176 | const AudioPortConfig* mixPortConfig = isInput ? sinks[0] : sources[0]; |
| 177 | if (!StreamBluetooth::checkConfigParams( |
| 178 | pcmConfig, AudioConfigBase{.sampleRate = mixPortConfig->sampleRate->value, |
| 179 | .channelMask = *(mixPortConfig->channelMask), |
| 180 | .format = *(mixPortConfig->format)})) { |
| 181 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 182 | } |
| 183 | if (int32_t handle = mixPortConfig->ext.get<AudioPortExt::mix>().handle; handle > 0) { |
| 184 | mConnections.insert(std::pair(handle, devicePortId)); |
| 185 | } |
| 186 | return ndk::ScopedAStatus::ok(); |
| 187 | } |
| 188 | |
| 189 | void ModuleBluetooth::onExternalDeviceConnectionChanged(const AudioPort& audioPort, |
| 190 | bool connected) { |
| 191 | if (!connected) mProxies.erase(audioPort.id); |
| 192 | } |
| 193 | |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 194 | ndk::ScopedAStatus ModuleBluetooth::createInputStream( |
| 195 | StreamContext&& context, const SinkMetadata& sinkMetadata, |
| 196 | const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) { |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 197 | CachedProxy proxy; |
| 198 | RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy)); |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 199 | return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata, |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 200 | microphones, getBtProfileManagerHandles(), |
| 201 | proxy.ptr, proxy.pcmConfig); |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | ndk::ScopedAStatus ModuleBluetooth::createOutputStream( |
| 205 | StreamContext&& context, const SourceMetadata& sourceMetadata, |
| 206 | const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) { |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 207 | CachedProxy proxy; |
| 208 | RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy)); |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 209 | return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata, |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 210 | offloadInfo, getBtProfileManagerHandles(), |
| 211 | proxy.ptr, proxy.pcmConfig); |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 212 | } |
| 213 | |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 214 | ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort, |
| 215 | int32_t nextPortId) { |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 216 | if (audioPort->ext.getTag() != AudioPortExt::device) { |
| 217 | LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); |
| 218 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| 219 | } |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 220 | if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) { |
| 221 | LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory AIDL service not available"; |
| 222 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| 223 | } |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 224 | const auto& devicePort = audioPort->ext.get<AudioPortExt::device>(); |
| 225 | const auto& description = devicePort.device.type; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 226 | // This method must return an error when the device can not be connected. |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 227 | if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) { |
| 228 | bool isA2dpEnabled = false; |
| 229 | if (!!mBluetoothA2dp) { |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 230 | RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled)); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 231 | } |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 232 | LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 233 | if (!isA2dpEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 234 | } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) { |
| 235 | bool isLeEnabled = false; |
| 236 | if (!!mBluetoothLe) { |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 237 | RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled)); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 238 | } |
Mikhail Naganov | 55045b5 | 2023-10-24 17:03:50 -0700 | [diff] [blame] | 239 | LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled; |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 240 | if (!isLeEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 241 | } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS && |
| 242 | description.type == AudioDeviceType::OUT_HEARING_AID) { |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 243 | // Hearing aids can use a number of profiles, no single switch exists. |
| 244 | } else { |
| 245 | LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString(); |
| 246 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 247 | } |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 248 | CachedProxy proxy; |
| 249 | RETURN_STATUS_IF_ERROR(createProxy(*audioPort, nextPortId, proxy)); |
| 250 | // Since the device is already connected and configured by the BT stack, provide |
| 251 | // the current configuration instead of all possible profiles. |
| 252 | const auto& pcmConfig = proxy.pcmConfig; |
| 253 | audioPort->profiles.clear(); |
| 254 | audioPort->profiles.push_back( |
| 255 | AudioProfile{.format = AudioFormatDescription{.type = AudioFormatType::PCM, |
| 256 | .pcm = pcmTypeFromBitsPerSample( |
| 257 | pcmConfig.bitsPerSample)}, |
| 258 | .channelMasks = std::vector<AudioChannelLayout>( |
| 259 | {channelLayoutFromChannelMode(pcmConfig.channelMode)}), |
| 260 | .sampleRates = std::vector<int>({pcmConfig.sampleRateHz})}); |
| 261 | LOG(DEBUG) << __func__ << ": " << audioPort->toString(); |
| 262 | return ndk::ScopedAStatus::ok(); |
Mikhail Naganov | 84bcc04 | 2023-10-05 17:36:57 -0700 | [diff] [blame] | 263 | } |
| 264 | |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 265 | ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) { |
| 266 | LOG(DEBUG) << __func__ << ": is not supported"; |
| 267 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 268 | } |
| 269 | |
| 270 | ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) { |
| 271 | LOG(DEBUG) << __func__ << ": is not supported"; |
| 272 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 273 | } |
| 274 | |
Mikhail Naganov | a88cf60 | 2023-12-13 14:35:11 -0800 | [diff] [blame] | 275 | int32_t ModuleBluetooth::getNominalLatencyMs(const AudioPortConfig& portConfig) { |
| 276 | const auto connectionsIt = mConnections.find(portConfig.ext.get<AudioPortExt::mix>().handle); |
| 277 | if (connectionsIt != mConnections.end()) { |
| 278 | const auto proxyIt = mProxies.find(connectionsIt->second); |
| 279 | if (proxyIt != mProxies.end()) { |
| 280 | auto proxy = proxyIt->second.ptr; |
| 281 | size_t dataIntervalUs = 0; |
| 282 | if (!proxy->getPreferredDataIntervalUs(dataIntervalUs)) { |
| 283 | LOG(WARNING) << __func__ << ": could not fetch preferred data interval"; |
| 284 | } |
| 285 | const bool isInput = portConfig.flags->getTag() == AudioIoFlags::input; |
| 286 | return isInput ? StreamInBluetooth::getNominalLatencyMs(dataIntervalUs) |
| 287 | : StreamOutBluetooth::getNominalLatencyMs(dataIntervalUs); |
| 288 | } |
| 289 | } |
| 290 | LOG(ERROR) << __func__ << ": no connection or proxy found for " << portConfig.toString(); |
| 291 | return Module::getNominalLatencyMs(portConfig); |
| 292 | } |
| 293 | |
| 294 | ndk::ScopedAStatus ModuleBluetooth::createProxy(const AudioPort& audioPort, int32_t instancePortId, |
| 295 | CachedProxy& proxy) { |
| 296 | const bool isInput = audioPort.flags.getTag() == AudioIoFlags::input; |
| 297 | proxy.ptr = isInput ? std::shared_ptr<BluetoothAudioPortAidl>( |
| 298 | std::make_shared<BluetoothAudioPortAidlIn>()) |
| 299 | : std::shared_ptr<BluetoothAudioPortAidl>( |
| 300 | std::make_shared<BluetoothAudioPortAidlOut>()); |
| 301 | const auto& devicePort = audioPort.ext.get<AudioPortExt::device>(); |
| 302 | if (const auto device = devicePort.device.type; !proxy.ptr->registerPort(device)) { |
| 303 | LOG(ERROR) << __func__ << ": failed to register BT port for " << device.toString(); |
| 304 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| 305 | } |
| 306 | if (!proxy.ptr->loadAudioConfig(proxy.pcmConfig)) { |
| 307 | LOG(ERROR) << __func__ << ": state=" << proxy.ptr->getState() |
| 308 | << ", failed to load audio config"; |
| 309 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| 310 | } |
| 311 | mProxies.insert(std::pair(instancePortId, proxy)); |
| 312 | return ndk::ScopedAStatus::ok(); |
| 313 | } |
| 314 | |
| 315 | ndk::ScopedAStatus ModuleBluetooth::fetchAndCheckProxy(const StreamContext& context, |
| 316 | CachedProxy& proxy) { |
| 317 | const auto connectionsIt = mConnections.find(context.getMixPortHandle()); |
| 318 | if (connectionsIt != mConnections.end()) { |
| 319 | const auto proxyIt = mProxies.find(connectionsIt->second); |
| 320 | if (proxyIt != mProxies.end()) { |
| 321 | proxy = proxyIt->second; |
| 322 | mProxies.erase(proxyIt); |
| 323 | } |
| 324 | mConnections.erase(connectionsIt); |
| 325 | } |
| 326 | if (proxy.ptr != nullptr) { |
| 327 | if (!StreamBluetooth::checkConfigParams( |
| 328 | proxy.pcmConfig, AudioConfigBase{.sampleRate = context.getSampleRate(), |
| 329 | .channelMask = context.getChannelLayout(), |
| 330 | .format = context.getFormat()})) { |
| 331 | return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| 332 | } |
| 333 | } |
| 334 | // Not having a proxy is OK, it may happen in VTS tests when streams are opened on unconnected |
| 335 | // mix ports. |
| 336 | return ndk::ScopedAStatus::ok(); |
| 337 | } |
| 338 | |
| 339 | ndk::ScopedAStatus ModuleBluetooth::findOrCreateProxy(const AudioPort& audioPort, |
| 340 | CachedProxy& proxy) { |
| 341 | if (auto proxyIt = mProxies.find(audioPort.id); proxyIt != mProxies.end()) { |
| 342 | proxy = proxyIt->second; |
| 343 | return ndk::ScopedAStatus::ok(); |
| 344 | } |
| 345 | return createProxy(audioPort, audioPort.id, proxy); |
| 346 | } |
| 347 | |
Mikhail Naganov | b03b5c4 | 2023-07-26 13:13:35 -0700 | [diff] [blame] | 348 | } // namespace aidl::android::hardware::audio::core |