sl4f_lib/factory_store/
facade.rs1use anyhow::Error;
6
7use crate::factory_store::types::{FactoryStoreProvider, ListFilesRequest, ReadFileRequest};
8
9use base64::engine::Engine as _;
10use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
11use fidl::endpoints::create_proxy;
12use fidl_fuchsia_factory::{
13 AlphaFactoryStoreProviderMarker, CastCredentialsFactoryStoreProviderMarker,
14 MiscFactoryStoreProviderMarker, PlayReadyFactoryStoreProviderMarker,
15 WeaveFactoryStoreProviderMarker, WidevineFactoryStoreProviderMarker,
16};
17use fidl_fuchsia_io as fio;
18use fuchsia_component::client::connect_to_protocol;
19use fuchsia_fs::directory::{DirentKind, readdir_recursive};
20use futures::stream::TryStreamExt;
21use serde_json::{Value, from_value, to_value};
22
23#[derive(Debug)]
25pub struct FactoryStoreFacade;
26impl FactoryStoreFacade {
27 pub fn new() -> Self {
28 FactoryStoreFacade {}
29 }
30
31 pub async fn list_files(&self, args: Value) -> Result<Value, Error> {
44 let req: ListFilesRequest = from_value(args)?;
45 let dir_proxy = self.get_directory_for_provider(req.provider)?;
46
47 let mut file_paths = Vec::new();
48 let mut stream = readdir_recursive(&dir_proxy, None);
49 while let Some(entry) = stream.try_next().await? {
50 if entry.kind == DirentKind::File {
51 file_paths.push(entry.name);
52 }
53 }
54
55 Ok(to_value(file_paths)?)
56 }
57
58 pub async fn read_file(&self, args: Value) -> Result<Value, Error> {
74 let req: ReadFileRequest = from_value(args)?;
75 let dir_proxy = self.get_directory_for_provider(req.provider)?;
76 let contents = fuchsia_fs::directory::read_file(&dir_proxy, &req.filename).await?;
77 Ok(to_value(BASE64_STANDARD.encode(&contents))?)
78 }
79
80 fn get_directory_for_provider(
85 &self,
86 provider: FactoryStoreProvider,
87 ) -> Result<fio::DirectoryProxy, Error> {
88 let (dir_proxy, dir_server_end) = create_proxy::<fio::DirectoryMarker>();
89
90 match provider {
91 FactoryStoreProvider::Alpha => {
92 let alpha_svc = connect_to_protocol::<AlphaFactoryStoreProviderMarker>()?;
93 alpha_svc.get_factory_store(dir_server_end)?;
94 }
95 FactoryStoreProvider::Cast => {
96 let cast_svc = connect_to_protocol::<CastCredentialsFactoryStoreProviderMarker>()?;
97 cast_svc.get_factory_store(dir_server_end)?;
98 }
99 FactoryStoreProvider::Misc => {
100 let misc_svc = connect_to_protocol::<MiscFactoryStoreProviderMarker>()?;
101 misc_svc.get_factory_store(dir_server_end)?;
102 }
103 FactoryStoreProvider::Playready => {
104 let playready_svc = connect_to_protocol::<PlayReadyFactoryStoreProviderMarker>()?;
105 playready_svc.get_factory_store(dir_server_end)?;
106 }
107 FactoryStoreProvider::Weave => {
108 let weave_svc = connect_to_protocol::<WeaveFactoryStoreProviderMarker>()?;
109 weave_svc.get_factory_store(dir_server_end)?;
110 }
111 FactoryStoreProvider::Widevine => {
112 let widevine_svc = connect_to_protocol::<WidevineFactoryStoreProviderMarker>()?;
113 widevine_svc.get_factory_store(dir_server_end)?;
114 }
115 }
116
117 Ok(dir_proxy)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use fuchsia_async as fasync;
125 use maplit::hashmap;
126 use serde_json::json;
127 use std::collections::HashMap;
128 use std::sync::LazyLock;
129
130 static GOLDEN_FILE_DATA: LazyLock<HashMap<&'static str, HashMap<&'static str, &'static str>>> =
131 LazyLock::new(|| {
132 hashmap! {
133 "alpha" => hashmap! {
134 "alpha.file" => "alpha info",
135 "alpha/data" => "alpha data"
136 },
137 "cast" => hashmap! {
138 "txt/info.txt" => "cast info.txt",
139 "more.extra" => "extra cast stuff",
140 },
141 "misc" => hashmap! {
142 "info/misc" => "misc.info",
143 "more.misc" => "more misc stuff"
144 },
145 "playready" => hashmap! {
146 "pr/pr/prinfo.dat" => "playready info",
147 "dat.stuff" => "playready stuff"
148 },
149 "weave" => hashmap! {
150 "weave.file" => "weave info",
151 "weave/data" => "weave data"
152 },
153 "widevine" => hashmap! {
154 "stuff.log" => "widevine stuff",
155 "wv/more_stuff" => "more_stuff from widevine",
156 }
157 }
158 });
159
160 #[fasync::run_singlethreaded(test)]
161 async fn list_files_with_no_message_fails() -> Result<(), Error> {
162 let factory_store_facade = FactoryStoreFacade::new();
163 factory_store_facade.list_files(json!("")).await.unwrap_err();
164 Ok(())
165 }
166
167 #[fasync::run_singlethreaded(test)]
168 async fn list_files_with_unknown_provider_fails() -> Result<(), Error> {
169 let factory_store_facade = FactoryStoreFacade::new();
170 factory_store_facade.list_files(json!({ "provider": "unknown" })).await.unwrap_err();
171 Ok(())
172 }
173
174 #[fasync::run_singlethreaded(test)]
175 async fn list_files() -> Result<(), Error> {
176 let factory_store_facade = FactoryStoreFacade::new();
177
178 for (provider, file_map) in GOLDEN_FILE_DATA.iter() {
179 let file_list_json_value =
180 factory_store_facade.list_files(json!({ "provider": provider })).await?;
181
182 let mut file_list: Vec<String> = from_value(file_list_json_value)?;
183 #[allow(suspicious_double_ref_op)] let mut expected_file_list: Vec<&str> =
185 file_map.keys().map(|entry| entry.clone()).collect();
186
187 expected_file_list.sort();
188 file_list.sort();
189 assert_eq!(expected_file_list, file_list);
190 }
191
192 Ok(())
193 }
194
195 #[fasync::run_singlethreaded(test)]
196 async fn read_file_with_unknown_provider_fails() -> Result<(), Error> {
197 let factory_store_facade = FactoryStoreFacade::new();
198 factory_store_facade
199 .read_file(json!({ "provider": "unknown", "filename": "missing_file" }))
200 .await
201 .unwrap_err();
202 Ok(())
203 }
204
205 #[fasync::run_singlethreaded(test)]
206 async fn read_file_with_unknown_file_fails() -> Result<(), Error> {
207 let factory_store_facade = FactoryStoreFacade::new();
208 factory_store_facade
209 .read_file(json!({ "provider": "cast", "filename": "missing_file" }))
210 .await
211 .unwrap_err();
212 Ok(())
213 }
214
215 #[fasync::run_singlethreaded(test)]
216 async fn read_files() -> Result<(), Error> {
217 let factory_store_facade = FactoryStoreFacade::new();
218
219 for (provider, file_map) in GOLDEN_FILE_DATA.iter() {
220 for (filename, expected_contents) in file_map.iter() {
221 let contents_value = factory_store_facade
222 .read_file(json!({ "provider": provider, "filename": filename }))
223 .await?;
224 let contents_base64: String = from_value(contents_value)?;
225 let contents = BASE64_STANDARD.decode(contents_base64.as_bytes())?;
226 assert_eq!(expected_contents.as_bytes(), &contents[..]);
227 }
228 }
229 Ok(())
230 }
231}