test_profile_server/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_bluetooth::types::{self as bt, PeerId};
6use futures::{Stream, StreamExt};
7use profile_client::ProfileClient;
8use std::pin::Pin;
9use std::task::{Context, Poll};
10use {fidl_fuchsia_bluetooth as fidl_bt, fidl_fuchsia_bluetooth_bredr as bredr};
11
12pub struct TestProfileServerEndpoints {
13    pub proxy: bredr::ProfileProxy,
14    pub client: ProfileClient,
15    pub test_server: TestProfileServer,
16}
17
18/// Used to specify the channel to expect on an incoming Connect message
19#[derive(Debug)]
20pub enum ConnectChannel {
21    L2CapPsm(u16),
22    RfcommChannel(u8), // Valid channels are 1-30
23}
24
25/// Holds all the server side resources associated with a `Profile`'s connection to
26/// fuchsia.bluetooth.bredr.Profile. Provides helper methods for common test related tasks.
27/// Some fields are optional because they are not populated until the Profile has completed
28/// registration.
29// TODO(b/333456020): Clean up `advertise_responder`
30pub struct TestProfileServer {
31    profile_request_stream: bredr::ProfileRequestStream,
32    search_results_proxy: Option<bredr::SearchResultsProxy>,
33    connection_receiver_proxy: Option<bredr::ConnectionReceiverProxy>,
34    advertise_responder: Option<bredr::ProfileAdvertiseResponder>,
35}
36
37impl From<bredr::ProfileRequestStream> for TestProfileServer {
38    fn from(profile_request_stream: bredr::ProfileRequestStream) -> Self {
39        Self {
40            profile_request_stream,
41            search_results_proxy: None,
42            connection_receiver_proxy: None,
43            advertise_responder: None,
44        }
45    }
46}
47
48impl TestProfileServer {
49    /// Create a new Profile proxy and stream, and create a profile client that wraps the proxy and a
50    /// test server that wraps the stream.
51    ///
52    /// If service_class_profile_id is Some, add a search for that service class.
53    ///
54    /// If service_definition is Some, advertise with that service definition.
55    ///
56    /// Returns a struct containing the proxy, profile client and test server.
57    pub fn new(
58        service_definition: Option<bredr::ServiceDefinition>,
59        service_class_profile_id: Option<bredr::ServiceClassProfileIdentifier>,
60    ) -> TestProfileServerEndpoints {
61        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<bredr::ProfileMarker>();
62
63        let mut client = match service_definition {
64            None => ProfileClient::new(proxy.clone()),
65            Some(service_definition) => {
66                let channel_params = fidl_bt::ChannelParameters::default();
67                ProfileClient::advertise(proxy.clone(), vec![service_definition], channel_params)
68                    .expect("Failed to advertise.")
69            }
70        };
71
72        if let Some(service_class_profile_id) = service_class_profile_id {
73            client.add_search(service_class_profile_id, None).expect("Failed to search for peers.");
74        }
75
76        let test_server = TestProfileServer::from(stream);
77
78        TestProfileServerEndpoints { proxy, client, test_server }
79    }
80
81    pub async fn expect_search(&mut self) {
82        let request = self.profile_request_stream.next().await;
83        match request {
84            Some(Ok(bredr::ProfileRequest::Search { payload, .. })) => {
85                self.search_results_proxy = Some(payload.results.unwrap().into_proxy());
86            }
87            _ => panic!(
88                "unexpected result on profile request stream while waiting for search: {:?}",
89                request
90            ),
91        }
92    }
93
94    pub async fn expect_advertise(&mut self) {
95        let request = self.profile_request_stream.next().await;
96        match request {
97            Some(Ok(bredr::ProfileRequest::Advertise { payload, responder, .. })) => {
98                self.connection_receiver_proxy = Some(payload.receiver.unwrap().into_proxy());
99                if let Some(_old_responder) = self.advertise_responder.replace(responder) {
100                    panic!("Got new advertise request before old request is complete.");
101                }
102            }
103            _ => panic!(
104                "unexpected result on profile request stream while waiting for advertisement: {:?}",
105                request
106            ),
107        }
108    }
109
110    pub async fn expect_connect(
111        &mut self,
112        expected_channel: Option<ConnectChannel>,
113    ) -> bt::Channel {
114        let request = self.profile_request_stream.next().await;
115        match request {
116            Some(Ok(bredr::ProfileRequest::Connect { connection, responder, .. })) => {
117                match (expected_channel, connection) {
118                    (None, _) => {}
119                    (
120                        Some(ConnectChannel::L2CapPsm(expected_psm)),
121                        bredr::ConnectParameters::L2cap(bredr::L2capParameters {
122                            psm: psm_option,
123                            ..
124                        }),
125                    ) => assert_eq!(Some(expected_psm), psm_option),
126                    (
127                        Some(ConnectChannel::RfcommChannel(expected_channel)),
128                        bredr::ConnectParameters::Rfcomm(bredr::RfcommParameters {
129                            channel: channel_option,
130                            ..
131                        }),
132                    ) => assert_eq!(Some(expected_channel), channel_option),
133                    (expected_channel, connection) => {
134                        panic!("On connect, expected {:?}, got {:?}", expected_channel, connection)
135                    }
136                }
137
138                let (near_bt_channel, far_bt_channel) = bt::Channel::create();
139                let far_bredr_channel: bredr::Channel =
140                    far_bt_channel.try_into().expect("BT Channel into FIDL BREDR Channel");
141                responder.send(Ok(far_bredr_channel)).expect("Send channel");
142                near_bt_channel
143            }
144            _ => panic!("unexpected result on profile request stream: {:?}", request),
145        }
146    }
147
148    pub fn send_service_found(
149        &mut self,
150        peer_id: PeerId,
151        protocol_list: Option<Vec<bredr::ProtocolDescriptor>>,
152        attributes: Vec<bredr::Attribute>,
153    ) -> fidl::client::QueryResponseFut<()> {
154        let search_results_proxy = self.search_results_proxy.as_ref().expect("Search result proxy");
155        search_results_proxy.service_found(&peer_id.into(), protocol_list.as_deref(), &attributes)
156    }
157
158    pub fn send_connected(
159        &mut self,
160        peer_id: PeerId,
161        protocol_list: Vec<bredr::ProtocolDescriptor>,
162    ) -> bt::Channel {
163        let (near_bt_channel, far_bt_channel) = bt::Channel::create();
164        let far_bredr_channel: bredr::Channel =
165            far_bt_channel.try_into().expect("BT Channel into FIDL BREDR Channel");
166
167        let connection_receiver_proxy =
168            self.connection_receiver_proxy.as_ref().expect("Connection receiver proxy");
169        connection_receiver_proxy
170            .connected(&peer_id.into(), far_bredr_channel, &protocol_list)
171            .expect("Connected");
172
173        near_bt_channel
174    }
175}
176
177/// Expose the underlying ProfileRequestStream
178impl Stream for TestProfileServer {
179    type Item = Result<bredr::ProfileRequest, fidl::Error>;
180
181    fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
182        let pinned_stream = Pin::new(&mut self.profile_request_stream);
183        pinned_stream.poll_next(context)
184    }
185}
186
187impl Drop for TestProfileServer {
188    fn drop(&mut self) {
189        // TODO(b/333456020): Clean-up to not store responder.
190        if let Some(responder) = self.advertise_responder.take() {
191            responder
192                .send(Ok(&bredr::ProfileAdvertiseResponse::default()))
193                .expect("Drop responder");
194        }
195    }
196}