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 //! flag value query module defines the flag value file read from mapped bytes
18 
19 use crate::{AconfigStorageError, FILE_VERSION};
20 use aconfig_storage_file::{flag_info::FlagInfoHeader, read_u8_from_bytes, FlagValueType};
21 use anyhow::anyhow;
22 
23 /// Get flag attribute bitfield
find_flag_attribute( buf: &[u8], flag_type: FlagValueType, flag_index: u32, ) -> Result<u8, AconfigStorageError>24 pub fn find_flag_attribute(
25     buf: &[u8],
26     flag_type: FlagValueType,
27     flag_index: u32,
28 ) -> Result<u8, AconfigStorageError> {
29     let interpreted_header = FlagInfoHeader::from_bytes(buf)?;
30     if interpreted_header.version > crate::FILE_VERSION {
31         return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
32             "Cannot read storage file with a higher version of {} with lib version {}",
33             interpreted_header.version,
34             FILE_VERSION
35         )));
36     }
37 
38     // get byte offset to the flag info
39     let mut head = match flag_type {
40         FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,
41     };
42 
43     if head >= interpreted_header.file_size as usize {
44         return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
45             "Flag info offset goes beyond the end of the file."
46         )));
47     }
48 
49     let val = read_u8_from_bytes(buf, &mut head)?;
50     Ok(val)
51 }
52 
53 #[cfg(test)]
54 mod tests {
55     use super::*;
56     use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit};
57 
58     #[test]
59     // this test point locks down query if flag has server override
test_is_flag_sticky()60     fn test_is_flag_sticky() {
61         let flag_info_list = create_test_flag_info_list().into_bytes();
62         for offset in 0..8 {
63             let attribute =
64                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
65             assert_eq!((attribute & FlagInfoBit::HasServerOverride as u8) != 0u8, false);
66         }
67     }
68 
69     #[test]
70     // this test point locks down query if flag is readwrite
test_is_flag_readwrite()71     fn test_is_flag_readwrite() {
72         let flag_info_list = create_test_flag_info_list().into_bytes();
73         let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true];
74         for offset in 0..8 {
75             let attribute =
76                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
77             assert_eq!(
78                 (attribute & FlagInfoBit::IsReadWrite as u8) != 0u8,
79                 baseline[offset as usize]
80             );
81         }
82     }
83 
84     #[test]
85     // this test point locks down query if flag has local override
test_flag_has_override()86     fn test_flag_has_override() {
87         let flag_info_list = create_test_flag_info_list().into_bytes();
88         for offset in 0..8 {
89             let attribute =
90                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
91             assert_eq!((attribute & FlagInfoBit::HasLocalOverride as u8) != 0u8, false);
92         }
93     }
94 
95     #[test]
96     // this test point locks down query beyond the end of boolean section
test_boolean_out_of_range()97     fn test_boolean_out_of_range() {
98         let flag_info_list = create_test_flag_info_list().into_bytes();
99         let error =
100             find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, 8).unwrap_err();
101         assert_eq!(
102             format!("{:?}", error),
103             "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"
104         );
105     }
106 
107     #[test]
108     // this test point locks down query error when file has a higher version
test_higher_version_storage_file()109     fn test_higher_version_storage_file() {
110         let mut info_list = create_test_flag_info_list();
111         info_list.header.version = crate::FILE_VERSION + 1;
112         let flag_info = info_list.into_bytes();
113         let error = find_flag_attribute(&flag_info[..], FlagValueType::Boolean, 4).unwrap_err();
114         assert_eq!(
115             format!("{:?}", error),
116             format!(
117                 "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
118                 crate::FILE_VERSION + 1,
119                 crate::FILE_VERSION
120             )
121         );
122     }
123 }
124