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
21 #include "BluetoothAudioSession.h"
22 #include "core-impl/ModuleBluetooth.h"
23 #include "core-impl/StreamBluetooth.h"
24
25 using aidl::android::hardware::audio::common::SinkMetadata;
26 using aidl::android::hardware::audio::common::SourceMetadata;
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;
31 using aidl::android::media::audio::common::AudioDeviceDescription;
32 using aidl::android::media::audio::common::AudioDeviceType;
33 using aidl::android::media::audio::common::AudioFormatDescription;
34 using aidl::android::media::audio::common::AudioFormatType;
35 using aidl::android::media::audio::common::AudioIoFlags;
36 using aidl::android::media::audio::common::AudioOffloadInfo;
37 using aidl::android::media::audio::common::AudioPort;
38 using aidl::android::media::audio::common::AudioPortConfig;
39 using aidl::android::media::audio::common::AudioPortExt;
40 using aidl::android::media::audio::common::AudioProfile;
41 using aidl::android::media::audio::common::Int;
42 using aidl::android::media::audio::common::MicrophoneInfo;
43 using aidl::android::media::audio::common::PcmType;
44 using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
45 using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
46 using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
47
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
52 namespace aidl::android::hardware::audio::core {
53
54 namespace {
55
pcmTypeFromBitsPerSample(int8_t bitsPerSample)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
channelLayoutFromChannelMode(ChannelMode mode)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
ModuleBluetooth(std::unique_ptr<Module::Configuration> && config)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
getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp> * _aidl_return)94 ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
95 std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
96 *_aidl_return = getBtA2dp().getInstance();
97 LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
98 return ndk::ScopedAStatus::ok();
99 }
100
getBluetoothLe(std::shared_ptr<IBluetoothLe> * _aidl_return)101 ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
102 *_aidl_return = getBtLe().getInstance();
103 LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
104 return ndk::ScopedAStatus::ok();
105 }
106
getBtA2dp()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
getBtLe()116 ChildInterface<BluetoothLe>& ModuleBluetooth::getBtLe() {
117 if (!mBluetoothLe) {
118 auto handle = ndk::SharedRefBase::make<BluetoothLe>();
119 handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
120 mBluetoothLe = handle;
121 }
122 return mBluetoothLe;
123 }
124
getBtProfileManagerHandles()125 ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
126 return std::make_tuple(std::weak_ptr<IBluetooth>(), getBtA2dp().getPtr(), getBtLe().getPtr());
127 }
128
getMicMute(bool * _aidl_return __unused)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
setMicMute(bool in_mute __unused)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
setAudioPortConfig(const AudioPortConfig & in_requested,AudioPortConfig * out_suggested,bool * _aidl_return)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
checkAudioPatchEndpointsMatch(const std::vector<AudioPortConfig * > & sources,const std::vector<AudioPortConfig * > & sinks)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
onExternalDeviceConnectionChanged(const AudioPort & audioPort,bool connected)189 void ModuleBluetooth::onExternalDeviceConnectionChanged(const AudioPort& audioPort,
190 bool connected) {
191 if (!connected) mProxies.erase(audioPort.id);
192 }
193
createInputStream(StreamContext && context,const SinkMetadata & sinkMetadata,const std::vector<MicrophoneInfo> & microphones,std::shared_ptr<StreamIn> * result)194 ndk::ScopedAStatus ModuleBluetooth::createInputStream(
195 StreamContext&& context, const SinkMetadata& sinkMetadata,
196 const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
197 CachedProxy proxy;
198 RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy));
199 return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata,
200 microphones, getBtProfileManagerHandles(),
201 proxy.ptr, proxy.pcmConfig);
202 }
203
createOutputStream(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo,std::shared_ptr<StreamOut> * result)204 ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
205 StreamContext&& context, const SourceMetadata& sourceMetadata,
206 const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
207 CachedProxy proxy;
208 RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy));
209 return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata,
210 offloadInfo, getBtProfileManagerHandles(),
211 proxy.ptr, proxy.pcmConfig);
212 }
213
populateConnectedDevicePort(AudioPort * audioPort,int32_t nextPortId)214 ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort,
215 int32_t nextPortId) {
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 }
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 }
224 const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
225 const auto& description = devicePort.device.type;
226 // This method must return an error when the device can not be connected.
227 // Since A2DP/LE status events are sent asynchronously, it is more reliable
228 // to attempt connecting to the BT stack rather than judge by the A2DP/LE status.
229 if (description.connection != AudioDeviceDescription::CONNECTION_BT_A2DP &&
230 description.connection != AudioDeviceDescription::CONNECTION_BT_LE &&
231 !(description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
232 description.type == AudioDeviceType::OUT_HEARING_AID)) {
233 LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString();
234 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
235 }
236 CachedProxy proxy;
237 RETURN_STATUS_IF_ERROR(createProxy(*audioPort, nextPortId, proxy));
238 // If the device is actually connected, it is configured by the BT stack.
239 // Provide the current configuration instead of all possible profiles.
240 const auto& pcmConfig = proxy.pcmConfig;
241 audioPort->profiles.clear();
242 audioPort->profiles.push_back(
243 AudioProfile{.format = AudioFormatDescription{.type = AudioFormatType::PCM,
244 .pcm = pcmTypeFromBitsPerSample(
245 pcmConfig.bitsPerSample)},
246 .channelMasks = std::vector<AudioChannelLayout>(
247 {channelLayoutFromChannelMode(pcmConfig.channelMode)}),
248 .sampleRates = std::vector<int>({pcmConfig.sampleRateHz})});
249 LOG(DEBUG) << __func__ << ": " << audioPort->toString();
250 return ndk::ScopedAStatus::ok();
251 }
252
onMasterMuteChanged(bool)253 ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
254 LOG(DEBUG) << __func__ << ": is not supported";
255 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
256 }
257
onMasterVolumeChanged(float)258 ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) {
259 LOG(DEBUG) << __func__ << ": is not supported";
260 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
261 }
262
getNominalLatencyMs(const AudioPortConfig & portConfig)263 int32_t ModuleBluetooth::getNominalLatencyMs(const AudioPortConfig& portConfig) {
264 const auto connectionsIt = mConnections.find(portConfig.ext.get<AudioPortExt::mix>().handle);
265 if (connectionsIt != mConnections.end()) {
266 const auto proxyIt = mProxies.find(connectionsIt->second);
267 if (proxyIt != mProxies.end()) {
268 auto proxy = proxyIt->second.ptr;
269 size_t dataIntervalUs = 0;
270 if (!proxy->getPreferredDataIntervalUs(dataIntervalUs)) {
271 LOG(WARNING) << __func__ << ": could not fetch preferred data interval";
272 }
273 const bool isInput = portConfig.flags->getTag() == AudioIoFlags::input;
274 return isInput ? StreamInBluetooth::getNominalLatencyMs(dataIntervalUs)
275 : StreamOutBluetooth::getNominalLatencyMs(dataIntervalUs);
276 }
277 }
278 LOG(ERROR) << __func__ << ": no connection or proxy found for " << portConfig.toString();
279 return Module::getNominalLatencyMs(portConfig);
280 }
281
createProxy(const AudioPort & audioPort,int32_t instancePortId,CachedProxy & proxy)282 ndk::ScopedAStatus ModuleBluetooth::createProxy(const AudioPort& audioPort, int32_t instancePortId,
283 CachedProxy& proxy) {
284 const bool isInput = audioPort.flags.getTag() == AudioIoFlags::input;
285 proxy.ptr = isInput ? std::shared_ptr<BluetoothAudioPortAidl>(
286 std::make_shared<BluetoothAudioPortAidlIn>())
287 : std::shared_ptr<BluetoothAudioPortAidl>(
288 std::make_shared<BluetoothAudioPortAidlOut>());
289 const auto& devicePort = audioPort.ext.get<AudioPortExt::device>();
290 const auto device = devicePort.device.type;
291 bool registrationSuccess = false;
292 for (int i = 0; i < kCreateProxyRetries && !registrationSuccess; ++i) {
293 registrationSuccess = proxy.ptr->registerPort(device);
294 usleep(kCreateProxyRetrySleepMs * 1000);
295 }
296 if (!registrationSuccess) {
297 LOG(ERROR) << __func__ << ": failed to register BT port for " << device.toString();
298 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
299 }
300 if (!proxy.ptr->loadAudioConfig(proxy.pcmConfig)) {
301 LOG(ERROR) << __func__ << ": state=" << proxy.ptr->getState()
302 << ", failed to load audio config";
303 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
304 }
305 mProxies.insert(std::pair(instancePortId, proxy));
306 return ndk::ScopedAStatus::ok();
307 }
308
fetchAndCheckProxy(const StreamContext & context,CachedProxy & proxy)309 ndk::ScopedAStatus ModuleBluetooth::fetchAndCheckProxy(const StreamContext& context,
310 CachedProxy& proxy) {
311 const auto connectionsIt = mConnections.find(context.getMixPortHandle());
312 if (connectionsIt != mConnections.end()) {
313 const auto proxyIt = mProxies.find(connectionsIt->second);
314 if (proxyIt != mProxies.end()) {
315 proxy = proxyIt->second;
316 mProxies.erase(proxyIt);
317 }
318 mConnections.erase(connectionsIt);
319 }
320 if (proxy.ptr != nullptr) {
321 if (!StreamBluetooth::checkConfigParams(
322 proxy.pcmConfig, AudioConfigBase{.sampleRate = context.getSampleRate(),
323 .channelMask = context.getChannelLayout(),
324 .format = context.getFormat()})) {
325 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
326 }
327 }
328 // Not having a proxy is OK, it may happen in VTS tests when streams are opened on unconnected
329 // mix ports.
330 return ndk::ScopedAStatus::ok();
331 }
332
findOrCreateProxy(const AudioPort & audioPort,CachedProxy & proxy)333 ndk::ScopedAStatus ModuleBluetooth::findOrCreateProxy(const AudioPort& audioPort,
334 CachedProxy& proxy) {
335 if (auto proxyIt = mProxies.find(audioPort.id); proxyIt != mProxies.end()) {
336 proxy = proxyIt->second;
337 return ndk::ScopedAStatus::ok();
338 }
339 return createProxy(audioPort, audioPort.id, proxy);
340 }
341
342 } // namespace aidl::android::hardware::audio::core
343