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