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

Commit 14521049 authored by Dennis Shen's avatar Dennis Shen
Browse files

aconfig: add top level flag read lib api

1, update android build soong targets as now libmemmap2 has been
introduced to android code base.

2, add three public apis for package offset query, flag offset query,
and flag value query. These apis assume the stoarge location pb file is
at /metadata/aconfig/aconfig_storage.pb. This file will be created by
storage daemon. For testing purposes, we also expose impl version of
these apis that accept a custom storage location pb file.

Bug: 321077378
Test: atest aconfig_storage_file.test
Change-Id: I91b284e2dfcdb8ff3674d463ed1c13696d9ae020
parent 81c7511f
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -13,6 +13,9 @@ rust_defaults {
        "libonce_cell",
        "libprotobuf",
        "libtempfile",
        "libmemmap2",
        "libcxx",
        "libthiserror",
    ],
}

@@ -23,10 +26,60 @@ rust_library {
    defaults: ["aconfig_storage_file.defaults"],
}

genrule {
    name: "ro.package.map",
    out: ["tests/tmp.ro.package.map"],
    srcs: ["tests/package.map"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
}

genrule {
    name: "ro.flag.map",
    out: ["tests/tmp.ro.flag.map"],
    srcs: ["tests/flag.map"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
}

genrule {
    name: "ro.flag.val",
    out: ["tests/tmp.ro.flag.val"],
    srcs: ["tests/flag.val"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
}

genrule {
    name: "rw.package.map",
    out: ["tests/tmp.rw.package.map"],
    srcs: ["tests/package.map"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod +w $(out)",
}

genrule {
    name: "rw.flag.map",
    out: ["tests/tmp.rw.flag.map"],
    srcs: ["tests/flag.map"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod +w $(out)",
}

genrule {
    name: "rw.flag.val",
    out: ["tests/tmp.rw.flag.val"],
    srcs: ["tests/flag.val"],
    cmd: "rm -f $(out);cp -f $(in) $(out);chmod +w $(out)",
}

rust_test_host {
    name: "aconfig_storage_file.test",
    test_suites: ["general-tests"],
    defaults: ["aconfig_storage_file.defaults"],
    data: [
        ":ro.package.map",
        ":ro.flag.map",
        ":ro.flag.val",
        ":rw.package.map",
        ":rw.flag.map",
        ":rw.flag.val",
    ],
}

rust_protobuf {
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@ memmap2 = "0.8.0"
protobuf = "3.2.0"
once_cell = "1.19.0"
tempfile = "3.9.0"
cxx = "1.0"
thiserror = "1.0.56"

[build-dependencies]
protobuf-codegen = "3.2.0"
+22 −15
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
//! flag table module defines the flag table file format and methods for serialization
//! and deserialization

use crate::AconfigStorageError::{self, BytesParseFail, HigherStorageFileVersion};
use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
use anyhow::{anyhow, Result};
use anyhow::anyhow;
pub type FlagOffset = u16;

/// Flag table header struct
#[derive(PartialEq, Debug)]
@@ -47,7 +49,7 @@ impl FlagTableHeader {
    }

    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
        let mut head = 0;
        Ok(Self {
            version: read_u32_from_bytes(bytes, &mut head)?,
@@ -85,7 +87,7 @@ impl FlagTableNode {
    }

    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
        let mut head = 0;
        let node = Self {
            package_id: read_u32_from_bytes(bytes, &mut head)?,
@@ -127,7 +129,7 @@ impl FlagTable {
    }

    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
        let header = FlagTableHeader::from_bytes(bytes)?;
        let num_flags = header.num_flags;
        let num_buckets = crate::get_table_size(num_flags)?;
@@ -144,7 +146,8 @@ impl FlagTable {
                head += node.as_bytes().len();
                Ok(node)
            })
            .collect::<Result<Vec<_>>>()?;
            .collect::<Result<Vec<_>, AconfigStorageError>>()
            .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg)))?;

        let table = Self { header, buckets, nodes };
        Ok(table)
@@ -152,14 +155,18 @@ impl FlagTable {
}

/// Query flag within package offset
pub fn find_flag_offset(buf: &[u8], package_id: u32, flag: &str) -> Result<Option<u16>> {
pub fn find_flag_offset(
    buf: &[u8],
    package_id: u32,
    flag: &str,
) -> Result<Option<FlagOffset>, AconfigStorageError> {
    let interpreted_header = FlagTableHeader::from_bytes(buf)?;
    if interpreted_header.version > crate::FILE_VERSION {
        return Err(anyhow!(
        return Err(HigherStorageFileVersion(anyhow!(
            "Cannot read storage file with a higher version of {} with lib version {}",
            interpreted_header.version,
            crate::FILE_VERSION
        ));
        )));
    }

    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
@@ -202,7 +209,7 @@ mod tests {
        }
    }

    pub fn create_test_flag_table() -> Result<FlagTable> {
    pub fn create_test_flag_table() -> FlagTable {
        let header = FlagTableHeader {
            version: crate::FILE_VERSION,
            container: String::from("system"),
@@ -240,13 +247,13 @@ mod tests {
            FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
            FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
        ];
        Ok(FlagTable { header, buckets, nodes })
        FlagTable { header, buckets, nodes }
    }

    #[test]
    // this test point locks down the table serialization
    fn test_serialization() {
        let flag_table = create_test_flag_table().unwrap();
        let flag_table = create_test_flag_table();

        let header: &FlagTableHeader = &flag_table.header;
        let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
@@ -267,7 +274,7 @@ mod tests {
    #[test]
    // this test point locks down table query
    fn test_flag_query() {
        let flag_table = create_test_flag_table().unwrap().as_bytes();
        let flag_table = create_test_flag_table().as_bytes();
        let baseline = vec![
            (0, "enabled_ro", 1u16),
            (0, "enabled_rw", 2u16),
@@ -288,7 +295,7 @@ mod tests {
    #[test]
    // this test point locks down table query of a non exist flag
    fn test_not_existed_flag_query() {
        let flag_table = create_test_flag_table().unwrap().as_bytes();
        let flag_table = create_test_flag_table().as_bytes();
        let flag_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
        assert_eq!(flag_offset, None);
        let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
@@ -298,14 +305,14 @@ mod tests {
    #[test]
    // this test point locks down query error when file has a higher version
    fn test_higher_version_storage_file() {
        let mut table = create_test_flag_table().unwrap();
        let mut table = create_test_flag_table();
        table.header.version = crate::FILE_VERSION + 1;
        let flag_table = table.as_bytes();
        let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
        assert_eq!(
            format!("{:?}", error),
            format!(
                "Cannot read storage file with a higher version of {} with lib version {}",
                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
                crate::FILE_VERSION + 1,
                crate::FILE_VERSION
            )
+24 −18
Original line number Diff line number Diff line
@@ -17,8 +17,9 @@
//! flag value module defines the flag value file format and methods for serialization
//! and deserialization

use crate::AconfigStorageError::{self, HigherStorageFileVersion, InvalidStorageFileOffset};
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
use anyhow::{anyhow, Result};
use anyhow::anyhow;

/// Flag value header struct
#[derive(PartialEq, Debug)]
@@ -45,7 +46,7 @@ impl FlagValueHeader {
    }

    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
        let mut head = 0;
        Ok(Self {
            version: read_u32_from_bytes(bytes, &mut head)?,
@@ -75,7 +76,7 @@ impl FlagValueList {
    }

    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
        let header = FlagValueHeader::from_bytes(bytes)?;
        let num_flags = header.num_flags;
        let mut head = header.as_bytes().len();
@@ -87,14 +88,14 @@ impl FlagValueList {
}

/// Query flag value
pub fn get_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool> {
pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
    let interpreted_header = FlagValueHeader::from_bytes(buf)?;
    if interpreted_header.version > crate::FILE_VERSION {
        return Err(anyhow!(
        return Err(HigherStorageFileVersion(anyhow!(
            "Cannot read storage file with a higher version of {} with lib version {}",
            interpreted_header.version,
            crate::FILE_VERSION
        ));
        )));
    }

    let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
@@ -102,7 +103,9 @@ pub fn get_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool> {
    // TODO: right now, there is only boolean flags, with more flag value types added
    // later, the end of boolean flag value section should be updated (b/322826265).
    if head >= interpreted_header.file_size as usize {
        return Err(anyhow!("Flag value offset goes beyond the end of the file."));
        return Err(InvalidStorageFileOffset(anyhow!(
            "Flag value offset goes beyond the end of the file."
        )));
    }

    let val = read_u8_from_bytes(buf, &mut head)?;
@@ -113,7 +116,7 @@ pub fn get_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool> {
mod tests {
    use super::*;

    pub fn create_test_flag_value_list() -> Result<FlagValueList> {
    pub fn create_test_flag_value_list() -> FlagValueList {
        let header = FlagValueHeader {
            version: crate::FILE_VERSION,
            container: String::from("system"),
@@ -122,13 +125,13 @@ mod tests {
            boolean_value_offset: 26,
        };
        let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
        Ok(FlagValueList { header, booleans })
        FlagValueList { header, booleans }
    }

    #[test]
    // this test point locks down the value list serialization
    fn test_serialization() {
        let flag_value_list = create_test_flag_value_list().unwrap();
        let flag_value_list = create_test_flag_value_list();

        let header: &FlagValueHeader = &flag_value_list.header;
        let reinterpreted_header = FlagValueHeader::from_bytes(&header.as_bytes());
@@ -143,10 +146,10 @@ mod tests {
    #[test]
    // this test point locks down flag value query
    fn test_flag_value_query() {
        let flag_value_list = create_test_flag_value_list().unwrap().as_bytes();
        let flag_value_list = create_test_flag_value_list().as_bytes();
        let baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
        for (offset, expected_value) in baseline.into_iter().enumerate() {
            let flag_value = get_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
            let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
            assert_eq!(flag_value, expected_value);
        }
    }
@@ -154,22 +157,25 @@ mod tests {
    #[test]
    // this test point locks down query beyond the end of boolean section
    fn test_boolean_out_of_range() {
        let flag_value_list = create_test_flag_value_list().unwrap().as_bytes();
        let error = get_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
        assert_eq!(format!("{:?}", error), "Flag value offset goes beyond the end of the file.");
        let flag_value_list = create_test_flag_value_list().as_bytes();
        let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
        assert_eq!(
            format!("{:?}", error),
            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
        );
    }

    #[test]
    // this test point locks down query error when file has a higher version
    fn test_higher_version_storage_file() {
        let mut value_list = create_test_flag_value_list().unwrap();
        let mut value_list = create_test_flag_value_list();
        value_list.header.version = crate::FILE_VERSION + 1;
        let flag_value = value_list.as_bytes();
        let error = get_boolean_flag_value(&flag_value[..], 4).unwrap_err();
        let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
        assert_eq!(
            format!("{:?}", error),
            format!(
                "Cannot read storage file with a higher version of {} with lib version {}",
                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
                crate::FILE_VERSION + 1,
                crate::FILE_VERSION
            )
+293 −20

File changed.

Preview size limit exceeded, changes collapsed.

Loading