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