1use bt_common::PeerId;
6use bt_common::core::{Address, AddressType};
7use bt_gatt::pii::GetPeerAddr;
8use bt_gatt::{Result, types};
9use fidl_fuchsia_bluetooth_sys::{AddressLookupLookupRequest, AddressLookupProxy, LookupError};
10use fuchsia_sync::Mutex;
11use std::collections::HashMap;
12
13use crate::to_fidl_peer_id;
14
15pub struct FuchsiaPeerAddr {
18 proxy: AddressLookupProxy,
19 cache: Mutex<HashMap<PeerId, (Address, AddressType)>>,
20}
21
22fn from_fidl_address(addr: fidl_fuchsia_bluetooth::Address) -> (Address, AddressType) {
23 let addr_type = match addr.type_ {
24 fidl_fuchsia_bluetooth::AddressType::Public => AddressType::Public,
25 fidl_fuchsia_bluetooth::AddressType::Random => AddressType::Random,
26 };
27 (addr.bytes.into(), addr_type)
28}
29
30impl FuchsiaPeerAddr {
31 pub fn new(proxy: AddressLookupProxy) -> Self {
32 Self { proxy, cache: Mutex::new(HashMap::new()) }
33 }
34}
35
36impl GetPeerAddr for FuchsiaPeerAddr {
37 async fn get_peer_address(&self, peer_id: PeerId) -> Result<(Address, AddressType)> {
38 if let Some(addr) = self.cache.lock().get(&peer_id) {
40 return Ok(*addr);
41 }
42
43 let result = self
45 .proxy
46 .lookup(&AddressLookupLookupRequest {
47 peer_id: Some(to_fidl_peer_id(&peer_id)),
48 ..Default::default()
49 })
50 .await;
51
52 match result {
53 Ok(Ok(addr)) => {
54 let addr = from_fidl_address(addr);
55 let _ = self.cache.lock().insert(peer_id, addr);
57 Ok(addr)
58 }
59 Ok(Err(LookupError::NotFound)) => Err(types::Error::PeerNotRecognized(peer_id)),
60 Ok(Err(e)) => Err(types::Error::from(format!("AddressLookup error: {:?}", e))),
61 Err(fidl_err) => Err(types::Error::other(fidl_err)),
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use fidl::endpoints::create_proxy_and_stream;
70 use fidl_fuchsia_bluetooth_sys::AddressLookupRequest;
71 use fuchsia_async as fasync;
72 use futures::pin_mut;
73 use futures::stream::StreamExt;
74 use futures::task::Poll;
75
76 #[test]
77 fn test_get_peer_address_success() {
78 let mut exec = fasync::TestExecutor::new();
79 let (proxy, mut stream) =
80 create_proxy_and_stream::<fidl_fuchsia_bluetooth_sys::AddressLookupMarker>();
81
82 let peer_id = PeerId(123);
83 let addr = fidl_fuchsia_bluetooth::Address {
84 type_: fidl_fuchsia_bluetooth::AddressType::Random,
85 bytes: [1, 2, 3, 4, 5, 6],
86 };
87
88 let lookup = FuchsiaPeerAddr::new(proxy);
89
90 let client_fut = lookup.get_peer_address(peer_id);
92 pin_mut!(client_fut);
93 assert!(exec.run_until_stalled(&mut client_fut).is_pending());
94
95 let stream_fut = stream.next();
97 pin_mut!(stream_fut);
98 let stream_result = exec.run_until_stalled(&mut stream_fut);
99 let Poll::Ready(Some(Ok(AddressLookupRequest::Lookup { payload, responder }))) =
100 stream_result
101 else {
102 panic!("Expected Lookup request, got {:?}", stream_result);
103 };
104 assert_eq!(peer_id.0, payload.peer_id.unwrap().value);
105 responder.send(Ok(&addr.into())).unwrap();
106
107 let client_result = exec.run_until_stalled(&mut client_fut);
109 let Poll::Ready(Ok((found_addr, found_type))) = client_result else {
110 panic!("Expected future to complete with Ok, got {:?}", client_result);
111 };
112 let (expected_addr, expected_type) = from_fidl_address(addr);
113 assert_eq!(found_addr, expected_addr);
114 assert_eq!(found_type, expected_type);
115
116 let client_fut2 = lookup.get_peer_address(peer_id);
118 pin_mut!(client_fut2);
119 let client_result2 = exec.run_until_stalled(&mut client_fut2);
120 let Poll::Ready(Ok((found_addr, found_type))) = client_result2 else {
121 panic!("Expected future to complete immediately from cache, got {:?}", client_result2);
122 };
123 assert_eq!(found_addr, expected_addr);
124 assert_eq!(found_type, expected_type);
125
126 let stream_fut = stream.next();
128 pin_mut!(stream_fut);
129 assert!(exec.run_until_stalled(&mut stream_fut).is_pending());
130 }
131
132 #[test]
133 fn test_get_peer_address_not_found() {
134 let mut exec = fasync::TestExecutor::new();
135 let (proxy, mut stream) =
136 create_proxy_and_stream::<fidl_fuchsia_bluetooth_sys::AddressLookupMarker>();
137
138 let peer_id = PeerId(123);
139
140 let lookup = FuchsiaPeerAddr::new(proxy);
141
142 let client_fut = lookup.get_peer_address(peer_id);
143 pin_mut!(client_fut);
144 assert!(exec.run_until_stalled(&mut client_fut).is_pending());
145
146 let stream_fut = stream.next();
148 pin_mut!(stream_fut);
149 let stream_result = exec.run_until_stalled(&mut stream_fut);
150 let Poll::Ready(Some(Ok(AddressLookupRequest::Lookup { payload, responder }))) =
151 stream_result
152 else {
153 panic!("Expected Lookup request, got {:?}", stream_result);
154 };
155 assert_eq!(peer_id.0, payload.peer_id.unwrap().value);
156 responder.send(Err(LookupError::NotFound)).unwrap();
157
158 let client_result = exec.run_until_stalled(&mut client_fut);
160 let Poll::Ready(Err(e)) = client_result else {
161 panic!("Expected future to complete with Err, got {:?}", client_result);
162 };
163 assert!(matches!(e, types::Error::PeerNotRecognized(id) if id == peer_id));
164 }
165}