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

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

Enforce supported container for mainline beta namespaces

For mainline beta namespaces, only platform container or the
corresponding mainline module container are supported. If a mainline
module A tries to create a flag in module B's namespace, the build
should fail.

Bug: b/406508083
Test: atest -c
Change-Id: I7ad0db1195e095952f06ba5680f57ac08cd6e90a
parent 0105ad32
Loading
Loading
Loading
Loading
+73 −18
Original line number Original line Diff line number Diff line
@@ -74,6 +74,8 @@ pub struct OutputFile {
pub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
pub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
pub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;
pub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;


pub const PLATFORM_CONTAINERS: [&str; 4] = ["system", "system_ext", "product", "vendor"];

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct NamespaceSetting {
pub struct NamespaceSetting {
    pub container: String,
    pub container: String,
@@ -94,6 +96,18 @@ impl MainlineBetaNamespaces {
        }
        }
    }
    }


    // for each mainline beta namespace, only platform and the corresponding
    // module containers are allowed
    fn supports_container(&self, pf: &ProtoParsedFlag) -> bool {
        match self.namespaces.get(pf.namespace()) {
            Some(setting) => {
                setting.container == pf.container()
                    || PLATFORM_CONTAINERS.iter().any(|&c| c == pf.container())
            }
            None => true, // we do not enforce this for flags in non mainline beta namespaces
        }
    }

    fn supports_exported_mode(&self, pf: &ProtoParsedFlag) -> bool {
    fn supports_exported_mode(&self, pf: &ProtoParsedFlag) -> bool {
        match self.namespaces.get(pf.namespace()) {
        match self.namespaces.get(pf.namespace()) {
            Some(setting) => {
            Some(setting) => {
@@ -131,6 +145,21 @@ fn assign_storage_backend(
    Ok(())
    Ok(())
}
}


fn verify_mainline_beta_namespace_flag(
    pf: &mut ProtoParsedFlag,
    beta_namespaces: &Option<MainlineBetaNamespaces>,
) -> Result<()> {
    if let Some(namespaces) = beta_namespaces {
        ensure!(
            namespaces.supports_container(pf),
            "Creating {} container flag in namespace {} is not allowed",
            pf.container(),
            pf.namespace()
        );
    }
    Ok(())
}

pub fn parse_flags(
pub fn parse_flags(
    package: &str,
    package: &str,
    container: &str,
    container: &str,
@@ -206,6 +235,7 @@ pub fn parse_flags(
            metadata.set_purpose(purpose);
            metadata.set_purpose(purpose);
            parsed_flag.metadata = Some(metadata).into();
            parsed_flag.metadata = Some(metadata).into();
            assign_storage_backend(&mut parsed_flag, &beta_namespaces)?;
            assign_storage_backend(&mut parsed_flag, &beta_namespaces)?;
            verify_mainline_beta_namespace_flag(&mut parsed_flag, &beta_namespaces)?;


            // verify ParsedFlag looks reasonable
            // verify ParsedFlag looks reasonable
            aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;
            aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;
@@ -509,11 +539,7 @@ fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {


// Check if a flag should be managed by aconfigd
// Check if a flag should be managed by aconfigd
pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {
pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {
    let is_platform_container = pf.container == Some("vendor".to_string())
    let is_platform_container = PLATFORM_CONTAINERS.iter().any(|&c| c == pf.container());
        || pf.container == Some("system".to_string())
        || pf.container == Some("system_ext".to_string())
        || pf.container == Some("product".to_string());

    let is_disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())
    let is_disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())
        && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into());
        && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into());


@@ -930,7 +956,7 @@ mod tests {
        decl: &'static str,
        decl: &'static str,
        val: Option<&'static str>,
        val: Option<&'static str>,
        config: Option<PathBuf>,
        config: Option<PathBuf>,
    ) -> ProtoParsedFlag {
    ) -> Result<ProtoParsedFlag> {
        let declaration =
        let declaration =
            vec![Input { source: "memory".to_string(), reader: Box::new(decl.as_bytes()) }];
            vec![Input { source: "memory".to_string(), reader: Box::new(decl.as_bytes()) }];


@@ -951,14 +977,12 @@ mod tests {
            ProtoFlagPermission::READ_WRITE,
            ProtoFlagPermission::READ_WRITE,
            true,
            true,
            config,
            config,
        )
        )?;
        .unwrap();


        let parsed_flags =
        let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes)?;
            aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();


        assert_eq!(1, parsed_flags.parsed_flag.len());
        assert_eq!(1, parsed_flags.parsed_flag.len());
        parsed_flags.parsed_flag.first().unwrap().clone()
        Ok(parsed_flags.parsed_flag.first().unwrap().clone())
    }
    }


    #[test]
    #[test]
@@ -978,7 +1002,8 @@ mod tests {


        // Case 1, regular RW flag without value file override
        // Case 1, regular RW flag without value file override
        let parsed_flag =
        let parsed_flag =
            get_parsed_flag_proto("test", "com.first", metadata_flag, None, config.clone());
            get_parsed_flag_proto("test", "com.first", metadata_flag, None, config.clone())
                .unwrap();
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());


        // Case 2, regular RW flag with value file override to RO
        // Case 2, regular RW flag with value file override to RO
@@ -996,7 +1021,8 @@ mod tests {
            metadata_flag,
            metadata_flag,
            Some(first_flag_value),
            Some(first_flag_value),
            config.clone(),
            config.clone(),
        );
        )
        .unwrap();
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());


        // Case 3, fixed read only flag
        // Case 3, fixed read only flag
@@ -1013,7 +1039,8 @@ mod tests {
        "#;
        "#;


        let parsed_flag =
        let parsed_flag =
            get_parsed_flag_proto("test", "com.first", metadata_flag, None, config.clone());
            get_parsed_flag_proto("test", "com.first", metadata_flag, None, config.clone())
                .unwrap();
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());


        // Case 4, mainline beta namespace fixed read only flag
        // Case 4, mainline beta namespace fixed read only flag
@@ -1034,7 +1061,8 @@ mod tests {
            metadata_flag,
            metadata_flag,
            None,
            None,
            config.clone(),
            config.clone(),
        );
        )
        .unwrap();
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::NONE, parsed_flag.metadata.storage());


        // Case 5, mainline beta namespace platform flag
        // Case 5, mainline beta namespace platform flag
