1mod serde;
6
7use fdf_component::ServiceOffer;
8use fidl_fuchsia_driver_framework as fdf;
9use fidl_next::{Responder, ServerEnd};
10use fidl_next_fuchsia_driver_metadata as fmetadata;
11use fidl_next_fuchsia_hardware_platform_device as fdevice_next;
12use fuchsia_async as fasync;
13use fuchsia_component::directory::Directory;
14use fuchsia_component::server::{ServiceFs, ServiceObjTrait};
15use std::sync::Arc;
16
17pub use serde::*;
18
19pub struct MetadataServer {
20 data: Option<Arc<Vec<u8>>>,
21 name: String,
22}
23
24impl MetadataServer {
25 pub fn new(name: impl Into<String>) -> Self {
26 Self { data: None, name: name.into() }
27 }
28
29 pub fn with_metadata<T: fidl::Persistable>(self, metadata: &T) -> Result<Self, zx::Status> {
30 let data = fidl::persist(metadata).map_err(|_| zx::Status::INTERNAL)?;
31 Ok(Self { data: Some(Arc::new(data)), name: self.name })
32 }
33
34 pub async fn forward_from_pdev(
35 self,
36 pdev: &fidl_fuchsia_hardware_platform_device::DeviceProxy,
37 ) -> Result<Self, zx::Status> {
38 let data = pdev
39 .get_metadata(&self.name)
40 .await
41 .map_err(|_| zx::Status::INTERNAL)?
42 .map_err(zx::Status::err_from_raw)?;
43 Ok(Self { data: Some(Arc::new(data)), name: self.name })
44 }
45
46 pub async fn forward_from_pdev_next(
47 self,
48 pdev: &fidl_next::Client<fdevice_next::Device>,
49 ) -> Result<Self, zx::Status> {
50 let data = pdev.get_metadata(&self.name).await.map_err(|_| zx::Status::INTERNAL)??.metadata;
51 Ok(Self { data: Some(Arc::new(data)), name: self.name })
52 }
53
54 pub async fn forward_from_incoming(
55 self,
56 incoming: &fdf_component::Incoming,
57 ) -> Result<Self, zx::Status> {
58 self.forward_from_incoming_instance(incoming, fuchsia_component::DEFAULT_SERVICE_INSTANCE)
59 .await
60 }
61
62 pub async fn forward_from_incoming_instance(
63 self,
64 incoming: &fdf_component::Incoming,
65 instance_name: &str,
66 ) -> Result<Self, zx::Status> {
67 let path = format!("/svc/{}/{}/metadata", self.name, instance_name);
68 let (client_end, server_end) = fidl_next::fuchsia::create_channel::<fmetadata::Metadata>();
69 incoming
70 .open(&path, fidl_fuchsia_io::Flags::PROTOCOL_SERVICE, server_end.into_untyped())
71 .map_err(|_| zx::Status::NOT_FOUND)?;
72 let client = client_end.spawn();
73 let data = match client.get_persisted_metadata().await {
74 Ok(Ok(resp)) => resp.persisted_metadata,
75 Ok(Err(status)) => return Err(status),
76 Err(
77 fidl_next::Error::Protocol(fidl_next::ProtocolError::PeerClosed)
78 | fidl_next::Error::Protocol(fidl_next::ProtocolError::PeerClosedWithEpitaph(_)),
79 ) => {
80 return Err(zx::Status::NOT_FOUND);
81 }
82 Err(_) => return Err(zx::Status::INTERNAL),
83 };
84 Ok(Self { data: Some(Arc::new(data)), name: self.name })
85 }
86
87 pub fn create_handler(&self, scope: fasync::ScopeHandle) -> Option<MetadataHandler> {
88 let data = self.data.clone()?;
89 Some(MetadataHandler { data, scope })
90 }
91
92 pub fn create_service_offer(&self) -> Option<ServiceOffer<fmetadata::Service>> {
93 Some(ServiceOffer::<fmetadata::Service>::new_next())
94 }
95
96 pub fn serve<O>(
97 &self,
98 fs: &mut ServiceFs<O>,
99 scope: fasync::ScopeHandle,
100 instance_name: impl Into<String>,
101 ) -> Option<fdf::Offer>
102 where
103 O: ServiceObjTrait,
104 {
105 let data = self.data.clone()?;
106 let handler = MetadataHandler { data, scope };
107
108 let offer = ServiceOffer::<fmetadata::Service>::new_with_name(self.name.clone())
109 .add_default_named_next(fs, instance_name, handler)
110 .build_zircon_offer_next();
111
112 Some(offer)
113 }
114
115 pub fn serve_on<O>(
122 &self,
123 fs: &mut ServiceFs<O>,
124 dispatcher: impl libasync::GetAsyncDispatcher,
125 instance_name: impl Into<String>,
126 ) -> Option<fdf::Offer>
127 where
128 O: ServiceObjTrait,
129 {
130 let data = self.data.clone()?;
131 let handler =
132 MetadataDispatcherHandler { data, dispatcher: dispatcher.get_async_dispatcher() };
133
134 let offer = ServiceOffer::<fmetadata::Service>::new_with_name(self.name.clone())
135 .add_default_named_next(fs, instance_name, handler)
136 .build_zircon_offer_next();
137
138 Some(offer)
139 }
140}
141
142pub struct MetadataHandler {
143 data: Arc<Vec<u8>>,
144 scope: fasync::ScopeHandle,
145}
146
147impl fmetadata::ServiceHandler for MetadataHandler {
148 fn metadata(&self, server_end: ServerEnd<fmetadata::Metadata>) {
149 let data = self.data.clone();
150 self.scope.spawn_local(async move {
151 let dispatcher = fidl_next::ServerDispatcher::new(server_end);
152 let _ = dispatcher.run_local(MetadataServerImpl { data }).await;
153 });
154 }
155}
156
157struct MetadataDispatcherHandler {
158 data: Arc<Vec<u8>>,
159 dispatcher: libasync::AsyncDispatcher,
160}
161
162impl fmetadata::ServiceHandler for MetadataDispatcherHandler {
163 fn metadata(&self, server_end: ServerEnd<fmetadata::Metadata>) {
164 let data = self.data.clone();
165 libasync_fidl::AsyncChannel::server_from_zx_channel_on_dispatcher(
166 server_end,
167 self.dispatcher.clone(),
168 )
169 .spawn(MetadataServerImpl { data });
170 }
171}
172
173struct MetadataServerImpl {
174 data: Arc<Vec<u8>>,
175}
176
177impl<T: fidl_next::Transport> fmetadata::MetadataServerHandler<T> for MetadataServerImpl {
178 async fn get_persisted_metadata(
179 &mut self,
180 responder: Responder<fmetadata::metadata::GetPersistedMetadata, T>,
181 ) {
182 let _ = responder.respond(self.data.as_slice()).await;
183 }
184}
185
186impl fmetadata::MetadataLocalServerHandler for MetadataServerImpl {
187 async fn get_persisted_metadata(
188 &mut self,
189 responder: Responder<fmetadata::metadata::GetPersistedMetadata>,
190 ) {
191 let _ = responder.respond(self.data.as_slice()).await;
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use fidl_fuchsia_driver_metadata::MetadataMarker;
199 use fidl_fuchsia_io as fio;
200 use futures::StreamExt;
201 use libasync::AsAsyncDispatcherRef;
202 use libasync_scope_dispatcher::ScopeDispatcher;
203
204 #[fuchsia::test]
205 async fn forward_from_incoming_serves_metadata() {
206 let scope = fasync::Scope::new();
207 let mut parent_fs = ServiceFs::new();
208 let expected = fidl_fuchsia_driver_metadata::Dictionary {
209 entries: Some(vec![fidl_fuchsia_driver_metadata::DictionaryEntry {
210 key: "key".to_string(),
211 value: fidl_fuchsia_driver_metadata::DictionaryValue::Int64(123),
212 }]),
213 ..Default::default()
214 };
215
216 let parent_server = MetadataServer::new("test.Metadata")
217 .with_metadata(&expected)
218 .expect("with_metadata failed");
219 parent_server
220 .serve(&mut parent_fs, scope.to_handle(), fuchsia_component::DEFAULT_SERVICE_INSTANCE)
221 .expect("serve failed");
222
223 let (client, server) =
224 fidl::endpoints::create_endpoints::<fidl_fuchsia_io::DirectoryMarker>();
225 parent_fs.serve_connection(server).expect("serve_connection failed");
226 scope.spawn_local(parent_fs.collect());
227
228 let incoming = fdf_component::Incoming::from(client);
229 let forwarded = MetadataServer::new("test.Metadata")
230 .forward_from_incoming(&incoming)
231 .await
232 .expect("forward_from_incoming failed");
233
234 let mut child_fs = ServiceFs::new();
235 forwarded.serve(&mut child_fs, scope.to_handle(), "child").expect("child serve failed");
236 let (child_client, child_server) =
237 fidl::endpoints::create_endpoints::<fidl_fuchsia_io::DirectoryMarker>();
238 child_fs.serve_connection(child_server).expect("child serve_connection failed");
239 scope.spawn_local(child_fs.collect());
240
241 let child_incoming = fdf_component::Incoming::from(child_client);
242 let (client_end, server_end) = fidl_next::fuchsia::create_channel::<fmetadata::Metadata>();
243 child_incoming
244 .open(
245 "/svc/test.Metadata/child/metadata",
246 fidl_fuchsia_io::Flags::PROTOCOL_SERVICE,
247 server_end.into_untyped(),
248 )
249 .expect("open failed");
250 let client = client_end.spawn();
251 let persisted = client
252 .get_persisted_metadata()
253 .await
254 .expect("FIDL call failed")
255 .expect("GetPersistedMetadata failed")
256 .persisted_metadata;
257 let decoded: fidl_fuchsia_driver_metadata::Dictionary =
258 fidl::unpersist(&persisted).expect("unpersist failed");
259 assert_eq!(decoded, expected);
260 }
261
262 #[fuchsia::test]
263 async fn forward_from_incoming_missing_returns_not_found() {
264 let scope = fasync::Scope::new();
265 let mut parent_fs =
266 ServiceFs::<fuchsia_component::server::ServiceObjLocal<'_, ()>>::new_local();
267 let (client, server) =
268 fidl::endpoints::create_endpoints::<fidl_fuchsia_io::DirectoryMarker>();
269 parent_fs.serve_connection(server).expect("serve_connection failed");
270 scope.spawn_local(parent_fs.collect());
271
272 let incoming = fdf_component::Incoming::from(client);
273 let result =
274 MetadataServer::new("test.MissingMetadata").forward_from_incoming(&incoming).await;
275 assert_eq!(result.err(), Some(zx::Status::NOT_FOUND));
276 }
277
278 #[fuchsia::test]
279 async fn serve_on_borrowed_dispatcher() {
280 let dispatcher = ScopeDispatcher::new();
281 let server = MetadataServer { data: Some(Arc::new(vec![1, 2, 3])), name: "test".into() };
282 let mut fs = ServiceFs::new();
283 assert!(
285 server.serve_on(&mut fs, dispatcher.as_async_dispatcher_ref(), "default").is_some()
286 );
287
288 let (dir, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
289 fs.serve_connection(dir_server).unwrap();
290 let _fs = fasync::Task::local(fs.collect::<()>());
291
292 let metadata = fuchsia_component::client::connect_to_named_protocol_at_dir_root::<
293 MetadataMarker,
294 >(&dir, "svc/test/default/metadata")
295 .unwrap();
296 assert_eq!(metadata.get_persisted_metadata().await.unwrap(), Ok(vec![1, 2, 3]));
297
298 dispatcher.shutdown().await;
299 }
300}