Skip to main content

sl4f_lib/server/
sl4f.rs

1// Copyright 2018 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 anyhow::{Context as _, Error};
6use fidl_fuchsia_testing_sl4f::{
7    FacadeIteratorMarker, FacadeIteratorSynchronousProxy, FacadeProviderMarker, FacadeProviderProxy,
8};
9use fuchsia_component::client::connect_to_protocol;
10use fuchsia_sync::RwLock;
11use log::{error, info, warn};
12use maplit::{convert_args, hashmap};
13use serde_json::{Value, json};
14use std::collections::{HashMap, HashSet};
15use std::sync::Arc;
16
17// Standardized sl4f types and constants
18use crate::bluetooth::avrcp_facade::AvrcpFacade;
19use crate::server::sl4f_types::{
20    AsyncCommandRequest, AsyncRequest, ClientData, CommandRequest, CommandResponse, Facade,
21    MethodId, RequestId,
22};
23
24// Audio related includes
25use crate::audio::commands::AudioFacade;
26
27// Session related includes
28use crate::modular::facade::ModularFacade;
29
30// Bluetooth related includes
31use crate::bluetooth::a2dp_facade::A2dpFacade;
32use crate::bluetooth::avdtp_facade::AvdtpFacade;
33use crate::bluetooth::ble_advertise_facade::BleAdvertiseFacade;
34use crate::bluetooth::bt_sys_facade::BluetoothSysFacade;
35use crate::bluetooth::gatt_client_facade::GattClientFacade;
36use crate::bluetooth::gatt_server_facade::GattServerFacade;
37use test_call_manager::TestCallManager as HfpFacade;
38use test_rfcomm_client::RfcommManager as RfcommFacade;
39
40use crate::bluetooth::profile_server_facade::ProfileServerFacade;
41
42// Common
43use crate::common_utils::common::{read_json_from_vmo, write_json_to_vmo};
44use crate::common_utils::error::Sl4fError;
45
46// Component related includes
47use crate::component::facade::ComponentFacade;
48
49// Device related includes
50use crate::device::facade::DeviceFacade;
51
52// Diagnostics related includes
53use crate::diagnostics::facade::DiagnosticsFacade;
54
55// Factory reset related includes
56use crate::factory_reset::facade::FactoryResetFacade;
57
58// Factory related includes
59use crate::factory_store::facade::FactoryStoreFacade;
60
61// Feedback related includes
62use crate::feedback_data_provider::facade::FeedbackDataProviderFacade;
63
64// File related includes
65use crate::file::facade::FileFacade;
66
67// Device Manager related includes
68use crate::hardware_power_statecontrol::facade::HardwarePowerStatecontrolFacade;
69
70// Hwinfo related includes
71use crate::hwinfo::facade::HwinfoFacade;
72
73// Input related includes
74use crate::input::facade::InputFacade;
75
76// Location related includes
77use crate::location::emergency_provider_facade::EmergencyProviderFacade;
78use crate::location::regulatory_region_facade::RegulatoryRegionFacade;
79
80// Logging related includes
81use crate::logging::facade::LoggingFacade;
82
83// Media session related includes
84use crate::media_session::facade::MediaSessionFacade;
85
86// Netstack related includes
87use crate::netstack::facade::NetstackFacade;
88
89// Paver related includes
90use crate::paver::facade::PaverFacade;
91
92// Power related includes
93use crate::power::facade::PowerFacade;
94
95// Proxy related includes
96use crate::proxy::facade::ProxyFacade;
97
98// Scenic related includes
99use crate::scenic::facade::ScenicFacade;
100
101// SetUi related includes
102use crate::setui::facade::SetUiFacade;
103
104// System Metrics related includes
105use crate::system_metrics::facade::SystemMetricsFacade;
106
107// Temperature related includes
108use crate::temperature::facade::TemperatureFacade;
109
110// Time related includes
111use crate::time::facade::TimeFacade;
112
113// Traceutil related includes
114use crate::traceutil::facade::TraceutilFacade;
115
116// Tracing related includes
117use crate::tracing::facade::TracingFacade;
118
119// Virtual Camera Device related includes
120use crate::virtual_camera::facade::VirtualCameraFacade;
121
122// Weave related includes
123use crate::weave::facade::WeaveFacade;
124
125// Webdriver related includes
126use crate::webdriver::facade::WebdriverFacade;
127
128// Wlan related includes
129use crate::wlan::facade::WlanFacade;
130
131// Wlan Policy related includes
132use crate::wlan_policy::facade::WlanPolicyFacade;
133
134// Wpan related includes
135use crate::wpan::facade::WpanFacade;
136
137/// Sl4f stores state for all facades and has access to information for all connected clients.
138///
139/// To add support for a new Facade implementation, see the hashmap in `Sl4f::new`.
140#[derive(Debug)]
141pub struct Sl4f {
142    // facades: Mapping of method prefix to object implementing that facade's API.
143    facades: HashMap<String, Arc<dyn Facade>>,
144
145    // NOTE: facade_provider and proxied_facades will eventually become a map from proxied facade
146    // to `FacadeProvider` client once we have support for multiple `FacadeProvider` instances.
147    // facade_provider: Channel to the `FacadeProvider` instance hosting private facades.
148    facade_provider: FacadeProviderProxy,
149
150    // proxied_facades: Set of facades hosted by facade_provider. May be empty.
151    proxied_facades: HashSet<String>,
152
153    // connected clients
154    clients: Arc<RwLock<Sl4fClients>>,
155}
156
157impl Sl4f {
158    pub fn new(clients: Arc<RwLock<Sl4fClients>>) -> Result<Sl4f, Error> {
159        fn to_arc_trait_object<'a, T: Facade + 'a>(facade: T) -> Arc<dyn Facade + 'a> {
160            Arc::new(facade) as Arc<dyn Facade>
161        }
162        // To add support for a new facade, define a new submodule with the Facade implementation
163        // and construct an instance and include it in the mapping below. The key is used to route
164        // requests to the appropriate Facade. Facade constructors should generally not fail, as a
165        // facade that returns an error here will prevent sl4f from starting.
166        let facades = convert_args!(
167            keys = String::from,
168            values = to_arc_trait_object,
169            hashmap!(
170                "a2dp_facade" => A2dpFacade::new(),
171                "audio_facade" => AudioFacade::new()?,
172                "avdtp_facade" => AvdtpFacade::new(),
173                "avrcp_facade" => AvrcpFacade::new(),
174                // TODO(https://fxbug.dev/42157579): Remove basemgr_facade in favor of modular_facade
175                "basemgr_facade" => ModularFacade::new(),
176                "modular_facade" => ModularFacade::new(),
177                "ble_advertise_facade" => BleAdvertiseFacade::new(),
178                "bt_sys_facade" => BluetoothSysFacade::new(),
179                "component_facade" => ComponentFacade::new(),
180                "diagnostics_facade" => DiagnosticsFacade::new(),
181                "device_facade" => DeviceFacade::new(),
182                "factory_reset_facade" => FactoryResetFacade::new(),
183                "factory_store_facade" => FactoryStoreFacade::new(),
184                "feedback_data_provider_facade" => FeedbackDataProviderFacade::new(),
185                "file_facade" => FileFacade::new(),
186                "gatt_client_facade" => GattClientFacade::new(),
187                "gatt_server_facade" => GattServerFacade::new(),
188                "hardware_power_statecontrol_facade" => HardwarePowerStatecontrolFacade::new(),
189                "hfp_facade" => HfpFacade::new(),
190                "hwinfo_facade" => HwinfoFacade::new(),
191                "input_facade" => InputFacade::new(),
192                "location_emergency_provider_facade" => EmergencyProviderFacade::new()?,
193                "location_regulatory_region_facade" => RegulatoryRegionFacade::new()?,
194                "logging_facade" => LoggingFacade::new(),
195                "media_session_facade" => MediaSessionFacade::new(),
196                "netstack_facade" => NetstackFacade::default(),
197                "rfcomm_facade" => RfcommFacade::new()?,
198                "paver" => PaverFacade::new(),
199                "power_facade" => PowerFacade::new(),
200                "profile_server_facade" => ProfileServerFacade::new(),
201                "proxy_facade" => ProxyFacade::new(),
202                "scenic_facade" => ScenicFacade::new(),
203                "setui_facade" => SetUiFacade::new(),
204                "system_metrics_facade" => SystemMetricsFacade::new(),
205                "temperature_facade" => TemperatureFacade::new(),
206                "time_facade" => TimeFacade::new(),
207                "traceutil_facade" => TraceutilFacade::new(),
208                "tracing_facade" => TracingFacade::new(),
209                "virtual_camera_facade" => VirtualCameraFacade::new(),
210                "weave_facade" => WeaveFacade::new(),
211                "webdriver_facade" => WebdriverFacade::new(),
212                "wlan" => WlanFacade::new()?,
213                "wlan_policy" => WlanPolicyFacade::new()?,
214                "wpan_facade" => WpanFacade::new(),
215            )
216        );
217
218        // Attempt to connect to the single `FacadeProvider` instance.
219        let mut proxied_facades = HashSet::<String>::new();
220        let facade_provider = match connect_to_protocol::<FacadeProviderMarker>() {
221            Ok(proxy) => proxy,
222            Err(error) => {
223                error!(error:%; "Failed to connect to FacadeProvider");
224                return Err(error.into());
225            }
226        };
227        // Get the names of the facades hosted by the `FacadeProvider`.
228        // NOTE: Due to the inability to actively verify that connection succeeds, there are
229        // multiple layers of error checking at which a PEER_CLOSED means that there never was a
230        // `FacadeProvider` to connect to.
231        let (client_end, server_end) = fidl::endpoints::create_endpoints::<FacadeIteratorMarker>();
232        match facade_provider.get_facades(server_end) {
233            Ok(_) => {
234                let facade_iter = FacadeIteratorSynchronousProxy::new(client_end.into_channel());
235                loop {
236                    match facade_iter.get_next(zx::MonotonicInstant::INFINITE) {
237                        Ok(facades) if facades.is_empty() => break, // Indicates completion.
238                        Ok(facades) => proxied_facades.extend(facades.into_iter()),
239                        // A PEER_CLOSED error before any facades are read indicates that there was
240                        // never a successful connection.
241                        Err(error) if error.is_closed() && proxied_facades.is_empty() => {
242                            break;
243                        }
244                        Err(error) => {
245                            error!(error:%; "Failed to get proxied facade list");
246                            proxied_facades.clear();
247                            break;
248                        }
249                    };
250                }
251            }
252            // The channel's server end was closed due to no `FacadeProvider` instance.
253            Err(error) if error.is_closed() => (),
254            Err(error) => {
255                error!(error:%; "Failed to get FacadeIterator");
256                return Err(error.into());
257            }
258        };
259
260        Ok(Sl4f { facades, facade_provider, proxied_facades, clients })
261    }
262
263    /// Gets the facade registered with the given name, if one exists.
264    pub fn get_facade(&self, name: &str) -> Option<Arc<dyn Facade>> {
265        self.facades.get(name).map(Arc::clone)
266    }
267
268    /// Implement the Facade trait method cleanup() to clean up state when "/cleanup" is queried.
269    pub async fn cleanup(&self) {
270        for facade in self.facades.values() {
271            facade.cleanup();
272        }
273        // If there are any proxied facades, make a synchronous request to cleanup transient state.
274        if !self.proxied_facades.is_empty() {
275            if let Err(error) = self.facade_provider.cleanup().await {
276                error!(error:%; "Failed to execute Cleanup()");
277            }
278        }
279        self.clients.write().cleanup_clients();
280    }
281
282    pub fn print_clients(&self) {
283        self.clients.read().print_clients();
284    }
285
286    /// Implement the Facade trait method print() to log state when "/print" is queried.
287    pub async fn print(&self) {
288        for facade in self.facades.values() {
289            facade.print();
290        }
291        // If there are any proxied facades, make a synchronous request to print state.
292        if !self.proxied_facades.is_empty() {
293            if let Err(error) = self.facade_provider.print().await {
294                error!(error:%; "Failed to execute Print()");
295            }
296        }
297    }
298
299    /// Returns true if the facade with the given name is hosted by a registered `FacadeProvider`.
300    /// # Arguments
301    /// * 'name' - A string representing the name of the facade.
302    pub fn has_proxy_facade(&self, name: &str) -> bool {
303        self.proxied_facades.contains(name)
304    }
305
306    /// Sends a request on a facade hosted by a registered `FacadeProvider` and waits
307    /// asynchronously for the response.
308    /// # Arguments
309    /// * 'facade' - A string representing the name of the facade.
310    /// * 'command' - A string representing the command to execute on the facade.
311    /// * 'args' - An arbitrary JSON Value containing any arguments to the command.
312    pub async fn handle_proxy_request(
313        &self,
314        facade: String,
315        command: String,
316        args: Value,
317    ) -> Result<Value, Error> {
318        // Populate a new VMO with a JSON blob containing the arguments.
319        let encode_params = async {
320            let params_blob = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0)?;
321            write_json_to_vmo(&params_blob, &args)?;
322            Ok::<zx::Vmo, Error>(params_blob)
323        };
324        let params_blob = match encode_params.await {
325            Ok(params_blob) => params_blob,
326            Err(error) => {
327                return Err(
328                    Sl4fError::new(&format!("Failed to write params with: {}", error)).into()
329                );
330            }
331        };
332
333        // Forward the request to the `FacadeProvider`.
334        match self.facade_provider.execute(&facade, &command, params_blob).await {
335            // Success with no response.
336            Ok((None, None)) => Ok(Value::Null),
337            // Success with response. The JSON blob must be read out from the returned VMO.
338            Ok((Some(vmo), None)) => match read_json_from_vmo(&vmo) {
339                Ok(result) => Ok(result),
340                Err(error) => {
341                    Err(Sl4fError::new(&format!("Failed to read result with: {}", error)).into())
342                }
343            },
344            // The command failed. Return the error string.
345            Ok((_, Some(string))) => Err(Sl4fError::new(&string).into()),
346            Err(error) => {
347                Err(Sl4fError::new(&format!("Failed to send command with {}", error)).into())
348            }
349        }
350    }
351}
352
353/// Metadata for clients utilizing the /init API.
354#[derive(Debug)]
355pub struct Sl4fClients {
356    // clients: map of clients that are connected to the sl4f server.
357    // key = session_id (unique for every ACTS instance) and value = Data about client (see
358    // sl4f_types.rs)
359    clients: HashMap<String, Vec<ClientData>>,
360}
361
362impl Sl4fClients {
363    pub fn new() -> Self {
364        Self { clients: HashMap::new() }
365    }
366
367    /// Registers a new connected client. Returns true if the client was already initialized.
368    fn init_client(&mut self, id: String) -> bool {
369        use std::collections::hash_map::Entry::*;
370        match self.clients.entry(id) {
371            Occupied(entry) => {
372                warn!(tag = "client_init"; "Key: {:?} already exists in clients. ", entry.key());
373                true
374            }
375            Vacant(entry) => {
376                entry.insert(Vec::new());
377                info!(tag = "client_init"; "Updated clients: {:?}", self.clients);
378                false
379            }
380        }
381    }
382
383    fn cleanup_clients(&mut self) {
384        self.clients.clear();
385    }
386
387    fn print_clients(&self) {
388        info!("SL4F Clients: {:?}", self.clients);
389    }
390}
391
392fn json<T>(content: &T) -> hyper::Response<hyper::Body>
393where
394    T: serde::Serialize,
395{
396    use std::convert::TryInto as _;
397
398    let application_json = "application/json".try_into().expect("json header value");
399    let data = serde_json::to_string(content).expect("encode json");
400
401    let mut response = hyper::Response::new(data.into());
402    assert_eq!(response.headers_mut().insert(hyper::header::CONTENT_TYPE, application_json), None);
403    response
404}
405
406/// Handles all incoming requests to SL4F server, routes accordingly
407pub async fn serve(
408    request: hyper::Request<hyper::Body>,
409    clients: Arc<RwLock<Sl4fClients>>,
410    sender: async_channel::Sender<AsyncRequest>,
411) -> hyper::Response<hyper::Body> {
412    use hyper::Method;
413
414    match (request.method(), request.uri().path()) {
415        (&Method::GET, "/") => {
416            // Parse the command request
417            info!(tag = "serve"; "Received command request via GET.");
418            client_request(request, &sender).await
419        }
420        (&Method::POST, "/") => {
421            // Parse the command request
422            info!(tag = "serve"; "Received command request via POST.");
423            client_request(request, &sender).await
424        }
425        (&Method::GET, "/init") => {
426            // Initialize a client
427            info!(tag = "serve"; "Received init request.");
428            client_init(request, &clients).await
429        }
430        (&Method::GET, "/print_clients") => {
431            // Print information about all clients
432            info!(tag = "serve"; "Received print client request.");
433            const PRINT_ACK: &str = "Successfully printed clients.";
434            json(&PRINT_ACK)
435        }
436        (&Method::GET, "/cleanup") => {
437            info!(tag = "serve"; "Received server cleanup request.");
438            server_cleanup(request, &sender).await
439        }
440        _ => {
441            error!(tag = "serve"; "Received unknown server request.");
442            const FAIL_REQUEST_ACK: &str = "Unknown GET request.";
443            let res = CommandResponse::new(json!(""), None, Some(FAIL_REQUEST_ACK.to_string()));
444            json(&res)
445        }
446    }
447}
448
449/// Given the request, map the test request to a FIDL query and execute
450/// asynchronously
451async fn client_request(
452    request: hyper::Request<hyper::Body>,
453    sender: &async_channel::Sender<AsyncRequest>,
454) -> hyper::Response<hyper::Body> {
455    const FAIL_TEST_ACK: &str = "Command failed";
456
457    let (request_id, method_id, method_params) = match parse_request(request).await {
458        Ok(res) => res,
459        Err(error) => {
460            error!(tag = "client_request", error:?; "Failed to parse request");
461            return json(&FAIL_TEST_ACK);
462        }
463    };
464
465    // Create channel for async thread to respond to
466    // Package response and ship over JSON RPC
467    let (async_sender, receiver) = futures::channel::oneshot::channel();
468    let req = AsyncCommandRequest::new(async_sender, method_id.clone(), method_params);
469    sender.send(AsyncRequest::Command(req)).await.expect("Failed to send request to async thread.");
470    let resp = receiver.await.expect("Async thread dropped responder.");
471
472    info!(
473        tag = "client_request",
474        method:? = method_id.method,
475        response:? = resp;
476        "Received async thread response"
477    );
478
479    // If the response has a return value, package into response, otherwise use error code
480    match resp.result {
481        Some(async_res) => {
482            let res = CommandResponse::new(request_id.into_response_id(), Some(async_res), None);
483            json(&res)
484        }
485        None => {
486            let res = CommandResponse::new(request_id.into_response_id(), None, resp.error);
487            json(&res)
488        }
489    }
490}
491
492/// Initializes a new client, adds to clients.
493async fn client_init(
494    request: hyper::Request<hyper::Body>,
495    clients: &Arc<RwLock<Sl4fClients>>,
496) -> hyper::Response<hyper::Body> {
497    const INIT_ACK: &str = "Recieved init request.";
498    const FAIL_INIT_ACK: &str = "Failed to init client.";
499
500    let (_, _, method_params) = match parse_request(request).await {
501        Ok(res) => res,
502        Err(_) => return json(&FAIL_INIT_ACK),
503    };
504
505    let client_id_raw = match method_params.get("client_id") {
506        Some(id) => Some(id).unwrap().clone(),
507        None => return json(&FAIL_INIT_ACK),
508    };
509
510    // Initialize client with key = id, val = client data
511    let client_id = client_id_raw.as_str().map(String::from).unwrap();
512
513    if clients.write().init_client(client_id) { json(&FAIL_INIT_ACK) } else { json(&INIT_ACK) }
514}
515
516/// Given a request, grabs the method id, name, and parameters
517/// Return Sl4fError if fail
518async fn parse_request(
519    request: hyper::Request<hyper::Body>,
520) -> Result<(RequestId, MethodId, Value), Error> {
521    use bytes::Buf as _;
522
523    let body = hyper::body::aggregate(request.into_body()).await.context("read request")?;
524
525    // Ignore the json_rpc field
526    let request_data: CommandRequest = match serde_json::from_reader(body.reader()) {
527        Ok(tdata) => tdata,
528        Err(_) => return Err(Sl4fError::new("Failed to unpack request data.").into()),
529    };
530
531    let request_id_raw = request_data.id;
532    let method_id_raw = request_data.method;
533    let method_params = request_data.params;
534    info!(tag = "parse_request",
535        request_id:? = request_id_raw,
536        name:? = method_id_raw,
537        args:? = method_params;
538        ""
539    );
540
541    let request_id = RequestId::new(request_id_raw);
542    // Separate the method_name field of the request into the method type (e.g bluetooth) and the
543    // actual method name itself, defaulting to an empty method id if not formatted properly.
544    let method_id = method_id_raw.parse().unwrap_or_default();
545    Ok((request_id, method_id, method_params))
546}
547
548async fn server_cleanup(
549    request: hyper::Request<hyper::Body>,
550    sender: &async_channel::Sender<AsyncRequest>,
551) -> hyper::Response<hyper::Body> {
552    const FAIL_CLEANUP_ACK: &str = "Failed to cleanup SL4F resources.";
553    const CLEANUP_ACK: &str = "Successful cleanup of SL4F resources.";
554
555    info!(tag = "server_cleanup"; "Cleaning up server state");
556    let (request_id, _, _) = match parse_request(request).await {
557        Ok(res) => res,
558        Err(_) => return json(&FAIL_CLEANUP_ACK),
559    };
560
561    // Create channel for async thread to respond to
562    let (async_sender, receiver) = futures::channel::oneshot::channel();
563
564    // Cleanup all resources associated with sl4f
565    sender
566        .send(AsyncRequest::Cleanup(async_sender))
567        .await
568        .expect("Failed to send request to async thread.");
569    let () = receiver.await.expect("Async thread dropped responder.");
570
571    let ack = CommandResponse::new(request_id.into_response_id(), Some(json!(CLEANUP_ACK)), None);
572    json(&ack)
573}