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