blob: 9084b3044c023c818d26abf6e1c6bf3077155f7b [file] [log] [blame]
Mikhail Naganovb03b5c42023-07-26 13:13:35 -07001/*
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 Naganov55045b52023-10-24 17:03:50 -070021#include "BluetoothAudioSession.h"
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070022#include "core-impl/ModuleBluetooth.h"
23#include "core-impl/StreamBluetooth.h"
24
Mikhail Naganov55045b52023-10-24 17:03:50 -070025using aidl::android::hardware::audio::common::SinkMetadata;
26using aidl::android::hardware::audio::common::SourceMetadata;
Mikhail Naganova88cf602023-12-13 14:35:11 -080027using aidl::android::hardware::bluetooth::audio::ChannelMode;
28using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
29using aidl::android::media::audio::common::AudioChannelLayout;
30using aidl::android::media::audio::common::AudioConfigBase;
Mikhail Naganov55045b52023-10-24 17:03:50 -070031using aidl::android::media::audio::common::AudioDeviceDescription;
32using aidl::android::media::audio::common::AudioDeviceType;
Mikhail Naganova88cf602023-12-13 14:35:11 -080033using aidl::android::media::audio::common::AudioFormatDescription;
34using aidl::android::media::audio::common::AudioFormatType;
35using aidl::android::media::audio::common::AudioIoFlags;
Mikhail Naganov55045b52023-10-24 17:03:50 -070036using aidl::android::media::audio::common::AudioOffloadInfo;
37using aidl::android::media::audio::common::AudioPort;
Mikhail Naganova88cf602023-12-13 14:35:11 -080038using aidl::android::media::audio::common::AudioPortConfig;
Mikhail Naganov55045b52023-10-24 17:03:50 -070039using aidl::android::media::audio::common::AudioPortExt;
Mikhail Naganova88cf602023-12-13 14:35:11 -080040using aidl::android::media::audio::common::AudioProfile;
41using aidl::android::media::audio::common::Int;
Mikhail Naganov55045b52023-10-24 17:03:50 -070042using aidl::android::media::audio::common::MicrophoneInfo;
Mikhail Naganova88cf602023-12-13 14:35:11 -080043using aidl::android::media::audio::common::PcmType;
Mikhail Naganov55045b52023-10-24 17:03:50 -070044using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
Mikhail Naganova88cf602023-12-13 14:35:11 -080045using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
Mikhail Naganov55045b52023-10-24 17:03:50 -070046using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070047
Devin Moore0b2b93d2023-11-20 22:10:21 +000048// TODO(b/312265159) bluetooth audio should be in its own process
49// Remove this and the shared_libs when that happens
50extern "C" binder_status_t createIBluetoothAudioProviderFactory();
51
Mikhail Naganov55045b52023-10-24 17:03:50 -070052namespace aidl::android::hardware::audio::core {
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070053
Mikhail Naganova88cf602023-12-13 14:35:11 -080054namespace {
55
56PcmType 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
69AudioChannelLayout 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 Moore0b2b93d2023-11-20 22:10:21 +000083ModuleBluetooth::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 Mohan18f0d512023-07-01 00:47:09 +053094ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
95 std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
Mikhail Naganov55045b52023-10-24 17:03:50 -070096 *_aidl_return = getBtA2dp().getInstance();
Ram Mohan18f0d512023-07-01 00:47:09 +053097 LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
98 return ndk::ScopedAStatus::ok();
99}
100
101ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
Mikhail Naganov55045b52023-10-24 17:03:50 -0700102 *_aidl_return = getBtLe().getInstance();
103 LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
104 return ndk::ScopedAStatus::ok();
105}
106
107ChildInterface<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
116ChildInterface<BluetoothLe>& ModuleBluetooth::getBtLe() {
Ram Mohan18f0d512023-07-01 00:47:09 +0530117 if (!mBluetoothLe) {
118 auto handle = ndk::SharedRefBase::make<BluetoothLe>();
119 handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
120 mBluetoothLe = handle;
121 }
Mikhail Naganov55045b52023-10-24 17:03:50 -0700122 return mBluetoothLe;
Ram Mohan18f0d512023-07-01 00:47:09 +0530123}
124
Mikhail Naganov55045b52023-10-24 17:03:50 -0700125ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
126 return std::make_tuple(std::weak_ptr<IBluetooth>(), getBtA2dp().getPtr(), getBtLe().getPtr());
Ram Mohan18f0d512023-07-01 00:47:09 +0530127}
128
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700129ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
130 LOG(DEBUG) << __func__ << ": is not supported";
131 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
132}
133
134ndk::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 Naganova88cf602023-12-13 14:35:11 -0800139ndk::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
168ndk::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
189void ModuleBluetooth::onExternalDeviceConnectionChanged(const AudioPort& audioPort,
190 bool connected) {
191 if (!connected) mProxies.erase(audioPort.id);
192}
193
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700194ndk::ScopedAStatus ModuleBluetooth::createInputStream(
195 StreamContext&& context, const SinkMetadata& sinkMetadata,
196 const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
Mikhail Naganova88cf602023-12-13 14:35:11 -0800197 CachedProxy proxy;
198 RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy));
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700199 return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata,
Mikhail Naganova88cf602023-12-13 14:35:11 -0800200 microphones, getBtProfileManagerHandles(),
201 proxy.ptr, proxy.pcmConfig);
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700202}
203
204ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
205 StreamContext&& context, const SourceMetadata& sourceMetadata,
206 const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
Mikhail Naganova88cf602023-12-13 14:35:11 -0800207 CachedProxy proxy;
208 RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy));
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700209 return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata,
Mikhail Naganova88cf602023-12-13 14:35:11 -0800210 offloadInfo, getBtProfileManagerHandles(),
211 proxy.ptr, proxy.pcmConfig);
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700212}
213
Mikhail Naganova88cf602023-12-13 14:35:11 -0800214ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort,
215 int32_t nextPortId) {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700216 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 Naganova88cf602023-12-13 14:35:11 -0800220 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 Naganov84bcc042023-10-05 17:36:57 -0700224 const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
225 const auto& description = devicePort.device.type;
Mikhail Naganova88cf602023-12-13 14:35:11 -0800226 // This method must return an error when the device can not be connected.
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700227 if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) {
228 bool isA2dpEnabled = false;
229 if (!!mBluetoothA2dp) {
Mikhail Naganov55045b52023-10-24 17:03:50 -0700230 RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled));
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700231 }
Mikhail Naganov55045b52023-10-24 17:03:50 -0700232 LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled;
Mikhail Naganova88cf602023-12-13 14:35:11 -0800233 if (!isA2dpEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700234 } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) {
235 bool isLeEnabled = false;
236 if (!!mBluetoothLe) {
Mikhail Naganov55045b52023-10-24 17:03:50 -0700237 RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled));
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700238 }
Mikhail Naganov55045b52023-10-24 17:03:50 -0700239 LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled;
Mikhail Naganova88cf602023-12-13 14:35:11 -0800240 if (!isLeEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700241 } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
242 description.type == AudioDeviceType::OUT_HEARING_AID) {
Mikhail Naganova88cf602023-12-13 14:35:11 -0800243 // 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 Naganov84bcc042023-10-05 17:36:57 -0700247 }
Mikhail Naganova88cf602023-12-13 14:35:11 -0800248 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 Naganov84bcc042023-10-05 17:36:57 -0700263}
264
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700265ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
266 LOG(DEBUG) << __func__ << ": is not supported";
267 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
268}
269
270ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) {
271 LOG(DEBUG) << __func__ << ": is not supported";
272 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
273}
274
Mikhail Naganova88cf602023-12-13 14:35:11 -0800275int32_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
294ndk::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
315ndk::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
339ndk::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 Naganovb03b5c42023-07-26 13:13:35 -0700348} // namespace aidl::android::hardware::audio::core