1 /*
2 * Copyright 2021 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
3 * www.ehima.com
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "state_machine.h"
19
20 #include <base/functional/bind.h>
21 #include <base/functional/callback.h>
22 #include <base/strings/string_number_conversions.h>
23
24 #include <map>
25
26 #include "bt_types.h"
27 #include "bta_gatt_queue.h"
28 #include "bta_le_audio_api.h"
29 #include "btm_iso_api.h"
30 #include "client_parser.h"
31 #include "codec_manager.h"
32 #include "content_control_id_keeper.h"
33 #include "devices.h"
34 #include "gd/common/strings.h"
35 #include "hcimsgs.h"
36 #include "le_audio_log_history.h"
37 #include "le_audio_types.h"
38 #include "osi/include/alarm.h"
39 #include "osi/include/log.h"
40 #include "osi/include/osi.h"
41 #include "osi/include/properties.h"
42
43 // clang-format off
44 /* ASCS state machine 1.0
45 *
46 * State machine manages group of ASEs to make transition from one state to
47 * another according to specification and keeping involved necessary externals
48 * like: ISO, CIG, ISO data path, audio path form/to upper layer.
49 *
50 * GroupStream (API): GroupStream method of this le audio implementation class
51 * object should allow transition from Idle (No Caching),
52 * Codec Configured (Caching after release) state to
53 * Streaming for all ASEs in group within time limit. Time
54 * limit should keep safe whole state machine from being
55 * stucked in any in-middle state, which is not a destination
56 * state.
57 *
58 * TODO Second functionality of streaming should be switch
59 * context which will base on previous state, context type.
60 *
61 * GroupStop (API): GroupStop method of this le audio implementation class
62 * object should allow safe transition from any state to Idle
63 * or Codec Configured (if caching supported).
64 *
65 * ╔══════════════════╦═════════════════════════════╦══════════════╦══════════════════╦══════╗
66 * ║ Current State ║ ASE Control Point Operation ║ Result ║ Next State ║ Note ║
67 * ╠══════════════════╬═════════════════════════════╬══════════════╬══════════════════╬══════╣
68 * ║ Idle ║ Config Codec ║ Success ║ Codec Configured ║ + ║
69 * ║ Codec Configured ║ Config Codec ║ Success ║ Codec Configured ║ - ║
70 * ║ Codec Configured ║ Release ║ Success ║ Releasing ║ + ║
71 * ║ Codec Configured ║ Config QoS ║ Success ║ QoS Configured ║ + ║
72 * ║ QoS Configured ║ Config Codec ║ Success ║ Codec Configured ║ - ║
73 * ║ QoS Configured ║ Config QoS ║ Success ║ QoS Configured ║ - ║
74 * ║ QoS Configured ║ Release ║ Success ║ Releasing ║ + ║
75 * ║ QoS Configured ║ Enable ║ Success ║ Enabling ║ + ║
76 * ║ Enabling ║ Release ║ Success ║ Releasing ║ + ║
77 * ║ Enabling ║ Update Metadata ║ Success ║ Enabling ║ - ║
78 * ║ Enabling ║ Disable ║ Success ║ Disabling ║ - ║
79 * ║ Enabling ║ Receiver Start Ready ║ Success ║ Streaming ║ + ║
80 * ║ Streaming ║ Update Metadata ║ Success ║ Streaming ║ - ║
81 * ║ Streaming ║ Disable ║ Success ║ Disabling ║ + ║
82 * ║ Streaming ║ Release ║ Success ║ Releasing ║ + ║
83 * ║ Disabling ║ Receiver Stop Ready ║ Success ║ QoS Configured ║ + ║
84 * ║ Disabling ║ Release ║ Success ║ Releasing ║ + ║
85 * ║ Releasing ║ Released (no caching) ║ Success ║ Idle ║ + ║
86 * ║ Releasing ║ Released (caching) ║ Success ║ Codec Configured ║ - ║
87 * ╚══════════════════╩═════════════════════════════╩══════════════╩══════════════════╩══════╝
88 *
89 * + - supported transition
90 * - - not supported
91 */
92 // clang-format on
93
94 using bluetooth::common::ToString;
95 using bluetooth::hci::IsoManager;
96 using bluetooth::le_audio::GroupStreamStatus;
97 using le_audio::CodecManager;
98 using le_audio::LeAudioDevice;
99 using le_audio::LeAudioDeviceGroup;
100 using le_audio::LeAudioGroupStateMachine;
101
102 using le_audio::types::ase;
103 using le_audio::types::AseState;
104 using le_audio::types::AudioContexts;
105 using le_audio::types::AudioStreamDataPathState;
106 using le_audio::types::BidirectionalPair;
107 using le_audio::types::CigState;
108 using le_audio::types::CodecLocation;
109 using le_audio::types::LeAudioContextType;
110
111 namespace {
112
113 constexpr int linkQualityCheckInterval = 4000;
114
link_quality_cb(void * data)115 static void link_quality_cb(void* data) {
116 // very ugly, but we need to pass just two bytes
117 uint16_t cis_conn_handle = *((uint16_t*)data);
118
119 IsoManager::GetInstance()->ReadIsoLinkQuality(cis_conn_handle);
120 }
121
122 class LeAudioGroupStateMachineImpl;
123 LeAudioGroupStateMachineImpl* instance;
124
125 class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
126 public:
LeAudioGroupStateMachineImpl(Callbacks * state_machine_callbacks_)127 LeAudioGroupStateMachineImpl(Callbacks* state_machine_callbacks_)
128 : state_machine_callbacks_(state_machine_callbacks_),
129 watchdog_(alarm_new("LeAudioStateMachineTimer")) {
130 log_history_ = LeAudioLogHistory::Get();
131 }
132
~LeAudioGroupStateMachineImpl()133 ~LeAudioGroupStateMachineImpl() {
134 alarm_free(watchdog_);
135 watchdog_ = nullptr;
136 log_history_->Cleanup();
137 log_history_ = nullptr;
138 }
139
AttachToStream(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)140 bool AttachToStream(LeAudioDeviceGroup* group,
141 LeAudioDevice* leAudioDevice) override {
142 LOG(INFO) << __func__ << " group id: " << group->group_id_
143 << " device: " << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_);
144
145 /* This function is used to attach the device to the stream.
146 * Limitation here is that device should be previously in the streaming
147 * group and just got reconnected.
148 */
149 if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
150 LOG_ERROR(" group not in the streaming state: %s",
151 ToString(group->GetState()).c_str());
152 return false;
153 }
154
155 BidirectionalPair<std::vector<uint8_t>> ccids = {
156 .sink = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
157 group->GetMetadataContexts().sink),
158 .source = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
159 group->GetMetadataContexts().source)};
160 if (!group->Configure(group->GetConfigurationContextType(),
161 group->GetMetadataContexts(), ccids)) {
162 LOG_ERROR(" failed to set ASE configuration");
163 return false;
164 }
165
166 PrepareAndSendCodecConfigure(group, leAudioDevice);
167 return true;
168 }
169
StartStream(LeAudioDeviceGroup * group,LeAudioContextType context_type,const BidirectionalPair<AudioContexts> & metadata_context_types,BidirectionalPair<std::vector<uint8_t>> ccid_lists)170 bool StartStream(
171 LeAudioDeviceGroup* group, LeAudioContextType context_type,
172 const BidirectionalPair<AudioContexts>& metadata_context_types,
173 BidirectionalPair<std::vector<uint8_t>> ccid_lists) override {
174 LOG_INFO(" current state: %s", ToString(group->GetState()).c_str());
175
176 switch (group->GetState()) {
177 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
178 if (group->GetConfigurationContextType() == context_type) {
179 if (group->Activate(context_type)) {
180 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
181 if (CigCreate(group)) {
182 return true;
183 }
184 }
185 LOG_INFO("Could not activate device, try to configure it again");
186 }
187
188 /* Deactivate previousely activated ASEs in case if there were just a
189 * reconfiguration (group target state as CODEC CONFIGURED) and no
190 * deactivation. Currently activated ASEs cannot be used for different
191 * context.
192 */
193 group->Deactivate();
194
195 /* We are going to reconfigure whole group. Clear Cises.*/
196 ReleaseCisIds(group);
197
198 /* If configuration is needed */
199 FALLTHROUGH;
200 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
201 if (!group->Configure(context_type, metadata_context_types,
202 ccid_lists)) {
203 LOG(ERROR) << __func__ << ", failed to set ASE configuration";
204 return false;
205 }
206
207 group->CigGenerateCisIds(context_type);
208 /* All ASEs should aim to achieve target state */
209 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
210 if (!PrepareAndSendCodecConfigToTheGroup(group)) {
211 group->PrintDebugState();
212 ClearGroup(group, true);
213 }
214 break;
215
216 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED: {
217 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
218 if (!leAudioDevice) {
219 LOG(ERROR) << __func__ << ", group has no active devices";
220 return false;
221 }
222
223 /* All ASEs should aim to achieve target state */
224 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
225 PrepareAndSendEnableToTheGroup(group);
226 break;
227 }
228
229 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
230 /* This case just updates the metadata for the stream, in case
231 * stream configuration is satisfied. We can do that already for
232 * all the devices in a group, without any state transitions.
233 */
234 if (!group->IsMetadataChanged(metadata_context_types, ccid_lists))
235 return true;
236
237 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
238 if (!leAudioDevice) {
239 LOG(ERROR) << __func__ << ", group has no active devices";
240 return false;
241 }
242
243 while (leAudioDevice) {
244 PrepareAndSendUpdateMetadata(leAudioDevice, metadata_context_types,
245 ccid_lists);
246 leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
247 }
248 break;
249 }
250
251 default:
252 LOG_ERROR("Unable to transit from %s",
253 ToString(group->GetState()).c_str());
254 return false;
255 }
256
257 return true;
258 }
259
ConfigureStream(LeAudioDeviceGroup * group,LeAudioContextType context_type,const BidirectionalPair<AudioContexts> & metadata_context_types,BidirectionalPair<std::vector<uint8_t>> ccid_lists)260 bool ConfigureStream(
261 LeAudioDeviceGroup* group, LeAudioContextType context_type,
262 const BidirectionalPair<AudioContexts>& metadata_context_types,
263 BidirectionalPair<std::vector<uint8_t>> ccid_lists) override {
264 if (group->GetState() > AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
265 LOG_ERROR(
266 "Stream should be stopped or in configured stream. Current state: %s",
267 ToString(group->GetState()).c_str());
268 return false;
269 }
270
271 group->Deactivate();
272 ReleaseCisIds(group);
273
274 if (!group->Configure(context_type, metadata_context_types, ccid_lists)) {
275 LOG_ERROR("Could not configure ASEs for group %d content type %d",
276 group->group_id_, int(context_type));
277
278 return false;
279 }
280
281 group->CigGenerateCisIds(context_type);
282 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
283 return PrepareAndSendCodecConfigToTheGroup(group);
284 }
285
SuspendStream(LeAudioDeviceGroup * group)286 void SuspendStream(LeAudioDeviceGroup* group) override {
287 /* All ASEs should aim to achieve target state */
288 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
289 auto status = PrepareAndSendDisableToTheGroup(group);
290 state_machine_callbacks_->StatusReportCb(group->group_id_, status);
291 }
292
StopStream(LeAudioDeviceGroup * group)293 void StopStream(LeAudioDeviceGroup* group) override {
294 if (group->IsReleasingOrIdle()) {
295 LOG(INFO) << __func__ << ", group: " << group->group_id_
296 << " already in releasing process";
297 return;
298 }
299
300 /* All Ases should aim to achieve target state */
301 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
302
303 auto status = PrepareAndSendReleaseToTheGroup(group);
304 state_machine_callbacks_->StatusReportCb(group->group_id_, status);
305 }
306
ProcessGattNotifEvent(uint8_t * value,uint16_t len,struct ase * ase,LeAudioDevice * leAudioDevice,LeAudioDeviceGroup * group)307 void ProcessGattNotifEvent(uint8_t* value, uint16_t len, struct ase* ase,
308 LeAudioDevice* leAudioDevice,
309 LeAudioDeviceGroup* group) override {
310 struct le_audio::client_parser::ascs::ase_rsp_hdr arh;
311
312 ParseAseStatusHeader(arh, len, value);
313
314 auto state = static_cast<AseState>(arh.state);
315
316 LOG_INFO(" %s , ASE id: %d, state changed %s -> %s ",
317 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), +ase->id,
318 ToString(ase->state).c_str(), ToString(state).c_str());
319
320 log_history_->AddLogHistory(
321 kLogAseStateNotif, leAudioDevice->group_id_, leAudioDevice->address_,
322 "ASE_ID " + std::to_string(arh.id) + ": " + ToString(state),
323 "curr: " + ToString(ase->state));
324
325 switch (state) {
326 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
327 AseStateMachineProcessIdle(arh, ase, group, leAudioDevice);
328 break;
329 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
330 AseStateMachineProcessCodecConfigured(
331 arh, ase, value + le_audio::client_parser::ascs::kAseRspHdrMinLen,
332 len - le_audio::client_parser::ascs::kAseRspHdrMinLen, group,
333 leAudioDevice);
334 break;
335 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
336 AseStateMachineProcessQosConfigured(arh, ase, group, leAudioDevice);
337 break;
338 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
339 AseStateMachineProcessEnabling(arh, ase, group, leAudioDevice);
340 break;
341 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
342 AseStateMachineProcessStreaming(
343 arh, ase, value + le_audio::client_parser::ascs::kAseRspHdrMinLen,
344 len - le_audio::client_parser::ascs::kAseRspHdrMinLen, group,
345 leAudioDevice);
346 break;
347 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING:
348 AseStateMachineProcessDisabling(arh, ase, group, leAudioDevice);
349 break;
350 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
351 AseStateMachineProcessReleasing(arh, ase, group, leAudioDevice);
352 break;
353 default:
354 LOG(ERROR) << __func__
355 << ", Wrong AES status: " << static_cast<int>(arh.state);
356 StopStream(group);
357 break;
358 }
359 }
360
ProcessHciNotifOnCigCreate(LeAudioDeviceGroup * group,uint8_t status,uint8_t cig_id,std::vector<uint16_t> conn_handles)361 void ProcessHciNotifOnCigCreate(LeAudioDeviceGroup* group, uint8_t status,
362 uint8_t cig_id,
363 std::vector<uint16_t> conn_handles) override {
364 /* TODO: What if not all cises will be configured ?
365 * conn_handle.size() != active ases in group
366 */
367
368 if (!group) {
369 LOG_ERROR(", group is null");
370 return;
371 }
372
373 log_history_->AddLogHistory(kLogHciEvent, group->group_id_,
374 RawAddress::kEmpty,
375 kLogCisCreateOp + "STATUS=" + loghex(status));
376
377 if (status != HCI_SUCCESS) {
378 if (status == HCI_ERR_COMMAND_DISALLOWED) {
379 /*
380 * We are here, because stack has no chance to remove CIG when it was
381 * shut during streaming. In the same time, controller probably was not
382 * Reseted, which creates the issue. Lets remove CIG and try to create
383 * it again.
384 */
385 group->SetCigState(CigState::RECOVERING);
386 IsoManager::GetInstance()->RemoveCig(group->group_id_, true);
387 return;
388 }
389
390 group->SetCigState(CigState::NONE);
391 LOG_ERROR(", failed to create CIG, reason: 0x%02x, new cig state: %s",
392 +status, ToString(group->cig_state_).c_str());
393 StopStream(group);
394 return;
395 }
396
397 ASSERT_LOG(group->GetCigState() == CigState::CREATING,
398 "Unexpected CIG creation group id: %d, cig state: %s",
399 group->group_id_, ToString(group->cig_state_).c_str());
400
401 group->SetCigState(CigState::CREATED);
402 LOG_INFO("Group: %p, id: %d cig state: %s, number of cis handles: %d",
403 group, group->group_id_, ToString(group->cig_state_).c_str(),
404 static_cast<int>(conn_handles.size()));
405
406 /* Assign all connection handles to cis ids */
407 group->CigAssignCisConnHandles(conn_handles);
408
409 /* Assign all connection handles to ases */
410 group->CigAssignCisConnHandlesToAses();
411
412 /* Last node configured, process group to codec configured state */
413 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
414
415 if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
416 PrepareAndSendQoSToTheGroup(group);
417 } else {
418 LOG_ERROR(", invalid state transition, from: %s , to: %s",
419 ToString(group->GetState()).c_str(),
420 ToString(group->GetTargetState()).c_str());
421 StopStream(group);
422 return;
423 }
424 }
425
FreeLinkQualityReports(LeAudioDevice * leAudioDevice)426 void FreeLinkQualityReports(LeAudioDevice* leAudioDevice) {
427 if (leAudioDevice->link_quality_timer == nullptr) return;
428
429 alarm_free(leAudioDevice->link_quality_timer);
430 leAudioDevice->link_quality_timer = nullptr;
431 }
432
ProcessHciNotifyOnCigRemoveRecovering(uint8_t status,LeAudioDeviceGroup * group)433 void ProcessHciNotifyOnCigRemoveRecovering(uint8_t status,
434 LeAudioDeviceGroup* group) {
435 group->SetCigState(CigState::NONE);
436
437 log_history_->AddLogHistory(kLogHciEvent, group->group_id_,
438 RawAddress::kEmpty,
439 kLogCigRemoveOp + " STATUS=" + loghex(status));
440 if (status != HCI_SUCCESS) {
441 LOG_ERROR(
442 "Could not recover from the COMMAND DISALLOAD on CigCreate. Status "
443 "on CIG remove is 0x%02x",
444 status);
445 StopStream(group);
446 return;
447 }
448 LOG_INFO("Succeed on CIG Recover - back to creating CIG");
449 if (!CigCreate(group)) {
450 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
451 group->group_id_);
452 StopStream(group);
453 }
454 }
455
ProcessHciNotifOnCigRemove(uint8_t status,LeAudioDeviceGroup * group)456 void ProcessHciNotifOnCigRemove(uint8_t status,
457 LeAudioDeviceGroup* group) override {
458 if (group->GetCigState() == CigState::RECOVERING) {
459 ProcessHciNotifyOnCigRemoveRecovering(status, group);
460 return;
461 }
462
463 log_history_->AddLogHistory(kLogHciEvent, group->group_id_,
464 RawAddress::kEmpty,
465 kLogCigRemoveOp + " STATUS=" + loghex(status));
466
467 if (status != HCI_SUCCESS) {
468 group->SetCigState(CigState::CREATED);
469 LOG_ERROR(
470 "failed to remove cig, id: %d, status 0x%02x, new cig state: %s",
471 group->group_id_, +status, ToString(group->GetCigState()).c_str());
472 return;
473 }
474
475 ASSERT_LOG(group->GetCigState() == CigState::REMOVING,
476 "Unexpected CIG remove group id: %d, cig state %s",
477 group->group_id_, ToString(group->GetCigState()).c_str());
478
479 group->SetCigState(CigState::NONE);
480
481 LeAudioDevice* leAudioDevice = group->GetFirstDevice();
482 if (!leAudioDevice) return;
483
484 do {
485 FreeLinkQualityReports(leAudioDevice);
486
487 for (auto& ase : leAudioDevice->ases_) {
488 ase.data_path_state = AudioStreamDataPathState::IDLE;
489 }
490 } while ((leAudioDevice = group->GetNextDevice(leAudioDevice)));
491 }
492
ProcessHciNotifSetupIsoDataPath(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t status,uint16_t conn_handle)493 void ProcessHciNotifSetupIsoDataPath(LeAudioDeviceGroup* group,
494 LeAudioDevice* leAudioDevice,
495 uint8_t status,
496 uint16_t conn_handle) override {
497 log_history_->AddLogHistory(
498 kLogHciEvent, group->group_id_, leAudioDevice->address_,
499 kLogSetDataPathOp + "cis_h:" + loghex(conn_handle) +
500 " STATUS=" + loghex(status));
501
502 if (status) {
503 LOG(ERROR) << __func__ << ", failed to setup data path";
504 StopStream(group);
505
506 return;
507 }
508
509 /* Update state for the given cis.*/
510 auto ase = leAudioDevice->GetFirstActiveAseByDataPathState(
511 AudioStreamDataPathState::CIS_ESTABLISHED);
512
513 if (!ase || ase->cis_conn_hdl != conn_handle) {
514 LOG(ERROR) << __func__ << " Cannot find ase by handle " << +conn_handle;
515 return;
516 }
517
518 ase->data_path_state = AudioStreamDataPathState::DATA_PATH_ESTABLISHED;
519
520 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
521 LOG(WARNING) << __func__ << " Group " << group->group_id_
522 << " is not targeting streaming state any more";
523 return;
524 }
525
526 AddCisToStreamConfiguration(group, ase);
527
528 ase = leAudioDevice->GetFirstActiveAseByDataPathState(
529 AudioStreamDataPathState::CIS_ESTABLISHED);
530 if (!ase) {
531 leAudioDevice = group->GetNextActiveDeviceByDataPathState(
532 leAudioDevice, AudioStreamDataPathState::CIS_ESTABLISHED);
533
534 if (!leAudioDevice) {
535 state_machine_callbacks_->StatusReportCb(group->group_id_,
536 GroupStreamStatus::STREAMING);
537 return;
538 }
539
540 ase = leAudioDevice->GetFirstActiveAseByDataPathState(
541 AudioStreamDataPathState::CIS_ESTABLISHED);
542 }
543
544 ASSERT_LOG(ase, "shouldn't be called without an active ASE");
545 PrepareDataPath(group->group_id_, ase);
546 }
547
ProcessHciNotifRemoveIsoDataPath(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t status,uint16_t conn_hdl)548 void ProcessHciNotifRemoveIsoDataPath(LeAudioDeviceGroup* group,
549 LeAudioDevice* leAudioDevice,
550 uint8_t status,
551 uint16_t conn_hdl) override {
552 log_history_->AddLogHistory(
553 kLogHciEvent, group->group_id_, leAudioDevice->address_,
554 kLogRemoveDataPathOp + "STATUS=" + loghex(status));
555
556 if (status != HCI_SUCCESS) {
557 LOG_ERROR(
558 "failed to remove ISO data path, reason: 0x%0x - contining stream "
559 "closing",
560 status);
561 /* Just continue - disconnecting CIS removes data path as well.*/
562 }
563
564 bool do_disconnect = false;
565
566 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(conn_hdl);
567 if (ases_pair.sink && (ases_pair.sink->data_path_state ==
568 AudioStreamDataPathState::DATA_PATH_ESTABLISHED)) {
569 ases_pair.sink->data_path_state =
570 AudioStreamDataPathState::CIS_DISCONNECTING;
571 do_disconnect = true;
572 }
573
574 if (ases_pair.source &&
575 ases_pair.source->data_path_state ==
576 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
577 ases_pair.source->data_path_state =
578 AudioStreamDataPathState::CIS_DISCONNECTING;
579 do_disconnect = true;
580 }
581
582 if (do_disconnect) {
583 RemoveCisFromStreamConfiguration(group, leAudioDevice, conn_hdl);
584 IsoManager::GetInstance()->DisconnectCis(conn_hdl, HCI_ERR_PEER_USER);
585
586 log_history_->AddLogHistory(
587 kLogStateMachineTag, group->group_id_, leAudioDevice->address_,
588 kLogCisDisconnectOp + "cis_h:" + loghex(conn_hdl));
589 }
590 }
591
ProcessHciNotifIsoLinkQualityRead(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t conn_handle,uint32_t txUnackedPackets,uint32_t txFlushedPackets,uint32_t txLastSubeventPackets,uint32_t retransmittedPackets,uint32_t crcErrorPackets,uint32_t rxUnreceivedPackets,uint32_t duplicatePackets)592 void ProcessHciNotifIsoLinkQualityRead(
593 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
594 uint8_t conn_handle, uint32_t txUnackedPackets, uint32_t txFlushedPackets,
595 uint32_t txLastSubeventPackets, uint32_t retransmittedPackets,
596 uint32_t crcErrorPackets, uint32_t rxUnreceivedPackets,
597 uint32_t duplicatePackets) {
598 LOG(INFO) << "conn_handle: " << loghex(conn_handle)
599 << ", txUnackedPackets: " << loghex(txUnackedPackets)
600 << ", txFlushedPackets: " << loghex(txFlushedPackets)
601 << ", txLastSubeventPackets: " << loghex(txLastSubeventPackets)
602 << ", retransmittedPackets: " << loghex(retransmittedPackets)
603 << ", crcErrorPackets: " << loghex(crcErrorPackets)
604 << ", rxUnreceivedPackets: " << loghex(rxUnreceivedPackets)
605 << ", duplicatePackets: " << loghex(duplicatePackets);
606 }
607
ReleaseCisIds(LeAudioDeviceGroup * group)608 void ReleaseCisIds(LeAudioDeviceGroup* group) {
609 if (group == nullptr) {
610 LOG_DEBUG(" Group is null.");
611 return;
612 }
613 LOG_DEBUG(" Releasing CIS is for group %d", group->group_id_);
614
615 LeAudioDevice* leAudioDevice = group->GetFirstDevice();
616 while (leAudioDevice != nullptr) {
617 for (auto& ase : leAudioDevice->ases_) {
618 ase.cis_id = le_audio::kInvalidCisId;
619 ase.cis_conn_hdl = 0;
620 }
621 leAudioDevice = group->GetNextDevice(leAudioDevice);
622 }
623
624 group->CigClearCis();
625 }
626
RemoveCigForGroup(LeAudioDeviceGroup * group)627 void RemoveCigForGroup(LeAudioDeviceGroup* group) {
628 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
629 ToString(group->cig_state_).c_str());
630 if (group->GetCigState() != CigState::CREATED) {
631 LOG_WARN("Group: %p, id: %d cig state: %s cannot be removed", group,
632 group->group_id_, ToString(group->cig_state_).c_str());
633 return;
634 }
635
636 group->SetCigState(CigState::REMOVING);
637 IsoManager::GetInstance()->RemoveCig(group->group_id_);
638 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
639 ToString(group->cig_state_).c_str());
640 log_history_->AddLogHistory(kLogStateMachineTag, group->group_id_,
641 RawAddress::kEmpty, kLogCigRemoveOp);
642 }
643
ProcessHciNotifAclDisconnected(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)644 void ProcessHciNotifAclDisconnected(LeAudioDeviceGroup* group,
645 LeAudioDevice* leAudioDevice) {
646 FreeLinkQualityReports(leAudioDevice);
647 /* mark ASEs as not used. */
648 leAudioDevice->DeactivateAllAses();
649
650 if (!group) {
651 LOG(ERROR) << __func__
652 << " group is null for device: "
653 << ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)
654 << " group_id: " << leAudioDevice->group_id_;
655 return;
656 }
657
658 /* If group is in Idle and not transitioning, update the current group
659 * audio context availability which could change due to disconnected group
660 * member.
661 */
662 if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) &&
663 !group->IsInTransition()) {
664 LOG_INFO("group: %d is in IDLE", group->group_id_);
665 group->UpdateAudioContextTypeAvailability();
666
667 /* When OnLeAudioDeviceSetStateTimeout happens, group will transition
668 * to IDLE, and after that an ACL disconnect will be triggered. We need
669 * to check if CIG is created and if it is, remove it so it can be created
670 * again after reconnect. Otherwise we will get Command Disallowed on CIG
671 * Create when starting stream.
672 */
673 if (group->GetCigState() == CigState::CREATED) {
674 LOG_INFO("CIG is in CREATED state so removing CIG for Group %d",
675 group->group_id_);
676 RemoveCigForGroup(group);
677 }
678 return;
679 }
680
681 LOG_DEBUG(
682 " device: %s, group connected: %d, all active ase disconnected:: %d",
683 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
684 group->IsAnyDeviceConnected(), group->HaveAllCisesDisconnected());
685
686 /* Update the current group audio context availability which could change
687 * due to disconnected group member.
688 */
689 group->UpdateAudioContextTypeAvailability();
690
691 if (group->IsAnyDeviceConnected()) {
692 /*
693 * ACL of one of the device has been dropped. If number of CISes has
694 * changed notify upper layer so the offloader can be updated with CIS
695 * information.
696 */
697 if (!group->HaveAllCisesDisconnected() &&
698 (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) &&
699 (group->GetTargetState() ==
700 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
701 /* We keep streaming but want others to let know user that it might be
702 * need to update offloader with new CIS configuration
703 */
704 state_machine_callbacks_->StatusReportCb(group->group_id_,
705 GroupStreamStatus::STREAMING);
706 }
707 return;
708 }
709
710 /* Group is not connected and all the CISes are down.
711 * Clean states and destroy HCI group
712 */
713 ClearGroup(group, true);
714 }
715
cancel_watchdog_if_needed(int group_id)716 void cancel_watchdog_if_needed(int group_id) {
717 if (alarm_is_scheduled(watchdog_)) {
718 log_history_->AddLogHistory(kLogStateMachineTag, group_id,
719 RawAddress::kEmpty, "WATCHDOG STOPPED");
720 alarm_cancel(watchdog_);
721 }
722 }
723
ProcessHciNotifCisEstablished(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,const bluetooth::hci::iso_manager::cis_establish_cmpl_evt * event)724 void ProcessHciNotifCisEstablished(
725 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
726 const bluetooth::hci::iso_manager::cis_establish_cmpl_evt* event)
727 override {
728 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
729
730 log_history_->AddLogHistory(
731 kLogHciEvent, group->group_id_, leAudioDevice->address_,
732 kLogCisEstablishedOp + "cis_h:" + loghex(event->cis_conn_hdl) +
733 " STATUS=" + loghex(event->status));
734
735 if (event->status) {
736 if (ases_pair.sink)
737 ases_pair.sink->data_path_state =
738 AudioStreamDataPathState::CIS_ASSIGNED;
739 if (ases_pair.source)
740 ases_pair.source->data_path_state =
741 AudioStreamDataPathState::CIS_ASSIGNED;
742
743 /* CIS establishment failed. Remove CIG if no other CIS is already created
744 * or pending. If CIS is established, this will be handled in disconnected
745 * complete event
746 */
747 if (group->HaveAllCisesDisconnected()) {
748 RemoveCigForGroup(group);
749 }
750
751 LOG(ERROR) << __func__
752 << ", failed to create CIS, status: " << loghex(event->status);
753
754 StopStream(group);
755 return;
756 }
757
758 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
759 LOG(ERROR) << __func__
760 << ", Unintended CIS establishement event came for group id:"
761 << group->group_id_;
762 StopStream(group);
763 return;
764 }
765
766 if (ases_pair.sink)
767 ases_pair.sink->data_path_state =
768 AudioStreamDataPathState::CIS_ESTABLISHED;
769 if (ases_pair.source)
770 ases_pair.source->data_path_state =
771 AudioStreamDataPathState::CIS_ESTABLISHED;
772
773 if (osi_property_get_bool("persist.bluetooth.iso_link_quality_report",
774 false)) {
775 leAudioDevice->link_quality_timer =
776 alarm_new_periodic("le_audio_cis_link_quality");
777 leAudioDevice->link_quality_timer_data = event->cis_conn_hdl;
778 alarm_set_on_mloop(leAudioDevice->link_quality_timer,
779 linkQualityCheckInterval, link_quality_cb,
780 &leAudioDevice->link_quality_timer_data);
781 }
782
783 if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
784 /* More cis established events has to come */
785 return;
786 }
787
788 if (!leAudioDevice->IsReadyToCreateStream()) {
789 /* Device still remains in ready to create stream state. It means that
790 * more enabling status notifications has to come. This may only happen
791 * for reconnection scenario for bi-directional CIS.
792 */
793 return;
794 }
795
796 /* All CISes created. Send start ready for source ASE before we can go
797 * to streaming state.
798 */
799 struct ase* ase = leAudioDevice->GetFirstActiveAse();
800 ASSERT_LOG(ase != nullptr,
801 "shouldn't be called without an active ASE, device %s, group "
802 "id: %d, cis handle 0x%04x",
803 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), event->cig_id,
804 event->cis_conn_hdl);
805
806 PrepareAndSendReceiverStartReady(leAudioDevice, ase);
807
808 /* Cis establishment may came after setting group state to streaming, e.g.
809 * for autonomous scenario when ase is sink */
810 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING &&
811 group->GetFirstActiveDeviceByDataPathState(
812 AudioStreamDataPathState::CIS_ESTABLISHED)) {
813 /* No more transition for group */
814 cancel_watchdog_if_needed(group->group_id_);
815 PrepareDataPath(group);
816 }
817 }
818
WriteToControlPoint(LeAudioDevice * leAudioDevice,std::vector<uint8_t> value)819 static void WriteToControlPoint(LeAudioDevice* leAudioDevice,
820 std::vector<uint8_t> value) {
821 tGATT_WRITE_TYPE write_type = GATT_WRITE_NO_RSP;
822
823 if (value.size() > (leAudioDevice->mtu_ - 3)) {
824 LOG_WARN("%s, using long write procedure (%d > %d)",
825 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
826 static_cast<int>(value.size()), (leAudioDevice->mtu_ - 3));
827
828 /* Note, that this type is actually LONG WRITE.
829 * Meaning all the Prepare Writes plus Execute is handled in the stack
830 */
831 write_type = GATT_WRITE_PREPARE;
832 }
833
834 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
835 leAudioDevice->ctp_hdls_.val_hdl, value,
836 write_type, NULL, NULL);
837 }
838
RemoveDataPathByCisHandle(LeAudioDevice * leAudioDevice,uint16_t cis_conn_hdl)839 static void RemoveDataPathByCisHandle(LeAudioDevice* leAudioDevice,
840 uint16_t cis_conn_hdl) {
841 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
842 uint8_t value = 0;
843
844 if (ases_pair.sink && ases_pair.sink->data_path_state ==
845 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
846 value |= bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput;
847 }
848
849 if (ases_pair.source &&
850 ases_pair.source->data_path_state ==
851 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
852 value |= bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionOutput;
853 }
854
855 if (value == 0) {
856 LOG_INFO("Data path was not set. Nothing to do here.");
857 return;
858 }
859
860 IsoManager::GetInstance()->RemoveIsoDataPath(cis_conn_hdl, value);
861
862 LeAudioLogHistory::Get()->AddLogHistory(
863 kLogStateMachineTag, leAudioDevice->group_id_, leAudioDevice->address_,
864 kLogRemoveDataPathOp + " cis_h:" + loghex(cis_conn_hdl));
865 }
866
ProcessHciNotifCisDisconnected(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,const bluetooth::hci::iso_manager::cis_disconnected_evt * event)867 void ProcessHciNotifCisDisconnected(
868 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
869 const bluetooth::hci::iso_manager::cis_disconnected_evt* event) override {
870 /* Reset the disconnected CIS states */
871
872 FreeLinkQualityReports(leAudioDevice);
873
874 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
875
876 log_history_->AddLogHistory(
877 kLogHciEvent, group->group_id_, leAudioDevice->address_,
878 kLogCisDisconnectedOp + "cis_h:" + loghex(event->cis_conn_hdl) +
879 " REASON=" + loghex(event->reason));
880
881 /* If this is peer disconnecting CIS, make sure to clear data path */
882 if (event->reason != HCI_ERR_CONN_CAUSE_LOCAL_HOST) {
883 RemoveDataPathByCisHandle(leAudioDevice, event->cis_conn_hdl);
884 // Make sure we won't stay in STREAMING state
885 if (ases_pair.sink &&
886 ases_pair.sink->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
887 ases_pair.sink->state =
888 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
889 }
890 if (ases_pair.source && ases_pair.source->state ==
891 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
892 ases_pair.source->state =
893 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
894 }
895 }
896
897 if (ases_pair.sink) {
898 ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_ASSIGNED;
899 }
900 if (ases_pair.source) {
901 ases_pair.source->data_path_state =
902 AudioStreamDataPathState::CIS_ASSIGNED;
903 }
904
905 RemoveCisFromStreamConfiguration(group, leAudioDevice, event->cis_conn_hdl);
906
907 auto target_state = group->GetTargetState();
908 switch (target_state) {
909 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
910 /* Something wrong happen when streaming or when creating stream.
911 * If there is other device connected and streaming, just leave it as it
912 * is, otherwise stop the stream.
913 */
914 if (!group->HaveAllCisesDisconnected()) {
915 /* There is ASE streaming for some device. Continue streaming. */
916 LOG_WARN(
917 "Group member disconnected during streaming. Cis handle 0x%04x",
918 event->cis_conn_hdl);
919 return;
920 }
921
922 LOG_INFO("Lost all members from the group %d", group->group_id_);
923 group->cises_.clear();
924 RemoveCigForGroup(group);
925
926 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
927 group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
928 /* If there is no more ase to stream. Notify it is in IDLE. */
929 state_machine_callbacks_->StatusReportCb(group->group_id_,
930 GroupStreamStatus::IDLE);
931 return;
932
933 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
934 /* Intentional group disconnect has finished, but the last CIS in the
935 * event came after the ASE notification.
936 * If group is already suspended and all CIS are disconnected, we can
937 * report SUSPENDED state.
938 */
939 if ((group->GetState() ==
940 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) &&
941 group->HaveAllCisesDisconnected()) {
942 /* No more transition for group */
943 cancel_watchdog_if_needed(group->group_id_);
944
945 state_machine_callbacks_->StatusReportCb(
946 group->group_id_, GroupStreamStatus::SUSPENDED);
947 return;
948 }
949 break;
950 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
951 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
952 /* Those two are used when closing the stream and CIS disconnection is
953 * expected */
954 if (!group->HaveAllCisesDisconnected()) {
955 LOG_DEBUG(
956 "Still waiting for all CISes being disconnected for group:%d",
957 group->group_id_);
958 return;
959 }
960
961 auto current_group_state = group->GetState();
962 LOG_INFO("group %d current state: %s, target state: %s",
963 group->group_id_,
964 bluetooth::common::ToString(current_group_state).c_str(),
965 bluetooth::common::ToString(target_state).c_str());
966 /* It might happen that controller notified about CIS disconnection
967 * later, after ASE state already changed.
968 * In such an event, there is need to notify upper layer about state
969 * from here.
970 */
971 cancel_watchdog_if_needed(group->group_id_);
972
973 if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
974 LOG_INFO(
975 "Cises disconnected for group %d, we are good in Idle state.",
976 group->group_id_);
977 ReleaseCisIds(group);
978 state_machine_callbacks_->StatusReportCb(group->group_id_,
979 GroupStreamStatus::IDLE);
980 } else if (current_group_state ==
981 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
982 auto reconfig = group->IsPendingConfiguration();
983 LOG_INFO(
984 "Cises disconnected for group: %d, we are good in Configured "
985 "state, reconfig=%d.",
986 group->group_id_, reconfig);
987
988 if (reconfig) {
989 group->ClearPendingConfiguration();
990 state_machine_callbacks_->StatusReportCb(
991 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
992 } else {
993 /* This is Autonomous change if both, target and current state
994 * is CODEC_CONFIGURED
995 */
996 if (target_state == current_group_state) {
997 state_machine_callbacks_->StatusReportCb(
998 group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
999 }
1000 }
1001 }
1002 RemoveCigForGroup(group);
1003 } break;
1004 default:
1005 break;
1006 }
1007
1008 /* We should send Receiver Stop Ready when acting as a source */
1009 if (ases_pair.source &&
1010 ases_pair.source->state == AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING) {
1011 std::vector<uint8_t> ids = {ases_pair.source->id};
1012 std::vector<uint8_t> value;
1013
1014 le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStopReady(ids,
1015 value);
1016 WriteToControlPoint(leAudioDevice, value);
1017
1018 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
1019 leAudioDevice->address_,
1020 kLogAseStopReadyOp + "ASE_ID " +
1021 std::to_string(ases_pair.source->id));
1022 }
1023
1024 /* Tear down CIS's data paths within the group */
1025 struct ase* ase = leAudioDevice->GetFirstActiveAseByDataPathState(
1026 AudioStreamDataPathState::DATA_PATH_ESTABLISHED);
1027 if (!ase) {
1028 leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
1029 /* No more ASEs to disconnect their CISes */
1030 if (!leAudioDevice) return;
1031
1032 ase = leAudioDevice->GetFirstActiveAse();
1033 }
1034
1035 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
1036 if (ase->data_path_state ==
1037 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
1038 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
1039 }
1040 }
1041
1042 private:
1043 static constexpr uint64_t kStateTransitionTimeoutMs = 3500;
1044 static constexpr char kStateTransitionTimeoutMsProp[] =
1045 "persist.bluetooth.leaudio.device.set.state.timeoutms";
1046 Callbacks* state_machine_callbacks_;
1047 alarm_t* watchdog_;
1048 LeAudioLogHistory* log_history_;
1049
1050 /* This callback is called on timeout during transition to target state */
OnStateTransitionTimeout(int group_id)1051 void OnStateTransitionTimeout(int group_id) {
1052 log_history_->AddLogHistory(kLogStateMachineTag, group_id,
1053 RawAddress::kEmpty, "WATCHDOG FIRED");
1054 state_machine_callbacks_->OnStateTransitionTimeout(group_id);
1055 }
1056
SetTargetState(LeAudioDeviceGroup * group,AseState state)1057 void SetTargetState(LeAudioDeviceGroup* group, AseState state) {
1058 auto current_state = ToString(group->GetTargetState());
1059 auto new_state = ToString(state);
1060
1061 LOG_DEBUG("Watchdog watch started for group=%d transition from %s to %s",
1062 group->group_id_, current_state.c_str(), new_state.c_str());
1063
1064 group->SetTargetState(state);
1065
1066 /* Group should tie in time to get requested status */
1067 uint64_t timeoutMs = kStateTransitionTimeoutMs;
1068 timeoutMs =
1069 osi_property_get_int32(kStateTransitionTimeoutMsProp, timeoutMs);
1070
1071 cancel_watchdog_if_needed(group->group_id_);
1072
1073 alarm_set_on_mloop(
1074 watchdog_, timeoutMs,
1075 [](void* data) {
1076 if (instance) instance->OnStateTransitionTimeout(PTR_TO_INT(data));
1077 },
1078 INT_TO_PTR(group->group_id_));
1079
1080 log_history_->AddLogHistory(kLogStateMachineTag, group->group_id_,
1081 RawAddress::kEmpty, "WATCHDOG STARTED");
1082 }
1083
AddCisToStreamConfiguration(LeAudioDeviceGroup * group,const struct ase * ase)1084 void AddCisToStreamConfiguration(LeAudioDeviceGroup* group,
1085 const struct ase* ase) {
1086 uint16_t cis_conn_hdl = ase->cis_conn_hdl;
1087 LOG_INFO("Adding cis handle 0x%04x (%s) to stream list", cis_conn_hdl,
1088 ase->direction == le_audio::types::kLeAudioDirectionSink
1089 ? "sink"
1090 : "source");
1091 auto* stream_conf = &group->stream_conf;
1092 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
1093 auto iter = std::find_if(
1094 stream_conf->sink_streams.begin(), stream_conf->sink_streams.end(),
1095 [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
1096
1097 ASSERT_LOG(iter == stream_conf->sink_streams.end(),
1098 "Stream is already there 0x%04x", cis_conn_hdl);
1099
1100 stream_conf->sink_streams.emplace_back(std::make_pair(
1101 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
1102
1103 stream_conf->sink_num_of_devices++;
1104 stream_conf->sink_num_of_channels += ase->codec_config.channel_count;
1105 stream_conf->sink_audio_channel_allocation |=
1106 *ase->codec_config.audio_channel_allocation;
1107
1108 if (stream_conf->sink_sample_frequency_hz == 0) {
1109 stream_conf->sink_sample_frequency_hz =
1110 ase->codec_config.GetSamplingFrequencyHz();
1111 } else {
1112 ASSERT_LOG(stream_conf->sink_sample_frequency_hz ==
1113 ase->codec_config.GetSamplingFrequencyHz(),
1114 "sample freq mismatch: %d!=%d",
1115 stream_conf->sink_sample_frequency_hz,
1116 ase->codec_config.GetSamplingFrequencyHz());
1117 }
1118
1119 if (stream_conf->sink_octets_per_codec_frame == 0) {
1120 stream_conf->sink_octets_per_codec_frame =
1121 *ase->codec_config.octets_per_codec_frame;
1122 } else {
1123 ASSERT_LOG(stream_conf->sink_octets_per_codec_frame ==
1124 *ase->codec_config.octets_per_codec_frame,
1125 "octets per frame mismatch: %d!=%d",
1126 stream_conf->sink_octets_per_codec_frame,
1127 *ase->codec_config.octets_per_codec_frame);
1128 }
1129
1130 if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) {
1131 stream_conf->sink_codec_frames_blocks_per_sdu =
1132 *ase->codec_config.codec_frames_blocks_per_sdu;
1133 } else {
1134 ASSERT_LOG(stream_conf->sink_codec_frames_blocks_per_sdu ==
1135 *ase->codec_config.codec_frames_blocks_per_sdu,
1136 "codec_frames_blocks_per_sdu: %d!=%d",
1137 stream_conf->sink_codec_frames_blocks_per_sdu,
1138 *ase->codec_config.codec_frames_blocks_per_sdu);
1139 }
1140
1141 if (stream_conf->sink_frame_duration_us == 0) {
1142 stream_conf->sink_frame_duration_us =
1143 ase->codec_config.GetFrameDurationUs();
1144 } else {
1145 ASSERT_LOG(stream_conf->sink_frame_duration_us ==
1146 ase->codec_config.GetFrameDurationUs(),
1147 "frame_duration_us: %d!=%d",
1148 stream_conf->sink_frame_duration_us,
1149 ase->codec_config.GetFrameDurationUs());
1150 }
1151
1152 LOG_INFO(
1153 " Added Sink Stream Configuration. CIS Connection Handle: %d"
1154 ", Audio Channel Allocation: %d"
1155 ", Sink Number Of Devices: %d"
1156 ", Sink Number Of Channels: %d",
1157 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
1158 stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels);
1159
1160 } else {
1161 /* Source case */
1162 auto iter = std::find_if(
1163 stream_conf->source_streams.begin(),
1164 stream_conf->source_streams.end(),
1165 [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
1166
1167 ASSERT_LOG(iter == stream_conf->source_streams.end(),
1168 "Stream is already there 0x%04x", cis_conn_hdl);
1169
1170 stream_conf->source_streams.emplace_back(std::make_pair(
1171 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
1172
1173 stream_conf->source_num_of_devices++;
1174 stream_conf->source_num_of_channels += ase->codec_config.channel_count;
1175 stream_conf->source_audio_channel_allocation |=
1176 *ase->codec_config.audio_channel_allocation;
1177
1178 if (stream_conf->source_sample_frequency_hz == 0) {
1179 stream_conf->source_sample_frequency_hz =
1180 ase->codec_config.GetSamplingFrequencyHz();
1181 } else {
1182 ASSERT_LOG(stream_conf->source_sample_frequency_hz ==
1183 ase->codec_config.GetSamplingFrequencyHz(),
1184 "sample freq mismatch: %d!=%d",
1185 stream_conf->source_sample_frequency_hz,
1186 ase->codec_config.GetSamplingFrequencyHz());
1187 }
1188
1189 if (stream_conf->source_octets_per_codec_frame == 0) {
1190 stream_conf->source_octets_per_codec_frame =
1191 *ase->codec_config.octets_per_codec_frame;
1192 } else {
1193 ASSERT_LOG(stream_conf->source_octets_per_codec_frame ==
1194 *ase->codec_config.octets_per_codec_frame,
1195 "octets per frame mismatch: %d!=%d",
1196 stream_conf->source_octets_per_codec_frame,
1197 *ase->codec_config.octets_per_codec_frame);
1198 }
1199
1200 if (stream_conf->source_codec_frames_blocks_per_sdu == 0) {
1201 stream_conf->source_codec_frames_blocks_per_sdu =
1202 *ase->codec_config.codec_frames_blocks_per_sdu;
1203 } else {
1204 ASSERT_LOG(stream_conf->source_codec_frames_blocks_per_sdu ==
1205 *ase->codec_config.codec_frames_blocks_per_sdu,
1206 "codec_frames_blocks_per_sdu: %d!=%d",
1207 stream_conf->source_codec_frames_blocks_per_sdu,
1208 *ase->codec_config.codec_frames_blocks_per_sdu);
1209 }
1210
1211 if (stream_conf->source_frame_duration_us == 0) {
1212 stream_conf->source_frame_duration_us =
1213 ase->codec_config.GetFrameDurationUs();
1214 } else {
1215 ASSERT_LOG(stream_conf->source_frame_duration_us ==
1216 ase->codec_config.GetFrameDurationUs(),
1217 "frame_duration_us: %d!=%d",
1218 stream_conf->source_frame_duration_us,
1219 ase->codec_config.GetFrameDurationUs());
1220 }
1221
1222 LOG_INFO(
1223 " Added Source Stream Configuration. CIS Connection Handle: %d"
1224 ", Audio Channel Allocation: %d"
1225 ", Source Number Of Devices: %d"
1226 ", Source Number Of Channels: %d",
1227 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
1228 stream_conf->source_num_of_devices,
1229 stream_conf->source_num_of_channels);
1230 }
1231
1232 /* Update offloader streams */
1233 group->CreateStreamVectorForOffloader(ase->direction);
1234 }
1235
RemoveCisFromStreamConfiguration(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint16_t cis_conn_hdl)1236 void RemoveCisFromStreamConfiguration(LeAudioDeviceGroup* group,
1237 LeAudioDevice* leAudioDevice,
1238 uint16_t cis_conn_hdl) {
1239 auto* stream_conf = &group->stream_conf;
1240
1241 LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl);
1242
1243 auto sink_channels = stream_conf->sink_num_of_channels;
1244 auto source_channels = stream_conf->source_num_of_channels;
1245
1246 if (!stream_conf->sink_streams.empty() ||
1247 !stream_conf->source_streams.empty()) {
1248 stream_conf->sink_streams.erase(
1249 std::remove_if(
1250 stream_conf->sink_streams.begin(),
1251 stream_conf->sink_streams.end(),
1252 [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
1253 if (!cis_conn_hdl) {
1254 cis_conn_hdl = pair.first;
1255 }
1256 auto ases_pair =
1257 leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
1258 if (ases_pair.sink && cis_conn_hdl == pair.first) {
1259 stream_conf->sink_num_of_devices--;
1260 stream_conf->sink_num_of_channels -=
1261 ases_pair.sink->codec_config.channel_count;
1262 stream_conf->sink_audio_channel_allocation &= ~pair.second;
1263 }
1264 return (ases_pair.sink && cis_conn_hdl == pair.first);
1265 }),
1266 stream_conf->sink_streams.end());
1267
1268 stream_conf->source_streams.erase(
1269 std::remove_if(
1270 stream_conf->source_streams.begin(),
1271 stream_conf->source_streams.end(),
1272 [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
1273 if (!cis_conn_hdl) {
1274 cis_conn_hdl = pair.first;
1275 }
1276 auto ases_pair =
1277 leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
1278 if (ases_pair.source && cis_conn_hdl == pair.first) {
1279 stream_conf->source_num_of_devices--;
1280 stream_conf->source_num_of_channels -=
1281 ases_pair.source->codec_config.channel_count;
1282 stream_conf->source_audio_channel_allocation &= ~pair.second;
1283 }
1284 return (ases_pair.source && cis_conn_hdl == pair.first);
1285 }),
1286 stream_conf->source_streams.end());
1287
1288 LOG_INFO(
1289 " Sink Number Of Devices: %d"
1290 ", Sink Number Of Channels: %d"
1291 ", Source Number Of Devices: %d"
1292 ", Source Number Of Channels: %d",
1293 stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels,
1294 stream_conf->source_num_of_devices,
1295 stream_conf->source_num_of_channels);
1296 }
1297
1298 if (stream_conf->sink_num_of_channels == 0) {
1299 group->ClearSinksFromConfiguration();
1300 }
1301
1302 if (stream_conf->source_num_of_channels == 0) {
1303 group->ClearSourcesFromConfiguration();
1304 }
1305
1306 /* Update offloader streams if needed */
1307 if (sink_channels > stream_conf->sink_num_of_channels) {
1308 group->CreateStreamVectorForOffloader(
1309 le_audio::types::kLeAudioDirectionSink);
1310 }
1311 if (source_channels > stream_conf->source_num_of_channels) {
1312 group->CreateStreamVectorForOffloader(
1313 le_audio::types::kLeAudioDirectionSource);
1314 }
1315
1316 group->CigUnassignCis(leAudioDevice);
1317 }
1318
CigCreate(LeAudioDeviceGroup * group)1319 bool CigCreate(LeAudioDeviceGroup* group) {
1320 uint32_t sdu_interval_mtos, sdu_interval_stom;
1321 uint16_t max_trans_lat_mtos, max_trans_lat_stom;
1322 uint8_t packing, framing, sca;
1323 std::vector<EXT_CIS_CFG> cis_cfgs;
1324
1325 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
1326 ToString(group->cig_state_).c_str());
1327
1328 if (group->GetCigState() != CigState::NONE) {
1329 LOG_WARN(" Group %p, id: %d has invalid cig state: %s ", group,
1330 group->group_id_, ToString(group->cig_state_).c_str());
1331 return false;
1332 }
1333
1334 sdu_interval_mtos =
1335 group->GetSduInterval(le_audio::types::kLeAudioDirectionSink);
1336 sdu_interval_stom =
1337 group->GetSduInterval(le_audio::types::kLeAudioDirectionSource);
1338 sca = group->GetSCA();
1339 packing = group->GetPacking();
1340 framing = group->GetFraming();
1341 max_trans_lat_mtos = group->GetMaxTransportLatencyMtos();
1342 max_trans_lat_stom = group->GetMaxTransportLatencyStom();
1343
1344 uint16_t max_sdu_size_mtos = 0;
1345 uint16_t max_sdu_size_stom = 0;
1346 uint8_t phy_mtos =
1347 group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSink);
1348 uint8_t phy_stom =
1349 group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSource);
1350 uint8_t rtn_mtos = 0;
1351 uint8_t rtn_stom = 0;
1352
1353 /* Currently assumed Sink/Source configuration is same across cis types.
1354 * If a cis in cises_ is currently associated with active device/ASE(s),
1355 * use the Sink/Source configuration for the same.
1356 * If a cis in cises_ is not currently associated with active device/ASE(s),
1357 * use the Sink/Source configuration for the cis in cises_
1358 * associated with a active device/ASE(s). When the same cis is associated
1359 * later, with active device/ASE(s), check if current configuration is
1360 * supported or not, if not, reconfigure CIG.
1361 */
1362 for (struct le_audio::types::cis& cis : group->cises_) {
1363 uint16_t max_sdu_size_mtos_temp =
1364 group->GetMaxSduSize(le_audio::types::kLeAudioDirectionSink, cis.id);
1365 uint16_t max_sdu_size_stom_temp = group->GetMaxSduSize(
1366 le_audio::types::kLeAudioDirectionSource, cis.id);
1367 uint8_t rtn_mtos_temp =
1368 group->GetRtn(le_audio::types::kLeAudioDirectionSink, cis.id);
1369 uint8_t rtn_stom_temp =
1370 group->GetRtn(le_audio::types::kLeAudioDirectionSource, cis.id);
1371
1372 max_sdu_size_mtos =
1373 max_sdu_size_mtos_temp ? max_sdu_size_mtos_temp : max_sdu_size_mtos;
1374 max_sdu_size_stom =
1375 max_sdu_size_stom_temp ? max_sdu_size_stom_temp : max_sdu_size_stom;
1376 rtn_mtos = rtn_mtos_temp ? rtn_mtos_temp : rtn_mtos;
1377 rtn_stom = rtn_stom_temp ? rtn_stom_temp : rtn_stom;
1378 }
1379
1380 for (struct le_audio::types::cis& cis : group->cises_) {
1381 EXT_CIS_CFG cis_cfg = {};
1382
1383 cis_cfg.cis_id = cis.id;
1384 cis_cfg.phy_mtos = phy_mtos;
1385 cis_cfg.phy_stom = phy_stom;
1386 if (cis.type == le_audio::types::CisType::CIS_TYPE_BIDIRECTIONAL) {
1387 cis_cfg.max_sdu_size_mtos = max_sdu_size_mtos;
1388 cis_cfg.rtn_mtos = rtn_mtos;
1389 cis_cfg.max_sdu_size_stom = max_sdu_size_stom;
1390 cis_cfg.rtn_stom = rtn_stom;
1391 cis_cfgs.push_back(cis_cfg);
1392 } else if (cis.type ==
1393 le_audio::types::CisType::CIS_TYPE_UNIDIRECTIONAL_SINK) {
1394 cis_cfg.max_sdu_size_mtos = max_sdu_size_mtos;
1395 cis_cfg.rtn_mtos = rtn_mtos;
1396 cis_cfg.max_sdu_size_stom = 0;
1397 cis_cfg.rtn_stom = 0;
1398 cis_cfgs.push_back(cis_cfg);
1399 } else {
1400 cis_cfg.max_sdu_size_mtos = 0;
1401 cis_cfg.rtn_mtos = 0;
1402 cis_cfg.max_sdu_size_stom = max_sdu_size_stom;
1403 cis_cfg.rtn_stom = rtn_stom;
1404 cis_cfgs.push_back(cis_cfg);
1405 }
1406 }
1407
1408 if ((sdu_interval_mtos == 0 && sdu_interval_stom == 0) ||
1409 (max_trans_lat_mtos == le_audio::types::kMaxTransportLatencyMin &&
1410 max_trans_lat_stom == le_audio::types::kMaxTransportLatencyMin) ||
1411 (max_sdu_size_mtos == 0 && max_sdu_size_stom == 0)) {
1412 LOG_ERROR(" Trying to create invalid group");
1413 group->PrintDebugState();
1414 return false;
1415 }
1416
1417 bluetooth::hci::iso_manager::cig_create_params param = {
1418 .sdu_itv_mtos = sdu_interval_mtos,
1419 .sdu_itv_stom = sdu_interval_stom,
1420 .sca = sca,
1421 .packing = packing,
1422 .framing = framing,
1423 .max_trans_lat_stom = max_trans_lat_stom,
1424 .max_trans_lat_mtos = max_trans_lat_mtos,
1425 .cis_cfgs = std::move(cis_cfgs),
1426 };
1427
1428 log_history_->AddLogHistory(
1429 kLogStateMachineTag, group->group_id_, RawAddress::kEmpty,
1430 kLogCigCreateOp + "#CIS: " + std::to_string(param.cis_cfgs.size()));
1431
1432 group->SetCigState(CigState::CREATING);
1433 IsoManager::GetInstance()->CreateCig(group->group_id_, std::move(param));
1434 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
1435 ToString(group->cig_state_).c_str());
1436 return true;
1437 }
1438
CisCreateForDevice(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1439 static bool CisCreateForDevice(LeAudioDeviceGroup* group,
1440 LeAudioDevice* leAudioDevice) {
1441 std::vector<EXT_CIS_CREATE_CFG> conn_pairs;
1442 struct ase* ase = leAudioDevice->GetFirstActiveAse();
1443
1444 /* Make sure CIG is there */
1445 if (group->GetCigState() != CigState::CREATED) {
1446 LOG_ERROR("CIG is not created for group_id %d ", group->group_id_);
1447 group->PrintDebugState();
1448 return false;
1449 }
1450
1451 std::stringstream extra_stream;
1452 do {
1453 /* First in ase pair is Sink, second Source */
1454 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl);
1455
1456 /* Already in pending state - bi-directional CIS */
1457 if (ase->data_path_state == AudioStreamDataPathState::CIS_PENDING)
1458 continue;
1459
1460 if (ases_pair.sink)
1461 ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_PENDING;
1462 if (ases_pair.source)
1463 ases_pair.source->data_path_state =
1464 AudioStreamDataPathState::CIS_PENDING;
1465
1466 uint16_t acl_handle =
1467 BTM_GetHCIConnHandle(leAudioDevice->address_, BT_TRANSPORT_LE);
1468 conn_pairs.push_back({.cis_conn_handle = ase->cis_conn_hdl,
1469 .acl_conn_handle = acl_handle});
1470 LOG_INFO(" cis handle: 0x%04x, acl handle: 0x%04x", ase->cis_conn_hdl,
1471 acl_handle);
1472 extra_stream << "cis_h:" << loghex(ase->cis_conn_hdl)
1473 << " acl_h:" << loghex(acl_handle) << ";;";
1474 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
1475
1476 LeAudioLogHistory::Get()->AddLogHistory(
1477 kLogStateMachineTag, leAudioDevice->group_id_, RawAddress::kEmpty,
1478 kLogCisCreateOp + "#CIS: " + std::to_string(conn_pairs.size()),
1479 extra_stream.str());
1480
1481 IsoManager::GetInstance()->EstablishCis(
1482 {.conn_pairs = std::move(conn_pairs)});
1483
1484 return true;
1485 }
1486
CisCreate(LeAudioDeviceGroup * group)1487 static bool CisCreate(LeAudioDeviceGroup* group) {
1488 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1489 struct ase* ase;
1490 std::vector<EXT_CIS_CREATE_CFG> conn_pairs;
1491
1492 LOG_ASSERT(leAudioDevice)
1493 << __func__ << " Shouldn't be called without an active device.";
1494
1495 /* Make sure CIG is there */
1496 if (group->GetCigState() != CigState::CREATED) {
1497 LOG_ERROR("CIG is not created for group_id %d ", group->group_id_);
1498 group->PrintDebugState();
1499 return false;
1500 }
1501
1502 do {
1503 ase = leAudioDevice->GetFirstActiveAse();
1504 LOG_ASSERT(ase) << __func__
1505 << " shouldn't be called without an active ASE";
1506 do {
1507 /* First is ase pair is Sink, second Source */
1508 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl);
1509
1510 /* Already in pending state - bi-directional CIS */
1511 if (ase->data_path_state == AudioStreamDataPathState::CIS_PENDING)
1512 continue;
1513
1514 if (ases_pair.sink)
1515 ases_pair.sink->data_path_state =
1516 AudioStreamDataPathState::CIS_PENDING;
1517 if (ases_pair.source)
1518 ases_pair.source->data_path_state =
1519 AudioStreamDataPathState::CIS_PENDING;
1520
1521 uint16_t acl_handle =
1522 BTM_GetHCIConnHandle(leAudioDevice->address_, BT_TRANSPORT_LE);
1523 conn_pairs.push_back({.cis_conn_handle = ase->cis_conn_hdl,
1524 .acl_conn_handle = acl_handle});
1525 DLOG(INFO) << __func__ << " cis handle: " << +ase->cis_conn_hdl
1526 << " acl handle : " << loghex(+acl_handle);
1527
1528 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
1529 } while ((leAudioDevice = group->GetNextActiveDevice(leAudioDevice)));
1530
1531 IsoManager::GetInstance()->EstablishCis(
1532 {.conn_pairs = std::move(conn_pairs)});
1533
1534 return true;
1535 }
1536
PrepareDataPath(int group_id,const struct ase * ase)1537 static void PrepareDataPath(int group_id, const struct ase* ase) {
1538 /* TODO: Handle HW offloading decode as we handle here, force to use SW
1539 * decode for now */
1540 auto data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci;
1541 if (CodecManager::GetInstance()->GetCodecLocation() !=
1542 CodecLocation::HOST) {
1543 data_path_id = bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
1544 }
1545 /* TODO: Need to set coding_format when we support the codec location inside
1546 * the controller, force to use transparent for now */
1547 bluetooth::hci::iso_manager::iso_data_path_params param = {
1548 .data_path_dir =
1549 ase->direction == le_audio::types::kLeAudioDirectionSink
1550 ? bluetooth::hci::iso_manager::kIsoDataPathDirectionIn
1551 : bluetooth::hci::iso_manager::kIsoDataPathDirectionOut,
1552 .data_path_id = data_path_id,
1553 .codec_id_format = bluetooth::hci::kIsoCodingFormatTransparent,
1554 .codec_id_company = ase->codec_id.vendor_company_id,
1555 .codec_id_vendor = ase->codec_id.vendor_codec_id,
1556 .controller_delay = 0x00000000,
1557 .codec_conf = std::vector<uint8_t>(),
1558 };
1559
1560 LeAudioLogHistory::Get()->AddLogHistory(
1561 kLogStateMachineTag, group_id, RawAddress::kEmpty,
1562 kLogSetDataPathOp + "cis_h:" + loghex(ase->cis_conn_hdl),
1563 "direction: " + loghex(param.data_path_dir));
1564
1565 IsoManager::GetInstance()->SetupIsoDataPath(ase->cis_conn_hdl,
1566 std::move(param));
1567 }
1568
PrepareDataPath(LeAudioDeviceGroup * group)1569 static inline void PrepareDataPath(LeAudioDeviceGroup* group) {
1570 auto* leAudioDevice = group->GetFirstActiveDeviceByDataPathState(
1571 AudioStreamDataPathState::CIS_ESTABLISHED);
1572 LOG_ASSERT(leAudioDevice)
1573 << __func__ << " Shouldn't be called without an active device.";
1574
1575 auto* ase = leAudioDevice->GetFirstActiveAseByDataPathState(
1576 AudioStreamDataPathState::CIS_ESTABLISHED);
1577 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
1578 PrepareDataPath(group->group_id_, ase);
1579 }
1580
ReleaseDataPath(LeAudioDeviceGroup * group)1581 static void ReleaseDataPath(LeAudioDeviceGroup* group) {
1582 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1583 LOG_ASSERT(leAudioDevice)
1584 << __func__ << " Shouldn't be called without an active device.";
1585
1586 auto ase = leAudioDevice->GetFirstActiveAseByDataPathState(
1587 AudioStreamDataPathState::DATA_PATH_ESTABLISHED);
1588 LOG_ASSERT(ase) << __func__
1589 << " Shouldn't be called without an active ASE.";
1590 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
1591 }
1592
SetAseState(LeAudioDevice * leAudioDevice,struct ase * ase,AseState state)1593 void SetAseState(LeAudioDevice* leAudioDevice, struct ase* ase,
1594 AseState state) {
1595 LOG_INFO("%s, ase_id: %d, %s -> %s",
1596 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
1597 ToString(ase->state).c_str(), ToString(state).c_str());
1598
1599 log_history_->AddLogHistory(
1600 kLogStateMachineTag, leAudioDevice->group_id_, leAudioDevice->address_,
1601 "ASE_ID " + std::to_string(ase->id) + ": " + kLogStateChangedOp,
1602 ToString(ase->state) + "->" + ToString(state));
1603
1604 ase->state = state;
1605 }
1606
AseStateMachineProcessIdle(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1607 void AseStateMachineProcessIdle(
1608 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
1609 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
1610 switch (ase->state) {
1611 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
1612 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
1613 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
1614 if (ase->id == 0x00) {
1615 /* Initial state of Ase - update id */
1616 LOG(INFO) << __func__
1617 << ", discovered ase id: " << static_cast<int>(arh.id);
1618 ase->id = arh.id;
1619 }
1620 break;
1621 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING: {
1622 SetAseState(leAudioDevice, ase, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
1623 ase->active = false;
1624 ase->configured_for_context_type =
1625 le_audio::types::LeAudioContextType::UNINITIALIZED;
1626
1627 if (!leAudioDevice->HaveAllActiveAsesSameState(
1628 AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
1629 /* More ASEs notification from this device has to come for this group
1630 */
1631 LOG_DEBUG("Wait for more ASE to configure for device %s",
1632 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
1633 return;
1634 }
1635
1636 /* Before continue with release, make sure this is what is requested.
1637 * If not (e.g. only single device got disconnected), stop here
1638 */
1639 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
1640 LOG_DEBUG("Autonomus change of stated for device %s, ase id: %d",
1641 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
1642 return;
1643 }
1644
1645 if (!group->HaveAllActiveDevicesAsesTheSameState(
1646 AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
1647 LOG_DEBUG("Waiting for more devices to get into idle state");
1648 return;
1649 }
1650
1651 /* Last node is in releasing state*/
1652 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
1653 group->PrintDebugState();
1654
1655 /* If all CISes are disconnected, notify upper layer about IDLE state,
1656 * otherwise wait for */
1657 if (!group->HaveAllCisesDisconnected()) {
1658 LOG_WARN(
1659 "Not all CISes removed before going to IDLE for group %d, "
1660 "waiting...",
1661 group->group_id_);
1662 group->PrintDebugState();
1663 return;
1664 }
1665
1666 cancel_watchdog_if_needed(group->group_id_);
1667 ReleaseCisIds(group);
1668 state_machine_callbacks_->StatusReportCb(group->group_id_,
1669 GroupStreamStatus::IDLE);
1670
1671 break;
1672 }
1673 default:
1674 LOG(ERROR) << __func__ << ", invalid state transition, from: "
1675 << static_cast<int>(ase->state) << ", to: "
1676 << static_cast<int>(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
1677 StopStream(group);
1678 break;
1679 }
1680 }
1681
PrepareAndSendQoSToTheGroup(LeAudioDeviceGroup * group)1682 void PrepareAndSendQoSToTheGroup(LeAudioDeviceGroup* group) {
1683 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1684 if (!leAudioDevice) {
1685 LOG_ERROR("No active device for the group");
1686 group->PrintDebugState();
1687 ClearGroup(group, true);
1688 return;
1689 }
1690
1691 for (; leAudioDevice;
1692 leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
1693 PrepareAndSendConfigQos(group, leAudioDevice);
1694 }
1695 }
1696
PrepareAndSendCodecConfigToTheGroup(LeAudioDeviceGroup * group)1697 bool PrepareAndSendCodecConfigToTheGroup(LeAudioDeviceGroup* group) {
1698 LOG_INFO("group_id: %d", group->group_id_);
1699 auto leAudioDevice = group->GetFirstActiveDevice();
1700 if (!leAudioDevice) {
1701 LOG_ERROR("No active device for the group");
1702 return false;
1703 }
1704
1705 for (; leAudioDevice;
1706 leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
1707 PrepareAndSendCodecConfigure(group, leAudioDevice);
1708 }
1709 return true;
1710 }
1711
PrepareAndSendCodecConfigure(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1712 void PrepareAndSendCodecConfigure(LeAudioDeviceGroup* group,
1713 LeAudioDevice* leAudioDevice) {
1714 struct le_audio::client_parser::ascs::ctp_codec_conf conf;
1715 std::vector<struct le_audio::client_parser::ascs::ctp_codec_conf> confs;
1716 struct ase* ase;
1717 std::stringstream msg_stream;
1718 std::stringstream extra_stream;
1719
1720 if (!group->CigAssignCisIds(leAudioDevice)) {
1721 LOG_ERROR(" unable to assign CIS IDs");
1722 StopStream(group);
1723 return;
1724 }
1725
1726 if (group->GetCigState() == CigState::CREATED)
1727 group->CigAssignCisConnHandlesToAses(leAudioDevice);
1728
1729 msg_stream << kLogAseConfigOp;
1730
1731 ase = leAudioDevice->GetFirstActiveAse();
1732 ASSERT_LOG(ase, "shouldn't be called without an active ASE");
1733 for (; ase != nullptr; ase = leAudioDevice->GetNextActiveAse(ase)) {
1734 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
1735 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
1736 ase->cis_id, ToString(ase->state).c_str());
1737 conf.ase_id = ase->id;
1738 conf.target_latency = ase->target_latency;
1739 conf.target_phy = group->GetTargetPhy(ase->direction);
1740 conf.codec_id = ase->codec_id;
1741 conf.codec_config = ase->codec_config;
1742 confs.push_back(conf);
1743
1744 msg_stream << "ASE_ID " << +conf.ase_id << ",";
1745 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
1746 extra_stream << "snk,";
1747 } else {
1748 extra_stream << "src,";
1749 }
1750 extra_stream << +conf.codec_id.coding_format << ","
1751 << +conf.target_latency << ";;";
1752 }
1753
1754 std::vector<uint8_t> value;
1755 le_audio::client_parser::ascs::PrepareAseCtpCodecConfig(confs, value);
1756 WriteToControlPoint(leAudioDevice, value);
1757
1758 log_history_->AddLogHistory(kLogControlPointCmd, group->group_id_,
1759 leAudioDevice->address_, msg_stream.str(),
1760 extra_stream.str());
1761 }
1762
AseStateMachineProcessCodecConfigured(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,uint8_t * data,uint16_t len,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1763 void AseStateMachineProcessCodecConfigured(
1764 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
1765 uint8_t* data, uint16_t len, LeAudioDeviceGroup* group,
1766 LeAudioDevice* leAudioDevice) {
1767 if (!group) {
1768 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
1769
1770 return;
1771 }
1772
1773 /* ase contain current ASE state. New state is in "arh" */
1774 switch (ase->state) {
1775 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE: {
1776 if (ase->id == 0x00) {
1777 /* Initial state of Ase - update id */
1778 LOG(INFO) << __func__
1779 << ", discovered ase id: " << static_cast<int>(arh.id);
1780 ase->id = arh.id;
1781 }
1782
1783 struct le_audio::client_parser::ascs::ase_codec_configured_state_params
1784 rsp;
1785
1786 /* Cache codec configured status values for further
1787 * configuration/reconfiguration
1788 */
1789 if (!ParseAseStatusCodecConfiguredStateParams(rsp, len, data)) {
1790 StopStream(group);
1791 return;
1792 }
1793
1794 uint16_t cig_curr_max_trans_lat_mtos =
1795 group->GetMaxTransportLatencyMtos();
1796 uint16_t cig_curr_max_trans_lat_stom =
1797 group->GetMaxTransportLatencyStom();
1798
1799 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1800 /* We are here because of the reconnection of the single device.
1801 * Reconfigure CIG if current CIG supported Max Transport Latency for
1802 * a direction, cannot be supported by the newly connected member
1803 * device's ASE for the direction.
1804 */
1805 if ((ase->direction == le_audio::types::kLeAudioDirectionSink &&
1806 cig_curr_max_trans_lat_mtos > rsp.max_transport_latency) ||
1807 (ase->direction == le_audio::types::kLeAudioDirectionSource &&
1808 cig_curr_max_trans_lat_stom > rsp.max_transport_latency)) {
1809 group->SetPendingConfiguration();
1810 StopStream(group);
1811 return;
1812 }
1813 }
1814
1815 ase->framing = rsp.framing;
1816 ase->preferred_phy = rsp.preferred_phy;
1817 /* Validate and update QoS settings to be consistent */
1818 if ((!ase->max_transport_latency ||
1819 ase->max_transport_latency > rsp.max_transport_latency) ||
1820 !ase->retrans_nb) {
1821 ase->max_transport_latency = rsp.max_transport_latency;
1822 ase->retrans_nb = rsp.preferred_retrans_nb;
1823 LOG_INFO(
1824 " Using server preferred QoS settings. Max Transport Latency: %d"
1825 ", Retransmission Number: %d",
1826 +ase->max_transport_latency, ase->retrans_nb);
1827 }
1828 ase->pres_delay_min = rsp.pres_delay_min;
1829 ase->pres_delay_max = rsp.pres_delay_max;
1830 ase->preferred_pres_delay_min = rsp.preferred_pres_delay_min;
1831 ase->preferred_pres_delay_max = rsp.preferred_pres_delay_max;
1832
1833 SetAseState(leAudioDevice, ase,
1834 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1835
1836 if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
1837 /* This is autonomus change of the remote device */
1838 LOG_DEBUG("Autonomus change for device %s, ase id %d. Just store it.",
1839 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
1840 return;
1841 }
1842
1843 if (leAudioDevice->HaveAnyUnconfiguredAses()) {
1844 /* More ASEs notification from this device has to come for this group
1845 */
1846 LOG_DEBUG("More Ases to be configured for the device %s",
1847 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
1848 return;
1849 }
1850
1851 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1852 /* We are here because of the reconnection of the single device. */
1853 PrepareAndSendConfigQos(group, leAudioDevice);
1854 return;
1855 }
1856
1857 /* Configure ASEs for next device in group */
1858 if (group->HaveAnyActiveDeviceInUnconfiguredState()) {
1859 LOG_DEBUG("Waiting for all the ASES in the Configured state");
1860 return;
1861 }
1862
1863 /* Last node configured, process group to codec configured state */
1864 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1865
1866 if (group->GetTargetState() ==
1867 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1868 if (!CigCreate(group)) {
1869 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
1870 group->group_id_);
1871 StopStream(group);
1872 }
1873 return;
1874 }
1875
1876 if (group->GetTargetState() ==
1877 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
1878 group->IsPendingConfiguration()) {
1879 LOG_INFO(" Configured state completed ");
1880
1881 /* If all CISes are disconnected, notify upper layer about IDLE
1882 * state, otherwise wait for */
1883 if (!group->HaveAllCisesDisconnected()) {
1884 LOG_WARN(
1885 "Not all CISes removed before going to CONFIGURED for group "
1886 "%d, "
1887 "waiting...",
1888 group->group_id_);
1889 group->PrintDebugState();
1890 return;
1891 }
1892
1893 group->ClearPendingConfiguration();
1894 state_machine_callbacks_->StatusReportCb(
1895 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
1896
1897 /* No more transition for group */
1898 cancel_watchdog_if_needed(group->group_id_);
1899 return;
1900 }
1901
1902 LOG_ERROR(", invalid state transition, from: %s to %s",
1903 ToString(group->GetState()).c_str(),
1904 ToString(group->GetTargetState()).c_str());
1905 StopStream(group);
1906
1907 break;
1908 }
1909 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
1910 /* Received Configured in Configured state. This could be done
1911 * autonomously because of the reconfiguration done by us
1912 */
1913
1914 struct le_audio::client_parser::ascs::ase_codec_configured_state_params
1915 rsp;
1916
1917 /* Cache codec configured status values for further
1918 * configuration/reconfiguration
1919 */
1920 if (!ParseAseStatusCodecConfiguredStateParams(rsp, len, data)) {
1921 StopStream(group);
1922 return;
1923 }
1924
1925 ase->framing = rsp.framing;
1926 ase->preferred_phy = rsp.preferred_phy;
1927 /* Validate and update QoS settings to be consistent */
1928 if ((!ase->max_transport_latency ||
1929 ase->max_transport_latency > rsp.max_transport_latency) ||
1930 !ase->retrans_nb) {
1931 ase->max_transport_latency = rsp.max_transport_latency;
1932 ase->retrans_nb = rsp.preferred_retrans_nb;
1933 LOG(INFO) << __func__ << " Using server preferred QoS settings."
1934 << " Max Transport Latency: " << +ase->max_transport_latency
1935 << ", Retransmission Number: " << +ase->retrans_nb;
1936 }
1937 ase->pres_delay_min = rsp.pres_delay_min;
1938 ase->pres_delay_max = rsp.pres_delay_max;
1939 ase->preferred_pres_delay_min = rsp.preferred_pres_delay_min;
1940 ase->preferred_pres_delay_max = rsp.preferred_pres_delay_max;
1941
1942 /* This may be a notification from a re-configured ASE */
1943 ase->reconfigure = false;
1944
1945 if (leAudioDevice->HaveAnyUnconfiguredAses()) {
1946 /* Waiting for others to be reconfigured */
1947 return;
1948 }
1949
1950 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1951 /* We are here because of the reconnection of the single device. */
1952 PrepareAndSendConfigQos(group, leAudioDevice);
1953 return;
1954 }
1955
1956 if (group->HaveAnyActiveDeviceInUnconfiguredState()) {
1957 LOG_DEBUG(
1958 "Waiting for all the devices to be configured for group id %d",
1959 group->group_id_);
1960 return;
1961 }
1962
1963 /* Last node configured, process group to codec configured state */
1964 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1965
1966 if (group->GetTargetState() ==
1967 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1968 if (!CigCreate(group)) {
1969 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
1970 group->group_id_);
1971 StopStream(group);
1972 }
1973 return;
1974 }
1975
1976 if (group->GetTargetState() ==
1977 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
1978 group->IsPendingConfiguration()) {
1979 LOG_INFO(" Configured state completed ");
1980 group->ClearPendingConfiguration();
1981 state_machine_callbacks_->StatusReportCb(
1982 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
1983
1984 /* No more transition for group */
1985 cancel_watchdog_if_needed(group->group_id_);
1986 return;
1987 }
1988
1989 LOG_ERROR(", Autonomouse change, from: %s to %s",
1990 ToString(group->GetState()).c_str(),
1991 ToString(group->GetTargetState()).c_str());
1992
1993 break;
1994 }
1995 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
1996 /* TODO: Config Codec */
1997 break;
1998 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
1999 LeAudioDevice* leAudioDeviceNext;
2000 SetAseState(leAudioDevice, ase,
2001 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
2002 ase->active = false;
2003
2004 if (!leAudioDevice->HaveAllActiveAsesSameState(
2005 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED)) {
2006 /* More ASEs notification from this device has to come for this group
2007 */
2008 LOG_DEBUG("Wait for more ASE to configure for device %s",
2009 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2010 return;
2011 }
2012
2013 /* Before continue with release, make sure this is what is requested.
2014 * If not (e.g. only single device got disconnected), stop here
2015 */
2016 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
2017 LOG_DEBUG("Autonomus change of stated for device %s, ase id: %d",
2018 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
2019 return;
2020 }
2021
2022 leAudioDeviceNext = group->GetNextActiveDevice(leAudioDevice);
2023
2024 /* Configure ASEs for next device in group */
2025 if (leAudioDeviceNext) {
2026 PrepareAndSendRelease(leAudioDeviceNext);
2027 } else {
2028 /* Last node is in releasing state*/
2029
2030 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
2031 /* Remote device has cache and keep staying in configured state after
2032 * release. Therefore, we assume this is a target state requested by
2033 * remote device.
2034 */
2035 group->SetTargetState(group->GetState());
2036
2037 if (!group->HaveAllCisesDisconnected()) {
2038 LOG_WARN(
2039 "Not all CISes removed before going to IDLE for group %d, "
2040 "waiting...",
2041 group->group_id_);
2042 group->PrintDebugState();
2043 return;
2044 }
2045
2046 cancel_watchdog_if_needed(group->group_id_);
2047
2048 state_machine_callbacks_->StatusReportCb(
2049 group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
2050 }
2051 break;
2052 default:
2053 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2054 << static_cast<int>(ase->state) << ", to: "
2055 << static_cast<int>(
2056 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2057 StopStream(group);
2058 break;
2059 }
2060 }
2061
AseStateMachineProcessQosConfigured(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2062 void AseStateMachineProcessQosConfigured(
2063 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2064 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2065 if (!group) {
2066 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2067
2068 return;
2069 }
2070
2071 switch (ase->state) {
2072 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
2073 SetAseState(leAudioDevice, ase,
2074 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2075
2076 if (!leAudioDevice->HaveAllActiveAsesSameState(
2077 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED)) {
2078 /* More ASEs notification from this device has to come for this group
2079 */
2080 return;
2081 }
2082
2083 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2084 /* We are here because of the reconnection of the single device. */
2085 PrepareAndSendEnable(leAudioDevice);
2086 return;
2087 }
2088
2089 if (!group->HaveAllActiveDevicesAsesTheSameState(
2090 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED)) {
2091 LOG_DEBUG("Waiting for all the devices to be in QoS state");
2092 return;
2093 }
2094
2095 PrepareAndSendEnableToTheGroup(group);
2096
2097 break;
2098 }
2099 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2100 /* TODO: Config Codec error/Config Qos/Config QoS error/Enable error */
2101 break;
2102 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
2103 if (ase->direction == le_audio::types::kLeAudioDirectionSource) {
2104 /* Source ASE cannot go from Streaming to QoS Configured state */
2105 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2106 << static_cast<int>(ase->state) << ", to: "
2107 << static_cast<int>(
2108 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2109 StopStream(group);
2110 return;
2111 }
2112
2113 SetAseState(leAudioDevice, ase,
2114 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2115
2116 /* Process the Disable Transition of the rest of group members if no
2117 * more ASE notifications has to come from this device. */
2118 if (leAudioDevice->IsReadyToSuspendStream()) ProcessGroupDisable(group);
2119
2120 break;
2121
2122 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING: {
2123 SetAseState(leAudioDevice, ase,
2124 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2125
2126 /* More ASEs notification from this device has to come for this group */
2127 if (!group->HaveAllActiveDevicesAsesTheSameState(
2128 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED))
2129 return;
2130
2131 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2132
2133 if (!group->HaveAllCisesDisconnected()) return;
2134
2135 if (group->GetTargetState() ==
2136 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
2137 /* No more transition for group */
2138 cancel_watchdog_if_needed(group->group_id_);
2139
2140 state_machine_callbacks_->StatusReportCb(
2141 group->group_id_, GroupStreamStatus::SUSPENDED);
2142 } else {
2143 LOG_ERROR(", invalid state transition, from: %s, to: %s",
2144 ToString(group->GetState()).c_str(),
2145 ToString(group->GetTargetState()).c_str());
2146 StopStream(group);
2147 return;
2148 }
2149 break;
2150 }
2151 default:
2152 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2153 << static_cast<int>(ase->state) << ", to: "
2154 << static_cast<int>(
2155 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2156 StopStream(group);
2157 break;
2158 }
2159 }
2160
ClearGroup(LeAudioDeviceGroup * group,bool report_idle_state)2161 void ClearGroup(LeAudioDeviceGroup* group, bool report_idle_state) {
2162 LOG_DEBUG("group_id: %d", group->group_id_);
2163 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
2164 group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
2165
2166 /* Clear group pending status */
2167 group->ClearPendingAvailableContextsChange();
2168 group->ClearPendingConfiguration();
2169
2170 cancel_watchdog_if_needed(group->group_id_);
2171 ReleaseCisIds(group);
2172 RemoveCigForGroup(group);
2173
2174 if (report_idle_state) {
2175 state_machine_callbacks_->StatusReportCb(group->group_id_,
2176 GroupStreamStatus::IDLE);
2177 }
2178 }
2179
PrepareAndSendEnableToTheGroup(LeAudioDeviceGroup * group)2180 void PrepareAndSendEnableToTheGroup(LeAudioDeviceGroup* group) {
2181 LOG_INFO("group_id: %d", group->group_id_);
2182
2183 auto leAudioDevice = group->GetFirstActiveDevice();
2184 if (!leAudioDevice) {
2185 LOG_ERROR("No active device for the group");
2186 group->PrintDebugState();
2187 ClearGroup(group, true);
2188 return;
2189 }
2190
2191 for (; leAudioDevice;
2192 leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
2193 PrepareAndSendEnable(leAudioDevice);
2194 }
2195 }
2196
PrepareAndSendEnable(LeAudioDevice * leAudioDevice)2197 void PrepareAndSendEnable(LeAudioDevice* leAudioDevice) {
2198 struct le_audio::client_parser::ascs::ctp_enable conf;
2199 std::vector<struct le_audio::client_parser::ascs::ctp_enable> confs;
2200 std::vector<uint8_t> value;
2201 struct ase* ase;
2202 std::stringstream msg_stream;
2203 std::stringstream extra_stream;
2204
2205 msg_stream << kLogAseEnableOp;
2206
2207 ase = leAudioDevice->GetFirstActiveAse();
2208 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2209 do {
2210 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2211 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
2212 ase->cis_id, ToString(ase->state).c_str());
2213 conf.ase_id = ase->id;
2214 conf.metadata = ase->metadata;
2215 confs.push_back(conf);
2216
2217 /* Below is just for log history */
2218 msg_stream << "ASE_ID " << +ase->id << ",";
2219 extra_stream << "meta: "
2220 << base::HexEncode(conf.metadata.data(),
2221 conf.metadata.size())
2222 << ";;";
2223
2224 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2225
2226 le_audio::client_parser::ascs::PrepareAseCtpEnable(confs, value);
2227 WriteToControlPoint(leAudioDevice, value);
2228
2229 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2230 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2231 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
2232 leAudioDevice->address_, msg_stream.str(),
2233 extra_stream.str());
2234 }
2235
PrepareAndSendDisableToTheGroup(LeAudioDeviceGroup * group)2236 GroupStreamStatus PrepareAndSendDisableToTheGroup(LeAudioDeviceGroup* group) {
2237 LOG_INFO("grop_id: %d", group->group_id_);
2238
2239 auto leAudioDevice = group->GetFirstActiveDevice();
2240 if (!leAudioDevice) {
2241 LOG_ERROR("No active device for the group");
2242 group->PrintDebugState();
2243 ClearGroup(group, false);
2244 return GroupStreamStatus::IDLE;
2245 }
2246
2247 for (; leAudioDevice;
2248 leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
2249 PrepareAndSendDisable(leAudioDevice);
2250 }
2251 return GroupStreamStatus::SUSPENDING;
2252 }
2253
PrepareAndSendDisable(LeAudioDevice * leAudioDevice)2254 void PrepareAndSendDisable(LeAudioDevice* leAudioDevice) {
2255 ase* ase = leAudioDevice->GetFirstActiveAse();
2256 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2257
2258 std::stringstream msg_stream;
2259 msg_stream << kLogAseDisableOp;
2260
2261 std::vector<uint8_t> ids;
2262 do {
2263 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2264 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
2265 ase->cis_id, ToString(ase->state).c_str());
2266 ids.push_back(ase->id);
2267
2268 msg_stream << "ASE_ID " << +ase->id << ", ";
2269 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2270
2271 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2272 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2273 std::vector<uint8_t> value;
2274 le_audio::client_parser::ascs::PrepareAseCtpDisable(ids, value);
2275
2276 WriteToControlPoint(leAudioDevice, value);
2277
2278 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
2279 leAudioDevice->address_, msg_stream.str());
2280 }
2281
PrepareAndSendReleaseToTheGroup(LeAudioDeviceGroup * group)2282 GroupStreamStatus PrepareAndSendReleaseToTheGroup(LeAudioDeviceGroup* group) {
2283 LOG_INFO("group_id: %d", group->group_id_);
2284 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
2285 if (!leAudioDevice) {
2286 LOG_ERROR("No active device for the group");
2287 group->PrintDebugState();
2288 ClearGroup(group, false);
2289 return GroupStreamStatus::IDLE;
2290 }
2291
2292 for (; leAudioDevice;
2293 leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
2294 PrepareAndSendRelease(leAudioDevice);
2295 }
2296
2297 return GroupStreamStatus::RELEASING;
2298 }
2299
PrepareAndSendRelease(LeAudioDevice * leAudioDevice)2300 void PrepareAndSendRelease(LeAudioDevice* leAudioDevice) {
2301 ase* ase = leAudioDevice->GetFirstActiveAse();
2302 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2303
2304 std::vector<uint8_t> ids;
2305 std::stringstream stream;
2306 stream << kLogAseReleaseOp;
2307
2308 do {
2309 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2310 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
2311 ase->cis_id, ToString(ase->state).c_str());
2312 ids.push_back(ase->id);
2313 stream << "ASE_ID " << +ase->id << ",";
2314 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2315
2316 std::vector<uint8_t> value;
2317 le_audio::client_parser::ascs::PrepareAseCtpRelease(ids, value);
2318 WriteToControlPoint(leAudioDevice, value);
2319
2320 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2321 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2322 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
2323 leAudioDevice->address_, stream.str());
2324 }
2325
PrepareAndSendConfigQos(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2326 void PrepareAndSendConfigQos(LeAudioDeviceGroup* group,
2327 LeAudioDevice* leAudioDevice) {
2328 std::vector<struct le_audio::client_parser::ascs::ctp_qos_conf> confs;
2329
2330 bool validate_transport_latency = false;
2331 bool validate_max_sdu_size = false;
2332
2333 std::stringstream msg_stream;
2334 msg_stream << kLogAseQoSConfigOp;
2335
2336 std::stringstream extra_stream;
2337
2338 for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
2339 ase = leAudioDevice->GetNextActiveAse(ase)) {
2340 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2341 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
2342 ase->cis_id, ToString(ase->state).c_str());
2343
2344 /* TODO: Configure first ASE qos according to context type */
2345 struct le_audio::client_parser::ascs::ctp_qos_conf conf;
2346 conf.ase_id = ase->id;
2347 conf.cig = group->group_id_;
2348 conf.cis = ase->cis_id;
2349 conf.framing = group->GetFraming();
2350 conf.phy = group->GetPhyBitmask(ase->direction);
2351 conf.max_sdu = ase->max_sdu_size;
2352 conf.retrans_nb = ase->retrans_nb;
2353 if (!group->GetPresentationDelay(&conf.pres_delay, ase->direction)) {
2354 LOG_ERROR("inconsistent presentation delay for group");
2355 group->PrintDebugState();
2356 StopStream(group);
2357 return;
2358 }
2359
2360 conf.sdu_interval = group->GetSduInterval(ase->direction);
2361 if (!conf.sdu_interval) {
2362 LOG_ERROR("unsupported SDU interval for group");
2363 group->PrintDebugState();
2364 StopStream(group);
2365 return;
2366 }
2367
2368 msg_stream << "ASE " << +conf.ase_id << ",";
2369 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
2370 conf.max_transport_latency = group->GetMaxTransportLatencyMtos();
2371 extra_stream << "snk,";
2372 } else {
2373 conf.max_transport_latency = group->GetMaxTransportLatencyStom();
2374 extra_stream << "src,";
2375 }
2376
2377 if (conf.max_transport_latency >
2378 le_audio::types::kMaxTransportLatencyMin) {
2379 validate_transport_latency = true;
2380 }
2381
2382 if (conf.max_sdu > 0) {
2383 validate_max_sdu_size = true;
2384 }
2385 confs.push_back(conf);
2386
2387 // dir...cis_id,sdu,lat,rtn,phy,frm;;
2388 extra_stream << +conf.cis << "," << +conf.max_sdu << ","
2389 << +conf.max_transport_latency << "," << +conf.retrans_nb
2390 << "," << +conf.phy << "," << +conf.framing << ";;";
2391 }
2392
2393 if (confs.size() == 0 || !validate_transport_latency ||
2394 !validate_max_sdu_size) {
2395 LOG_ERROR("Invalid configuration or latency or sdu size");
2396 group->PrintDebugState();
2397 StopStream(group);
2398 return;
2399 }
2400
2401 std::vector<uint8_t> value;
2402 le_audio::client_parser::ascs::PrepareAseCtpConfigQos(confs, value);
2403 WriteToControlPoint(leAudioDevice, value);
2404
2405 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2406 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2407 log_history_->AddLogHistory(kLogControlPointCmd, group->group_id_,
2408 leAudioDevice->address_, msg_stream.str(),
2409 extra_stream.str());
2410 }
2411
PrepareAndSendUpdateMetadata(LeAudioDevice * leAudioDevice,const BidirectionalPair<AudioContexts> & context_types,const BidirectionalPair<std::vector<uint8_t>> & ccid_lists)2412 void PrepareAndSendUpdateMetadata(
2413 LeAudioDevice* leAudioDevice,
2414 const BidirectionalPair<AudioContexts>& context_types,
2415 const BidirectionalPair<std::vector<uint8_t>>& ccid_lists) {
2416 std::vector<struct le_audio::client_parser::ascs::ctp_update_metadata>
2417 confs;
2418
2419 std::stringstream msg_stream;
2420 msg_stream << kLogAseUpdateMetadataOp;
2421
2422 std::stringstream extra_stream;
2423
2424 if (!leAudioDevice->IsMetadataChanged(context_types, ccid_lists)) return;
2425
2426 /* Request server to update ASEs with new metadata */
2427 for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
2428 ase = leAudioDevice->GetNextActiveAse(ase)) {
2429 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2430 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id,
2431 ase->cis_id, ToString(ase->state).c_str());
2432
2433 if (ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING &&
2434 ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2435 /* This might happen when update metadata happens on late connect */
2436 LOG_DEBUG(
2437 "Metadata for ase_id %d cannot be updated due to invalid ase state "
2438 "- see log above",
2439 ase->id);
2440 continue;
2441 }
2442
2443 msg_stream << "ASE_ID " << +ase->id << ",";
2444 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
2445 extra_stream << "snk,";
2446 } else {
2447 extra_stream << "src,";
2448 }
2449
2450 /* Filter multidirectional audio context for each ase direction */
2451 auto directional_audio_context =
2452 context_types.get(ase->direction) &
2453 leAudioDevice->GetAvailableContexts(ase->direction);
2454 if (directional_audio_context.any()) {
2455 ase->metadata = leAudioDevice->GetMetadata(
2456 directional_audio_context, ccid_lists.get(ase->direction));
2457 } else {
2458 ase->metadata = leAudioDevice->GetMetadata(
2459 AudioContexts(LeAudioContextType::UNSPECIFIED),
2460 std::vector<uint8_t>());
2461 }
2462
2463 struct le_audio::client_parser::ascs::ctp_update_metadata conf;
2464
2465 conf.ase_id = ase->id;
2466 conf.metadata = ase->metadata;
2467 confs.push_back(conf);
2468
2469 extra_stream << "meta: "
2470 << base::HexEncode(conf.metadata.data(),
2471 conf.metadata.size())
2472 << ";;";
2473 }
2474
2475 if (confs.size() != 0) {
2476 std::vector<uint8_t> value;
2477 le_audio::client_parser::ascs::PrepareAseCtpUpdateMetadata(confs, value);
2478 WriteToControlPoint(leAudioDevice, value);
2479
2480 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2481 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2482
2483 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
2484 leAudioDevice->address_, msg_stream.str(),
2485 extra_stream.str());
2486 }
2487 }
2488
PrepareAndSendReceiverStartReady(LeAudioDevice * leAudioDevice,struct ase * ase)2489 void PrepareAndSendReceiverStartReady(LeAudioDevice* leAudioDevice,
2490 struct ase* ase) {
2491 std::vector<uint8_t> ids;
2492 std::vector<uint8_t> value;
2493 std::stringstream stream;
2494
2495 stream << kLogAseStartReadyOp;
2496
2497 do {
2498 if (ase->direction == le_audio::types::kLeAudioDirectionSource) {
2499 stream << "ASE_ID " << +ase->id << ",";
2500 ids.push_back(ase->id);
2501 }
2502 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2503
2504 if (ids.size() > 0) {
2505 le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStartReady(
2506 ids, value);
2507 WriteToControlPoint(leAudioDevice, value);
2508
2509 LOG_INFO("group_id: %d, %s", leAudioDevice->group_id_,
2510 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
2511 log_history_->AddLogHistory(kLogControlPointCmd, leAudioDevice->group_id_,
2512 leAudioDevice->address_, stream.str());
2513 }
2514 }
2515
AseStateMachineProcessEnabling(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2516 void AseStateMachineProcessEnabling(
2517 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2518 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2519 if (!group) {
2520 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2521 return;
2522 }
2523
2524 switch (ase->state) {
2525 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2526 SetAseState(leAudioDevice, ase,
2527 AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
2528
2529 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2530 if (ase->data_path_state < AudioStreamDataPathState::CIS_PENDING) {
2531 /* We are here because of the reconnection of the single device. */
2532 if (!CisCreateForDevice(group, leAudioDevice)) {
2533 StopStream(group);
2534 return;
2535 }
2536 }
2537
2538 if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
2539 /* More cis established events has to come */
2540 return;
2541 }
2542
2543 if (!leAudioDevice->IsReadyToCreateStream()) {
2544 /* Device still remains in ready to create stream state. It means
2545 * that more enabling status notifications has to come.
2546 */
2547 return;
2548 }
2549
2550 /* All CISes created. Send start ready for source ASE before we can go
2551 * to streaming state.
2552 */
2553 struct ase* ase = leAudioDevice->GetFirstActiveAse();
2554 ASSERT_LOG(ase != nullptr,
2555 "shouldn't be called without an active ASE, device %s",
2556 leAudioDevice->address_.ToString().c_str());
2557 PrepareAndSendReceiverStartReady(leAudioDevice, ase);
2558
2559 return;
2560 }
2561
2562 if (leAudioDevice->IsReadyToCreateStream())
2563 ProcessGroupEnable(group);
2564
2565 break;
2566
2567 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2568 /* Enable/Switch Content */
2569 break;
2570 default:
2571 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2572 << static_cast<int>(ase->state) << ", to: "
2573 << static_cast<int>(
2574 AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
2575 StopStream(group);
2576 break;
2577 }
2578 }
2579
AseStateMachineProcessStreaming(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,uint8_t * data,uint16_t len,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2580 void AseStateMachineProcessStreaming(
2581 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2582 uint8_t* data, uint16_t len, LeAudioDeviceGroup* group,
2583 LeAudioDevice* leAudioDevice) {
2584 if (!group) {
2585 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2586
2587 return;
2588 }
2589
2590 switch (ase->state) {
2591 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2592 /* As per ASCS 1.0 :
2593 * If a CIS has been established and the server is acting as Audio Sink
2594 * for the ASE, and if the server is ready to receive audio data
2595 * transmitted by the client, the server may autonomously initiate the
2596 * Receiver Start Ready, as defined in Section 5.4, without first
2597 * sending a notification of the ASE characteristic value in the
2598 * Enabling state.
2599 */
2600 if (ase->direction != le_audio::types::kLeAudioDirectionSink) {
2601 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2602 << static_cast<int>(ase->state) << ", to: "
2603 << static_cast<int>(
2604 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2605 StopStream(group);
2606 return;
2607 }
2608
2609 SetAseState(leAudioDevice, ase,
2610 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2611
2612 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2613 /* We are here because of the reconnection of the single device. */
2614 PrepareDataPath(group);
2615 return;
2616 }
2617
2618 if (leAudioDevice->IsReadyToCreateStream())
2619 ProcessGroupEnable(group);
2620
2621 break;
2622
2623 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING: {
2624 std::vector<uint8_t> value;
2625
2626 SetAseState(leAudioDevice, ase,
2627 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2628
2629 if (!group->HaveAllActiveDevicesAsesTheSameState(
2630 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
2631 /* More ASEs notification form this device has to come for this group
2632 */
2633
2634 return;
2635 }
2636
2637 /* This case may happen because of the reconnection device. */
2638 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2639 /* Not all CISes establish evens came */
2640 if (group->GetFirstActiveDeviceByDataPathState(
2641 AudioStreamDataPathState::CIS_PENDING))
2642 return;
2643
2644 /* Streaming status notification came after setting data path */
2645 if (!group->GetFirstActiveDeviceByDataPathState(
2646 AudioStreamDataPathState::CIS_ESTABLISHED))
2647 return;
2648 PrepareDataPath(group);
2649 return;
2650 }
2651
2652 /* Last node is in streaming state */
2653 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2654
2655 /* Not all CISes establish evens came */
2656 if (!group->IsGroupStreamReady()) return;
2657
2658 if (group->GetTargetState() ==
2659 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2660 /* No more transition for group */
2661 cancel_watchdog_if_needed(group->group_id_);
2662 PrepareDataPath(group);
2663
2664 return;
2665 } else {
2666 LOG_ERROR(", invalid state transition, from: %s, to: %s",
2667 ToString(group->GetState()).c_str(),
2668 ToString(group->GetTargetState()).c_str());
2669 StopStream(group);
2670 return;
2671 }
2672
2673 break;
2674 }
2675 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
2676 struct le_audio::client_parser::ascs::ase_transient_state_params rsp;
2677
2678 if (!ParseAseStatusTransientStateParams(rsp, len, data)) {
2679 StopStream(group);
2680 return;
2681 }
2682
2683 /* Cache current set up metadata values for for further possible
2684 * reconfiguration
2685 */
2686 if (!rsp.metadata.empty()) {
2687 ase->metadata = rsp.metadata;
2688 }
2689
2690 break;
2691 }
2692 default:
2693 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2694 << static_cast<int>(ase->state) << ", to: "
2695 << static_cast<int>(
2696 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2697 StopStream(group);
2698 break;
2699 }
2700 }
2701
AseStateMachineProcessDisabling(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2702 void AseStateMachineProcessDisabling(
2703 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2704 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2705 if (!group) {
2706 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2707
2708 return;
2709 }
2710
2711 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
2712 /* Sink ASE state machine does not have Disabling state */
2713 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2714 ToString(group->GetState()).c_str(),
2715 ToString(group->GetTargetState()).c_str());
2716 StopStream(group);
2717 return;
2718 }
2719
2720 switch (ase->state) {
2721 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2722 /* TODO: Disable */
2723 break;
2724 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
2725 SetAseState(leAudioDevice, ase,
2726 AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);
2727
2728 /* Process the Disable Transition of the rest of group members if no
2729 * more ASE notifications has to come from this device. */
2730 if (leAudioDevice->IsReadyToSuspendStream())
2731 ProcessGroupDisable(group);
2732
2733 break;
2734
2735 default:
2736 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2737 << static_cast<int>(ase->state) << ", to: "
2738 << static_cast<int>(
2739 AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);
2740 StopStream(group);
2741 break;
2742 }
2743 }
2744
DisconnectCisIfNeeded(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,struct ase * ase)2745 void DisconnectCisIfNeeded(LeAudioDeviceGroup* group,
2746 LeAudioDevice* leAudioDevice, struct ase* ase) {
2747 LOG_DEBUG(
2748 "Group id: %d, %s, ase id: %d, cis_handle: 0x%04x, direction: %s, "
2749 "data_path_state: %s",
2750 group->group_id_, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
2751 ase->id, ase->cis_conn_hdl,
2752 ase->direction == le_audio::types::kLeAudioDirectionSink ? "sink"
2753 : "source",
2754 bluetooth::common::ToString(ase->data_path_state).c_str());
2755
2756 auto bidirection_ase = leAudioDevice->GetAseToMatchBidirectionCis(ase);
2757 if (bidirection_ase != nullptr &&
2758 bidirection_ase->data_path_state ==
2759 AudioStreamDataPathState::CIS_ESTABLISHED &&
2760 (bidirection_ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING ||
2761 bidirection_ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING)) {
2762 LOG_INFO("Still waiting for the bidirectional ase %d to be released (%s)",
2763 bidirection_ase->id,
2764 bluetooth::common::ToString(bidirection_ase->state).c_str());
2765 return;
2766 }
2767
2768 RemoveCisFromStreamConfiguration(group, leAudioDevice, ase->cis_conn_hdl);
2769 IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl,
2770 HCI_ERR_PEER_USER);
2771 log_history_->AddLogHistory(
2772 kLogStateMachineTag, group->group_id_, leAudioDevice->address_,
2773 kLogCisDisconnectOp + "cis_h:" + loghex(ase->cis_conn_hdl));
2774 }
2775
AseStateMachineProcessReleasing(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2776 void AseStateMachineProcessReleasing(
2777 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2778 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2779 if (!group) {
2780 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2781
2782 return;
2783 }
2784
2785 switch (ase->state) {
2786 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
2787 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING: {
2788 SetAseState(leAudioDevice, ase,
2789 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2790 break;
2791 }
2792 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2793 SetAseState(leAudioDevice, ase,
2794 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2795
2796 if (group->HaveAllActiveDevicesAsesTheSameState(
2797 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING)) {
2798 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2799
2800 /* At this point all of the active ASEs within group are released. */
2801 RemoveCigForGroup(group);
2802 }
2803
2804 break;
2805
2806 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2807 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
2808 SetAseState(leAudioDevice, ase,
2809 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2810
2811 /* Happens when bi-directional completive ASE releasing state came */
2812 if (ase->data_path_state == AudioStreamDataPathState::CIS_DISCONNECTING)
2813 break;
2814
2815 if (ase->data_path_state ==
2816 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
2817 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
2818 } else if (ase->data_path_state ==
2819 AudioStreamDataPathState::CIS_ESTABLISHED ||
2820 ase->data_path_state ==
2821 AudioStreamDataPathState::CIS_PENDING) {
2822 DisconnectCisIfNeeded(group, leAudioDevice, ase);
2823 } else {
2824 DLOG(INFO) << __func__ << ", Nothing to do ase data path state: "
2825 << static_cast<int>(ase->data_path_state);
2826 }
2827 break;
2828 }
2829 default:
2830 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2831 << static_cast<int>(ase->state) << ", to: "
2832 << static_cast<int>(
2833 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2834 break;
2835 }
2836 }
2837
ProcessGroupEnable(LeAudioDeviceGroup * group)2838 void ProcessGroupEnable(LeAudioDeviceGroup* group) {
2839 if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING) {
2840 if (!group->IsGroupReadyToCreateStream()) {
2841 LOG_DEBUG(
2842 "Waiting for more ASEs to be in enabling or directly in streaming "
2843 "state");
2844 return;
2845 }
2846
2847 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
2848 }
2849
2850 /* At this point all of the active ASEs within group are enabled. The server
2851 * might perform autonomous state transition for Sink ASE and skip Enabling
2852 * state notification and transit to Streaming directly. So check the group
2853 * state, because we might be ready to create CIS. */
2854 if (group->HaveAllActiveDevicesAsesTheSameState(
2855 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
2856 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2857 }
2858
2859 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2860 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2861 ToString(group->GetState()).c_str(),
2862 ToString(group->GetTargetState()).c_str());
2863 StopStream(group);
2864 return;
2865 }
2866
2867 if (!CisCreate(group)) {
2868 StopStream(group);
2869 }
2870 }
2871
ProcessGroupDisable(LeAudioDeviceGroup * group)2872 void ProcessGroupDisable(LeAudioDeviceGroup* group) {
2873 /* Disable ASEs for next device in group. */
2874 if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING) {
2875 if (!group->IsGroupReadyToSuspendStream()) {
2876 LOG_INFO("Waiting for all devices to be in disable state");
2877 return;
2878 }
2879 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);
2880 }
2881
2882 /* At this point all of the active ASEs within group are disabled. As there
2883 * is no Disabling state for Sink ASE, it might happen that all of the
2884 * active ASEs are Sink ASE and will transit to QoS state. So check
2885 * the group state, because we might be ready to release data path. */
2886 if (group->HaveAllActiveDevicesAsesTheSameState(
2887 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED)) {
2888 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2889 }
2890
2891 /* Transition to QoS configured is done by CIS disconnection */
2892 if (group->GetTargetState() ==
2893 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
2894 ReleaseDataPath(group);
2895 } else {
2896 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2897 ToString(group->GetState()).c_str(),
2898 ToString(group->GetTargetState()).c_str());
2899 StopStream(group);
2900 }
2901 }
2902 };
2903 } // namespace
2904
2905 namespace le_audio {
Initialize(Callbacks * state_machine_callbacks_)2906 void LeAudioGroupStateMachine::Initialize(Callbacks* state_machine_callbacks_) {
2907 if (instance) {
2908 LOG(ERROR) << "Already initialized";
2909 return;
2910 }
2911
2912 instance = new LeAudioGroupStateMachineImpl(state_machine_callbacks_);
2913 }
2914
Cleanup()2915 void LeAudioGroupStateMachine::Cleanup() {
2916 if (!instance) return;
2917
2918 LeAudioGroupStateMachineImpl* ptr = instance;
2919 instance = nullptr;
2920
2921 delete ptr;
2922 }
2923
Get()2924 LeAudioGroupStateMachine* LeAudioGroupStateMachine::Get() {
2925 CHECK(instance);
2926 return instance;
2927 }
2928 } // namespace le_audio
2929