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