@@ -1049,7 +1077,8 @@ mod tests {
        }
        }
        "#;
        "#;
        let parsed_flag =
        let parsed_flag =
            get_parsed_flag_proto("system", "com.first", metadata_flag, None, config.clone());
            get_parsed_flag_proto("system", "com.first", metadata_flag, None, config.clone())
                .unwrap();
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());


        // Case 6, mainline beta namespace mainline flag
        // Case 6, mainline beta namespace mainline flag
@@ -1069,7 +1098,8 @@ mod tests {
            metadata_flag,
            metadata_flag,
            None,
            None,
            config.clone(),
            config.clone(),
        );
        )
        .unwrap();
        assert_eq!(ProtoFlagStorageBackend::DEVICE_CONFIG, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::DEVICE_CONFIG, parsed_flag.metadata.storage());


        // Case 7, mainline beta namespace mainline flag but without config
        // Case 7, mainline beta namespace mainline flag but without config
@@ -1084,8 +1114,33 @@ mod tests {
        }
        }
        "#;
        "#;
        let parsed_flag =
        let parsed_flag =
            get_parsed_flag_proto("com.android.tethering", "com.first", metadata_flag, None, None);
            get_parsed_flag_proto("com.android.tethering", "com.first", metadata_flag, None, None)
                .unwrap();
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());
        assert_eq!(ProtoFlagStorageBackend::ACONFIGD, parsed_flag.metadata.storage());

        // Case 8, mainline beta namespace invalid container
        let metadata_flag = r#"
        package: "com.first"
        container: "com.android.tethering"
        flag {
            name: "first"
            namespace: "com_android_networkstack"
            description: "This is the description of this feature flag."
            bug: "123"
        }
        "#;
        let error = get_parsed_flag_proto(
            "com.android.tethering",
            "com.first",
            metadata_flag,
            None,
            config.clone(),
        )
        .unwrap_err();
        assert_eq!(
            format!("{:?}", error),
            "Creating com.android.tethering container flag in namespace com_android_networkstack is not allowed"
        );
    }
    }


    #[test]
    #[test]