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::{FlagInfoHeader, FlagInfoList, FlagInfoNode, StorageFileType};
21 use anyhow::{anyhow, Result};
22
new_header(container: &str, num_flags: u32, version: u32) -> FlagInfoHeader23 fn new_header(container: &str, num_flags: u32, version: u32) -> FlagInfoHeader {
24 FlagInfoHeader {
25 version,
26 container: String::from(container),
27 file_type: StorageFileType::FlagInfo as u8,
28 file_size: 0,
29 num_flags,
30 boolean_flag_offset: 0,
31 }
32 }
33
create_flag_info( container: &str, packages: &[FlagPackage], version: u32, ) -> Result<FlagInfoList>34 pub fn create_flag_info(
35 container: &str,
36 packages: &[FlagPackage],
37 version: u32,
38 ) -> Result<FlagInfoList> {
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
50 let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
51
52 let mut is_flag_rw = vec![false; num_flags as usize];
53 for pkg in filtered_packages {
54 let start_index = pkg.boolean_start_index as usize;
55 let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
56 for pf in pkg.boolean_flags {
57 let fid = flag_ids
58 .get(pf.name())
59 .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
60 is_flag_rw[start_index + (*fid as usize)] =
61 pf.permission() == ProtoFlagPermission::READ_WRITE;
62 }
63 }
64
65 let mut list = FlagInfoList {
66 header: new_header(container, num_flags, version),
67 nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
68 };
69
70 // initialize all header fields
71 list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
72 let bytes_per_node = FlagInfoNode::create(false).into_bytes().len() as u32;
73 list.header.file_size = list.header.boolean_flag_offset + num_flags * bytes_per_node;
74
75 Ok(list)
76 }
77
78 #[cfg(test)]
79 mod tests {
80 use super::*;
81 use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
82 use aconfig_storage_file::DEFAULT_FILE_VERSION;
83
create_test_flag_info_list_from_source() -> Result<FlagInfoList>84 pub fn create_test_flag_info_list_from_source() -> Result<FlagInfoList> {
85 let caches = parse_all_test_flags();
86 let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);
87 create_flag_info("mockup", &packages, DEFAULT_FILE_VERSION)
88 }
89
90 #[test]
91 // this test point locks down the flag info creation and each field
test_list_contents()92 fn test_list_contents() {
93 let flag_info_list = create_test_flag_info_list_from_source();
94 assert!(flag_info_list.is_ok());
95 let expected_flag_info_list =
96 aconfig_storage_file::test_utils::create_test_flag_info_list(DEFAULT_FILE_VERSION);
97 assert_eq!(flag_info_list.unwrap(), expected_flag_info_list);
98 }
99 }
100