Loading floss/hcidoc/src/groups/informational.rs +136 −47 Original line number Diff line number Diff line Loading @@ -83,6 +83,24 @@ impl AddressType { } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] enum Transport { Unknown, BREDR, LE, } impl fmt::Display for Transport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let str = match self { Transport::Unknown => "??", Transport::BREDR => "BR", Transport::LE => "LE", }; write!(f, "{}", str) } } #[derive(Clone, Copy, PartialEq)] enum InitiatorType { Unknown, Loading Loading @@ -124,8 +142,8 @@ struct DeviceInformation { names: HashSet<String>, address: Address, address_type: AddressType, acls: Vec<AclInformation>, acl_state: AclState, acls: HashMap<Transport, Vec<AclInformation>>, acl_state: HashMap<Transport, AclState>, } impl DeviceInformation { Loading @@ -134,30 +152,52 @@ impl DeviceInformation { names: HashSet::new(), address: address, address_type: AddressType::None, acls: vec![], acl_state: AclState::None, acls: HashMap::from([(Transport::BREDR, vec![]), (Transport::LE, vec![])]), acl_state: HashMap::from([ (Transport::BREDR, AclState::None), (Transport::LE, AclState::None), ]), } } fn is_connection_active(&self, transport: Transport) -> bool { if transport == Transport::Unknown { return false; } fn is_connection_active(&self) -> bool { // not empty and last connection's end time is not set. return !self.acls.is_empty() && self.acls.last().unwrap().end_time == INVALID_TS; return !self.acls[&transport].is_empty() && self.acls[&transport].last().unwrap().end_time == INVALID_TS; } fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation { if !self.is_connection_active() { let acl = AclInformation::new(*handle); self.acls.push(acl); fn get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { assert_ne!(transport, Transport::Unknown, "device allocating unknown transport"); if !self.is_connection_active(transport) { let acl = AclInformation::new(handle, transport); self.acls.get_mut(&transport).unwrap().push(acl); } return self.acls.last_mut().unwrap(); return self.acls.get_mut(&transport).unwrap().last_mut().unwrap(); } fn report_connection_start( &mut self, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, ) { if transport == Transport::Unknown { return; } fn report_connection_start(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) { let mut acl = AclInformation::new(handle); let initiator = self.acl_state.get_connection_initiator(); let mut acl = AclInformation::new(handle, transport); let initiator = self.acl_state[&transport].get_connection_initiator(); acl.report_start(initiator, ts); self.acls.push(acl); self.acl_state = AclState::Connected; self.acls.get_mut(&transport).unwrap().push(acl); self.acl_state.insert(transport, AclState::Connected); } fn report_connection_end( Loading @@ -166,9 +206,25 @@ impl DeviceInformation { initiator: InitiatorType, ts: NaiveDateTime, ) { let acl = self.get_or_allocate_connection(&handle); acl.report_end(initiator, ts); self.acl_state = AclState::None; for transport in [Transport::BREDR, Transport::LE] { if self.is_connection_active(transport) { if self.acls[&transport].last().unwrap().handle == handle { self.acls .get_mut(&transport) .unwrap() .last_mut() .unwrap() .report_end(initiator, ts); self.acl_state.insert(transport, AclState::None); return; } } } eprintln!( "device {} receive disconnection of handle {} without corresponding connection at {}", self.address, handle, ts ); } fn print_names(names: &HashSet<String>) -> String { Loading @@ -190,7 +246,10 @@ impl fmt::Display for DeviceInformation { device_names = DeviceInformation::print_names(&self.names), num_connections = self.acls.len() ); for acl in &self.acls { for acl in &self.acls[&Transport::BREDR] { let _ = write!(f, "{}", acl); } for acl in &self.acls[&Transport::LE] { let _ = write!(f, "{}", acl); } Loading @@ -209,6 +268,7 @@ struct AclInformation { start_time: NaiveDateTime, end_time: NaiveDateTime, handle: ConnectionHandle, transport: Transport, start_initiator: InitiatorType, end_initiator: InitiatorType, active_profiles: HashMap<ProfileId, ProfileInformation>, Loading @@ -218,11 +278,12 @@ struct AclInformation { } impl AclInformation { pub fn new(handle: ConnectionHandle) -> Self { pub fn new(handle: ConnectionHandle, transport: Transport) -> Self { AclInformation { start_time: INVALID_TS, end_time: INVALID_TS, handle: handle, handle, transport, start_initiator: InitiatorType::Unknown, end_initiator: InitiatorType::Unknown, active_profiles: HashMap::new(), Loading Loading @@ -387,7 +448,8 @@ impl fmt::Display for AclInformation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( f, " Handle: {handle}, {timestamp_initiator_info}", " Handle: {handle} ({transport}), {timestamp_initiator_info}", transport = self.transport, handle = self.handle, timestamp_initiator_info = print_timestamps_and_initiator( self.start_time, Loading Loading @@ -566,23 +628,28 @@ impl InformationalRule { fn get_or_allocate_unknown_connection( &mut self, handle: &ConnectionHandle, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { if !self.unknown_connections.contains_key(handle) { self.unknown_connections.insert(*handle, AclInformation::new(*handle)); if !self.unknown_connections.contains_key(&handle) { self.unknown_connections.insert(handle, AclInformation::new(handle, transport)); } return self.unknown_connections.get_mut(handle).unwrap(); return self.unknown_connections.get_mut(&handle).unwrap(); } fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation { if !self.handles.contains_key(&handle) { let conn = self.get_or_allocate_unknown_connection(&handle); fn get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { if !self.handles.contains_key(&handle) || transport == Transport::Unknown { let conn = self.get_or_allocate_unknown_connection(handle, transport); return conn; } let address = &self.handles.get(handle).unwrap().clone(); let address = &self.handles.get(&handle).unwrap().clone(); let device = self.get_or_allocate_device(address); return device.get_or_allocate_connection(handle); return device.get_or_allocate_connection(handle, transport); } fn report_address_type(&mut self, address: &Address, address_type: AddressType) { Loading @@ -595,19 +662,20 @@ impl InformationalRule { device.names.insert(name.into()); } fn report_acl_state(&mut self, address: &Address, state: AclState) { fn report_acl_state(&mut self, address: &Address, transport: Transport, state: AclState) { let device = self.get_or_allocate_device(address); device.acl_state = state; device.acl_state.insert(transport, state); } fn report_connection_start( &mut self, address: &Address, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, ) { let device = self.get_or_allocate_device(address); device.report_connection_start(handle, ts); device.report_connection_start(handle, transport, ts); self.handles.insert(handle, *address); self.pending_disconnections.remove(&handle); } Loading @@ -624,14 +692,14 @@ impl InformationalRule { } let device = self.devices.get_mut(address).unwrap(); if !device.is_connection_active() { if !device.is_connection_active(Transport::BREDR) { // SCO is connected, but ACL is not. This is weird, but let's ignore for simplicity. eprintln!("[{}] SCO is connected, but ACL is not.", address); return; } // Whatever handle value works here - we aren't allocating a new one. let acl = device.get_or_allocate_connection(&0); let acl = device.get_or_allocate_connection(0, Transport::BREDR); let acl_handle = acl.handle; // We need to listen the HCI commands to determine the correct initiator. // Here we just assume host for simplicity. Loading @@ -654,7 +722,7 @@ impl InformationalRule { // This might be a SCO disconnection event, so check that first if self.sco_handles.contains_key(&handle) { let acl_handle = self.sco_handles[&handle]; let conn = self.get_or_allocate_connection(&acl_handle); let conn = self.get_or_allocate_connection(acl_handle, Transport::BREDR); // in case of HFP failure, the initiator here would be set to peer, which is incorrect, // but when printing we detect by the timestamp that it was a failure anyway. conn.report_profile_end( Loading @@ -677,7 +745,7 @@ impl InformationalRule { self.sco_handles.retain(|_sco_handle, acl_handle| *acl_handle != handle); } else { // Unknown device. let conn = self.get_or_allocate_unknown_connection(&handle); let conn = self.get_or_allocate_unknown_connection(handle, Transport::Unknown); conn.report_end(initiator, ts); } } Loading Loading @@ -731,7 +799,7 @@ impl InformationalRule { initiator: InitiatorType, ts: NaiveDateTime, ) { let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); conn.report_l2cap_conn_req(psm, cid, initiator, ts); } Loading @@ -747,7 +815,7 @@ impl InformationalRule { if status == ConnectionResponseResult::Pending { return; } let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); let cid_info = CidInformation { host_cid, peer_cid }; conn.report_l2cap_conn_rsp(status, cid_info, initiator, ts); } Loading @@ -760,7 +828,7 @@ impl InformationalRule { initiator: InitiatorType, ts: NaiveDateTime, ) { let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); let cid_info = CidInformation { host_cid, peer_cid }; conn.report_l2cap_disconn_rsp(cid_info, initiator, ts); } Loading @@ -774,6 +842,7 @@ impl Rule for InformationalRule { self.report_connection_start( &ev.get_bd_addr(), ev.get_connection_handle(), Transport::BREDR, packet.ts, ); Loading Loading @@ -830,10 +899,15 @@ impl Rule for InformationalRule { } // Determining LE initiator is complex, for simplicity assume host inits. self.report_acl_state(&ev.get_peer_address(), AclState::Initiating); self.report_acl_state( &ev.get_peer_address(), Transport::LE, AclState::Initiating, ); self.report_connection_start( &ev.get_peer_address(), ev.get_connection_handle(), Transport::LE, packet.ts, ); self.report_address_type(&ev.get_peer_address(), AddressType::LE); Loading @@ -845,10 +919,15 @@ impl Rule for InformationalRule { } // Determining LE initiator is complex, for simplicity assume host inits. self.report_acl_state(&ev.get_peer_address(), AclState::Initiating); self.report_acl_state( &ev.get_peer_address(), Transport::LE, AclState::Initiating, ); self.report_connection_start( &ev.get_peer_address(), ev.get_connection_handle(), Transport::LE, packet.ts, ); self.report_address_type(&ev.get_peer_address(), AddressType::LE); Loading Loading @@ -881,11 +960,19 @@ impl Rule for InformationalRule { self.report_reset(packet.ts); } CommandChild::CreateConnection(cmd) => { self.report_acl_state(&cmd.get_bd_addr(), AclState::Initiating); self.report_acl_state( &cmd.get_bd_addr(), Transport::BREDR, AclState::Initiating, ); self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR); } CommandChild::AcceptConnectionRequest(cmd) => { self.report_acl_state(&cmd.get_bd_addr(), AclState::Accepting); self.report_acl_state( &cmd.get_bd_addr(), Transport::BREDR, AclState::Accepting, ); self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR); } CommandChild::Disconnect(cmd) => { Loading Loading @@ -1001,7 +1088,9 @@ impl Rule for InformationalRule { * (5) Address, alphabetically */ fn sort_addresses(a: &DeviceInformation, b: &DeviceInformation) -> Ordering { let connection_order = a.acls.is_empty().cmp(&b.acls.is_empty()); let a_empty = a.acls[&Transport::BREDR].is_empty() && a.acls[&Transport::LE].is_empty(); let b_empty = b.acls[&Transport::BREDR].is_empty() && b.acls[&Transport::LE].is_empty(); let connection_order = a_empty.cmp(&b_empty); if connection_order != Ordering::Equal { return connection_order; } Loading Loading
floss/hcidoc/src/groups/informational.rs +136 −47 Original line number Diff line number Diff line Loading @@ -83,6 +83,24 @@ impl AddressType { } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] enum Transport { Unknown, BREDR, LE, } impl fmt::Display for Transport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let str = match self { Transport::Unknown => "??", Transport::BREDR => "BR", Transport::LE => "LE", }; write!(f, "{}", str) } } #[derive(Clone, Copy, PartialEq)] enum InitiatorType { Unknown, Loading Loading @@ -124,8 +142,8 @@ struct DeviceInformation { names: HashSet<String>, address: Address, address_type: AddressType, acls: Vec<AclInformation>, acl_state: AclState, acls: HashMap<Transport, Vec<AclInformation>>, acl_state: HashMap<Transport, AclState>, } impl DeviceInformation { Loading @@ -134,30 +152,52 @@ impl DeviceInformation { names: HashSet::new(), address: address, address_type: AddressType::None, acls: vec![], acl_state: AclState::None, acls: HashMap::from([(Transport::BREDR, vec![]), (Transport::LE, vec![])]), acl_state: HashMap::from([ (Transport::BREDR, AclState::None), (Transport::LE, AclState::None), ]), } } fn is_connection_active(&self, transport: Transport) -> bool { if transport == Transport::Unknown { return false; } fn is_connection_active(&self) -> bool { // not empty and last connection's end time is not set. return !self.acls.is_empty() && self.acls.last().unwrap().end_time == INVALID_TS; return !self.acls[&transport].is_empty() && self.acls[&transport].last().unwrap().end_time == INVALID_TS; } fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation { if !self.is_connection_active() { let acl = AclInformation::new(*handle); self.acls.push(acl); fn get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { assert_ne!(transport, Transport::Unknown, "device allocating unknown transport"); if !self.is_connection_active(transport) { let acl = AclInformation::new(handle, transport); self.acls.get_mut(&transport).unwrap().push(acl); } return self.acls.last_mut().unwrap(); return self.acls.get_mut(&transport).unwrap().last_mut().unwrap(); } fn report_connection_start( &mut self, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, ) { if transport == Transport::Unknown { return; } fn report_connection_start(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) { let mut acl = AclInformation::new(handle); let initiator = self.acl_state.get_connection_initiator(); let mut acl = AclInformation::new(handle, transport); let initiator = self.acl_state[&transport].get_connection_initiator(); acl.report_start(initiator, ts); self.acls.push(acl); self.acl_state = AclState::Connected; self.acls.get_mut(&transport).unwrap().push(acl); self.acl_state.insert(transport, AclState::Connected); } fn report_connection_end( Loading @@ -166,9 +206,25 @@ impl DeviceInformation { initiator: InitiatorType, ts: NaiveDateTime, ) { let acl = self.get_or_allocate_connection(&handle); acl.report_end(initiator, ts); self.acl_state = AclState::None; for transport in [Transport::BREDR, Transport::LE] { if self.is_connection_active(transport) { if self.acls[&transport].last().unwrap().handle == handle { self.acls .get_mut(&transport) .unwrap() .last_mut() .unwrap() .report_end(initiator, ts); self.acl_state.insert(transport, AclState::None); return; } } } eprintln!( "device {} receive disconnection of handle {} without corresponding connection at {}", self.address, handle, ts ); } fn print_names(names: &HashSet<String>) -> String { Loading @@ -190,7 +246,10 @@ impl fmt::Display for DeviceInformation { device_names = DeviceInformation::print_names(&self.names), num_connections = self.acls.len() ); for acl in &self.acls { for acl in &self.acls[&Transport::BREDR] { let _ = write!(f, "{}", acl); } for acl in &self.acls[&Transport::LE] { let _ = write!(f, "{}", acl); } Loading @@ -209,6 +268,7 @@ struct AclInformation { start_time: NaiveDateTime, end_time: NaiveDateTime, handle: ConnectionHandle, transport: Transport, start_initiator: InitiatorType, end_initiator: InitiatorType, active_profiles: HashMap<ProfileId, ProfileInformation>, Loading @@ -218,11 +278,12 @@ struct AclInformation { } impl AclInformation { pub fn new(handle: ConnectionHandle) -> Self { pub fn new(handle: ConnectionHandle, transport: Transport) -> Self { AclInformation { start_time: INVALID_TS, end_time: INVALID_TS, handle: handle, handle, transport, start_initiator: InitiatorType::Unknown, end_initiator: InitiatorType::Unknown, active_profiles: HashMap::new(), Loading Loading @@ -387,7 +448,8 @@ impl fmt::Display for AclInformation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( f, " Handle: {handle}, {timestamp_initiator_info}", " Handle: {handle} ({transport}), {timestamp_initiator_info}", transport = self.transport, handle = self.handle, timestamp_initiator_info = print_timestamps_and_initiator( self.start_time, Loading Loading @@ -566,23 +628,28 @@ impl InformationalRule { fn get_or_allocate_unknown_connection( &mut self, handle: &ConnectionHandle, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { if !self.unknown_connections.contains_key(handle) { self.unknown_connections.insert(*handle, AclInformation::new(*handle)); if !self.unknown_connections.contains_key(&handle) { self.unknown_connections.insert(handle, AclInformation::new(handle, transport)); } return self.unknown_connections.get_mut(handle).unwrap(); return self.unknown_connections.get_mut(&handle).unwrap(); } fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation { if !self.handles.contains_key(&handle) { let conn = self.get_or_allocate_unknown_connection(&handle); fn get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation { if !self.handles.contains_key(&handle) || transport == Transport::Unknown { let conn = self.get_or_allocate_unknown_connection(handle, transport); return conn; } let address = &self.handles.get(handle).unwrap().clone(); let address = &self.handles.get(&handle).unwrap().clone(); let device = self.get_or_allocate_device(address); return device.get_or_allocate_connection(handle); return device.get_or_allocate_connection(handle, transport); } fn report_address_type(&mut self, address: &Address, address_type: AddressType) { Loading @@ -595,19 +662,20 @@ impl InformationalRule { device.names.insert(name.into()); } fn report_acl_state(&mut self, address: &Address, state: AclState) { fn report_acl_state(&mut self, address: &Address, transport: Transport, state: AclState) { let device = self.get_or_allocate_device(address); device.acl_state = state; device.acl_state.insert(transport, state); } fn report_connection_start( &mut self, address: &Address, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, ) { let device = self.get_or_allocate_device(address); device.report_connection_start(handle, ts); device.report_connection_start(handle, transport, ts); self.handles.insert(handle, *address); self.pending_disconnections.remove(&handle); } Loading @@ -624,14 +692,14 @@ impl InformationalRule { } let device = self.devices.get_mut(address).unwrap(); if !device.is_connection_active() { if !device.is_connection_active(Transport::BREDR) { // SCO is connected, but ACL is not. This is weird, but let's ignore for simplicity. eprintln!("[{}] SCO is connected, but ACL is not.", address); return; } // Whatever handle value works here - we aren't allocating a new one. let acl = device.get_or_allocate_connection(&0); let acl = device.get_or_allocate_connection(0, Transport::BREDR); let acl_handle = acl.handle; // We need to listen the HCI commands to determine the correct initiator. // Here we just assume host for simplicity. Loading @@ -654,7 +722,7 @@ impl InformationalRule { // This might be a SCO disconnection event, so check that first if self.sco_handles.contains_key(&handle) { let acl_handle = self.sco_handles[&handle]; let conn = self.get_or_allocate_connection(&acl_handle); let conn = self.get_or_allocate_connection(acl_handle, Transport::BREDR); // in case of HFP failure, the initiator here would be set to peer, which is incorrect, // but when printing we detect by the timestamp that it was a failure anyway. conn.report_profile_end( Loading @@ -677,7 +745,7 @@ impl InformationalRule { self.sco_handles.retain(|_sco_handle, acl_handle| *acl_handle != handle); } else { // Unknown device. let conn = self.get_or_allocate_unknown_connection(&handle); let conn = self.get_or_allocate_unknown_connection(handle, Transport::Unknown); conn.report_end(initiator, ts); } } Loading Loading @@ -731,7 +799,7 @@ impl InformationalRule { initiator: InitiatorType, ts: NaiveDateTime, ) { let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); conn.report_l2cap_conn_req(psm, cid, initiator, ts); } Loading @@ -747,7 +815,7 @@ impl InformationalRule { if status == ConnectionResponseResult::Pending { return; } let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); let cid_info = CidInformation { host_cid, peer_cid }; conn.report_l2cap_conn_rsp(status, cid_info, initiator, ts); } Loading @@ -760,7 +828,7 @@ impl InformationalRule { initiator: InitiatorType, ts: NaiveDateTime, ) { let conn = self.get_or_allocate_connection(&handle); let conn = self.get_or_allocate_connection(handle, Transport::BREDR); let cid_info = CidInformation { host_cid, peer_cid }; conn.report_l2cap_disconn_rsp(cid_info, initiator, ts); } Loading @@ -774,6 +842,7 @@ impl Rule for InformationalRule { self.report_connection_start( &ev.get_bd_addr(), ev.get_connection_handle(), Transport::BREDR, packet.ts, ); Loading Loading @@ -830,10 +899,15 @@ impl Rule for InformationalRule { } // Determining LE initiator is complex, for simplicity assume host inits. self.report_acl_state(&ev.get_peer_address(), AclState::Initiating); self.report_acl_state( &ev.get_peer_address(), Transport::LE, AclState::Initiating, ); self.report_connection_start( &ev.get_peer_address(), ev.get_connection_handle(), Transport::LE, packet.ts, ); self.report_address_type(&ev.get_peer_address(), AddressType::LE); Loading @@ -845,10 +919,15 @@ impl Rule for InformationalRule { } // Determining LE initiator is complex, for simplicity assume host inits. self.report_acl_state(&ev.get_peer_address(), AclState::Initiating); self.report_acl_state( &ev.get_peer_address(), Transport::LE, AclState::Initiating, ); self.report_connection_start( &ev.get_peer_address(), ev.get_connection_handle(), Transport::LE, packet.ts, ); self.report_address_type(&ev.get_peer_address(), AddressType::LE); Loading Loading @@ -881,11 +960,19 @@ impl Rule for InformationalRule { self.report_reset(packet.ts); } CommandChild::CreateConnection(cmd) => { self.report_acl_state(&cmd.get_bd_addr(), AclState::Initiating); self.report_acl_state( &cmd.get_bd_addr(), Transport::BREDR, AclState::Initiating, ); self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR); } CommandChild::AcceptConnectionRequest(cmd) => { self.report_acl_state(&cmd.get_bd_addr(), AclState::Accepting); self.report_acl_state( &cmd.get_bd_addr(), Transport::BREDR, AclState::Accepting, ); self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR); } CommandChild::Disconnect(cmd) => { Loading Loading @@ -1001,7 +1088,9 @@ impl Rule for InformationalRule { * (5) Address, alphabetically */ fn sort_addresses(a: &DeviceInformation, b: &DeviceInformation) -> Ordering { let connection_order = a.acls.is_empty().cmp(&b.acls.is_empty()); let a_empty = a.acls[&Transport::BREDR].is_empty() && a.acls[&Transport::LE].is_empty(); let b_empty = b.acls[&Transport::BREDR].is_empty() && b.acls[&Transport::LE].is_empty(); let connection_order = a_empty.cmp(&b_empty); if connection_order != Ordering::Equal { return connection_order; } Loading