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

Commit 5badb85e authored by Marybeth Fair's avatar Marybeth Fair Committed by Gerrit Code Review
Browse files

Merge "Refactor fingerprint logic." into main

parents 897f4567 c505dbfa
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -68,6 +68,14 @@ aconfig_values {
    ],
}

aconfig_values {
    name: "aconfig.test.flag.second_values",
    package: "com.android.aconfig.test",
    srcs: [
        "tests/third.values",
    ],
}

aconfig_value_set {
    name: "aconfig.test.flag.value_set",
    values: [
+62 −16
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
use anyhow::{bail, ensure, Context, Result};
use itertools::Itertools;
use protobuf::Message;
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::hash::Hasher;
use std::io::Read;
use std::path::PathBuf;
@@ -425,23 +425,34 @@ where

#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
                    // protect hardcoded offset reads.
pub fn compute_flag_offsets_fingerprint(flags_map: &HashMap<String, u16>) -> Result<u64> {
    let mut hasher = SipHasher13::new();

    // Need to sort to ensure the data is added to the hasher in the same order
    // each run.
    let sorted_map: BTreeMap<&String, &u16> = flags_map.iter().collect();
                    // Creates a fingerprint of the flag names (which requires sorting the vector).
                    // Fingerprint is used by both codegen and storage files.
pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> Result<u64> {
    flag_names.sort();

    for (flag, offset) in sorted_map {
        // See https://docs.rs/siphasher/latest/siphasher/#note for use of write
        // over write_i16. Similarly, use to_be_bytes rather than to_ne_bytes to
        // ensure consistency.
    let mut hasher = SipHasher13::new();
    for flag in flag_names {
        hasher.write(flag.as_bytes());
        hasher.write(&offset.to_be_bytes());
    }
    Ok(hasher.finish())
}

#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
                    // protect hardcoded offset reads.
                    // Converts ProtoParsedFlags into a vector of strings containing all of the flag
                    // names. Helper fn for creating fingerprint for codegen files. Flags must all
                    // belong to the same package.
fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {
    let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>();

    // All flags must belong to the same package as the fingerprint is per-package.
    let Some(_package) = find_unique_package(&separated_flags) else {
        bail!("No parsed flags, or the parsed flags use different packages.");
    };

    Ok(separated_flags.into_iter().map(|flag| flag.name.unwrap()).collect::<Vec<_>>())
}

#[cfg(test)]
mod tests {
    use super::*;
@@ -450,15 +461,50 @@ mod tests {
    #[test]
    fn test_offset_fingerprint() {
        let parsed_flags = crate::test::parse_test_flags();
        let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();
        let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();
        let expected_fingerprint = 10709892481002252132u64;
        let expected_fingerprint: u64 = 5801144784618221668;

        let hash_result = compute_flag_offsets_fingerprint(&flag_ids);
        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
        let hash_result = compute_flags_fingerprint(&mut extracted_flags);

        assert_eq!(hash_result.unwrap(), expected_fingerprint);
    }

    #[test]
    fn test_offset_fingerprint_matches_from_package() {
        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();

        // All test flags are in the same package, so fingerprint from all of them.
        let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap();
        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);

        let mut flag_names_vec = parsed_flags
            .parsed_flag
            .clone()
            .into_iter()
            .map(|flag| flag.name.unwrap())
            .map(String::from)
            .collect::<Vec<_>>();
        let result_from_names = compute_flags_fingerprint(&mut flag_names_vec);

        // Assert the same hash is generated for each case.
        assert_eq!(result_from_parsed_flags.unwrap(), result_from_names.unwrap());
    }

    #[test]
    fn test_offset_fingerprint_different_packages_does_not_match() {
        // Parse flags from two packages.
        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();
        let second_parsed_flags = crate::test::parse_second_package_flags();

        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags).unwrap();
        let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap();
        let second_result = compute_flags_fingerprint(&mut second_extracted_flags).unwrap();

        // Different flags should have a different fingerprint.
        assert_ne!(result_from_parsed_flags, second_result);
    }

    #[test]
    fn test_parse_flags() {
        let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
+18 −0
Original line number Diff line number Diff line
@@ -295,6 +295,24 @@ parsed_flag {
        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
    }

    pub fn parse_second_package_flags() -> ProtoParsedFlags {
        let bytes = crate::commands::parse_flags(
            "com.android.aconfig.second_test",
            Some("system"),
            vec![Input {
                source: "tests/test_second_package.aconfig".to_string(),
                reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()),
            }],
            vec![Input {
                source: "tests/third.values".to_string(),
                reader: Box::new(include_bytes!("../tests/third.values").as_slice()),
            }],
            crate::commands::DEFAULT_FLAG_PERMISSION,
        )
        .unwrap();
        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
    }

    pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
        let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
        let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
+10 −0
Original line number Diff line number Diff line
package: "com.android.aconfig.second_test"
container: "system"

flag {
    name: "testing_flag"
    namespace: "another_namespace"
    description: "This is a flag for testing."
    bug: "123"
}
+6 −0
Original line number Diff line number Diff line
flag_value {
    package: "com.android.aconfig.second_test"
    name: "testing_flag"
    state: DISABLED
    permission: READ_WRITE
}