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