blob: ea2c54afe19e2d7df9eb71563dc4a15ed26d8155 [file] [log] [blame]
Grzegorz Kolodziejczykb5f2d232019-10-24 12:31:20 +02001/*
2 * Copyright 2020 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 "BTAudioProviderSession"
18
19#include "BluetoothAudioSession.h"
20
21#include <android-base/logging.h>
22#include <android-base/stringprintf.h>
23
24namespace android {
25namespace bluetooth {
26namespace audio {
27
28using ::android::hardware::audio::common::V5_0::AudioContentType;
29using ::android::hardware::audio::common::V5_0::AudioUsage;
30using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
31using ::android::hardware::audio::common::V5_0::SourceMetadata;
32using ::android::hardware::bluetooth::audio::V2_0::CodecType;
33using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
34
35const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
36 .codecType = CodecType::UNKNOWN,
37 .encodedAudioBitrate = 0x00000000,
38 .peerMtu = 0xffff,
39 .isScmstEnabled = false,
40 .config = {}};
41AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
42 {};
43AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
44
45static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
46static constexpr int kFmqReceiveTimeoutMs =
47 1000; // 1000 ms timeout for receiving
48static constexpr int kWritePollMs = 1; // polled non-blocking interval
49static constexpr int kReadPollMs = 1; // polled non-blocking interval
50
51static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
52 return {.tv_sec = static_cast<long>(TS.tvSec),
53 .tv_nsec = static_cast<long>(TS.tvNSec)};
54}
55
56BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
57 : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
58 invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
59 invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
60}
61
62// The report function is used to report that the Bluetooth stack has started
63// this session without any failure, and will invoke session_changed_cb_ to
64// notify those registered bluetooth_audio outputs
65void BluetoothAudioSession::OnSessionStarted(
66 const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
67 const AudioConfiguration& audio_config) {
68 std::lock_guard<std::recursive_mutex> guard(mutex_);
69 if (stack_iface == nullptr) {
70 LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
71 << ", IBluetoothAudioPort Invalid";
72 } else if (!UpdateAudioConfig(audio_config)) {
73 LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
74 << ", AudioConfiguration=" << toString(audio_config)
75 << " Invalid";
76 } else if (!UpdateDataPath(dataMQ)) {
77 LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
78 << " DataMQ Invalid";
79 audio_config_ =
80 (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
81 ? kInvalidOffloadAudioConfiguration
82 : kInvalidSoftwareAudioConfiguration);
83 } else {
84 stack_iface_ = stack_iface;
85 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
86 << ", AudioConfiguration=" << toString(audio_config);
87 ReportSessionStatus();
88 }
89}
90
91// The report function is used to report that the Bluetooth stack has ended the
92// session, and will invoke session_changed_cb_ to notify registered
93// bluetooth_audio outputs
94void BluetoothAudioSession::OnSessionEnded() {
95 std::lock_guard<std::recursive_mutex> guard(mutex_);
96 bool toggled = IsSessionReady();
97 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
98 audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
99 ? kInvalidOffloadAudioConfiguration
100 : kInvalidSoftwareAudioConfiguration);
101 stack_iface_ = nullptr;
102 UpdateDataPath(nullptr);
103 if (toggled) {
104 ReportSessionStatus();
105 }
106}
107
108// invoking the registered session_changed_cb_
109void BluetoothAudioSession::ReportSessionStatus() {
110 // This is locked already by OnSessionStarted / OnSessionEnded
111 if (observers_.empty()) {
112 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
113 << " has NO port state observer";
114 return;
115 }
116 for (auto& observer : observers_) {
117 uint16_t cookie = observer.first;
118 std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
119 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
120 << " notify to bluetooth_audio=0x"
121 << android::base::StringPrintf("%04x", cookie);
122 cb->session_changed_cb_(cookie);
123 }
124}
125
126// The report function is used to report that the Bluetooth stack has notified
127// the result of startStream or suspendStream, and will invoke
128// control_result_cb_ to notify registered bluetooth_audio outputs
129void BluetoothAudioSession::ReportControlStatus(
130 bool start_resp, const BluetoothAudioStatus& status) {
131 std::lock_guard<std::recursive_mutex> guard(mutex_);
132 if (observers_.empty()) {
133 LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
134 << " has NO port state observer";
135 return;
136 }
137 for (auto& observer : observers_) {
138 uint16_t cookie = observer.first;
139 std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
140 LOG(INFO) << __func__ << " - status=" << toString(status)
141 << " for SessionType=" << toString(session_type_)
142 << ", bluetooth_audio=0x"
143 << android::base::StringPrintf("%04x", cookie)
144 << (start_resp ? " started" : " suspended");
145 cb->control_result_cb_(cookie, start_resp, status);
146 }
147}
148
149// The function helps to check if this session is ready or not
150// @return: true if the Bluetooth stack has started the specified session
151bool BluetoothAudioSession::IsSessionReady() {
152 std::lock_guard<std::recursive_mutex> guard(mutex_);
153 bool dataMQ_valid =
154 (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
155 (mDataMQ != nullptr && mDataMQ->isValid()));
156 return stack_iface_ != nullptr && dataMQ_valid;
157}
158
159bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
160 if (dataMQ == nullptr) {
161 // usecase of reset by nullptr
162 mDataMQ = nullptr;
163 return true;
164 }
165 std::unique_ptr<DataMQ> tempDataMQ;
166 tempDataMQ.reset(new DataMQ(*dataMQ));
167 if (!tempDataMQ || !tempDataMQ->isValid()) {
168 mDataMQ = nullptr;
169 return false;
170 }
171 mDataMQ = std::move(tempDataMQ);
172 return true;
173}
174
175bool BluetoothAudioSession::UpdateAudioConfig(
176 const AudioConfiguration& audio_config) {
177 bool is_software_session =
178 (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
179 session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
180 session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
181 session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
182 bool is_offload_session =
183 (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
184 auto audio_config_discriminator = audio_config.getDiscriminator();
185 bool is_software_audio_config =
186 (is_software_session &&
187 audio_config_discriminator ==
188 AudioConfiguration::hidl_discriminator::pcmConfig);
189 bool is_offload_audio_config =
190 (is_offload_session &&
191 audio_config_discriminator ==
192 AudioConfiguration::hidl_discriminator::codecConfig);
193 if (!is_software_audio_config && !is_offload_audio_config) {
194 return false;
195 }
196 audio_config_ = audio_config;
197 return true;
198}
199
200// The control function helps the bluetooth_audio module to register
201// PortStatusCallbacks
202// @return: cookie - the assigned number to this bluetooth_audio output
203uint16_t BluetoothAudioSession::RegisterStatusCback(
204 const PortStatusCallbacks& cbacks) {
205 std::lock_guard<std::recursive_mutex> guard(mutex_);
206 uint16_t cookie = ObserversCookieGetInitValue(session_type_);
207 uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
208
209 while (cookie < cookie_upper_bound) {
210 if (observers_.find(cookie) == observers_.end()) {
211 break;
212 }
213 ++cookie;
214 }
215 if (cookie >= cookie_upper_bound) {
216 LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
217 << " has " << observers_.size()
218 << " observers already (No Resource)";
219 return kObserversCookieUndefined;
220 }
221 std::shared_ptr<struct PortStatusCallbacks> cb =
222 std::make_shared<struct PortStatusCallbacks>();
223 *cb = cbacks;
224 observers_[cookie] = cb;
225 return cookie;
226}
227
228// The control function helps the bluetooth_audio module to unregister
229// PortStatusCallbacks
230// @param: cookie - indicates which bluetooth_audio output is
231void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
232 std::lock_guard<std::recursive_mutex> guard(mutex_);
233 if (observers_.erase(cookie) != 1) {
234 LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
235 << " no such provider=0x"
236 << android::base::StringPrintf("%04x", cookie);
237 }
238}
239
240// The control function is for the bluetooth_audio module to get the current
241// AudioConfiguration
242const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
243 std::lock_guard<std::recursive_mutex> guard(mutex_);
244 if (IsSessionReady()) {
245 return audio_config_;
246 } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
247 return kInvalidOffloadAudioConfiguration;
248 } else {
249 return kInvalidSoftwareAudioConfiguration;
250 }
251}
252
253// Those control functions are for the bluetooth_audio module to start, suspend,
254// stop stream, to check position, and to update metadata.
255bool BluetoothAudioSession::StartStream() {
256 std::lock_guard<std::recursive_mutex> guard(mutex_);
257 if (!IsSessionReady()) {
258 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
259 << " has NO session";
260 return false;
261 }
262 auto hal_retval = stack_iface_->startStream();
263 if (!hal_retval.isOk()) {
264 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
265 << toString(session_type_) << " failed";
266 return false;
267 }
268 return true;
269}
270
271bool BluetoothAudioSession::SuspendStream() {
272 std::lock_guard<std::recursive_mutex> guard(mutex_);
273 if (!IsSessionReady()) {
274 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
275 << " has NO session";
276 return false;
277 }
278 auto hal_retval = stack_iface_->suspendStream();
279 if (!hal_retval.isOk()) {
280 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
281 << toString(session_type_) << " failed";
282 return false;
283 }
284 return true;
285}
286
287void BluetoothAudioSession::StopStream() {
288 std::lock_guard<std::recursive_mutex> guard(mutex_);
289 if (!IsSessionReady()) {
290 return;
291 }
292 auto hal_retval = stack_iface_->stopStream();
293 if (!hal_retval.isOk()) {
294 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
295 << toString(session_type_) << " failed";
296 }
297}
298
299bool BluetoothAudioSession::GetPresentationPosition(
300 uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
301 timespec* data_position) {
302 std::lock_guard<std::recursive_mutex> guard(mutex_);
303 if (!IsSessionReady()) {
304 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
305 << " has NO session";
306 return false;
307 }
308 bool retval = false;
309 auto hal_retval = stack_iface_->getPresentationPosition(
310 [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
311 BluetoothAudioStatus status,
312 const uint64_t& remoteDeviceAudioDelayNanos,
313 uint64_t transmittedOctets,
314 const TimeSpec& transmittedOctetsTimeStamp) {
315 if (status == BluetoothAudioStatus::SUCCESS) {
316 if (remote_delay_report_ns)
317 *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
318 if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
319 if (data_position)
320 *data_position =
321 timespec_convert_from_hal(transmittedOctetsTimeStamp);
322 retval = true;
323 }
324 });
325 if (!hal_retval.isOk()) {
326 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
327 << toString(session_type_) << " failed";
328 return false;
329 }
330 return retval;
331}
332
333void BluetoothAudioSession::UpdateTracksMetadata(
334 const struct source_metadata* source_metadata) {
335 std::lock_guard<std::recursive_mutex> guard(mutex_);
336 if (!IsSessionReady()) {
337 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
338 << " has NO session";
339 return;
340 }
341
342 ssize_t track_count = source_metadata->track_count;
343 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
344 << track_count << " track(s)";
345 if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
346 session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
347 return;
348 }
349
350 struct playback_track_metadata* track = source_metadata->tracks;
351 SourceMetadata sourceMetadata;
352 PlaybackTrackMetadata* halMetadata;
353
354 sourceMetadata.tracks.resize(track_count);
355 halMetadata = sourceMetadata.tracks.data();
356 while (track_count && track) {
357 halMetadata->usage = static_cast<AudioUsage>(track->usage);
358 halMetadata->contentType =
359 static_cast<AudioContentType>(track->content_type);
360 halMetadata->gain = track->gain;
361 LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
362 << ", usage=" << toString(halMetadata->usage)
363 << ", content=" << toString(halMetadata->contentType)
364 << ", gain=" << halMetadata->gain;
365 --track_count;
366 ++track;
367 ++halMetadata;
368 }
369 auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
370 if (!hal_retval.isOk()) {
371 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
372 << toString(session_type_) << " failed";
373 }
374}
375
376// The control function writes stream to FMQ
377size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
378 size_t bytes) {
379 if (buffer == nullptr || !bytes) return 0;
380 size_t totalWritten = 0;
381 int ms_timeout = kFmqSendTimeoutMs;
382 do {
383 std::unique_lock<std::recursive_mutex> lock(mutex_);
384 if (!IsSessionReady()) break;
385 size_t availableToWrite = mDataMQ->availableToWrite();
386 if (availableToWrite) {
387 if (availableToWrite > (bytes - totalWritten)) {
388 availableToWrite = bytes - totalWritten;
389 }
390
391 if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
392 availableToWrite)) {
393 ALOGE("FMQ datapath writing %zu/%zu failed", totalWritten, bytes);
394 return totalWritten;
395 }
396 totalWritten += availableToWrite;
397 } else if (ms_timeout >= kWritePollMs) {
398 lock.unlock();
399 usleep(kWritePollMs * 1000);
400 ms_timeout -= kWritePollMs;
401 } else {
402 ALOGD("out data %zu/%zu overflow %d ms", totalWritten, bytes,
403 (kFmqSendTimeoutMs - ms_timeout));
404 return totalWritten;
405 }
406 } while (totalWritten < bytes);
407 return totalWritten;
408}
409
410// The control function reads stream from FMQ
411size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
412 if (buffer == nullptr || !bytes) return 0;
413 size_t totalRead = 0;
414 int ms_timeout = kFmqReceiveTimeoutMs;
415 do {
416 std::unique_lock<std::recursive_mutex> lock(mutex_);
417 if (!IsSessionReady()) break;
418 size_t availableToRead = mDataMQ->availableToRead();
419 if (availableToRead) {
420 if (availableToRead > (bytes - totalRead)) {
421 availableToRead = bytes - totalRead;
422 }
423 if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
424 availableToRead)) {
425 ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
426 return totalRead;
427 }
428 totalRead += availableToRead;
429 } else if (ms_timeout >= kReadPollMs) {
430 lock.unlock();
431 usleep(kReadPollMs * 1000);
432 ms_timeout -= kReadPollMs;
433 continue;
434 } else {
435 ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
436 (kFmqReceiveTimeoutMs - ms_timeout));
437 return totalRead;
438 }
439 } while (totalRead < bytes);
440 return totalRead;
441}
442
443std::unique_ptr<BluetoothAudioSessionInstance>
444 BluetoothAudioSessionInstance::instance_ptr =
445 std::unique_ptr<BluetoothAudioSessionInstance>(
446 new BluetoothAudioSessionInstance());
447
448// API to fetch the session
449std::shared_ptr<BluetoothAudioSession>
450BluetoothAudioSessionInstance::GetSessionInstance(
451 const SessionType& session_type) {
452 std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
453 if (!instance_ptr->sessions_map_.empty()) {
454 auto entry = instance_ptr->sessions_map_.find(session_type);
455 if (entry != instance_ptr->sessions_map_.end()) {
456 return entry->second;
457 }
458 }
459 std::shared_ptr<BluetoothAudioSession> session_ptr =
460 std::make_shared<BluetoothAudioSession>(session_type);
461 instance_ptr->sessions_map_[session_type] = session_ptr;
462 return session_ptr;
463}
464
465} // namespace audio
466} // namespace bluetooth
467} // namespace android