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