Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0329cda7 authored by Dennis Shen's avatar Dennis Shen Committed by Gerrit Code Review
Browse files

Merge "aconfig: create aconfig_storage_file binary to print the storage files" into main

parents cdde8f39 19030498
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ rust_defaults {
    rustlibs: [
        "libanyhow",
        "libthiserror",
        "libtempfile",
    ],
}

+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ once_cell = "1.19.0"
tempfile = "3.9.0"
cxx = "1.0"
thiserror = "1.0.56"
clap = { version = "4.1.8", features = ["derive"] }

[build-dependencies]
protobuf-codegen = "3.2.0"
+49 −57
Original line number Diff line number Diff line
@@ -20,9 +20,10 @@
use crate::AconfigStorageError::{self, BytesParseFail};
use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
use anyhow::anyhow;
use std::fmt;

/// Flag table header struct
#[derive(PartialEq, Debug)]
#[derive(PartialEq)]
pub struct FlagTableHeader {
    pub version: u32,
    pub container: String,
@@ -32,6 +33,23 @@ pub struct FlagTableHeader {
    pub node_offset: u32,
}

/// Implement debug print trait for header
impl fmt::Debug for FlagTableHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "Version: {}, Container: {}, File Size: {}",
            self.version, self.container, self.file_size
        )?;
        writeln!(
            f,
            "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}",
            self.num_flags, self.bucket_offset, self.node_offset
        )?;
        Ok(())
    }
}

impl FlagTableHeader {
    /// Serialize to bytes
    pub fn as_bytes(&self) -> Vec<u8> {
@@ -62,7 +80,7 @@ impl FlagTableHeader {
}

/// Flag table node struct
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Clone)]
pub struct FlagTableNode {
    pub package_id: u32,
    pub flag_name: String,
@@ -71,6 +89,18 @@ pub struct FlagTableNode {
    pub next_offset: Option<u32>,
}

/// Implement debug print trait for node
impl fmt::Debug for FlagTableNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}",
            self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset
        )?;
        Ok(())
    }
}

impl FlagTableNode {
    /// Serialize to bytes
    pub fn as_bytes(&self) -> Vec<u8> {
@@ -108,13 +138,28 @@ impl FlagTableNode {
    }
}

#[derive(PartialEq, Debug)]
#[derive(PartialEq)]
pub struct FlagTable {
    pub header: FlagTableHeader,
    pub buckets: Vec<Option<u32>>,
    pub nodes: Vec<FlagTableNode>,
}

/// Implement debug print trait for flag table
impl fmt::Debug for FlagTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Header:")?;
        write!(f, "{:?}", self.header)?;
        writeln!(f, "Buckets:")?;
        writeln!(f, "{:?}", self.buckets)?;
        writeln!(f, "Nodes:")?;
        for node in self.nodes.iter() {
            write!(f, "{:?}", node)?;
        }
        Ok(())
    }
}

