sl4f_lib/factory_store/
facade.rs1use anyhow::Error;
6
7use crate::factory_store::types::{FactoryStoreProvider, ListFilesRequest, ReadFileRequest};
8
9use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
10use base64::engine::Engine as _;
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::{readdir_recursive, DirentKind};
20use futures::stream::TryStreamExt;
21use serde_json::{from_value, to_value, 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 lazy_static::lazy_static;
126 use maplit::hashmap;
127 use serde_json::json;
128 use std::collections::HashMap;
129
130 lazy_static! {
131 static ref GOLDEN_FILE_DATA: HashMap<&'static str, HashMap<&'static str, &'static str>> = hashmap! {
132 "alpha" => hashmap! {
133 "alpha.file" => "alpha info",
134 "alpha/data" => "alpha data"
135 },
136 "cast" => hashmap! {
137 "txt/info.txt" => "cast info.txt",
138 "more.extra" => "extra cast stuff",
139 },
140 "misc" => hashmap! {
141 "info/misc" => "misc.info",
142 "more.misc" => "more misc stuff"
143 },
144 "playready" => hashmap! {
145 "pr/pr/prinfo.dat" => "playready info",
146 "dat.stuff" => "playready stuff"
147 },
148 "weave" => hashmap! {
149 "weave.file" => "weave info",
150 "weave/data" => "weave data"
151 },
152 "widevine" => hashmap! {
153 "stuff.log" => "widevine stuff",
154 "wv/more_stuff" => "more_stuff from widevine",
155 }
156 };
157 }
158
159 #[fasync::run_singlethreaded(test)]
160 async fn list_files_with_no_message_fails() -> Result<(), Error> {
161 let factory_store_facade = FactoryStoreFacade::new();
162 factory_store_facade.list_files(json!("")).await.unwrap_err();
163 Ok(())
164 }
165
166 #[fasync::run_singlethreaded(test)]
167 async fn list_files_with_unknown_provider_fails() -> Result<(), Error> {
168 let factory_store_facade = FactoryStoreFacade::new();
169 factory_store_facade.list_files(json!({ "provider": "unknown" })).await.unwrap_err();
170 Ok(())
171 }
172
173 #[fasync::run_singlethreaded(test)]
174 async fn list_files() -> Result<(), Error> {
175 let factory_store_facade = FactoryStoreFacade::new();
176
177 for (provider, file_map) in GOLDEN_FILE_DATA.iter() {
178 let file_list_json_value =
179 factory_store_facade.list_files(json!({ "provider": provider })).await?;
180
181 let mut file_list: Vec<String> = from_value(file_list_json_value)?;
182 #[allow(suspicious_double_ref_op)] let mut expected_file_list: Vec<&str> =
184 file_map.keys().map(|entry| entry.clone()).collect();
185
186 expected_file_list.sort();
187 file_list.sort();
188 assert_eq!(expected_file_list, file_list);
189 }
190
191 Ok(())
192 }
193
194 #[fasync::run_singlethreaded(test)]
195 async fn read_file_with_unknown_provider_fails() -> Result<(), Error> {
196 let factory_store_facade = FactoryStoreFacade::new();
197 factory_store_facade
198 .read_file(json!({ "provider": "unknown", "filename": "missing_file" }))
199 .await
200 .unwrap_err();
201 Ok(())
202 }
203
204 #[fasync::run_singlethreaded(test)]
205 async fn read_file_with_unknown_file_fails() -> Result<(), Error> {
206 let factory_store_facade = FactoryStoreFacade::new();
207 factory_store_facade
208 .read_file(json!({ "provider": "cast", "filename": "missing_file" }))
209 .await
210 .unwrap_err();
211 Ok(())
212 }
213
214 #[fasync::run_singlethreaded(test)]
215 async fn read_files() -> Result<(), Error> {
216 let factory_store_facade = FactoryStoreFacade::new();
217
218 for (provider, file_map) in GOLDEN_FILE_DATA.iter() {
219 for (filename, expected_contents) in file_map.iter() {
220 let contents_value = factory_store_facade
221 .read_file(json!({ "provider": provider, "filename": filename }))
222 .await?;
223 let contents_base64: String = from_value(contents_value)?;
224 let contents = BASE64_STANDARD.decode(contents_base64.as_bytes())?;
225 assert_eq!(expected_contents.as_bytes(), &contents[..]);
226 }
227 }
228 Ok(())
229 }
230}