blob: 0cee7f4002cbb4acc83dd842d3921290ff751def [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_StreamBluetooth"
18
19#include <Utils.h>
20#include <android-base/logging.h>
21#include <audio_utils/clock.h>
22
Mikhail Naganov55045b52023-10-24 17:03:50 -070023#include "BluetoothAudioSession.h"
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070024#include "core-impl/StreamBluetooth.h"
25
Mikhail Naganov55045b52023-10-24 17:03:50 -070026using aidl::android::hardware::audio::common::frameCountFromDurationUs;
27using aidl::android::hardware::audio::common::SinkMetadata;
28using aidl::android::hardware::audio::common::SourceMetadata;
29using aidl::android::hardware::audio::core::VendorParameter;
30using aidl::android::hardware::bluetooth::audio::ChannelMode;
31using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
32using aidl::android::hardware::bluetooth::audio::PresentationPosition;
33using aidl::android::media::audio::common::AudioChannelLayout;
34using aidl::android::media::audio::common::AudioDevice;
35using aidl::android::media::audio::common::AudioDeviceAddress;
36using aidl::android::media::audio::common::AudioFormatDescription;
37using aidl::android::media::audio::common::AudioFormatType;
38using aidl::android::media::audio::common::AudioOffloadInfo;
39using aidl::android::media::audio::common::MicrophoneDynamicInfo;
40using aidl::android::media::audio::common::MicrophoneInfo;
41using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
42using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
43using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
44using android::bluetooth::audio::aidl::BluetoothStreamState;
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070045
Mikhail Naganov55045b52023-10-24 17:03:50 -070046namespace aidl::android::hardware::audio::core {
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070047
48constexpr int kBluetoothDefaultInputBufferMs = 20;
49constexpr int kBluetoothDefaultOutputBufferMs = 10;
50// constexpr int kBluetoothSpatializerOutputBufferMs = 10;
51
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070052// pcm configuration params are not really used by the module
Ram Mohan18f0d512023-07-01 00:47:09 +053053StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata,
Mikhail Naganov55045b52023-10-24 17:03:50 -070054 ModuleBluetooth::BtProfileHandles&& btHandles)
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070055 : StreamCommonImpl(context, metadata),
56 mSampleRate(getContext().getSampleRate()),
57 mChannelLayout(getContext().getChannelLayout()),
58 mFormat(getContext().getFormat()),
59 mFrameSizeBytes(getContext().getFrameSize()),
Ram Mohan18f0d512023-07-01 00:47:09 +053060 mIsInput(isInput(metadata)),
Mikhail Naganov55045b52023-10-24 17:03:50 -070061 mBluetoothA2dp(std::move(std::get<ModuleBluetooth::BtInterface::BTA2DP>(btHandles))),
62 mBluetoothLe(std::move(std::get<ModuleBluetooth::BtInterface::BTLE>(btHandles))) {
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070063 mPreferredDataIntervalUs =
Mikhail Naganov55045b52023-10-24 17:03:50 -070064 (mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs) * 1000;
65 mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070066 mIsInitialized = false;
67 mIsReadyToClose = false;
68}
69
70::android::status_t StreamBluetooth::init() {
71 return ::android::OK; // defering this till we get AudioDeviceDescription
72}
73
74const StreamCommonInterface::ConnectedDevices& StreamBluetooth::getConnectedDevices() const {
75 std::lock_guard guard(mLock);
76 return StreamCommonImpl::getConnectedDevices();
77}
78
79ndk::ScopedAStatus StreamBluetooth::setConnectedDevices(
80 const std::vector<AudioDevice>& connectedDevices) {
81 if (mIsInput && connectedDevices.size() > 1) {
82 LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
83 << ") for input stream";
84 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
85 }
86 for (const auto& connectedDevice : connectedDevices) {
87 if (connectedDevice.address.getTag() != AudioDeviceAddress::mac) {
88 LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
89 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
90 }
91 }
92 std::lock_guard guard(mLock);
93 RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
94 mIsInitialized = false; // updated connected device list, need initialization
95 return ndk::ScopedAStatus::ok();
96}
97
98::android::status_t StreamBluetooth::drain(StreamDescriptor::DrainMode) {
99 usleep(1000);
100 return ::android::OK;
101}
102
103::android::status_t StreamBluetooth::flush() {
104 usleep(1000);
105 return ::android::OK;
106}
107
108::android::status_t StreamBluetooth::pause() {
109 return standby();
110}
111
112::android::status_t StreamBluetooth::transfer(void* buffer, size_t frameCount,
113 size_t* actualFrameCount, int32_t* latencyMs) {
114 std::lock_guard guard(mLock);
115 if (!mIsInitialized || mIsReadyToClose) {
116 // 'setConnectedDevices' has been called or stream is ready to close, so no transfers
117 *actualFrameCount = 0;
118 *latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
119 return ::android::OK;
120 }
121 *actualFrameCount = 0;
122 *latencyMs = 0;
123 for (auto proxy : mBtDeviceProxies) {
124 if (!proxy->start()) {
125 LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to start ";
126 return -EIO;
127 }
128 const size_t fc = std::min(frameCount, mPreferredFrameCount);
129 const size_t bytesToTransfer = fc * mFrameSizeBytes;
130 if (mIsInput) {
131 const size_t totalRead = proxy->readData(buffer, bytesToTransfer);
132 *actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes);
133 } else {
134 const size_t totalWrite = proxy->writeData(buffer, bytesToTransfer);
135 *actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes);
136 }
137 PresentationPosition presentation_position;
138 if (!proxy->getPresentationPosition(presentation_position)) {
139 LOG(ERROR) << __func__ << ": getPresentationPosition returned error ";
140 return ::android::UNKNOWN_ERROR;
141 }
142 *latencyMs =
143 std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos /
144 NANOS_PER_MILLISECOND));
145 }
146 return ::android::OK;
147}
148
149::android::status_t StreamBluetooth::initialize() {
150 if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
151 LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory service not available";
152 return ::android::UNKNOWN_ERROR;
153 }
154 if (StreamCommonImpl::getConnectedDevices().empty()) {
155 LOG(ERROR) << __func__ << ", has no connected devices";
156 return ::android::NO_INIT;
157 }
158 // unregister older proxies (if any)
159 for (auto proxy : mBtDeviceProxies) {
160 proxy->stop();
161 proxy->unregisterPort();
162 }
163 mBtDeviceProxies.clear();
164 for (auto it = StreamCommonImpl::getConnectedDevices().begin();
165 it != StreamCommonImpl::getConnectedDevices().end(); ++it) {
166 std::shared_ptr<BluetoothAudioPortAidl> proxy =
167 mIsInput ? std::shared_ptr<BluetoothAudioPortAidl>(
168 std::make_shared<BluetoothAudioPortAidlIn>())
169 : std::shared_ptr<BluetoothAudioPortAidl>(
170 std::make_shared<BluetoothAudioPortAidlOut>());
171 if (proxy->registerPort(it->type)) {
172 LOG(ERROR) << __func__ << ": cannot init HAL";
173 return ::android::UNKNOWN_ERROR;
174 }
175 PcmConfiguration config;
176 if (!proxy->loadAudioConfig(&config)) {
177 LOG(ERROR) << __func__ << ": state=" << proxy->getState()
178 << " failed to get audio config";
179 return ::android::UNKNOWN_ERROR;
180 }
181 // TODO: Ensure minimum duration for spatialized output?
182 // WAR to support Mono / 16 bits per sample as the Bluetooth stack required
183 if (!mIsInput && config.channelMode == ChannelMode::MONO && config.bitsPerSample == 16) {
184 proxy->forcePcmStereoToMono(true);
185 config.channelMode = ChannelMode::STEREO;
186 LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO";
187 }
188 if (!checkConfigParams(config)) {
189 LOG(ERROR) << __func__ << " checkConfigParams failed";
190 return ::android::UNKNOWN_ERROR;
191 }
192 mBtDeviceProxies.push_back(std::move(proxy));
193 }
194 mIsInitialized = true;
195 return ::android::OK;
196}
197
198bool StreamBluetooth::checkConfigParams(
199 ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config) {
200 if ((int)mSampleRate != config.sampleRateHz) {
201 LOG(ERROR) << __func__ << ": Sample Rate mismatch, stream val = " << mSampleRate
202 << " hal val = " << config.sampleRateHz;
203 return false;
204 }
205 auto channelCount = aidl::android::hardware::audio::common::getChannelCount(mChannelLayout);
206 if ((config.channelMode == ChannelMode::MONO && channelCount != 1) ||
207 (config.channelMode == ChannelMode::STEREO && channelCount != 2)) {
208 LOG(ERROR) << __func__ << ": Channel count mismatch, stream val = " << channelCount
209 << " hal val = " << toString(config.channelMode);
210 return false;
211 }
212 if (mFormat.type != AudioFormatType::PCM) {
213 LOG(ERROR) << __func__ << ": unexpected format type "
214 << aidl::android::media::audio::common::toString(mFormat.type);
215 return false;
216 }
217 int8_t bps = aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(mFormat.pcm) * 8;
218 if (bps != config.bitsPerSample) {
219 LOG(ERROR) << __func__ << ": bits per sample mismatch, stream val = " << bps
220 << " hal val = " << config.bitsPerSample;
221 return false;
222 }
223 if (config.dataIntervalUs > 0) {
224 mPreferredDataIntervalUs =
225 std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
Mikhail Naganov55045b52023-10-24 17:03:50 -0700226 mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700227 }
228 return true;
229}
230
231ndk::ScopedAStatus StreamBluetooth::prepareToClose() {
232 std::lock_guard guard(mLock);
233 mIsReadyToClose = true;
234 return ndk::ScopedAStatus::ok();
235}
236
237::android::status_t StreamBluetooth::standby() {
238 std::lock_guard guard(mLock);
239 if (!mIsInitialized) {
240 if (auto status = initialize(); status != ::android::OK) return status;
241 }
242 for (auto proxy : mBtDeviceProxies) {
243 if (!proxy->suspend()) {
244 LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to stand by ";
245 return -EIO;
246 }
247 }
248 return ::android::OK;
249}
250
251::android::status_t StreamBluetooth::start() {
252 std::lock_guard guard(mLock);
253 if (!mIsInitialized) return initialize();
254 return ::android::OK;
255}
256
257void StreamBluetooth::shutdown() {
258 std::lock_guard guard(mLock);
259 for (auto proxy : mBtDeviceProxies) {
260 proxy->stop();
261 proxy->unregisterPort();
262 }
263 mBtDeviceProxies.clear();
264}
265
266ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadata) {
267 std::lock_guard guard(mLock);
268 if (!mIsInitialized) return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
269 bool isOk = true;
270 if (isInput(metadata)) {
271 isOk = mBtDeviceProxies[0]->updateSinkMetadata(std::get<SinkMetadata>(metadata));
272 } else {
273 for (auto proxy : mBtDeviceProxies) {
274 if (!proxy->updateSourceMetadata(std::get<SourceMetadata>(metadata))) isOk = false;
275 }
276 }
277 return isOk ? ndk::ScopedAStatus::ok()
278 : ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
279}
280
Ram Mohan18f0d512023-07-01 00:47:09 +0530281ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() {
282 if (mIsInput) {
283 LOG(WARNING) << __func__ << ": not handled";
284 return ndk::ScopedAStatus::ok();
285 }
286 auto applyParam = [](const std::shared_ptr<BluetoothAudioPortAidl>& proxy,
287 bool isEnabled) -> bool {
288 if (!isEnabled) {
289 if (proxy->suspend()) return proxy->setState(BluetoothStreamState::DISABLED);
290 return false;
291 }
292 return proxy->standby();
293 };
294 bool hasA2dpParam, enableA2dp;
295 auto btA2dp = mBluetoothA2dp.lock();
296 hasA2dpParam = btA2dp != nullptr && btA2dp->isEnabled(&enableA2dp).isOk();
297 bool hasLeParam, enableLe;
298 auto btLe = mBluetoothLe.lock();
299 hasLeParam = btLe != nullptr && btLe->isEnabled(&enableLe).isOk();
300 std::unique_lock lock(mLock);
301 ::android::base::ScopedLockAssertion lock_assertion(mLock);
302 if (!mIsInitialized) {
303 LOG(WARNING) << __func__ << ": init not done";
304 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
305 }
306 for (auto proxy : mBtDeviceProxies) {
307 if ((hasA2dpParam && proxy->isA2dp() && !applyParam(proxy, enableA2dp)) ||
308 (hasLeParam && proxy->isLeAudio() && !applyParam(proxy, enableLe))) {
309 LOG(DEBUG) << __func__ << ": applyParam failed";
310 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
311 }
312 }
313 return ndk::ScopedAStatus::ok();
314}
315
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700316StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
Ram Mohan18f0d512023-07-01 00:47:09 +0530317 const std::vector<MicrophoneInfo>& microphones,
Mikhail Naganov55045b52023-10-24 17:03:50 -0700318 ModuleBluetooth::BtProfileHandles&& btProfileHandles)
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700319 : StreamIn(std::move(context), microphones),
Mikhail Naganov459b7332023-08-03 10:26:21 -0700320 StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles)) {}
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700321
322ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones(
323 std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
324 LOG(DEBUG) << __func__ << ": not supported";
325 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
326}
327
328StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
329 const SourceMetadata& sourceMetadata,
Ram Mohan18f0d512023-07-01 00:47:09 +0530330 const std::optional<AudioOffloadInfo>& offloadInfo,
Mikhail Naganov55045b52023-10-24 17:03:50 -0700331 ModuleBluetooth::BtProfileHandles&& btProfileHandles)
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700332 : StreamOut(std::move(context), offloadInfo),
Mikhail Naganov459b7332023-08-03 10:26:21 -0700333 StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles)) {}
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700334
335} // namespace aidl::android::hardware::audio::core