1 /*
2 * Copyright (C) 2024 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 crate::commands::assign_flag_ids;
18 use crate::storage::FlagPackage;
19 use aconfig_protos::{ProtoFlagPermission, ProtoFlagState};
20 use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType};
21 use anyhow::{anyhow, Result};
22
new_header(container: &str, num_flags: u32, version: u32) -> FlagValueHeader23 fn new_header(container: &str, num_flags: u32, version: u32) -> FlagValueHeader {
24 FlagValueHeader {
25 version,
26 container: String::from(container),
27 file_type: StorageFileType::FlagVal as u8,
28 file_size: 0,
29 num_flags,
30 boolean_value_offset: 0,
31 }
32 }
33
create_flag_value( container: &str, packages: &[FlagPackage], version: u32, ) -> Result<FlagValueList>34 pub fn create_flag_value(
35 container: &str,
36 packages: &[FlagPackage],
37 version: u32,
38 ) -> Result<FlagValueList> {
39 // Exclude system/vendor/product flags that are RO+disabled.
40 let mut filtered_packages = packages.to_vec();
41 if container == "system" || container == "vendor" || container == "product" {
42 for package in filtered_packages.iter_mut() {
43 package.boolean_flags.retain(|b| {
44 !(b.state == Some(ProtoFlagState::DISABLED.into())
45 && b.permission == Some(ProtoFlagPermission::READ_ONLY.into()))
46 });
47 }
48 }
49 let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
50 let mut list = FlagValueList {
51 header: new_header(container, num_flags, version),
52 booleans: vec![false; num_flags as usize],
53 };
54 for pkg in filtered_packages {
55 let start_index = pkg.boolean_start_index as usize;
56 let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
57 for pf in pkg.boolean_flags.iter() {
58 let fid = flag_ids
59 .get(pf.name())
60 .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
61
62 list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
63 }
64 }
65
66 // initialize all header fields
67 list.header.boolean_value_offset = list.header.into_bytes().len() as u32;
68 list.header.file_size = list.header.boolean_value_offset + num_flags;
69
70 Ok(list)
71 }
72
73 #[cfg(test)]
74 mod tests {
75 use aconfig_storage_file::DEFAULT_FILE_VERSION;
76
77 use super::*;
78 use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
79
create_test_flag_value_list_from_source() -> Result<FlagValueList>80 pub fn create_test_flag_value_list_from_source() -> Result<FlagValueList> {
81 let caches = parse_all_test_flags();
82 let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);
83 create_flag_value("mockup", &packages, DEFAULT_FILE_VERSION)
84 }
85
86 #[test]
87 // this test point locks down the flag value creation and each field
test_list_contents()88 fn test_list_contents() {
89 let flag_value_list = create_test_flag_value_list_from_source();
90 assert!(flag_value_list.is_ok());
91 let expected_flag_value_list =
92 aconfig_storage_file::test_utils::create_test_flag_value_list(DEFAULT_FILE_VERSION);
93 assert_eq!(flag_value_list.unwrap(), expected_flag_value_list);
94 }
95 }
96