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