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_table;
18 pub mod flag_value;
19 pub mod package_table;
20
21 use anyhow::{anyhow, Result};
22 use std::collections::{HashMap, HashSet};
23
24 use crate::storage::{
25 flag_table::create_flag_table, flag_value::create_flag_value,
26 package_table::create_package_table,
27 };
28 use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
29 use aconfig_storage_file::StorageFileType;
30
31 pub struct FlagPackage<'a> {
32 pub package_name: &'a str,
33 pub package_id: u32,
34 pub flag_names: HashSet<&'a str>,
35 pub boolean_flags: Vec<&'a ProtoParsedFlag>,
36 // The index of the first boolean flag in this aconfig package among all boolean
37 // flags in this container.
38 pub boolean_start_index: u32,
39 }
40
41 impl<'a> FlagPackage<'a> {
new(package_name: &'a str, package_id: u32) -> Self42 fn new(package_name: &'a str, package_id: u32) -> Self {
43 FlagPackage {
44 package_name,
45 package_id,
46 flag_names: HashSet::new(),
47 boolean_flags: vec![],
48 boolean_start_index: 0,
49 }
50 }
51
insert(&mut self, pf: &'a ProtoParsedFlag)52 fn insert(&mut self, pf: &'a ProtoParsedFlag) {
53 if self.flag_names.insert(pf.name()) {
54 self.boolean_flags.push(pf);
55 }
56 }
57 }
58
group_flags_by_package<'a, I>(parsed_flags_vec_iter: I) -> Vec<FlagPackage<'a>> where I: Iterator<Item = &'a ProtoParsedFlags>,59 pub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I) -> Vec<FlagPackage<'a>>
60 where
61 I: Iterator<Item = &'a ProtoParsedFlags>,
62 {
63 // group flags by package
64 let mut packages: Vec<FlagPackage<'a>> = Vec::new();
65 let mut package_index: HashMap<&str, usize> = HashMap::new();
66 for parsed_flags in parsed_flags_vec_iter {
67 for parsed_flag in parsed_flags.parsed_flag.iter() {
68 let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len()));
69 if index == packages.len() {
70 packages.push(FlagPackage::new(parsed_flag.package(), index as u32));
71 }
72 packages[index].insert(parsed_flag);
73 }
74 }
75
76 // cacluate boolean flag start index for each package
77 let mut boolean_start_index = 0;
78 for p in packages.iter_mut() {
79 p.boolean_start_index = boolean_start_index;
80 boolean_start_index += p.boolean_flags.len() as u32;
81 }
82
83 packages
84 }
85
generate_storage_file<'a, I>( container: &str, parsed_flags_vec_iter: I, file: &StorageFileType, ) -> Result<Vec<u8>> where I: Iterator<Item = &'a ProtoParsedFlags>,86 pub fn generate_storage_file<'a, I>(
87 container: &str,
88 parsed_flags_vec_iter: I,
89 file: &StorageFileType,
90 ) -> Result<Vec<u8>>
91 where
92 I: Iterator<Item = &'a ProtoParsedFlags>,
93 {
94 let packages = group_flags_by_package(parsed_flags_vec_iter);
95
96 match file {
97 StorageFileType::PackageMap => {
98 let package_table = create_package_table(container, &packages)?;
99 Ok(package_table.into_bytes())
100 }
101 StorageFileType::FlagMap => {
102 let flag_table = create_flag_table(container, &packages)?;
103 Ok(flag_table.into_bytes())
104 }
105 StorageFileType::FlagVal => {
106 let flag_value = create_flag_value(container, &packages)?;
107 Ok(flag_value.into_bytes())
108 }
109 _ => Err(anyhow!("aconfig does not support the creation of this storage file type")),
110 }
111 }
112
113 #[cfg(test)]
114 mod tests {
115 use super::*;
116 use crate::Input;
117
parse_all_test_flags() -> Vec<ProtoParsedFlags>118 pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {
119 let aconfig_files = [
120 (
121 "com.android.aconfig.storage.test_1",
122 "storage_test_1.aconfig",
123 include_bytes!("../../tests/storage_test_1.aconfig").as_slice(),
124 "storage_test_1.value",
125 include_bytes!("../../tests/storage_test_1.values").as_slice(),
126 ),
127 (
128 "com.android.aconfig.storage.test_2",
129 "storage_test_2.aconfig",
130 include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
131 "storage_test_2.value",
132 include_bytes!("../../tests/storage_test_2.values").as_slice(),
133 ),
134 (
135 "com.android.aconfig.storage.test_4",
136 "storage_test_4.aconfig",
137 include_bytes!("../../tests/storage_test_4.aconfig").as_slice(),
138 "storage_test_4.value",
139 include_bytes!("../../tests/storage_test_4.values").as_slice(),
140 ),
141 ];
142 aconfig_files
143 .into_iter()
144 .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| {
145 let bytes = crate::commands::parse_flags(
146 pkg,
147 Some("system"),
148 vec![Input {
149 source: format!("tests/{}", aconfig_file).to_string(),
150 reader: Box::new(aconfig_content),
151 }],
152 vec![Input {
153 source: format!("tests/{}", value_file).to_string(),
154 reader: Box::new(value_content),
155 }],
156 crate::commands::DEFAULT_FLAG_PERMISSION,
157 )
158 .unwrap();
159 aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
160 })
161 .collect()
162 }
163
164 #[test]
test_flag_package()165 fn test_flag_package() {
166 let caches = parse_all_test_flags();
167 let packages = group_flags_by_package(caches.iter());
168
169 for pkg in packages.iter() {
170 let pkg_name = pkg.package_name;
171 assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
172 for pf in pkg.boolean_flags.iter() {
173 assert!(pkg.flag_names.contains(pf.name()));
174 assert_eq!(pf.package(), pkg_name);
175 }
176 }
177
178 assert_eq!(packages.len(), 3);
179
180 assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
181 assert_eq!(packages[0].package_id, 0);
182 assert_eq!(packages[0].flag_names.len(), 3);
183 assert!(packages[0].flag_names.contains("enabled_rw"));
184 assert!(packages[0].flag_names.contains("disabled_rw"));
185 assert!(packages[0].flag_names.contains("enabled_ro"));
186 assert_eq!(packages[0].boolean_start_index, 0);
187
188 assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
189 assert_eq!(packages[1].package_id, 1);
190 assert_eq!(packages[1].flag_names.len(), 3);
191 assert!(packages[1].flag_names.contains("enabled_ro"));
192 assert!(packages[1].flag_names.contains("disabled_rw"));
193 assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
194 assert_eq!(packages[1].boolean_start_index, 3);
195
196 assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
197 assert_eq!(packages[2].package_id, 2);
198 assert_eq!(packages[2].flag_names.len(), 2);
199 assert!(packages[2].flag_names.contains("enabled_rw"));
200 assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
201 assert_eq!(packages[2].boolean_start_index, 6);
202 }
203 }
204