diff options
author | 2024-10-23 18:52:13 +0800 | |
---|---|---|
committer | 2024-10-24 20:32:14 +0800 | |
commit | 55b05336b553109c4e11af59cd8a1d887c054eb5 (patch) | |
tree | 963a78d9e42171ab3d4d3e879344e0e861411e0b /floss | |
parent | 6976c7308423f0fabe539c09f86bafc351d35521 (diff) |
Hcidoc: Support multiple concurrent connections with the same address
Devices which uses the same address for BREDR and LE connections will
mess up with the internal processing of hcidoc because it assumed
only one connection at any given time per address.
Bug: 375370824
Test: m -j
Test: manually with pixel buds
Flag: EXEMPT, floss only change
Change-Id: I7e4d3ed24d15b74da70e44a480c67180b4233162
Diffstat (limited to 'floss')
-rw-r--r-- | floss/hcidoc/src/groups/informational.rs | 183 |
1 files changed, 136 insertions, 47 deletions
diff --git a/floss/hcidoc/src/groups/informational.rs b/floss/hcidoc/src/groups/informational.rs index dbaa6c8ae3..1827b64282 100644 --- a/floss/hcidoc/src/groups/informational.rs +++ b/floss/hcidoc/src/groups/informational.rs @@ -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, @@ -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 { @@ -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) -> bool { + fn is_connection_active(&self, transport: Transport) -> bool { + if transport == Transport::Unknown { + return false; + } + // 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, ts: NaiveDateTime) { - let mut acl = AclInformation::new(handle); - let initiator = self.acl_state.get_connection_initiator(); + fn report_connection_start( + &mut self, + handle: ConnectionHandle, + transport: Transport, + ts: NaiveDateTime, + ) { + if transport == Transport::Unknown { + return; + } + + 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( @@ -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 { @@ -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); } @@ -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>, @@ -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(), @@ -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, @@ -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) { @@ -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); } @@ -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. @@ -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( @@ -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); } } @@ -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); } @@ -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); } @@ -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); } @@ -774,6 +842,7 @@ impl Rule for InformationalRule { self.report_connection_start( &ev.get_bd_addr(), ev.get_connection_handle(), + Transport::BREDR, packet.ts, ); @@ -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); @@ -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); @@ -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) => { @@ -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; } |