bt_test_harness/
host_realm.rs1use crate::emulator::EMULATOR_ROOT_DRIVER_URL;
6use crate::host_realm::mpsc::Receiver;
7use anyhow::{format_err, Error};
8use fidl::endpoints::ClientEnd;
9use fidl_fuchsia_bluetooth_host::{HostMarker, ReceiverMarker, ReceiverRequestStream};
10use fidl_fuchsia_component::{CreateChildArgs, RealmMarker};
11use fidl_fuchsia_component_decl::{
12 Child, CollectionRef, ConfigOverride, ConfigSingleValue, ConfigValue, Durability, StartupMode,
13};
14use fidl_fuchsia_logger::LogSinkMarker;
15use fuchsia_bluetooth::constants::{
16 BT_HOST, BT_HOST_COLLECTION, BT_HOST_URL, DEV_DIR, HCI_DEVICE_DIR,
17};
18use fuchsia_component::server::ServiceFs;
19use fuchsia_component_test::{
20 Capability, ChildOptions, LocalComponentHandles, RealmBuilder, RealmInstance, Ref, Route,
21 ScopedInstance,
22};
23use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
24use futures::channel::mpsc;
25use futures::{SinkExt, StreamExt};
26use std::sync::{Arc, Mutex};
27use {fidl_fuchsia_driver_test as fdt, fidl_fuchsia_io as fio};
28
29mod constants {
30 pub mod receiver {
31 pub const MONIKER: &str = "receiver";
32 }
33}
34
35pub async fn add_host_routes(
36 builder: &RealmBuilder,
37 to: impl Into<fuchsia_component_test::Ref> + Clone,
38) -> Result<(), Error> {
39 builder
41 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
42 name: "fuchsia.bluetooth.LegacyPairing".parse()?,
43 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
44 }))
45 .await?;
46 builder
47 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
48 name: "fuchsia.bluetooth.ScoOffloadPathIndex".parse()?,
49 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Uint8(6)),
50 }))
51 .await?;
52 builder
53 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
54 name: "fuchsia.power.SuspendEnabled".parse()?,
55 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
56 }))
57 .await?;
58
59 builder
60 .add_route(
61 Route::new()
62 .capability(Capability::configuration("fuchsia.bluetooth.LegacyPairing"))
63 .from(Ref::self_())
64 .to(to.clone()),
65 )
66 .await?;
67 builder
68 .add_route(
69 Route::new()
70 .capability(Capability::configuration("fuchsia.bluetooth.ScoOffloadPathIndex"))
71 .from(Ref::self_())
72 .to(to.clone()),
73 )
74 .await?;
75
76 builder
77 .add_route(
78 Route::new()
79 .capability(Capability::configuration("fuchsia.power.SuspendEnabled"))
80 .from(Ref::self_())
81 .to(to.clone()),
82 )
83 .await?;
84
85 builder
87 .add_route(
88 Route::new()
89 .capability(Capability::directory("dev-class").subdir("bt-hci").as_("dev-bt-hci"))
90 .from(Ref::child(fuchsia_driver_test::COMPONENT_NAME))
91 .to(to),
92 )
93 .await?;
94 Ok(())
95}
96
97pub struct HostRealm {
98 realm: RealmInstance,
99 receiver: Mutex<Option<Receiver<ClientEnd<HostMarker>>>>,
100}
101
102impl HostRealm {
103 pub async fn create(test_component: String) -> Result<Self, Error> {
104 let resolved_test_component = {
107 let client = fuchsia_component::client::connect_to_protocol_at_path::<
108 fidl_fuchsia_component_resolution::ResolverMarker,
109 >("/svc/fuchsia.component.resolution.Resolver-hermetic")
110 .unwrap();
111 client
112 .resolve(test_component.as_str())
113 .await
114 .unwrap()
115 .expect("Failed to resolve test component")
116 };
117
118 let builder = RealmBuilder::new().await?;
119 let _ = builder.driver_test_realm_setup().await?;
120
121 let (sender, receiver) = mpsc::channel(128);
126 let host_receiver = builder
127 .add_local_child(
128 constants::receiver::MONIKER,
129 move |handles| {
130 let sender_clone = sender.clone();
131 Box::pin(Self::fake_receiver_component(sender_clone, handles))
132 },
133 ChildOptions::new().eager(),
134 )
135 .await?;
136
137 let mut realm_decl = builder.get_realm_decl().await?;
139 realm_decl.collections.push(cm_rust::CollectionDecl {
140 name: BT_HOST_COLLECTION.parse().unwrap(),
141 durability: Durability::SingleRun,
142 environment: None,
143 allowed_offers: cm_types::AllowedOffers::StaticOnly,
144 allow_long_names: false,
145 persistent_storage: None,
146 });
147 builder.replace_realm_decl(realm_decl).await.unwrap();
148
149 add_host_routes(&builder, Ref::collection(BT_HOST_COLLECTION.to_string())).await?;
150
151 builder
153 .add_route(
154 Route::new()
155 .capability(Capability::protocol::<LogSinkMarker>())
156 .capability(Capability::dictionary("diagnostics"))
157 .from(Ref::parent())
158 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
159 )
160 .await?;
161 builder
162 .add_route(
163 Route::new()
164 .capability(Capability::protocol::<ReceiverMarker>())
165 .from(&host_receiver)
166 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
167 )
168 .await?;
169 builder
170 .add_route(
171 Route::new()
172 .capability(Capability::protocol::<RealmMarker>())
173 .from(Ref::framework())
174 .to(Ref::parent()),
175 )
176 .await?;
177
178 let instance = builder.build().await?;
179
180 let args = fdt::RealmArgs {
182 root_driver: Some(EMULATOR_ROOT_DRIVER_URL.to_string()),
183 software_devices: Some(vec![fidl_fuchsia_driver_test::SoftwareDevice {
184 device_name: "bt-hci-emulator".to_string(),
185 device_id: bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_BT_HCI_EMULATOR,
186 }]),
187 test_component: Some(resolved_test_component),
188 ..Default::default()
189 };
190 instance.driver_test_realm_start(args).await?;
191
192 Ok(Self { realm: instance, receiver: Some(receiver).into() })
193 }
194
195 pub async fn create_bt_host_in_collection(
199 realm: &Arc<HostRealm>,
200 filename: &str,
201 ) -> Result<ClientEnd<HostMarker>, Error> {
202 let component_name = format!("{BT_HOST}_{filename}"); let device_path = format!("{DEV_DIR}/{HCI_DEVICE_DIR}/{filename}");
204 let collection_ref = CollectionRef { name: BT_HOST_COLLECTION.to_owned() };
205 let child_decl = Child {
206 name: Some(component_name.to_owned()),
207 url: Some(BT_HOST_URL.to_owned()),
208 startup: Some(StartupMode::Lazy),
209 config_overrides: Some(vec![ConfigOverride {
210 key: Some("device_path".to_string()),
211 value: Some(ConfigValue::Single(ConfigSingleValue::String(
212 device_path.to_string(),
213 ))),
214 ..ConfigOverride::default()
215 }]),
216 ..Default::default()
217 };
218
219 let realm_proxy =
220 realm.instance().connect_to_protocol_at_exposed_dir::<RealmMarker>().unwrap();
221 let _ = realm_proxy
222 .create_child(&collection_ref, &child_decl, CreateChildArgs::default())
223 .await
224 .map_err(|e| format_err!("{e:?}"))?
225 .map_err(|e| format_err!("{e:?}"))?;
226
227 let host = realm.receiver().next().await.unwrap();
228 Ok(host)
229 }
230
231 async fn fake_receiver_component(
232 sender: mpsc::Sender<ClientEnd<HostMarker>>,
233 handles: LocalComponentHandles,
234 ) -> Result<(), Error> {
235 let mut fs = ServiceFs::new();
236 let _ = fs.dir("svc").add_fidl_service(move |mut req_stream: ReceiverRequestStream| {
237 let mut sender_clone = sender.clone();
238 fuchsia_async::Task::local(async move {
239 let (host_server, _) =
240 req_stream.next().await.unwrap().unwrap().into_add_host().unwrap();
241 sender_clone.send(host_server).await.expect("Host sent successfully");
242 })
243 .detach()
244 });
245
246 let _ = fs.serve_connection(handles.outgoing_dir)?;
247 fs.collect::<()>().await;
248 Ok(())
249 }
250
251 pub fn instance(&self) -> &ScopedInstance {
252 &self.realm.root
253 }
254
255 pub fn dev(&self) -> Result<fio::DirectoryProxy, Error> {
256 self.realm.driver_test_realm_connect_to_dev()
257 }
258
259 pub fn receiver(&self) -> Receiver<ClientEnd<HostMarker>> {
260 self.receiver.lock().expect("REASON").take().unwrap()
261 }
262}