/// Flag table struct
impl FlagTable {
    /// Serialize to bytes
@@ -156,60 +201,7 @@ impl FlagTable {
#[cfg(test)]
mod tests {
    use super::*;

    impl FlagTableNode {
        // create test baseline, syntactic sugar
        fn new_expected(
            package_id: u32,
            flag_name: &str,
            flag_type: u16,
            flag_id: u16,
            next_offset: Option<u32>,
        ) -> Self {
            Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
        }
    }

    pub fn create_test_flag_table() -> FlagTable {
        let header = FlagTableHeader {
            version: 1234,
            container: String::from("system"),
            file_size: 320,
            num_flags: 8,
            bucket_offset: 30,
            node_offset: 98,
        };
        let buckets: Vec<Option<u32>> = vec![
            Some(98),
            Some(124),
            None,
            None,
            None,
            Some(177),
            None,
            Some(203),
            None,
            Some(261),
            None,
            None,
            None,
            None,
            None,
            Some(293),
            None,
        ];
        let nodes = vec![
            FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
            FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
            FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
            FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
            FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
            FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
            FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
            FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
        ];
        FlagTable { header, buckets, nodes }
    }
    use crate::test_utils::create_test_flag_table;

    #[test]
    // this test point locks down the table serialization
+32 −14
Original line number Diff line number Diff line
@@ -19,9 +19,10 @@

use crate::AconfigStorageError;
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
use std::fmt;

/// Flag value header struct
#[derive(PartialEq, Debug)]
#[derive(PartialEq)]
pub struct FlagValueHeader {
    pub version: u32,
    pub container: String,
@@ -30,6 +31,23 @@ pub struct FlagValueHeader {
    pub boolean_value_offset: u32,
}

/// Implement debug print trait for header
impl fmt::Debug for FlagValueHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "Version: {}, Container: {}, File Size: {}",
            self.version, self.container, self.file_size
        )?;
        writeln!(
            f,
            "Num of Flags: {}, Value Offset:{}",
            self.num_flags, self.boolean_value_offset
        )?;
        Ok(())
    }
}

impl FlagValueHeader {
    /// Serialize to bytes
    pub fn as_bytes(&self) -> Vec<u8> {
@@ -58,12 +76,23 @@ impl FlagValueHeader {
}

/// Flag value list struct
#[derive(PartialEq, Debug)]
#[derive(PartialEq)]
pub struct FlagValueList {
    pub header: FlagValueHeader,
    pub booleans: Vec<bool>,
}

/// Implement debug print trait for flag value
impl fmt::Debug for FlagValueList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Header:")?;
        write!(f, "{:?}", self.header)?;
        writeln!(f, "Values:")?;
        writeln!(f, "{:?}", self.booleans)?;
        Ok(())
    }
}

impl FlagValueList {
    /// Serialize to bytes
    pub fn as_bytes(&self) -> Vec<u8> {
@@ -89,18 +118,7 @@ impl FlagValueList {
#[cfg(test)]
mod tests {
    use super::*;

    pub fn create_test_flag_value_list() -> FlagValueList {
        let header = FlagValueHeader {
            version: 1234,
            container: String::from("system"),
            file_size: 34,
            num_flags: 8,
            boolean_value_offset: 26,
        };
        let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
        FlagValueList { header, booleans }
    }
    use crate::test_utils::create_test_flag_value_list;

    #[test]
    // this test point locks down the value list serialization
+89 −0
Original line number Diff line number Diff line
@@ -36,9 +36,14 @@ pub mod flag_table;
pub mod flag_value;
pub mod package_table;

#[cfg(test)]
mod test_utils;

use anyhow::anyhow;
use std::collections::hash_map::DefaultHasher;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Read;

pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
@@ -166,4 +171,88 @@ pub enum AconfigStorageError {

    #[error("invalid storage file byte offset")]
    InvalidStorageFileOffset(#[source] anyhow::Error),

    #[error("failed to create file")]
    FileCreationFail(#[source] anyhow::Error),
}

/// Read in storage file as bytes
pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
    let mut file = File::open(file_path).map_err(|errmsg| {
        AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
    })?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).map_err(|errmsg| {
        AconfigStorageError::FileReadFail(anyhow!(
            "Failed to read 4 bytes from file {}: {}",
            file_path,
            errmsg
        ))
    })?;
    Ok(buffer)
}

/// List flag values from storage files
pub fn list_flags(
    package_map: &str,
    flag_map: &str,
    flag_val: &str,
) -> Result<Vec<(String, bool)>, AconfigStorageError> {
    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;

    let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
    for node in package_table.nodes.iter() {
        package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset);
    }

    let mut flags = Vec::new();
    for node in flag_table.nodes.iter() {
        let (package_name, package_offset) = package_info[node.package_id as usize];
        let full_flag_name = String::from(package_name) + "/" + &node.flag_name;
        let flag_offset = package_offset + node.flag_id as u32;
        let flag_value = flag_value_list.booleans[flag_offset as usize];
        flags.push((full_flag_name, flag_value));
    }

    flags.sort_by(|v1, v2| v1.0.cmp(&v2.0));
    Ok(flags)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{
        create_test_flag_table, create_test_flag_value_list, create_test_package_table,
        write_bytes_to_temp_file,
    };

    #[test]
    // this test point locks down the flag list api
    fn test_list_flag() {
        let package_table =
            write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap();
        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap();
        let flag_value_list =
            write_bytes_to_temp_file(&create_test_flag_value_list().as_bytes()).unwrap();

        let package_table_path = package_table.path().display().to_string();
        let flag_table_path = flag_table.path().display().to_string();
        let flag_value_list_path = flag_value_list.path().display().to_string();

        let flags =
            list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
        let expected = [
            (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false),
            (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true),
            (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false),
            (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false),
            (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true),
            (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true),
            (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false),
            (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true),
        ];
        assert_eq!(flags, expected);
    }
}
Loading