xref: /aosp_15_r20/build/make/tools/aconfig/aconfig_storage_file/src/lib.rs (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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 //! `aconfig_storage_file` is a crate that defines aconfig storage file format, it
18 //! also includes apis to read flags from storage files. It provides three apis to
19 //! interface with storage files:
20 //!
21 //! 1, function to get package flag value start offset
22 //! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`
23 //!
24 //! 2, function to get flag offset within a specific package
25 //! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`
26 //!
27 //! 3, function to get the actual flag value given the global offset (combined package and
28 //! flag offset).
29 //! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
30 //!
31 //! Note these are low level apis that are expected to be only used in auto generated flag
32 //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
33 //! please refer to the g3doc go/android-flags
34 
35 pub mod flag_info;
36 pub mod flag_table;
37 pub mod flag_value;
38 pub mod package_table;
39 pub mod protos;
40 pub mod sip_hasher13;
41 pub mod test_utils;
42 
43 use anyhow::anyhow;
44 use serde::{Deserialize, Serialize};
45 use std::cmp::Ordering;
46 use std::fs::File;
47 use std::hash::Hasher;
48 use std::io::Read;
49 
50 pub use crate::flag_info::{FlagInfoBit, FlagInfoHeader, FlagInfoList, FlagInfoNode};
51 pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
52 pub use crate::flag_value::{FlagValueHeader, FlagValueList};
53 pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
54 pub use crate::sip_hasher13::SipHasher13;
55 
56 use crate::AconfigStorageError::{
57     BytesParseFail, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType,
58 };
59 
60 /// The max storage file version from which we can safely read/write. May be
61 /// experimental.
62 pub const MAX_SUPPORTED_FILE_VERSION: u32 = 2;
63 
64 /// The newest fully-released version. Unless otherwise specified, this is the
65 /// version we will write.
66 pub const DEFAULT_FILE_VERSION: u32 = 1;
67 
68 /// Good hash table prime number
69 pub(crate) const HASH_PRIMES: [u32; 29] = [
70     7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241,
71     786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,
72     402653189, 805306457, 1610612741,
73 ];
74 
75 /// Storage file type enum
76 #[derive(Clone, Debug, PartialEq, Eq)]
77 pub enum StorageFileType {
78     PackageMap = 0,
79     FlagMap = 1,
80     FlagVal = 2,
81     FlagInfo = 3,
82 }
83 
84 impl TryFrom<&str> for StorageFileType {
85     type Error = anyhow::Error;
86 
try_from(value: &str) -> std::result::Result<Self, Self::Error>87     fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
88         match value {
89             "package_map" => Ok(Self::PackageMap),
90             "flag_map" => Ok(Self::FlagMap),
91             "flag_val" => Ok(Self::FlagVal),
92             "flag_info" => Ok(Self::FlagInfo),
93             _ => Err(anyhow!(
94                 "Invalid storage file type, valid types are package_map|flag_map|flag_val|flag_info"
95             )),
96         }
97     }
98 }
99 
100 impl TryFrom<u8> for StorageFileType {
101     type Error = anyhow::Error;
102 
try_from(value: u8) -> Result<Self, Self::Error>103     fn try_from(value: u8) -> Result<Self, Self::Error> {
104         match value {
105             x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),
106             x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),
107             x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),
108             x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo),
109             _ => Err(anyhow!("Invalid storage file type")),
110         }
111     }
112 }
113 
114 /// Flag type enum as stored by storage file
115 /// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.
116 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
117 pub enum StoredFlagType {
118     ReadWriteBoolean = 0,
119     ReadOnlyBoolean = 1,
120     FixedReadOnlyBoolean = 2,
121 }
122 
123 impl TryFrom<u16> for StoredFlagType {
124     type Error = AconfigStorageError;
125 
try_from(value: u16) -> Result<Self, Self::Error>126     fn try_from(value: u16) -> Result<Self, Self::Error> {
127         match value {
128             x if x == Self::ReadWriteBoolean as u16 => Ok(Self::ReadWriteBoolean),
129             x if x == Self::ReadOnlyBoolean as u16 => Ok(Self::ReadOnlyBoolean),
130             x if x == Self::FixedReadOnlyBoolean as u16 => Ok(Self::FixedReadOnlyBoolean),
131             _ => Err(InvalidStoredFlagType(anyhow!("Invalid stored flag type"))),
132         }
133     }
134 }
135 
136 /// Flag value type enum, one FlagValueType maps to many StoredFlagType
137 /// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16
138 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
139 pub enum FlagValueType {
140     Boolean = 0,
141 }
142 
143 impl TryFrom<StoredFlagType> for FlagValueType {
144     type Error = AconfigStorageError;
145 
try_from(value: StoredFlagType) -> Result<Self, Self::Error>146     fn try_from(value: StoredFlagType) -> Result<Self, Self::Error> {
147         match value {
148             StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean),
149             StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean),
150             StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean),
151         }
152     }
153 }
154 
155 impl TryFrom<u16> for FlagValueType {
156     type Error = AconfigStorageError;
157 
try_from(value: u16) -> Result<Self, Self::Error>158     fn try_from(value: u16) -> Result<Self, Self::Error> {
159         match value {
160             x if x == Self::Boolean as u16 => Ok(Self::Boolean),
161             _ => Err(InvalidFlagValueType(anyhow!("Invalid flag value type"))),
162         }
163     }
164 }
165 
166 /// Storage query api error
167 #[non_exhaustive]
168 #[derive(thiserror::Error, Debug)]
169 pub enum AconfigStorageError {
170     #[error("failed to read the file")]
171     FileReadFail(#[source] anyhow::Error),
172 
173     #[error("fail to parse protobuf")]
174     ProtobufParseFail(#[source] anyhow::Error),
175 
176     #[error("storage files not found for this container")]
177     StorageFileNotFound(#[source] anyhow::Error),
178 
179     #[error("fail to map storage file")]
180     MapFileFail(#[source] anyhow::Error),
181 
182     #[error("fail to get mapped file")]
183     ObtainMappedFileFail(#[source] anyhow::Error),
184 
185     #[error("fail to flush mapped storage file")]
186     MapFlushFail(#[source] anyhow::Error),
187 
188     #[error("number of items in hash table exceed limit")]
189     HashTableSizeLimit(#[source] anyhow::Error),
190 
191     #[error("failed to parse bytes into data")]
192     BytesParseFail(#[source] anyhow::Error),
193 
194     #[error("cannot parse storage files with a higher version")]
195     HigherStorageFileVersion(#[source] anyhow::Error),
196 
197     #[error("invalid storage file byte offset")]
198     InvalidStorageFileOffset(#[source] anyhow::Error),
199 
200     #[error("failed to create file")]
201     FileCreationFail(#[source] anyhow::Error),
202 
203     #[error("invalid stored flag type")]
204     InvalidStoredFlagType(#[source] anyhow::Error),
205 
206     #[error("invalid flag value type")]
207     InvalidFlagValueType(#[source] anyhow::Error),
208 }
209 
210 /// Get the right hash table size given number of entries in the table. Use a
211 /// load factor of 0.5 for performance.
get_table_size(entries: u32) -> Result<u32, AconfigStorageError>212 pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
213     HASH_PRIMES
214         .iter()
215         .find(|&&num| num >= 2 * entries)
216         .copied()
217         .ok_or(HashTableSizeLimit(anyhow!("Number of items in a hash table exceeds limit")))
218 }
219 
220 /// Get the corresponding bucket index given the key and number of buckets
get_bucket_index(val: &[u8], num_buckets: u32) -> u32221 pub(crate) fn get_bucket_index(val: &[u8], num_buckets: u32) -> u32 {
222     let mut s = SipHasher13::new();
223     s.write(val);
224     s.write_u8(0xff);
225     let ret = (s.finish() % num_buckets as u64) as u32;
226     ret
227 }
228 
229 /// Read and parse bytes as u8
read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError>230 pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {
231     let val =
232         u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| {
233             BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg))
234         })?);
235     *head += 1;
236     Ok(val)
237 }
238 
239 /// Read and parse bytes as u16
read_u16_from_bytes( buf: &[u8], head: &mut usize, ) -> Result<u16, AconfigStorageError>240 pub(crate) fn read_u16_from_bytes(
241     buf: &[u8],
242     head: &mut usize,
243 ) -> Result<u16, AconfigStorageError> {
244     let val =
245         u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| {
246             BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
247         })?);
248     *head += 2;
249     Ok(val)
250 }
251 
252 /// Read and parse the first 4 bytes of buf as u32.
read_u32_from_start_of_bytes(buf: &[u8]) -> Result<u32, AconfigStorageError>253 pub fn read_u32_from_start_of_bytes(buf: &[u8]) -> Result<u32, AconfigStorageError> {
254     read_u32_from_bytes(buf, &mut 0)
255 }
256 
257 /// Read and parse bytes as u32
read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError>258 pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError> {
259     let val =
260         u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| {
261             BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
262         })?);
263     *head += 4;
264     Ok(val)
265 }
266 
267 // Read and parse bytes as u64
read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError>268 pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> {
269     let val =
270         u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| {
271             BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg))
272         })?);
273     *head += 8;
274     Ok(val)
275 }
276 
277 /// Read and parse bytes as string
read_str_from_bytes( buf: &[u8], head: &mut usize, ) -> Result<String, AconfigStorageError>278 pub(crate) fn read_str_from_bytes(
279     buf: &[u8],
280     head: &mut usize,
281 ) -> Result<String, AconfigStorageError> {
282     let num_bytes = read_u32_from_bytes(buf, head)? as usize;
283     let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())
284         .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
285     *head += num_bytes;
286     Ok(val)
287 }
288 
289 /// Read in storage file as bytes
read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError>290 pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
291     let mut file = File::open(file_path).map_err(|errmsg| {
292         AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
293     })?;
294     let mut buffer = Vec::new();
295     file.read_to_end(&mut buffer).map_err(|errmsg| {
296         AconfigStorageError::FileReadFail(anyhow!(
297             "Failed to read bytes from file {}: {}",
298             file_path,
299             errmsg
300         ))
301     })?;
302     Ok(buffer)
303 }
304 
305 /// Flag value summary
306 #[derive(Debug, PartialEq)]
307 pub struct FlagValueSummary {
308     pub package_name: String,
309     pub flag_name: String,
310     pub flag_value: String,
311     pub value_type: StoredFlagType,
312 }
313 
314 /// List flag values from storage files
list_flags( package_map: &str, flag_map: &str, flag_val: &str, ) -> Result<Vec<FlagValueSummary>, AconfigStorageError>315 pub fn list_flags(
316     package_map: &str,
317     flag_map: &str,
318     flag_val: &str,
319 ) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {
320     let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
321     let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
322     let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
323 
324     let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
325     for node in package_table.nodes.iter() {
326         package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
327     }
328 
329     let mut flags = Vec::new();
330     for node in flag_table.nodes.iter() {
331         let (package_name, boolean_start_index) = package_info[node.package_id as usize];
332         let flag_index = boolean_start_index + node.flag_index as u32;
333         let flag_value = flag_value_list.booleans[flag_index as usize];
334         flags.push(FlagValueSummary {
335             package_name: String::from(package_name),
336             flag_name: node.flag_name.clone(),
337             flag_value: flag_value.to_string(),
338             value_type: node.flag_type,
339         });
340     }
341 
342     flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
343         Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
344         other => other,
345     });
346     Ok(flags)
347 }
348 
349 /// Flag value and info summary
350 #[derive(Debug, PartialEq)]
351 pub struct FlagValueAndInfoSummary {
352     pub package_name: String,
353     pub flag_name: String,
354     pub flag_value: String,
355     pub value_type: StoredFlagType,
356     pub is_readwrite: bool,
357     pub has_server_override: bool,
358     pub has_local_override: bool,
359 }
360 
361 /// List flag values and info from storage files
list_flags_with_info( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError>362 pub fn list_flags_with_info(
363     package_map: &str,
364     flag_map: &str,
365     flag_val: &str,
366     flag_info: &str,
367 ) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {
368     let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
369     let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
370     let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
371     let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;
372 
373     let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
374     for node in package_table.nodes.iter() {
375         package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
376     }
377 
378     let mut flags = Vec::new();
379     for node in flag_table.nodes.iter() {
380         let (package_name, boolean_start_index) = package_info[node.package_id as usize];
381         let flag_index = boolean_start_index + node.flag_index as u32;
382         let flag_value = flag_value_list.booleans[flag_index as usize];
383         let flag_attribute = flag_info.nodes[flag_index as usize].attributes;
384         flags.push(FlagValueAndInfoSummary {
385             package_name: String::from(package_name),
386             flag_name: node.flag_name.clone(),
387             flag_value: flag_value.to_string(),
388             value_type: node.flag_type,
389             is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,
390             has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,
391             has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,
392         });
393     }
394 
395     flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
396         Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
397         other => other,
398     });
399     Ok(flags)
400 }
401 
402 // *************************************** //
403 // CC INTERLOP
404 // *************************************** //
405 
406 // Exported rust data structure and methods, c++ code will be generated
407 #[cxx::bridge]
408 mod ffi {
409     /// flag value summary cxx return
410     pub struct FlagValueSummaryCXX {
411         pub package_name: String,
412         pub flag_name: String,
413         pub flag_value: String,
414         pub value_type: String,
415     }
416 
417     /// flag value and info summary cxx return
418     pub struct FlagValueAndInfoSummaryCXX {
419         pub package_name: String,
420         pub flag_name: String,
421         pub flag_value: String,
422         pub value_type: String,
423         pub is_readwrite: bool,
424         pub has_server_override: bool,
425         pub has_local_override: bool,
426     }
427 
428     /// list flag result cxx return
429     pub struct ListFlagValueResultCXX {
430         pub query_success: bool,
431         pub error_message: String,
432         pub flags: Vec<FlagValueSummaryCXX>,
433     }
434 
435     /// list flag with info result cxx return
436     pub struct ListFlagValueAndInfoResultCXX {
437         pub query_success: bool,
438         pub error_message: String,
439         pub flags: Vec<FlagValueAndInfoSummaryCXX>,
440     }
441 
442     // Rust export to c++
443     extern "Rust" {
list_flags_cxx( package_map: &str, flag_map: &str, flag_val: &str, ) -> ListFlagValueResultCXX444         pub fn list_flags_cxx(
445             package_map: &str,
446             flag_map: &str,
447             flag_val: &str,
448         ) -> ListFlagValueResultCXX;
449 
list_flags_with_info_cxx( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> ListFlagValueAndInfoResultCXX450         pub fn list_flags_with_info_cxx(
451             package_map: &str,
452             flag_map: &str,
453             flag_val: &str,
454             flag_info: &str,
455         ) -> ListFlagValueAndInfoResultCXX;
456     }
457 }
458 
459 /// implement flag value summary cxx return type
460 impl ffi::FlagValueSummaryCXX {
new(summary: FlagValueSummary) -> Self461     pub(crate) fn new(summary: FlagValueSummary) -> Self {
462         Self {
463             package_name: summary.package_name,
464             flag_name: summary.flag_name,
465             flag_value: summary.flag_value,
466             value_type: format!("{:?}", summary.value_type),
467         }
468     }
469 }
470 
471 /// implement flag value and info summary cxx return type
472 impl ffi::FlagValueAndInfoSummaryCXX {
new(summary: FlagValueAndInfoSummary) -> Self473     pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {
474         Self {
475             package_name: summary.package_name,
476             flag_name: summary.flag_name,
477             flag_value: summary.flag_value,
478             value_type: format!("{:?}", summary.value_type),
479             is_readwrite: summary.is_readwrite,
480             has_server_override: summary.has_server_override,
481             has_local_override: summary.has_local_override,
482         }
483     }
484 }
485 
486 /// implement list flag cxx interlop
list_flags_cxx( package_map: &str, flag_map: &str, flag_val: &str, ) -> ffi::ListFlagValueResultCXX487 pub fn list_flags_cxx(
488     package_map: &str,
489     flag_map: &str,
490     flag_val: &str,
491 ) -> ffi::ListFlagValueResultCXX {
492     match list_flags(package_map, flag_map, flag_val) {
493         Ok(summary) => ffi::ListFlagValueResultCXX {
494             query_success: true,
495             error_message: String::new(),
496             flags: summary.into_iter().map(ffi::FlagValueSummaryCXX::new).collect(),
497         },
498         Err(errmsg) => ffi::ListFlagValueResultCXX {
499             query_success: false,
500             error_message: format!("{:?}", errmsg),
501             flags: Vec::new(),
502         },
503     }
504 }
505 
506 /// implement list flag with info cxx interlop
list_flags_with_info_cxx( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> ffi::ListFlagValueAndInfoResultCXX507 pub fn list_flags_with_info_cxx(
508     package_map: &str,
509     flag_map: &str,
510     flag_val: &str,
511     flag_info: &str,
512 ) -> ffi::ListFlagValueAndInfoResultCXX {
513     match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {
514         Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {
515             query_success: true,
516             error_message: String::new(),
517             flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),
518         },
519         Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {
520             query_success: false,
521             error_message: format!("{:?}", errmsg),
522             flags: Vec::new(),
523         },
524     }
525 }
526 
527 #[cfg(test)]
528 mod tests {
529     use super::*;
530     use crate::test_utils::{
531         create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
532         create_test_package_table, write_bytes_to_temp_file,
533     };
534 
535     #[test]
536     // this test point locks down the flag list api
test_list_flag()537     fn test_list_flag() {
538         let package_table =
539             write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())
540                 .unwrap();
541         let flag_table =
542             write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())
543                 .unwrap();
544         let flag_value_list = write_bytes_to_temp_file(
545             &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),
546         )
547         .unwrap();
548 
549         let package_table_path = package_table.path().display().to_string();
550         let flag_table_path = flag_table.path().display().to_string();
551         let flag_value_list_path = flag_value_list.path().display().to_string();
552 
553         let flags =
554             list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
555         let expected = [
556             FlagValueSummary {
557                 package_name: String::from("com.android.aconfig.storage.test_1"),
558                 flag_name: String::from("disabled_rw"),
559                 value_type: StoredFlagType::ReadWriteBoolean,
560                 flag_value: String::from("false"),
561             },
562             FlagValueSummary {
563                 package_name: String::from("com.android.aconfig.storage.test_1"),
564                 flag_name: String::from("enabled_ro"),
565                 value_type: StoredFlagType::ReadOnlyBoolean,
566                 flag_value: String::from("true"),
567             },
568             FlagValueSummary {
569                 package_name: String::from("com.android.aconfig.storage.test_1"),
570                 flag_name: String::from("enabled_rw"),
571                 value_type: StoredFlagType::ReadWriteBoolean,
572                 flag_value: String::from("true"),
573             },
574             FlagValueSummary {
575                 package_name: String::from("com.android.aconfig.storage.test_2"),
576                 flag_name: String::from("disabled_rw"),
577                 value_type: StoredFlagType::ReadWriteBoolean,
578                 flag_value: String::from("false"),
579             },
580             FlagValueSummary {
581                 package_name: String::from("com.android.aconfig.storage.test_2"),
582                 flag_name: String::from("enabled_fixed_ro"),
583                 value_type: StoredFlagType::FixedReadOnlyBoolean,
584                 flag_value: String::from("true"),
585             },
586             FlagValueSummary {
587                 package_name: String::from("com.android.aconfig.storage.test_2"),
588                 flag_name: String::from("enabled_ro"),
589                 value_type: StoredFlagType::ReadOnlyBoolean,
590                 flag_value: String::from("true"),
591             },
592             FlagValueSummary {
593                 package_name: String::from("com.android.aconfig.storage.test_4"),
594                 flag_name: String::from("enabled_fixed_ro"),
595                 value_type: StoredFlagType::FixedReadOnlyBoolean,
596                 flag_value: String::from("true"),
597             },
598             FlagValueSummary {
599                 package_name: String::from("com.android.aconfig.storage.test_4"),
600                 flag_name: String::from("enabled_rw"),
601                 value_type: StoredFlagType::ReadWriteBoolean,
602                 flag_value: String::from("true"),
603             },
604         ];
605         assert_eq!(flags, expected);
606     }
607 
608     #[test]
609     // this test point locks down the flag list with info api
test_list_flag_with_info()610     fn test_list_flag_with_info() {
611         let package_table =
612             write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())
613                 .unwrap();
614         let flag_table =
615             write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())
616                 .unwrap();
617         let flag_value_list = write_bytes_to_temp_file(
618             &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),
619         )
620         .unwrap();
621         let flag_info_list = write_bytes_to_temp_file(
622             &create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(),
623         )
624         .unwrap();
625 
626         let package_table_path = package_table.path().display().to_string();
627         let flag_table_path = flag_table.path().display().to_string();
628         let flag_value_list_path = flag_value_list.path().display().to_string();
629         let flag_info_list_path = flag_info_list.path().display().to_string();
630 
631         let flags = list_flags_with_info(
632             &package_table_path,
633             &flag_table_path,
634             &flag_value_list_path,
635             &flag_info_list_path,
636         )
637         .unwrap();
638         let expected = [
639             FlagValueAndInfoSummary {
640                 package_name: String::from("com.android.aconfig.storage.test_1"),
641                 flag_name: String::from("disabled_rw"),
642                 value_type: StoredFlagType::ReadWriteBoolean,
643                 flag_value: String::from("false"),
644                 is_readwrite: true,
645                 has_server_override: false,
646                 has_local_override: false,
647             },
648             FlagValueAndInfoSummary {
649                 package_name: String::from("com.android.aconfig.storage.test_1"),
650                 flag_name: String::from("enabled_ro"),
651                 value_type: StoredFlagType::ReadOnlyBoolean,
652                 flag_value: String::from("true"),
653                 is_readwrite: false,
654                 has_server_override: false,
655                 has_local_override: false,
656             },
657             FlagValueAndInfoSummary {
658                 package_name: String::from("com.android.aconfig.storage.test_1"),
659                 flag_name: String::from("enabled_rw"),
660                 value_type: StoredFlagType::ReadWriteBoolean,
661                 flag_value: String::from("true"),
662                 is_readwrite: true,
663                 has_server_override: false,
664                 has_local_override: false,
665             },
666             FlagValueAndInfoSummary {
667                 package_name: String::from("com.android.aconfig.storage.test_2"),
668                 flag_name: String::from("disabled_rw"),
669                 value_type: StoredFlagType::ReadWriteBoolean,
670                 flag_value: String::from("false"),
671                 is_readwrite: true,
672                 has_server_override: false,
673                 has_local_override: false,
674             },
675             FlagValueAndInfoSummary {
676                 package_name: String::from("com.android.aconfig.storage.test_2"),
677                 flag_name: String::from("enabled_fixed_ro"),
678                 value_type: StoredFlagType::FixedReadOnlyBoolean,
679                 flag_value: String::from("true"),
680                 is_readwrite: false,
681                 has_server_override: false,
682                 has_local_override: false,
683             },
684             FlagValueAndInfoSummary {
685                 package_name: String::from("com.android.aconfig.storage.test_2"),
686                 flag_name: String::from("enabled_ro"),
687                 value_type: StoredFlagType::ReadOnlyBoolean,
688                 flag_value: String::from("true"),
689                 is_readwrite: false,
690                 has_server_override: false,
691                 has_local_override: false,
692             },
693             FlagValueAndInfoSummary {
694                 package_name: String::from("com.android.aconfig.storage.test_4"),
695                 flag_name: String::from("enabled_fixed_ro"),
696                 value_type: StoredFlagType::FixedReadOnlyBoolean,
697                 flag_value: String::from("true"),
698                 is_readwrite: false,
699                 has_server_override: false,
700                 has_local_override: false,
701             },
702             FlagValueAndInfoSummary {
703                 package_name: String::from("com.android.aconfig.storage.test_4"),
704                 flag_name: String::from("enabled_rw"),
705                 value_type: StoredFlagType::ReadWriteBoolean,
706                 flag_value: String::from("true"),
707                 is_readwrite: true,
708                 has_server_override: false,
709                 has_local_override: false,
710             },
711         ];
712         assert_eq!(flags, expected);
713     }
714 }
715