rust/bt-ascs: CIS establish/release handling

Change-Id: Ia57027b152a515f2bf7567791ffbf40d3a7f2785
Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/1900
diff --git a/rust/bt-ascs/src/server.rs b/rust/bt-ascs/src/server.rs
index 982968f..9a329fa 100644
--- a/rust/bt-ascs/src/server.rs
+++ b/rust/bt-ascs/src/server.rs
@@ -300,8 +300,41 @@
         Ok(())
     }
 
-    pub fn release(&mut self, _id: AseId) -> Result<(), Error> {
-        unimplemented!()
+    pub fn cis_established(
+        &mut self,
+        peer_id: PeerId,
+        ase_id: AseId,
+        cis: (CigId, CisId),
+    ) -> Result<(), Error> {
+        let endpoints =
+            self.client_endpoints.get_mut(&peer_id).ok_or(Error::UnknownPeer(peer_id))?;
+        endpoints.established_cis(ase_id, cis);
+        for operation in endpoints.autonomous_operations() {
+            self.queue_operation_unpin(peer_id, operation);
+        }
+        Ok(())
+    }
+
+    pub fn cis_released(
+        &mut self,
+        peer_id: PeerId,
+        ase_id: AseId,
+        cis: (CigId, CisId),
+    ) -> Result<(), Error> {
+        let endpoints =
+            self.client_endpoints.get_mut(&peer_id).ok_or(Error::UnknownPeer(peer_id))?;
+        endpoints.released_cis(ase_id, cis);
+        for operation in endpoints.autonomous_operations() {
+            self.queue_operation_unpin(peer_id, operation);
+        }
+        Ok(())
+    }
+
+    fn queue_operation_unpin(&mut self, peer_id: PeerId, op: AseControlOperation) {
+        let (events, fut) =
+            op.apply(peer_id, self.client_endpoints.get(&peer_id).unwrap().endpoints.clone());
+        self.outgoing_events.extend(events);
+        self.responses.push(fut);
     }
 
     fn queue_operation(self: std::pin::Pin<&mut Self>, peer_id: PeerId, op: AseControlOperation) {
@@ -521,6 +554,7 @@
 struct ClientEndpoints {
     endpoints: HashMap<AseId, AudioStreamEndpoint>,
     handles: HashMap<Handle, AseId>,
+    established: HashMap<AseId, Vec<(CigId, CisId)>>,
 }
 
 impl ClientEndpoints {
@@ -549,12 +583,46 @@
                 )
             })
             .unzip();
-        Self { endpoints, handles }
+        Self { endpoints, handles, established: Default::default() }
     }
 
     fn clone_for_peer(&self, _peer_id: PeerId) -> Self {
         // TODO: Randomize the ASE_IDs.  Handles need to stay the same.
-        Self { endpoints: self.endpoints.clone(), handles: self.handles.clone() }
+        Self {
+            endpoints: self.endpoints.clone(),
+            handles: self.handles.clone(),
+            established: Default::default(),
+        }
+    }
+
+    fn established_cis(&mut self, ase_id: AseId, cis: (CigId, CisId)) {
+        self.established.entry(ase_id).or_default().push(cis);
+    }
+
+    fn released_cis(&mut self, ase_id: AseId, cis: (CigId, CisId)) {
+        self.established.get_mut(&ase_id).map(|established| established.retain(|i| i != &cis));
+    }
+
+    fn autonomous_operations(&self) -> Vec<AseControlOperation> {
+        let mut operations = Vec::new();
+        for endpoint in self.endpoints.values() {
+            match endpoint.state {
+                AseState::Enabling
+                    if self.established.get(&endpoint.ase_id).is_some_and(|e| !e.is_empty()) =>
+                {
+                    operations.push(AseControlOperation::ReceiverStartReady {
+                        ases: vec![endpoint.ase_id],
+                    });
+                }
+                AseState::Releasing
+                    if self.established.get(&endpoint.ase_id).is_some_and(|e| e.is_empty()) =>
+                {
+                    operations.push(AseControlOperation::Released { ase_id: endpoint.ase_id });
+                }
+                _ => continue,
+            }
+        }
+        operations
     }
 }
 
diff --git a/rust/bt-ascs/src/types.rs b/rust/bt-ascs/src/types.rs
index 6eb27c9..1ca6f3f 100644
--- a/rust/bt-ascs/src/types.rs
+++ b/rust/bt-ascs/src/types.rs
@@ -22,6 +22,8 @@
     PublishError(bt_gatt::types::Error),
     #[error("Unsupported configuration: {0}")]
     Unsupported(String),
+    #[error("Unknown Peer: {0}")]
+    UnknownPeer(bt_common::PeerId),
 }
 
 #[non_exhaustive]
@@ -714,7 +716,7 @@
             return (Err(Self::Error::BufferTooSmall), buf.len());
         }
         let val = u32::from_le_bytes([buf[0], buf[1], buf[2], 0]);
-        if val < 0xFF || val > 0x0FFFFF {
+        if (val < 0xFF) || (val > 0x0FFFFF) {
             return (Err(Self::Error::OutOfRange), Self::BYTE_SIZE);
         }
         (Ok(SduInterval(val)), Self::BYTE_SIZE)
@@ -1087,6 +1089,26 @@
         assert_eq!(codec_config.target_phy, TargetPhy::Le2MPhy);
         assert_eq!(codec_config.codec_id, CodecId::Assigned(bt_common::core::CodingFormat::Lc3));
         assert_eq!(codec_config.codec_specific_configuration.len(), 0x10);
+
+        #[rustfmt::skip]
+        let encoded_qos = &[
+            0x02, 0x01, // Qos OpCode, 1 number_of_ases
+            0x03, // ASE_ID,
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x27, 0x00, // SduInterval
+            0x01, 0x02, 0x28, 0x00, 0x02, 0xf4,
+            0x01, 0x18, 0x00, 0x80,
+        ];
+        assert_eq!(QosConfiguration::BYTE_SIZE, encoded_qos.len() - 2);
+        let (qos_config, consumed) = QosConfiguration::decode(&encoded_qos[2..]);
+        assert!(qos_config.is_ok());
+        assert_eq!(consumed, encoded_qos.len() - 2);
+        let encoded_qos: Vec<u8> = encoded_qos.into_iter().copied().collect();
+        let operation = AseControlOperation::try_from(encoded_qos);
+        let Ok(_operation) = operation else {
+            panic!("Expected decode to work correctly, for {operation:?}");
+        };
     }
 
     #[test]