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