Skip to main content

bt_test_harness/
core_realm.rs

1// Copyright 2021 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 crate::emulator::EMULATOR_ROOT_DRIVER_URL;
6use anyhow::{Error, format_err};
7use fidl_fuchsia_bluetooth_bredr as fbredr;
8use fidl_fuchsia_bluetooth_gatt as fbgatt;
9use fidl_fuchsia_bluetooth_le as fble;
10use fidl_fuchsia_bluetooth_snoop::SnoopMarker;
11use fidl_fuchsia_bluetooth_sys as fbsys;
12use fidl_fuchsia_device::NameProviderMarker;
13use fidl_fuchsia_driver_test as fdt;
14use fidl_fuchsia_hardware_bluetooth as fhbt;
15use fidl_fuchsia_io as fio;
16use fidl_fuchsia_logger::LogSinkMarker;
17use fidl_fuchsia_stash::SecureStoreMarker;
18use fuchsia_component_test::{
19    Capability, ChildOptions, RealmBuilder, RealmInstance, Ref, Route, ScopedInstance,
20};
21use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
22use futures::FutureExt;
23use realmbuilder_mock_helpers::stateless_mock_responder;
24
25pub const SHARED_STATE_INDEX: &str = "BT-CORE-REALM";
26pub const DEFAULT_TEST_DEVICE_NAME: &str = "fuchsia-bt-integration-test";
27
28// Use relative URLs because the library `deps` on all of these components, so any
29// components that depend (even transitively) on CoreRealm will include these components in
30// their package.
31mod constants {
32    pub mod bt_init {
33        pub const URL: &str = "#meta/test-bt-init.cm";
34        pub const MONIKER: &str = "bt-init";
35    }
36    pub mod secure_stash {
37        pub const URL: &str = "#meta/test-stash-secure.cm";
38        pub const MONIKER: &str = "secure-stash";
39    }
40    pub mod mock_name_provider {
41        pub const MONIKER: &str = "mock-name-provider";
42    }
43    pub mod mock_snoop {
44        pub const MONIKER: &str = "mock-snoop";
45    }
46}
47
48/// The CoreRealm represents a hermetic, fully-functional instance of the Fuchsia Bluetooth core
49/// stack, complete with all components (bt-init, bt-gap, bt-host, bt-rfcomm) and a bt-hci
50/// emulator. Clients should use the `create` method to construct an instance, and the `instance`
51/// method to access the various production capabilities and test interfaces (e.g. from the bt-hci
52/// emulator) exposed from the core stack. Clients of the CoreRealm must offer the `tmp` storage
53/// capability from the test manager to the "#realm_builder" underlying the RealmInstance.
54pub struct CoreRealm {
55    realm: RealmInstance,
56}
57
58impl CoreRealm {
59    pub async fn create(test_component: String) -> Result<Self, Error> {
60        // We need to resolve our test component manually. Eventually component framework could provide
61        // an introspection way of resolving your own component.
62        let resolved_test_component = {
63            let client = fuchsia_component::client::connect_to_protocol_at_path::<
64                fidl_fuchsia_component_resolution::ResolverMarker,
65            >("/svc/fuchsia.component.resolution.Resolver-hermetic")
66            .unwrap();
67            client
68                .resolve(test_component.as_str())
69                .await
70                .unwrap()
71                .expect("Failed to resolve test component")
72        };
73
74        let builder = RealmBuilder::new().await?;
75        let _ = builder.driver_test_realm_setup().await?;
76
77        // Create the components within CoreRealm
78        let bt_init = builder
79            .add_child(
80                constants::bt_init::MONIKER,
81                constants::bt_init::URL,
82                ChildOptions::new().eager(),
83            )
84            .await?;
85        let secure_stash = builder
86            .add_child(
87                constants::secure_stash::MONIKER,
88                constants::secure_stash::URL,
89                ChildOptions::new(),
90            )
91            .await?;
92        let mock_name_provider = builder
93            .add_local_child(
94                constants::mock_name_provider::MONIKER,
95                |handles| {
96                    stateless_mock_responder::<NameProviderMarker, _>(handles, |req| {
97                        let responder = req
98                            .into_get_device_name()
99                            .ok_or_else(|| format_err!("got unexpected NameProviderRequest"))?;
100                        Ok(responder.send(Ok(DEFAULT_TEST_DEVICE_NAME))?)
101                    })
102                    .boxed()
103                },
104                ChildOptions::new(),
105            )
106            .await?;
107        let mock_snoop = builder
108            .add_local_child(
109                constants::mock_snoop::MONIKER,
110                |handles| {
111                    stateless_mock_responder::<SnoopMarker, _>(handles, |req| {
112                        // just drop the request, should be sufficient
113                        let _ = req
114                            .into_start()
115                            .ok_or_else(|| format_err!("got unexpected SnoopRequest"))?;
116                        Ok(())
117                    })
118                    .boxed()
119                },
120                ChildOptions::new(),
121            )
122            .await?;
123
124        // Add capability routing between components within CoreRealm
125        builder
126            .add_route(
127                Route::new()
128                    .capability(Capability::protocol::<LogSinkMarker>())
129                    .capability(Capability::dictionary("diagnostics"))
130                    .from(Ref::parent())
131                    .to(&bt_init)
132                    .to(&secure_stash),
133            )
134            .await?;
135        builder
136            .add_route(
137                Route::new()
138                    .capability(Capability::storage("tmp"))
139                    .from(Ref::parent())
140                    .to(&secure_stash),
141            )
142            .await?;
143        builder
144            .add_route(
145                Route::new()
146                    .capability(Capability::protocol::<SecureStoreMarker>())
147                    .from(&secure_stash)
148                    .to(&bt_init),
149            )
150            .await?;
151        builder
152            .add_route(
153                Route::new()
154                    .capability(Capability::protocol::<NameProviderMarker>())
155                    .from(&mock_name_provider)
156                    .to(&bt_init),
157            )
158            .await?;
159        builder
160            .add_route(
161                Route::new()
162                    .capability(Capability::protocol::<SnoopMarker>())
163                    .from(&mock_snoop)
164                    .to(&bt_init),
165            )
166            .await?;
167        builder
168            .add_route(
169                Route::new()
170                    .capability(Capability::protocol::<fbgatt::Server_Marker>())
171                    .capability(Capability::protocol::<fble::CentralMarker>())
172                    .capability(Capability::protocol::<fble::PeripheralMarker>())
173                    .capability(Capability::protocol::<fbsys::AccessMarker>())
174                    .capability(Capability::protocol::<fbsys::HostWatcherMarker>())
175                    .capability(Capability::protocol::<fbredr::ProfileMarker>())
176                    .capability(Capability::protocol::<fbsys::BootstrapMarker>())
177                    .from(&bt_init)
178                    .to(Ref::parent()),
179            )
180            .await?;
181
182        // Add the `fuchsia.bluetooth.FastPairProvider` capability to the realm with its value
183        // set to false as all Core Realm tests do not require it.
184        builder
185            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
186                name: "fuchsia.bluetooth.FastPairProvider".parse()?,
187                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
188            }))
189            .await?;
190        builder
191            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
192                name: "fuchsia.bluetooth.Rfcomm".parse()?,
193                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(true)),
194            }))
195            .await?;
196        builder
197            .add_route(
198                Route::new()
199                    .capability(Capability::configuration("fuchsia.bluetooth.FastPairProvider"))
200                    .capability(Capability::configuration("fuchsia.bluetooth.Rfcomm"))
201                    .from(Ref::self_())
202                    .to(&bt_init),
203            )
204            .await?;
205
206        let dtr_exposes = vec![Capability::service::<fhbt::ServiceMarker>().into()];
207        let _ = builder.driver_test_realm_add_dtr_exposes(&dtr_exposes).await?;
208
209        builder
210            .add_route(
211                Route::new()
212                    .capability(Capability::service::<fhbt::ServiceMarker>())
213                    .from(Ref::child(fuchsia_driver_test::COMPONENT_NAME))
214                    .to(&bt_init),
215            )
216            .await?;
217
218        crate::host_realm::add_host_routes(&builder, &bt_init).await?;
219        let instance = builder.build().await?;
220
221        // Start DriverTestRealm
222        let args = fdt::RealmArgs {
223            root_driver: Some(EMULATOR_ROOT_DRIVER_URL.to_string()),
224            software_devices: Some(vec![fidl_fuchsia_driver_test::SoftwareDevice {
225                device_name: "bt-hci-emulator".to_string(),
226                device_id: bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_BT_HCI_EMULATOR,
227            }]),
228            test_component: Some(resolved_test_component),
229            dtr_exposes: Some(dtr_exposes),
230            ..Default::default()
231        };
232        instance.driver_test_realm_start(args).await?;
233
234        Ok(Self { realm: instance })
235    }
236
237    pub fn instance(&self) -> &ScopedInstance {
238        &self.realm.root
239    }
240
241    pub fn dev(&self) -> Result<fio::DirectoryProxy, Error> {
242        self.realm.driver_test_realm_connect_to_dev()
243    }
244}