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 struct HostRealm {
36 realm: RealmInstance,
37 receiver: Mutex<Option<Receiver<ClientEnd<HostMarker>>>>,
38}
39
40impl HostRealm {
41 pub async fn create(test_component: String) -> Result<Self, Error> {
42 let resolved_test_component = {
45 let client = fuchsia_component::client::connect_to_protocol_at_path::<
46 fidl_fuchsia_component_resolution::ResolverMarker,
47 >("/svc/fuchsia.component.resolution.Resolver-hermetic")
48 .unwrap();
49 client
50 .resolve(test_component.as_str())
51 .await
52 .unwrap()
53 .expect("Failed to resolve test component")
54 };
55
56 let builder = RealmBuilder::new().await?;
57 let _ = builder.driver_test_realm_setup().await?;
58
59 let (sender, receiver) = mpsc::channel(128);
64 let host_receiver = builder
65 .add_local_child(
66 constants::receiver::MONIKER,
67 move |handles| {
68 let sender_clone = sender.clone();
69 Box::pin(Self::fake_receiver_component(sender_clone, handles))
70 },
71 ChildOptions::new().eager(),
72 )
73 .await?;
74
75 let mut realm_decl = builder.get_realm_decl().await?;
77 realm_decl.collections.push(cm_rust::CollectionDecl {
78 name: BT_HOST_COLLECTION.parse().unwrap(),
79 durability: Durability::SingleRun,
80 environment: None,
81 allowed_offers: cm_types::AllowedOffers::StaticOnly,
82 allow_long_names: false,
83 persistent_storage: None,
84 });
85 builder.replace_realm_decl(realm_decl).await.unwrap();
86
87 builder
89 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
90 name: "fuchsia.bluetooth.LegacyPairing".parse()?,
91 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
92 }))
93 .await?;
94
95 builder
96 .add_route(
97 Route::new()
98 .capability(Capability::configuration("fuchsia.bluetooth.LegacyPairing"))
99 .from(Ref::self_())
100 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
101 )
102 .await?;
103
104 builder
106 .add_route(
107 Route::new()
108 .capability(Capability::protocol::<LogSinkMarker>())
109 .capability(Capability::dictionary("diagnostics"))
110 .from(Ref::parent())
111 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
112 )
113 .await?;
114 builder
115 .add_route(
116 Route::new()
117 .capability(
118 Capability::directory("dev-class").subdir("bt-hci").as_("dev-bt-hci"),
119 )
120 .from(Ref::child(fuchsia_driver_test::COMPONENT_NAME))
121 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
122 )
123 .await?;
124 builder
125 .add_route(
126 Route::new()
127 .capability(Capability::protocol::<ReceiverMarker>())
128 .from(&host_receiver)
129 .to(Ref::collection(BT_HOST_COLLECTION.to_string())),
130 )
131 .await?;
132 builder
133 .add_route(
134 Route::new()
135 .capability(Capability::protocol::<RealmMarker>())
136 .from(Ref::framework())
137 .to(Ref::parent()),
138 )
139 .await?;
140
141 let instance = builder.build().await?;
142
143 let args = fdt::RealmArgs {
145 root_driver: Some(EMULATOR_ROOT_DRIVER_URL.to_string()),
146 software_devices: Some(vec![fidl_fuchsia_driver_test::SoftwareDevice {
147 device_name: "bt-hci-emulator".to_string(),
148 device_id: bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_BT_HCI_EMULATOR,
149 }]),
150 test_component: Some(resolved_test_component),
151 ..Default::default()
152 };
153 instance.driver_test_realm_start(args).await?;
154
155 Ok(Self { realm: instance, receiver: Some(receiver).into() })
156 }
157
158 pub async fn create_bt_host_in_collection(
162 realm: &Arc<HostRealm>,
163 filename: &str,
164 ) -> Result<ClientEnd<HostMarker>, Error> {
165 let component_name = format!("{BT_HOST}_{filename}"); let device_path = format!("{DEV_DIR}/{HCI_DEVICE_DIR}/{filename}");
167 let collection_ref = CollectionRef { name: BT_HOST_COLLECTION.to_owned() };
168 let child_decl = Child {
169 name: Some(component_name.to_owned()),
170 url: Some(BT_HOST_URL.to_owned()),
171 startup: Some(StartupMode::Lazy),
172 config_overrides: Some(vec![ConfigOverride {
173 key: Some("device_path".to_string()),
174 value: Some(ConfigValue::Single(ConfigSingleValue::String(
175 device_path.to_string(),
176 ))),
177 ..ConfigOverride::default()
178 }]),
179 ..Default::default()
180 };
181
182 let realm_proxy =
183 realm.instance().connect_to_protocol_at_exposed_dir::<RealmMarker>().unwrap();
184 let _ = realm_proxy
185 .create_child(&collection_ref, &child_decl, CreateChildArgs::default())
186 .await
187 .map_err(|e| format_err!("{e:?}"))?
188 .map_err(|e| format_err!("{e:?}"))?;
189
190 let host = realm.receiver().next().await.unwrap();
191 Ok(host)
192 }
193
194 async fn fake_receiver_component(
195 sender: mpsc::Sender<ClientEnd<HostMarker>>,
196 handles: LocalComponentHandles,
197 ) -> Result<(), Error> {
198 let mut fs = ServiceFs::new();
199 let _ = fs.dir("svc").add_fidl_service(move |mut req_stream: ReceiverRequestStream| {
200 let mut sender_clone = sender.clone();
201 fuchsia_async::Task::local(async move {
202 let (host_server, _) =
203 req_stream.next().await.unwrap().unwrap().into_add_host().unwrap();
204 sender_clone.send(host_server).await.expect("Host sent successfully");
205 })
206 .detach()
207 });
208
209 let _ = fs.serve_connection(handles.outgoing_dir)?;
210 fs.collect::<()>().await;
211 Ok(())
212 }
213
214 pub fn instance(&self) -> &ScopedInstance {
215 &self.realm.root
216 }
217
218 pub fn dev(&self) -> Result<fio::DirectoryProxy, Error> {
219 self.realm.driver_test_realm_connect_to_dev()
220 }
221
222 pub fn receiver(&self) -> Receiver<ClientEnd<HostMarker>> {
223 self.receiver.lock().expect("REASON").take().unwrap()
224 }
225}