• 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 use anyhow::{bail, ensure, Context, Result};
18 use convert_finalized_flags::FinalizedFlagMap;
19 use itertools::Itertools;
20 use protobuf::Message;
21 use std::collections::HashMap;
22 use std::hash::Hasher;
23 use std::io::Read;
24 use std::path::PathBuf;
25 
26 use crate::codegen::cpp::generate_cpp_code;
27 use crate::codegen::java::{generate_java_code, JavaCodegenConfig};
28 use crate::codegen::rust::generate_rust_code;
29 use crate::codegen::CodegenMode;
30 use crate::dump::{DumpFormat, DumpPredicate};
31 use crate::storage::generate_storage_file;
32 use aconfig_protos::{
33     ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
34     ProtoParsedFlags, ProtoTracepoint,
35 };
36 use aconfig_storage_file::sip_hasher13::SipHasher13;
37 use aconfig_storage_file::StorageFileType;
38 
39 pub struct Input {
40     pub source: String,
41     pub reader: Box<dyn Read>,
42 }
43 
44 impl Input {
try_parse_flags(&mut self) -> Result<ProtoParsedFlags>45     fn try_parse_flags(&mut self) -> Result<ProtoParsedFlags> {
46         let mut buffer = Vec::new();
47         self.reader
48             .read_to_end(&mut buffer)
49             .with_context(|| format!("failed to read {}", self.source))?;
50         aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
51             .with_context(|| self.error_context())
52     }
53 
error_context(&self) -> String54     fn error_context(&self) -> String {
55         format!("failed to parse {}", self.source)
56     }
57 }
58 
59 pub struct OutputFile {
60     pub path: PathBuf, // relative to some root directory only main knows about
61     pub contents: Vec<u8>,
62 }
63 
64 pub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
65 pub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;
66 
parse_flags( package: &str, container: Option<&str>, declarations: Vec<Input>, values: Vec<Input>, default_permission: ProtoFlagPermission, allow_read_write: bool, ) -> Result<Vec<u8>>67 pub fn parse_flags(
68     package: &str,
69     container: Option<&str>,
70     declarations: Vec<Input>,
71     values: Vec<Input>,
72     default_permission: ProtoFlagPermission,
73     allow_read_write: bool,
74 ) -> Result<Vec<u8>> {
75     let mut parsed_flags = ProtoParsedFlags::new();
76 
77     for mut input in declarations {
78         let mut contents = String::new();
79         input
80             .reader
81             .read_to_string(&mut contents)
82             .with_context(|| format!("failed to read {}", input.source))?;
83 
84         let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents)
85             .with_context(|| input.error_context())?;
86         ensure!(
87             package == flag_declarations.package(),
88             "failed to parse {}: expected package {}, got {}",
89             input.source,
90             package,
91             flag_declarations.package()
92         );
93         if let Some(c) = container {
94             ensure!(
95                 c == flag_declarations.container(),
96                 "failed to parse {}: expected container {}, got {}",
97                 input.source,
98                 c,
99                 flag_declarations.container()
100             );
101         }
102         for mut flag_declaration in flag_declarations.flag.into_iter() {
103             aconfig_protos::flag_declaration::verify_fields(&flag_declaration)
104                 .with_context(|| input.error_context())?;
105 
106             // create ParsedFlag using FlagDeclaration and default values
107             let mut parsed_flag = ProtoParsedFlag::new();
108             if let Some(c) = container {
109                 parsed_flag.set_container(c.to_string());
110             }
111             parsed_flag.set_package(package.to_string());
112             parsed_flag.set_name(flag_declaration.take_name());
113             parsed_flag.set_namespace(flag_declaration.take_namespace());
114             parsed_flag.set_description(flag_declaration.take_description());
115             parsed_flag.bug.append(&mut flag_declaration.bug);
116             parsed_flag.set_state(DEFAULT_FLAG_STATE);
117             let flag_permission = if flag_declaration.is_fixed_read_only() {
118                 ProtoFlagPermission::READ_ONLY
119             } else {
120                 default_permission
121             };
122             parsed_flag.set_permission(flag_permission);
123             parsed_flag.set_is_fixed_read_only(flag_declaration.is_fixed_read_only());
124             parsed_flag.set_is_exported(flag_declaration.is_exported());
125             let mut tracepoint = ProtoTracepoint::new();
126             tracepoint.set_source(input.source.clone());
127             tracepoint.set_state(DEFAULT_FLAG_STATE);
128             tracepoint.set_permission(flag_permission);
129             parsed_flag.trace.push(tracepoint);
130 
131             let mut metadata = ProtoFlagMetadata::new();
132             let purpose = flag_declaration.metadata.purpose();
133             metadata.set_purpose(purpose);
134             parsed_flag.metadata = Some(metadata).into();
135 
136             // verify ParsedFlag looks reasonable
137             aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;
138 
139             // verify ParsedFlag can be added
140             ensure!(
141                 parsed_flags.parsed_flag.iter().all(|other| other.name() != parsed_flag.name()),
142                 "failed to declare flag {} from {}: flag already declared",
143                 parsed_flag.name(),
144                 input.source
145             );
146 
147             // add ParsedFlag to ParsedFlags
148             parsed_flags.parsed_flag.push(parsed_flag);
149         }
150     }
151 
152     for mut input in values {
153         let mut contents = String::new();
154         input
155             .reader
156             .read_to_string(&mut contents)
157             .with_context(|| format!("failed to read {}", input.source))?;
158         let flag_values = aconfig_protos::flag_values::try_from_text_proto(&contents)
159             .with_context(|| input.error_context())?;
160         for flag_value in flag_values.flag_value.into_iter() {
161             aconfig_protos::flag_value::verify_fields(&flag_value)
162                 .with_context(|| input.error_context())?;
163 
164             let Some(parsed_flag) = parsed_flags
165                 .parsed_flag
166                 .iter_mut()
167                 .find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name())
168             else {
169                 // (silently) skip unknown flags
170                 continue;
171             };
172 
173             ensure!(
174                 !parsed_flag.is_fixed_read_only()
175                     || flag_value.permission() == ProtoFlagPermission::READ_ONLY,
176                 "failed to set permission of flag {}, since this flag is fixed read only flag",
177                 flag_value.name()
178             );
179 
180             parsed_flag.set_state(flag_value.state());
181             parsed_flag.set_permission(flag_value.permission());
182             let mut tracepoint = ProtoTracepoint::new();
183             tracepoint.set_source(input.source.clone());
184             tracepoint.set_state(flag_value.state());
185             tracepoint.set_permission(flag_value.permission());
186             parsed_flag.trace.push(tracepoint);
187         }
188     }
189 
190     if !allow_read_write {
191         if let Some(pf) = parsed_flags
192             .parsed_flag
193             .iter()
194             .find(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)
195         {
196             bail!("flag {} has permission READ_WRITE, but allow_read_write is false", pf.name());
197         }
198     }
199 
200     // Create a sorted parsed_flags
201     aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
202     aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?;
203     let mut output = Vec::new();
204     parsed_flags.write_to_vec(&mut output)?;
205     Ok(output)
206 }
207 
create_java_lib( mut input: Input, codegen_mode: CodegenMode, allow_instrumentation: bool, new_exported: bool, single_exported_file: bool, finalized_flags: FinalizedFlagMap, ) -> Result<Vec<OutputFile>>208 pub fn create_java_lib(
209     mut input: Input,
210     codegen_mode: CodegenMode,
211     allow_instrumentation: bool,
212     new_exported: bool,
213     single_exported_file: bool,
214     finalized_flags: FinalizedFlagMap,
215 ) -> Result<Vec<OutputFile>> {
216     let parsed_flags = input.try_parse_flags()?;
217     let modified_parsed_flags =
218         modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?;
219     let Some(package) = find_unique_package(&modified_parsed_flags) else {
220         bail!("no parsed flags, or the parsed flags use different packages");
221     };
222     let package = package.to_string();
223     let mut flag_names = extract_flag_names(parsed_flags)?;
224     let package_fingerprint = compute_flags_fingerprint(&mut flag_names);
225     let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
226     let config = JavaCodegenConfig {
227         codegen_mode,
228         flag_ids,
229         allow_instrumentation,
230         package_fingerprint,
231         new_exported,
232         single_exported_file,
233         finalized_flags,
234     };
235     generate_java_code(&package, modified_parsed_flags.into_iter(), config)
236 }
237 
create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>>238 pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
239     // TODO(327420679): Enable export mode for native flag library
240     ensure!(
241         codegen_mode != CodegenMode::Exported,
242         "Exported mode for generated c/c++ flag library is disabled"
243     );
244     let parsed_flags = input.try_parse_flags()?;
245     let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
246     let Some(package) = find_unique_package(&modified_parsed_flags) else {
247         bail!("no parsed flags, or the parsed flags use different packages");
248     };
249     let package = package.to_string();
250     let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
251     generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode, flag_ids)
252 }
253 
create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile>254 pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
255     // // TODO(327420679): Enable export mode for native flag library
256     ensure!(
257         codegen_mode != CodegenMode::Exported,
258         "Exported mode for generated rust flag library is disabled"
259     );
260     let parsed_flags = input.try_parse_flags()?;
261     let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
262     let Some(package) = find_unique_package(&modified_parsed_flags) else {
263         bail!("no parsed flags, or the parsed flags use different packages");
264     };
265     let package = package.to_string();
266     let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
267     generate_rust_code(&package, flag_ids, modified_parsed_flags.into_iter(), codegen_mode)
268 }
269 
create_storage( caches: Vec<Input>, container: &str, file: &StorageFileType, version: u32, ) -> Result<Vec<u8>>270 pub fn create_storage(
271     caches: Vec<Input>,
272     container: &str,
273     file: &StorageFileType,
274     version: u32,
275 ) -> Result<Vec<u8>> {
276     let parsed_flags_vec: Vec<ProtoParsedFlags> =
277         caches.into_iter().map(|mut input| input.try_parse_flags()).collect::<Result<Vec<_>>>()?;
278     generate_storage_file(container, parsed_flags_vec.iter(), file, version)
279 }
280 
create_device_config_defaults(mut input: Input) -> Result<Vec<u8>>281 pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
282     let parsed_flags = input.try_parse_flags()?;
283     let mut output = Vec::new();
284     for parsed_flag in parsed_flags
285         .parsed_flag
286         .into_iter()
287         .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)
288     {
289         let line = format!(
290             "{}:{}={}\n",
291             parsed_flag.namespace(),
292             parsed_flag.fully_qualified_name(),
293             match parsed_flag.state() {
294                 ProtoFlagState::ENABLED => "enabled",
295                 ProtoFlagState::DISABLED => "disabled",
296             }
297         );
298         output.extend_from_slice(line.as_bytes());
299     }
300     Ok(output)
301 }
302 
create_device_config_sysprops(mut input: Input) -> Result<Vec<u8>>303 pub fn create_device_config_sysprops(mut input: Input) -> Result<Vec<u8>> {
304     let parsed_flags = input.try_parse_flags()?;
305     let mut output = Vec::new();
306     for parsed_flag in parsed_flags
307         .parsed_flag
308         .into_iter()
309         .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)
310     {
311         let line = format!(
312             "persist.device_config.{}={}\n",
313             parsed_flag.fully_qualified_name(),
314             match parsed_flag.state() {
315                 ProtoFlagState::ENABLED => "true",
316                 ProtoFlagState::DISABLED => "false",
317             }
318         );
319         output.extend_from_slice(line.as_bytes());
320     }
321     Ok(output)
322 }
323 
dump_parsed_flags( mut input: Vec<Input>, format: DumpFormat, filters: &[&str], dedup: bool, ) -> Result<Vec<u8>>324 pub fn dump_parsed_flags(
325     mut input: Vec<Input>,
326     format: DumpFormat,
327     filters: &[&str],
328     dedup: bool,
329 ) -> Result<Vec<u8>> {
330     let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =
331         input.iter_mut().map(|i| i.try_parse_flags()).collect();
332     let parsed_flags: ProtoParsedFlags =
333         aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
334     let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {
335         vec![Box::new(|_| true)]
336     } else {
337         filters
338             .iter()
339             .map(|f| crate::dump::create_filter_predicate(f))
340             .collect::<Result<Vec<_>>>()?
341     };
342     crate::dump::dump_parsed_flags(
343         parsed_flags.parsed_flag.into_iter().filter(|flag| filters.iter().any(|p| p(flag))),
344         format,
345     )
346 }
347 
find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str>348 fn find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str> {
349     let package = parsed_flags.first().map(|pf| pf.package())?;
350     if parsed_flags.iter().any(|pf| pf.package() != package) {
351         return None;
352     }
353     Some(package)
354 }
355 
modify_parsed_flags_based_on_mode( parsed_flags: ProtoParsedFlags, codegen_mode: CodegenMode, ) -> Result<Vec<ProtoParsedFlag>>356 pub fn modify_parsed_flags_based_on_mode(
357     parsed_flags: ProtoParsedFlags,
358     codegen_mode: CodegenMode,
359 ) -> Result<Vec<ProtoParsedFlag>> {
360     fn exported_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag {
361         parsed_flag.set_state(ProtoFlagState::DISABLED);
362         parsed_flag.set_permission(ProtoFlagPermission::READ_WRITE);
363         parsed_flag.set_is_fixed_read_only(false);
364         parsed_flag
365     }
366 
367     fn force_read_only_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag {
368         parsed_flag.set_permission(ProtoFlagPermission::READ_ONLY);
369         parsed_flag
370     }
371 
372     let modified_parsed_flags: Vec<_> = match codegen_mode {
373         CodegenMode::Exported => parsed_flags
374             .parsed_flag
375             .into_iter()
376             .filter(|pf| pf.is_exported())
377             .map(exported_mode_flag_modifier)
378             .collect(),
379         CodegenMode::ForceReadOnly => parsed_flags
380             .parsed_flag
381             .into_iter()
382             .filter(|pf| !pf.is_exported())
383             .map(force_read_only_mode_flag_modifier)
384             .collect(),
385         CodegenMode::Production | CodegenMode::Test => {
386             parsed_flags.parsed_flag.into_iter().collect()
387         }
388     };
389     if modified_parsed_flags.is_empty() {
390         bail!("{codegen_mode} library contains no {codegen_mode} flags");
391     }
392 
393     Ok(modified_parsed_flags)
394 }
395 
assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result<HashMap<String, u16>> where I: Iterator<Item = &'a ProtoParsedFlag> + Clone,396 pub fn assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result<HashMap<String, u16>>
397 where
398     I: Iterator<Item = &'a ProtoParsedFlag> + Clone,
399 {
400     assert!(parsed_flags_iter.clone().tuple_windows().all(|(a, b)| a.name() <= b.name()));
401     let mut flag_ids = HashMap::new();
402     let mut flag_idx = 0;
403     for pf in parsed_flags_iter {
404         if package != pf.package() {
405             return Err(anyhow::anyhow!("encountered a flag not in current package"));
406         }
407 
408         // put a cap on how many flags a package can contain to 65535
409         if flag_idx > u16::MAX as u32 {
410             return Err(anyhow::anyhow!("the number of flags in a package cannot exceed 65535"));
411         }
412 
413         if should_include_flag(pf) {
414             flag_ids.insert(pf.name().to_string(), flag_idx as u16);
415             flag_idx += 1;
416         }
417     }
418     Ok(flag_ids)
419 }
420 
421 // Creates a fingerprint of the flag names (which requires sorting the vector).
422 // Fingerprint is used by both codegen and storage files.
compute_flags_fingerprint(flag_names: &mut Vec<String>) -> u64423 pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> u64 {
424     flag_names.sort();
425 
426     let mut hasher = SipHasher13::new();
427     for flag in flag_names {
428         hasher.write(flag.as_bytes());
429     }
430     hasher.finish()
431 }
432 
433 // Converts ProtoParsedFlags into a vector of strings containing all of the flag
434 // names. Helper fn for creating fingerprint for codegen files. Flags must all
435 // belong to the same package.
extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>>436 fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {
437     let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>();
438 
439     // All flags must belong to the same package as the fingerprint is per-package.
440     let Some(_package) = find_unique_package(&separated_flags) else {
441         bail!("No parsed flags, or the parsed flags use different packages.");
442     };
443 
444     Ok(separated_flags
445         .into_iter()
446         .filter(should_include_flag)
447         .map(|flag| flag.name.unwrap())
448         .collect::<Vec<_>>())
449 }
450 
451 // Exclude system/vendor/product flags that are RO+disabled.
should_include_flag(pf: &ProtoParsedFlag) -> bool452 pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {
453     let should_filter_container = pf.container == Some("vendor".to_string())
454         || pf.container == Some("system".to_string())
455         || pf.container == Some("system_ext".to_string())
456         || pf.container == Some("product".to_string());
457 
458     let disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())
459         && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into());
460 
461     !should_filter_container || !disabled_ro
462 }
463 
464 #[cfg(test)]
465 mod tests {
466     use super::*;
467     use aconfig_protos::ProtoFlagPurpose;
468 
469     #[test]
test_offset_fingerprint()470     fn test_offset_fingerprint() {
471         let parsed_flags = crate::test::parse_test_flags();
472         let expected_fingerprint: u64 = 11551379960324242360;
473 
474         let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
475         let hash_result = compute_flags_fingerprint(&mut extracted_flags);
476 
477         assert_eq!(hash_result, expected_fingerprint);
478     }
479 
480     #[test]
test_offset_fingerprint_matches_from_package()481     fn test_offset_fingerprint_matches_from_package() {
482         let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();
483 
484         // All test flags are in the same package, so fingerprint from all of them.
485         let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap();
486         let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);
487 
488         let mut flag_names_vec = parsed_flags
489             .parsed_flag
490             .clone()
491             .into_iter()
492             .filter(should_include_flag)
493             .map(|flag| flag.name.unwrap())
494             .map(String::from)
495             .collect::<Vec<_>>();
496         let result_from_names = compute_flags_fingerprint(&mut flag_names_vec);
497 
498         // Assert the same hash is generated for each case.
499         assert_eq!(result_from_parsed_flags, result_from_names);
500     }
501 
502     #[test]
test_offset_fingerprint_different_packages_does_not_match()503     fn test_offset_fingerprint_different_packages_does_not_match() {
504         // Parse flags from two packages.
505         let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();
506         let second_parsed_flags = crate::test::parse_second_package_flags();
507 
508         let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
509         let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);
510         let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap();
511         let second_result = compute_flags_fingerprint(&mut second_extracted_flags);
512 
513         // Different flags should have a different fingerprint.
514         assert_ne!(result_from_parsed_flags, second_result);
515     }
516 
517     #[test]
test_parse_flags()518     fn test_parse_flags() {
519         let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
520         aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
521 
522         let enabled_ro =
523             parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap();
524         assert!(aconfig_protos::parsed_flag::verify_fields(enabled_ro).is_ok());
525         assert_eq!("com.android.aconfig.test", enabled_ro.package());
526         assert_eq!("enabled_ro", enabled_ro.name());
527         assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description());
528         assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state());
529         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission());
530         assert_eq!(ProtoFlagPurpose::PURPOSE_BUGFIX, enabled_ro.metadata.purpose());
531         assert_eq!(3, enabled_ro.trace.len());
532         assert!(!enabled_ro.is_fixed_read_only());
533         assert_eq!("tests/test.aconfig", enabled_ro.trace[0].source());
534         assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state());
535         assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission());
536         assert_eq!("tests/first.values", enabled_ro.trace[1].source());
537         assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[1].state());
538         assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[1].permission());
539         assert_eq!("tests/second.values", enabled_ro.trace[2].source());
540         assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());
541         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());
542 
543         assert_eq!(9, parsed_flags.parsed_flag.len());
544         for pf in parsed_flags.parsed_flag.iter() {
545             if pf.name().starts_with("enabled_fixed_ro") {
546                 continue;
547             }
548             let first = pf.trace.first().unwrap();
549             assert_eq!(DEFAULT_FLAG_STATE, first.state());
550             assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission());
551 
552             let last = pf.trace.last().unwrap();
553             assert_eq!(pf.state(), last.state());
554             assert_eq!(pf.permission(), last.permission());
555         }
556 
557         let enabled_fixed_ro =
558             parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_fixed_ro").unwrap();
559         assert!(enabled_fixed_ro.is_fixed_read_only());
560         assert_eq!(ProtoFlagState::ENABLED, enabled_fixed_ro.state());
561         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.permission());
562         assert_eq!(2, enabled_fixed_ro.trace.len());
563         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[0].permission());
564         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[1].permission());
565     }
566 
567     #[test]
test_parse_flags_setting_default()568     fn test_parse_flags_setting_default() {
569         let first_flag = r#"
570         package: "com.first"
571         flag {
572             name: "first"
573             namespace: "first_ns"
574             description: "This is the description of the first flag."
575             bug: "123"
576         }
577         "#;
578         let declaration =
579             vec![Input { source: "momery".to_string(), reader: Box::new(first_flag.as_bytes()) }];
580         let value: Vec<Input> = vec![];
581 
582         let flags_bytes = crate::commands::parse_flags(
583             "com.first",
584             None,
585             declaration,
586             value,
587             ProtoFlagPermission::READ_ONLY,
588             true,
589         )
590         .unwrap();
591         let parsed_flags =
592             aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
593         assert_eq!(1, parsed_flags.parsed_flag.len());
594         let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
595         assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
596         assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());
597     }
598 
599     #[test]
test_parse_flags_package_mismatch_between_declaration_and_command_line()600     fn test_parse_flags_package_mismatch_between_declaration_and_command_line() {
601         let first_flag = r#"
602         package: "com.declaration.package"
603         container: "first.container"
604         flag {
605             name: "first"
606             namespace: "first_ns"
607             description: "This is the description of the first flag."
608             bug: "123"
609         }
610         "#;
611         let declaration =
612             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
613 
614         let value: Vec<Input> = vec![];
615 
616         let error = crate::commands::parse_flags(
617             "com.argument.package",
618             Some("first.container"),
619             declaration,
620             value,
621             ProtoFlagPermission::READ_WRITE,
622             true,
623         )
624         .unwrap_err();
625         assert_eq!(
626             format!("{:?}", error),
627             "failed to parse memory: expected package com.argument.package, got com.declaration.package"
628         );
629     }
630 
631     #[test]
test_parse_flags_container_mismatch_between_declaration_and_command_line()632     fn test_parse_flags_container_mismatch_between_declaration_and_command_line() {
633         let first_flag = r#"
634         package: "com.first"
635         container: "declaration.container"
636         flag {
637             name: "first"
638             namespace: "first_ns"
639             description: "This is the description of the first flag."
640             bug: "123"
641         }
642         "#;
643         let declaration =
644             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
645 
646         let value: Vec<Input> = vec![];
647 
648         let error = crate::commands::parse_flags(
649             "com.first",
650             Some("argument.container"),
651             declaration,
652             value,
653             ProtoFlagPermission::READ_WRITE,
654             true,
655         )
656         .unwrap_err();
657         assert_eq!(
658             format!("{:?}", error),
659             "failed to parse memory: expected container argument.container, got declaration.container"
660         );
661     }
662     #[test]
test_parse_flags_no_allow_read_write_default_error()663     fn test_parse_flags_no_allow_read_write_default_error() {
664         let first_flag = r#"
665         package: "com.first"
666         container: "com.first.container"
667         flag {
668             name: "first"
669             namespace: "first_ns"
670             description: "This is the description of the first flag."
671             bug: "123"
672         }
673         "#;
674         let declaration =
675             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
676 
677         let error = crate::commands::parse_flags(
678             "com.first",
679             Some("com.first.container"),
680             declaration,
681             vec![],
682             ProtoFlagPermission::READ_WRITE,
683             false,
684         )
685         .unwrap_err();
686         assert_eq!(
687             format!("{:?}", error),
688             "flag first has permission READ_WRITE, but allow_read_write is false"
689         );
690     }
691 
692     #[test]
test_parse_flags_no_allow_read_write_value_error()693     fn test_parse_flags_no_allow_read_write_value_error() {
694         let first_flag = r#"
695         package: "com.first"
696         container: "com.first.container"
697         flag {
698             name: "first"
699             namespace: "first_ns"
700             description: "This is the description of the first flag."
701             bug: "123"
702         }
703         "#;
704         let declaration =
705             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
706 
707         let first_flag_value = r#"
708         flag_value {
709             package: "com.first"
710             name: "first"
711             state: DISABLED
712             permission: READ_WRITE
713         }
714         "#;
715         let value = vec![Input {
716             source: "memory".to_string(),
717             reader: Box::new(first_flag_value.as_bytes()),
718         }];
719         let error = crate::commands::parse_flags(
720             "com.first",
721             Some("com.first.container"),
722             declaration,
723             value,
724             ProtoFlagPermission::READ_ONLY,
725             false,
726         )
727         .unwrap_err();
728         assert_eq!(
729             format!("{:?}", error),
730             "flag first has permission READ_WRITE, but allow_read_write is false"
731         );
732     }
733 
734     #[test]
test_parse_flags_no_allow_read_write_success()735     fn test_parse_flags_no_allow_read_write_success() {
736         let first_flag = r#"
737         package: "com.first"
738         container: "com.first.container"
739         flag {
740             name: "first"
741             namespace: "first_ns"
742             description: "This is the description of the first flag."
743             bug: "123"
744         }
745         "#;
746         let declaration =
747             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
748 
749         let first_flag_value = r#"
750         flag_value {
751             package: "com.first"
752             name: "first"
753             state: DISABLED
754             permission: READ_ONLY
755         }
756         "#;
757         let value = vec![Input {
758             source: "memory".to_string(),
759             reader: Box::new(first_flag_value.as_bytes()),
760         }];
761         let flags_bytes = crate::commands::parse_flags(
762             "com.first",
763             Some("com.first.container"),
764             declaration,
765             value,
766             ProtoFlagPermission::READ_ONLY,
767             false,
768         )
769         .unwrap();
770         let parsed_flags =
771             aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
772         assert_eq!(1, parsed_flags.parsed_flag.len());
773         let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
774         assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
775         assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());
776     }
777 
778     #[test]
test_parse_flags_override_fixed_read_only()779     fn test_parse_flags_override_fixed_read_only() {
780         let first_flag = r#"
781         package: "com.first"
782         container: "com.first.container"
783         flag {
784             name: "first"
785             namespace: "first_ns"
786             description: "This is the description of the first flag."
787             bug: "123"
788             is_fixed_read_only: true
789         }
790         "#;
791         let declaration =
792             vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
793 
794         let first_flag_value = r#"
795         flag_value {
796             package: "com.first"
797             name: "first"
798             state: DISABLED
799             permission: READ_WRITE
800         }
801         "#;
802         let value = vec![Input {
803             source: "memory".to_string(),
804             reader: Box::new(first_flag_value.as_bytes()),
805         }];
806         let error = crate::commands::parse_flags(
807             "com.first",
808             Some("com.first.container"),
809             declaration,
810             value,
811             ProtoFlagPermission::READ_WRITE,
812             true,
813         )
814         .unwrap_err();
815         assert_eq!(
816             format!("{:?}", error),
817             "failed to set permission of flag first, since this flag is fixed read only flag"
818         );
819     }
820 
821     #[test]
test_parse_flags_metadata()822     fn test_parse_flags_metadata() {
823         let metadata_flag = r#"
824         package: "com.first"
825         flag {
826             name: "first"
827             namespace: "first_ns"
828             description: "This is the description of this feature flag."
829             bug: "123"
830             metadata {
831                 purpose: PURPOSE_FEATURE
832             }
833         }
834         "#;
835         let declaration = vec![Input {
836             source: "memory".to_string(),
837             reader: Box::new(metadata_flag.as_bytes()),
838         }];
839         let value: Vec<Input> = vec![];
840 
841         let flags_bytes = crate::commands::parse_flags(
842             "com.first",
843             None,
844             declaration,
845             value,
846             ProtoFlagPermission::READ_ONLY,
847             true,
848         )
849         .unwrap();
850         let parsed_flags =
851             aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
852         assert_eq!(1, parsed_flags.parsed_flag.len());
853         let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
854         assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose());
855     }
856 
857     #[test]
test_create_device_config_defaults()858     fn test_create_device_config_defaults() {
859         let input = parse_test_flags_as_input();
860         let bytes = create_device_config_defaults(input).unwrap();
861         let text = std::str::from_utf8(&bytes).unwrap();
862         assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.disabled_rw_exported=disabled\nother_namespace:com.android.aconfig.test.disabled_rw_in_other_namespace=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
863     }
864 
865     #[test]
test_create_device_config_sysprops()866     fn test_create_device_config_sysprops() {
867         let input = parse_test_flags_as_input();
868         let bytes = create_device_config_sysprops(input).unwrap();
869         let text = std::str::from_utf8(&bytes).unwrap();
870         assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.disabled_rw_exported=false\npersist.device_config.com.android.aconfig.test.disabled_rw_in_other_namespace=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
871     }
872 
873     #[test]
test_dump()874     fn test_dump() {
875         let input = parse_test_flags_as_input();
876         let bytes = dump_parsed_flags(
877             vec![input],
878             DumpFormat::Custom("{fully_qualified_name}".to_string()),
879             &[],
880             false,
881         )
882         .unwrap();
883         let text = std::str::from_utf8(&bytes).unwrap();
884         assert!(text.contains("com.android.aconfig.test.disabled_ro"));
885     }
886 
887     #[test]
test_dump_multiple_filters()888     fn test_dump_multiple_filters() {
889         let input = parse_test_flags_as_input();
890         let bytes = dump_parsed_flags(
891             vec![input],
892             DumpFormat::Custom("{fully_qualified_name}".to_string()),
893             &["container:system+state:ENABLED", "container:system+permission:READ_WRITE"],
894             false,
895         )
896         .unwrap();
897         let text = std::str::from_utf8(&bytes).unwrap();
898         let expected_flag_list = &[
899             "com.android.aconfig.test.disabled_rw",
900             "com.android.aconfig.test.disabled_rw_exported",
901             "com.android.aconfig.test.disabled_rw_in_other_namespace",
902             "com.android.aconfig.test.enabled_fixed_ro",
903             "com.android.aconfig.test.enabled_fixed_ro_exported",
904             "com.android.aconfig.test.enabled_ro",
905             "com.android.aconfig.test.enabled_ro_exported",
906             "com.android.aconfig.test.enabled_rw",
907         ];
908         assert_eq!(expected_flag_list.map(|s| format!("{}\n", s)).join(""), text);
909     }
910 
911     #[test]
test_dump_textproto_format_dedup()912     fn test_dump_textproto_format_dedup() {
913         let input = parse_test_flags_as_input();
914         let input2 = parse_test_flags_as_input();
915         let bytes =
916             dump_parsed_flags(vec![input, input2], DumpFormat::Textproto, &[], true).unwrap();
917         let text = std::str::from_utf8(&bytes).unwrap();
918         assert_eq!(
919             None,
920             crate::test::first_significant_code_diff(
921                 crate::test::TEST_FLAGS_TEXTPROTO.trim(),
922                 text.trim()
923             )
924         );
925     }
926 
parse_test_flags_as_input() -> Input927     fn parse_test_flags_as_input() -> Input {
928         let parsed_flags = crate::test::parse_test_flags();
929         let binary_proto = parsed_flags.write_to_bytes().unwrap();
930         let cursor = std::io::Cursor::new(binary_proto);
931         let reader = Box::new(cursor);
932         Input { source: "test.data".to_string(), reader }
933     }
934 
935     #[test]
test_modify_parsed_flags_based_on_mode_prod()936     fn test_modify_parsed_flags_based_on_mode_prod() {
937         let parsed_flags = crate::test::parse_test_flags();
938         let p_parsed_flags =
939             modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::Production)
940                 .unwrap();
941         assert_eq!(parsed_flags.parsed_flag.len(), p_parsed_flags.len());
942         for (i, item) in p_parsed_flags.iter().enumerate() {
943             assert!(parsed_flags.parsed_flag[i].eq(item));
944         }
945     }
946 
947     #[test]
test_modify_parsed_flags_based_on_mode_exported()948     fn test_modify_parsed_flags_based_on_mode_exported() {
949         let parsed_flags = crate::test::parse_test_flags();
950         let p_parsed_flags =
951             modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap();
952         assert_eq!(3, p_parsed_flags.len());
953         for flag in p_parsed_flags.iter() {
954             assert_eq!(ProtoFlagState::DISABLED, flag.state());
955             assert_eq!(ProtoFlagPermission::READ_WRITE, flag.permission());
956             assert!(!flag.is_fixed_read_only());
957             assert!(flag.is_exported());
958         }
959 
960         let mut parsed_flags = crate::test::parse_test_flags();
961         parsed_flags.parsed_flag.retain(|pf| !pf.is_exported());
962         let error =
963             modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap_err();
964         assert_eq!("exported library contains no exported flags", format!("{:?}", error));
965     }
966 
967     #[test]
test_assign_flag_ids()968     fn test_assign_flag_ids() {
969         let parsed_flags = crate::test::parse_test_flags();
970         let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();
971         let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();
972         let expected_flag_ids = HashMap::from([
973             (String::from("disabled_rw"), 0_u16),
974             (String::from("disabled_rw_exported"), 1_u16),
975             (String::from("disabled_rw_in_other_namespace"), 2_u16),
976             (String::from("enabled_fixed_ro"), 3_u16),
977             (String::from("enabled_fixed_ro_exported"), 4_u16),
978             (String::from("enabled_ro"), 5_u16),
979             (String::from("enabled_ro_exported"), 6_u16),
980             (String::from("enabled_rw"), 7_u16),
981         ]);
982         assert_eq!(flag_ids, expected_flag_ids);
983     }
984 
985     #[test]
test_modify_parsed_flags_based_on_mode_force_read_only()986     fn test_modify_parsed_flags_based_on_mode_force_read_only() {
987         let parsed_flags = crate::test::parse_test_flags();
988         let p_parsed_flags =
989             modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::ForceReadOnly)
990                 .unwrap();
991         assert_eq!(6, p_parsed_flags.len());
992         for pf in p_parsed_flags {
993             assert_eq!(ProtoFlagPermission::READ_ONLY, pf.permission());
994         }
995 
996         let mut parsed_flags = crate::test::parse_test_flags();
997         parsed_flags.parsed_flag.retain_mut(|pf| pf.is_exported());
998         let error = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::ForceReadOnly)
999             .unwrap_err();
1000         assert_eq!(
1001             "force-read-only library contains no force-read-only flags",
1002             format!("{:?}", error)
1003         );
1004     }
1005 }
1006