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