• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 pub mod flag_info;
18 pub mod flag_table;
19 pub mod flag_value;
20 pub mod package_table;
21 
22 use anyhow::Result;
23 use std::collections::{HashMap, HashSet};
24 
25 use crate::commands::compute_flags_fingerprint;
26 use crate::storage::{
27     flag_info::create_flag_info, flag_table::create_flag_table, flag_value::create_flag_value,
28     package_table::create_package_table,
29 };
30 use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags};
31 use aconfig_storage_file::StorageFileType;
32 
33 #[derive(Clone)]
34 pub struct FlagPackage<'a> {
35     pub package_name: &'a str,
36     pub package_id: u32,
37     pub fingerprint: u64,
38     pub flag_names: HashSet<&'a str>,
39     pub boolean_flags: Vec<&'a ProtoParsedFlag>,
40     // The index of the first boolean flag in this aconfig package among all boolean
41     // flags in this container.
42     pub boolean_start_index: u32,
43 }
44 
45 impl<'a> FlagPackage<'a> {
new(package_name: &'a str, package_id: u32) -> Self46     fn new(package_name: &'a str, package_id: u32) -> Self {
47         FlagPackage {
48             package_name,
49             package_id,
50             fingerprint: 0,
51             flag_names: HashSet::new(),
52             boolean_flags: vec![],
53             boolean_start_index: 0,
54         }
55     }
56 
insert(&mut self, pf: &'a ProtoParsedFlag)57     fn insert(&mut self, pf: &'a ProtoParsedFlag) {
58         if self.flag_names.insert(pf.name()) {
59             self.boolean_flags.push(pf);
60         }
61     }
62 }
63 
group_flags_by_package<'a, I>(parsed_flags_vec_iter: I, version: u32) -> Vec<FlagPackage<'a>> where I: Iterator<Item = &'a ProtoParsedFlags>,64 pub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I, version: u32) -> Vec<FlagPackage<'a>>
65 where
66     I: Iterator<Item = &'a ProtoParsedFlags>,
67 {
68     // group flags by package
69     let mut packages: Vec<FlagPackage<'a>> = Vec::new();
70     let mut package_index: HashMap<&str, usize> = HashMap::new();
71     for parsed_flags in parsed_flags_vec_iter {
72         for parsed_flag in parsed_flags.parsed_flag.iter() {
73             let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len()));
74             if index == packages.len() {
75                 packages.push(FlagPackage::new(parsed_flag.package(), index as u32));
76             }
77 
78             // Exclude system/vendor/product flags that are RO+disabled.
79             if (parsed_flag.container == Some("system".to_string())
80                 || parsed_flag.container == Some("vendor".to_string())
81                 || parsed_flag.container == Some("product".to_string()))
82                 && parsed_flag.permission == Some(ProtoFlagPermission::READ_ONLY.into())
83                 && parsed_flag.state == Some(ProtoFlagState::DISABLED.into())
84             {
85                 continue;
86             }
87 
88             packages[index].insert(parsed_flag);
89         }
90     }
91 
92     // Calculate boolean flag start index for each package
93     let mut boolean_start_index = 0;
94     for p in packages.iter_mut() {
95         p.boolean_start_index = boolean_start_index;
96         boolean_start_index += p.boolean_flags.len() as u32;
97 
98         if version >= 2 {
99             let mut flag_names_vec =
100                 p.flag_names.clone().into_iter().map(String::from).collect::<Vec<_>>();
101             let fingerprint = compute_flags_fingerprint(&mut flag_names_vec);
102             p.fingerprint = fingerprint;
103         }
104     }
105 
106     packages
107 }
108 
generate_storage_file<'a, I>( container: &str, parsed_flags_vec_iter: I, file: &StorageFileType, version: u32, ) -> Result<Vec<u8>> where I: Iterator<Item = &'a ProtoParsedFlags>,109 pub fn generate_storage_file<'a, I>(
110     container: &str,
111     parsed_flags_vec_iter: I,
112     file: &StorageFileType,
113     version: u32,
114 ) -> Result<Vec<u8>>
115 where
116     I: Iterator<Item = &'a ProtoParsedFlags>,
117 {
118     let packages = group_flags_by_package(parsed_flags_vec_iter, version);
119 
120     match file {
121         StorageFileType::PackageMap => {
122             let package_table = create_package_table(container, &packages, version)?;
123             Ok(package_table.into_bytes())
124         }
125         StorageFileType::FlagMap => {
126             let flag_table = create_flag_table(container, &packages, version)?;
127             Ok(flag_table.into_bytes())
128         }
129         StorageFileType::FlagVal => {
130             let flag_value = create_flag_value(container, &packages, version)?;
131             Ok(flag_value.into_bytes())
132         }
133         StorageFileType::FlagInfo => {
134             let flag_info = create_flag_info(container, &packages, version)?;
135             Ok(flag_info.into_bytes())
136         }
137     }
138 }
139 
140 #[cfg(test)]
141 mod tests {
142     use aconfig_storage_file::DEFAULT_FILE_VERSION;
143 
144     use super::*;
145     use crate::Input;
146 
parse_all_test_flags() -> Vec<ProtoParsedFlags>147     pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {
148         let aconfig_files = [
149             (
150                 "com.android.aconfig.storage.test_1",
151                 "storage_test_1.aconfig",
152                 include_bytes!("../../tests/storage_test_1.aconfig").as_slice(),
153                 "storage_test_1.value",
154                 include_bytes!("../../tests/storage_test_1.values").as_slice(),
155             ),
156             (
157                 "com.android.aconfig.storage.test_2",
158                 "storage_test_2.aconfig",
159                 include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
160                 "storage_test_2.value",
161                 include_bytes!("../../tests/storage_test_2.values").as_slice(),
162             ),
163             (
164                 "com.android.aconfig.storage.test_4",
165                 "storage_test_4.aconfig",
166                 include_bytes!("../../tests/storage_test_4.aconfig").as_slice(),
167                 "storage_test_4.value",
168                 include_bytes!("../../tests/storage_test_4.values").as_slice(),
169             ),
170         ];
171         aconfig_files
172             .into_iter()
173             .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| {
174                 let bytes = crate::commands::parse_flags(
175                     pkg,
176                     Some("system"),
177                     vec![Input {
178                         source: format!("tests/{}", aconfig_file).to_string(),
179                         reader: Box::new(aconfig_content),
180                     }],
181                     vec![Input {
182                         source: format!("tests/{}", value_file).to_string(),
183                         reader: Box::new(value_content),
184                     }],
185                     crate::commands::DEFAULT_FLAG_PERMISSION,
186                     true,
187                 )
188                 .unwrap();
189                 aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
190             })
191             .collect()
192     }
193 
194     #[test]
test_flag_package()195     fn test_flag_package() {
196         let caches = parse_all_test_flags();
197         let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);
198 
199         for pkg in packages.iter() {
200             let pkg_name = pkg.package_name;
201             assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
202             for pf in pkg.boolean_flags.iter() {
203                 assert!(pkg.flag_names.contains(pf.name()));
204                 assert_eq!(pf.package(), pkg_name);
205             }
206         }
207 
208         assert_eq!(packages.len(), 3);
209 
210         assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
211         assert_eq!(packages[0].package_id, 0);
212         assert_eq!(packages[0].flag_names.len(), 3);
213         assert!(packages[0].flag_names.contains("enabled_rw"));
214         assert!(packages[0].flag_names.contains("disabled_rw"));
215         assert!(packages[0].flag_names.contains("enabled_ro"));
216         assert_eq!(packages[0].boolean_start_index, 0);
217         assert_eq!(packages[0].fingerprint, 0);
218 
219         assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
220         assert_eq!(packages[1].package_id, 1);
221         assert_eq!(packages[1].flag_names.len(), 3);
222         assert!(packages[1].flag_names.contains("enabled_ro"));
223         assert!(packages[1].flag_names.contains("disabled_rw"));
224         assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
225         assert_eq!(packages[1].boolean_start_index, 3);
226         assert_eq!(packages[0].fingerprint, 0);
227 
228         assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
229         assert_eq!(packages[2].package_id, 2);
230         assert_eq!(packages[2].flag_names.len(), 2);
231         assert!(packages[2].flag_names.contains("enabled_rw"));
232         assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
233         assert_eq!(packages[2].boolean_start_index, 6);
234         assert_eq!(packages[2].fingerprint, 0);
235     }
236 
237     #[test]
test_flag_package_with_fingerprint()238     fn test_flag_package_with_fingerprint() {
239         let caches = parse_all_test_flags();
240         let packages = group_flags_by_package(caches.iter(), 2);
241 
242         for pkg in packages.iter() {
243             let pkg_name = pkg.package_name;
244             assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
245             for pf in pkg.boolean_flags.iter() {
246                 assert!(pkg.flag_names.contains(pf.name()));
247                 assert_eq!(pf.package(), pkg_name);
248             }
249         }
250 
251         assert_eq!(packages.len(), 3);
252 
253         assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
254         assert_eq!(packages[0].package_id, 0);
255         assert_eq!(packages[0].flag_names.len(), 3);
256         assert!(packages[0].flag_names.contains("enabled_rw"));
257         assert!(packages[0].flag_names.contains("disabled_rw"));
258         assert!(packages[0].flag_names.contains("enabled_ro"));
259         assert_eq!(packages[0].boolean_start_index, 0);
260         assert_eq!(packages[0].fingerprint, 15248948510590158086u64);
261 
262         assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
263         assert_eq!(packages[1].package_id, 1);
264         assert_eq!(packages[1].flag_names.len(), 3);
265         assert!(packages[1].flag_names.contains("enabled_ro"));
266         assert!(packages[1].flag_names.contains("disabled_rw"));
267         assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
268         assert_eq!(packages[1].boolean_start_index, 3);
269         assert_eq!(packages[1].fingerprint, 4431940502274857964u64);
270 
271         assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
272         assert_eq!(packages[2].package_id, 2);
273         assert_eq!(packages[2].flag_names.len(), 2);
274         assert!(packages[2].flag_names.contains("enabled_rw"));
275         assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
276         assert_eq!(packages[2].boolean_start_index, 6);
277         assert_eq!(packages[2].fingerprint, 16233229917711622375u64);
278     }
279 }
280