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