1 /*
2 * Copyright 2018 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
24 namespace android {
25 namespace bluetooth {
26 namespace audio {
27
28 using ::android::hardware::audio::common::V5_0::AudioContentType;
29 using ::android::hardware::audio::common::V5_0::AudioUsage;
30 using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
31 using ::android::hardware::audio::common::V5_0::SourceMetadata;
32 using ::android::hardware::bluetooth::audio::V2_0::CodecType;
33 using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
34
35 const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
36 .codecType = CodecType::UNKNOWN,
37 .encodedAudioBitrate = 0x00000000,
38 .peerMtu = 0xffff,
39 .isScmstEnabled = false,
40 .config = {}};
41 AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
42 {};
43 AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
44
45 static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
46 static constexpr int kFmqReceiveTimeoutMs =
47 1000; // 1000 ms timeout for receiving
48 static constexpr int kWritePollMs = 1; // polled non-blocking interval
49 static constexpr int kReadPollMs = 1; // polled non-blocking interval
50
timespec_convert_from_hal(const TimeSpec & TS)51 static 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
BluetoothAudioSession(const SessionType & session_type)56 BluetoothAudioSession::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
OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,const DataMQ::Descriptor * dataMQ,const AudioConfiguration & audio_config)65 void 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
OnSessionEnded()94 void 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_
ReportSessionStatus()109 void 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
ReportControlStatus(bool start_resp,const BluetoothAudioStatus & status)129 void 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
IsSessionReady()151 bool 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
UpdateDataPath(const DataMQ::Descriptor * dataMQ)159 bool 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
UpdateAudioConfig(const AudioConfiguration & audio_config)175 bool 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 bool is_offload_session =
181 (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
182 auto audio_config_discriminator = audio_config.getDiscriminator();
183 bool is_software_audio_config =
184 (is_software_session &&
185 audio_config_discriminator ==
186 AudioConfiguration::hidl_discriminator::pcmConfig);
187 bool is_offload_audio_config =
188 (is_offload_session &&
189 audio_config_discriminator ==
190 AudioConfiguration::hidl_discriminator::codecConfig);
191 if (!is_software_audio_config && !is_offload_audio_config) {
192 return false;
193 }
194 audio_config_ = audio_config;
195 return true;
196 }
197
198 // The control function helps the bluetooth_audio module to register
199 // PortStatusCallbacks
200 // @return: cookie - the assigned number to this bluetooth_audio output
RegisterStatusCback(const PortStatusCallbacks & cbacks)201 uint16_t BluetoothAudioSession::RegisterStatusCback(
202 const PortStatusCallbacks& cbacks) {
203 std::lock_guard<std::recursive_mutex> guard(mutex_);
204 uint16_t cookie = ObserversCookieGetInitValue(session_type_);
205 uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
206
207 while (cookie < cookie_upper_bound) {
208 if (observers_.find(cookie) == observers_.end()) {
209 break;
210 }
211 ++cookie;
212 }
213 if (cookie >= cookie_upper_bound) {
214 LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
215 << " has " << observers_.size()
216 << " observers already (No Resource)";
217 return kObserversCookieUndefined;
218 }
219 std::shared_ptr<struct PortStatusCallbacks> cb =
220 std::make_shared<struct PortStatusCallbacks>();
221 *cb = cbacks;
222 observers_[cookie] = cb;
223 return cookie;
224 }
225
226 // The control function helps the bluetooth_audio module to unregister
227 // PortStatusCallbacks
228 // @param: cookie - indicates which bluetooth_audio output is
UnregisterStatusCback(uint16_t cookie)229 void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
230 std::lock_guard<std::recursive_mutex> guard(mutex_);
231 if (observers_.erase(cookie) != 1) {
232 LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
233 << " no such provider=0x"
234 << android::base::StringPrintf("%04x", cookie);
235 }
236 }
237
238 // The control function is for the bluetooth_audio module to get the current
239 // AudioConfiguration
GetAudioConfig()240 const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
241 std::lock_guard<std::recursive_mutex> guard(mutex_);
242 if (IsSessionReady()) {
243 return audio_config_;
244 } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
245 return kInvalidOffloadAudioConfiguration;
246 } else {
247 return kInvalidSoftwareAudioConfiguration;
248 }
249 }
250
251 // Those control functions are for the bluetooth_audio module to start, suspend,
252 // stop stream, to check position, and to update metadata.
StartStream()253 bool BluetoothAudioSession::StartStream() {
254 std::lock_guard<std::recursive_mutex> guard(mutex_);
255 if (!IsSessionReady()) {
256 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
257 << " has NO session";
258 return false;
259 }
260 auto hal_retval = stack_iface_->startStream();
261 if (!hal_retval.isOk()) {
262 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
263 << toString(session_type_) << " failed";
264 return false;
265 }
266 return true;
267 }
268
SuspendStream()269 bool BluetoothAudioSession::SuspendStream() {
270 std::lock_guard<std::recursive_mutex> guard(mutex_);
271 if (!IsSessionReady()) {
272 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
273 << " has NO session";
274 return false;
275 }
276 auto hal_retval = stack_iface_->suspendStream();
277 if (!hal_retval.isOk()) {
278 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
279 << toString(session_type_) << " failed";
280 return false;
281 }
282 return true;
283 }
284
StopStream()285 void BluetoothAudioSession::StopStream() {
286 std::lock_guard<std::recursive_mutex> guard(mutex_);
287 if (!IsSessionReady()) {
288 return;
289 }
290 auto hal_retval = stack_iface_->stopStream();
291 if (!hal_retval.isOk()) {
292 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
293 << toString(session_type_) << " failed";
294 }
295 }
296
GetPresentationPosition(uint64_t * remote_delay_report_ns,uint64_t * total_bytes_readed,timespec * data_position)297 bool BluetoothAudioSession::GetPresentationPosition(
298 uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
299 timespec* data_position) {
300 std::lock_guard<std::recursive_mutex> guard(mutex_);
301 if (!IsSessionReady()) {
302 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
303 << " has NO session";
304 return false;
305 }
306 bool retval = false;
307 auto hal_retval = stack_iface_->getPresentationPosition(
308 [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
309 BluetoothAudioStatus status,
310 const uint64_t& remoteDeviceAudioDelayNanos,
311 uint64_t transmittedOctets,
312 const TimeSpec& transmittedOctetsTimeStamp) {
313 if (status == BluetoothAudioStatus::SUCCESS) {
314 if (remote_delay_report_ns)
315 *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
316 if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
317 if (data_position)
318 *data_position =
319 timespec_convert_from_hal(transmittedOctetsTimeStamp);
320 retval = true;
321 }
322 });
323 if (!hal_retval.isOk()) {
324 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
325 << toString(session_type_) << " failed";
326 return false;
327 }
328 return retval;
329 }
330
UpdateTracksMetadata(const struct source_metadata * source_metadata)331 void BluetoothAudioSession::UpdateTracksMetadata(
332 const struct source_metadata* source_metadata) {
333 std::lock_guard<std::recursive_mutex> guard(mutex_);
334 if (!IsSessionReady()) {
335 LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
336 << " has NO session";
337 return;
338 }
339
340 ssize_t track_count = source_metadata->track_count;
341 LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
342 << track_count << " track(s)";
343 if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
344 session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
345 return;
346 }
347
348 struct playback_track_metadata* track = source_metadata->tracks;
349 SourceMetadata sourceMetadata;
350 PlaybackTrackMetadata* halMetadata;
351
352 sourceMetadata.tracks.resize(track_count);
353 halMetadata = sourceMetadata.tracks.data();
354 while (track_count && track) {
355 halMetadata->usage = static_cast<AudioUsage>(track->usage);
356 halMetadata->contentType =
357 static_cast<AudioContentType>(track->content_type);
358 halMetadata->gain = track->gain;
359 LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
360 << ", usage=" << toString(halMetadata->usage)
361 << ", content=" << toString(halMetadata->contentType)
362 << ", gain=" << halMetadata->gain;
363 --track_count;
364 ++track;
365 ++halMetadata;
366 }
367 auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
368 if (!hal_retval.isOk()) {
369 LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
370 << toString(session_type_) << " failed";
371 }
372 }
373
374 // The control function writes stream to FMQ
OutWritePcmData(const void * buffer,size_t bytes)375 size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
376 size_t bytes) {
377 if (buffer == nullptr || !bytes) return 0;
378 size_t totalWritten = 0;
379 int ms_timeout = kFmqSendTimeoutMs;
380 do {
381 std::unique_lock<std::recursive_mutex> lock(mutex_);
382 if (!IsSessionReady()) break;
383 size_t availableToWrite = mDataMQ->availableToWrite();
384 if (availableToWrite) {
385 if (availableToWrite > (bytes - totalWritten)) {
386 availableToWrite = bytes - totalWritten;
387 }
388
389 if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
390 availableToWrite)) {
391 ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
392 return totalWritten;
393 }
394 totalWritten += availableToWrite;
395 } else if (ms_timeout >= kWritePollMs) {
396 lock.unlock();
397 usleep(kWritePollMs * 1000);
398 ms_timeout -= kWritePollMs;
399 } else {
400 ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
401 (kFmqSendTimeoutMs - ms_timeout));
402 return totalWritten;
403 }
404 } while (totalWritten < bytes);
405 return totalWritten;
406 }
407
408 // The control function reads stream from FMQ
InReadPcmData(void * buffer,size_t bytes)409 size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
410 if (buffer == nullptr || !bytes) return 0;
411 size_t totalRead = 0;
412 int ms_timeout = kFmqReceiveTimeoutMs;
413 do {
414 std::unique_lock<std::recursive_mutex> lock(mutex_);
415 if (!IsSessionReady()) break;
416 size_t availableToRead = mDataMQ->availableToRead();
417 if (availableToRead) {
418 if (availableToRead > (bytes - totalRead)) {
419 availableToRead = bytes - totalRead;
420 }
421 if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
422 availableToRead)) {
423 ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
424 return totalRead;
425 }
426 totalRead += availableToRead;
427 } else if (ms_timeout >= kReadPollMs) {
428 lock.unlock();
429 usleep(kReadPollMs * 1000);
430 ms_timeout -= kReadPollMs;
431 continue;
432 } else {
433 ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
434 (kFmqReceiveTimeoutMs - ms_timeout));
435 return totalRead;
436 }
437 } while (totalRead < bytes);
438 return totalRead;
439 }
440
441 std::unique_ptr<BluetoothAudioSessionInstance>
442 BluetoothAudioSessionInstance::instance_ptr =
443 std::unique_ptr<BluetoothAudioSessionInstance>(
444 new BluetoothAudioSessionInstance());
445
446 // API to fetch the session of A2DP / Hearing Aid
447 std::shared_ptr<BluetoothAudioSession>
GetSessionInstance(const SessionType & session_type)448 BluetoothAudioSessionInstance::GetSessionInstance(
449 const SessionType& session_type) {
450 std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
451 if (!instance_ptr->sessions_map_.empty()) {
452 auto entry = instance_ptr->sessions_map_.find(session_type);
453 if (entry != instance_ptr->sessions_map_.end()) {
454 return entry->second;
455 }
456 }
457 std::shared_ptr<BluetoothAudioSession> session_ptr =
458 std::make_shared<BluetoothAudioSession>(session_type);
459 instance_ptr->sessions_map_[session_type] = session_ptr;
460 return session_ptr;
461 }
462
463 } // namespace audio
464 } // namespace bluetooth
465 } // namespace android
466