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