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