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

Commit a3e7af7f authored by Zhi Dou's avatar Zhi Dou
Browse files

add skip_api_filter_list

Currently all flags in the allow_list (renamed to non_api_flags_list)
will be filtered out from the flag library for external apps.

However there is an existing flag used in SettingsLib, and need to be
shared for the usage of the apps not in Gerrit. The clean up plan for
the flag is once the feature is released, the flag can be removed from
the code without replacing to other calls. Temporary adding this file to
support this case.

Test: atest exported-flag-check-test
Bug: 408440684
Change-Id: Ic7d1c0463b5eda6ca514fa92c34dc385d7e32db9
parent d860e1e6
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
// This flag is used in SettingsLibSettingsTheme. This flag is
// exported, but it is not used for any API. It should be removed from
// API flag libary. However because of the current setup, this flag should
// still be in the library. This flag should be removed from the code base
// after release of the new feature
com.android.settingslib.widget.theme.flags.is_expressive_design_enabled
+49 −18
Original line number Original line Diff line number Diff line
@@ -27,7 +27,7 @@ use utils::{
    get_exported_flags_from_binary_proto, read_flag_from_binary, FlagId,
    get_exported_flags_from_binary_proto, read_flag_from_binary, FlagId,
};
};


const HELP: &str = "CCheck Exported Flags
const HELP: &str = "Check Exported Flags


This tool ensures that exported flags are used as intended. Exported flags, marked with
This tool ensures that exported flags are used as intended. Exported flags, marked with
`is_exported: true` in their declaration, are designed to control access to specific API
`is_exported: true` in their declaration, are designed to control access to specific API
@@ -35,14 +35,39 @@ features. This tool identifies and reports any exported flags that are not curre
associated with an API feature, preventing unnecessary flag proliferation and maintaining
associated with an API feature, preventing unnecessary flag proliferation and maintaining
a clear API design.
a clear API design.


This tool works as follows:
Commands:

This tool offers two commands:

1. validate-exported-flags :This command verifies that all exported flags within 
   the current source tree are actively used to guard API features.

Arguments:
    --parsed-flags-file: Current aconfig flag values from source tree
    --api-signature-file: API signature files from source tree (*current.txt files)
    --finalized-flags-file: The previous finalized-flags.txt files from prebuilts/sdk

Example:
exported-flag-tool validate-exported-flags \
    --parsed-flags-file out/soong/aconfig/parsed_flags.pb \
    --api-signature-file frameworks/base/api/current.txt \
    --api-signature-file external/library/api/current.txt \
    --finalized-flags-file prebuilts/sdk/34/public/finalized-flags.txt

2. filter-api-flags: This command processes an input list of flags and filters it, based on
   the non-api lists to produce an output file containing only the exported flags that
   are used for controlling API features.

Arguments:
    --cache: The path to the input aconfig flag proto file
    --out: The output file

Example:
exported-flag-tool filter-api-flags \
    --cache build/intermediate/foo_flags.pb \
    --cache build/intermediate/bar_flags.pb \
    --out build/intermediate/api_relevant_exported_flags.pb


  - Read API signature files from source tree (*current.txt files) [--api-signature-file]
  - Read the current aconfig flag values from source tree [--parsed-flags-file]
  - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file]
  - Extract the flags slated for API by scanning through the API signature files
  - Merge the found flags with the recorded flags from previous API finalizations
  - Error if exported flags are not in the set
";
";


fn cli() -> Command {
fn cli() -> Command {
@@ -86,7 +111,7 @@ fn validate_exported_flags<R: Read>(
    parsed_flags_file: R,
    parsed_flags_file: R,
    api_signature_files: Vec<R>,
    api_signature_files: Vec<R>,
    finalized_flags_file: R,
    finalized_flags_file: R,
    allow_flag_file: R,
    non_api_flags: R,
    allow_flag_package: R,
    allow_flag_package: R,
) -> Result<Vec<FlagId>> {
) -> Result<Vec<FlagId>> {
    let mut flags_used_with_flaggedapi_annotation = HashSet::new();
    let mut flags_used_with_flaggedapi_annotation = HashSet::new();
@@ -96,7 +121,7 @@ fn validate_exported_flags<R: Read>(
    }
    }
    let all_flags = get_exported_flags_from_binary_proto(parsed_flags_file)?;
    let all_flags = get_exported_flags_from_binary_proto(parsed_flags_file)?;
    let already_finalized_flags = read_flag_from_binary(finalized_flags_file)?;
    let already_finalized_flags = read_flag_from_binary(finalized_flags_file)?;
    let allow_flag_set = read_flag_from_binary(allow_flag_file)?;
    let allow_flag_set = read_flag_from_binary(non_api_flags)?;
    let allow_package_set = read_flag_from_binary(allow_flag_package)?;
    let allow_package_set = read_flag_from_binary(allow_flag_package)?;


    let exported_flags = check_all_exported_flags(
    let exported_flags = check_all_exported_flags(
@@ -119,15 +144,15 @@ fn main() -> Result<()> {
            let parsed_flags_file = open_single_file(sub_matches, "parsed-flags-file")?;
            let parsed_flags_file = open_single_file(sub_matches, "parsed-flags-file")?;
            let api_signature_files = open_multiple_files(sub_matches, "api-signature-file")?;
            let api_signature_files = open_multiple_files(sub_matches, "api-signature-file")?;
            let finalized_flags_file = open_single_file(sub_matches, "finalized-flags-file")?;
            let finalized_flags_file = open_single_file(sub_matches, "finalized-flags-file")?;
            let allow_flag_file = include_str!("../allow_flag_list.txt");
            let non_api_flags = include_str!("../non_api_flags_list.txt");
            let allow_flag_package = include_str!("../allow_package_list.txt");
            let non_api_flags_packages = include_str!("../non_api_flags_packages.txt");


            let exported_flags = validate_exported_flags(
            let exported_flags = validate_exported_flags(
                parsed_flags_file,
                parsed_flags_file,
                api_signature_files,
                api_signature_files,
                finalized_flags_file,
                finalized_flags_file,
                Box::new(allow_flag_file.as_bytes()),
                Box::new(non_api_flags.as_bytes()),
                Box::new(allow_flag_package.as_bytes()),
                Box::new(non_api_flags_packages.as_bytes()),
            )?;
            )?;


            ensure!(
            ensure!(
@@ -144,8 +169,14 @@ fn main() -> Result<()> {
                bail!("argument out is missing");
                bail!("argument out is missing");
            };
            };
            let out_file = PathBuf::from(out_file_arg);
            let out_file = PathBuf::from(out_file_arg);
            let allow_flag_file = &include_str!("../allow_flag_list.txt");
            let mut non_api_flags_set =
            let filtered_cache = filter_api_flags(cache, Box::new(allow_flag_file.as_bytes()))?;
                read_flag_from_binary(&include_bytes!("../non_api_flags_list.txt")[..])?;
            let skip_flags_set =
                read_flag_from_binary(&include_bytes!("../skip_api_filter_list.txt")[..])?;
            skip_flags_set.iter().for_each(|flag| {
                non_api_flags_set.remove(flag);
            });
            let filtered_cache = filter_api_flags(cache, &non_api_flags_set)?;
            let parent = out_file
            let parent = out_file
                .parent()
                .parent()
                .ok_or(anyhow!("unable to locate parent of output file {}", out_file.display()))?;
                .ok_or(anyhow!("unable to locate parent of output file {}", out_file.display()))?;
@@ -172,14 +203,14 @@ mod tests {
            vec![&include_bytes!("../tests/api-signature-file.txt")[..]];
            vec![&include_bytes!("../tests/api-signature-file.txt")[..]];
        let all_flags_to_be_finalized = include_bytes!("../tests/flags.protobuf");
        let all_flags_to_be_finalized = include_bytes!("../tests/flags.protobuf");
        let already_finalized_flags = include_bytes!("../tests/finalized-flags.txt");
        let already_finalized_flags = include_bytes!("../tests/finalized-flags.txt");
        let allow_flag_file = "record_finalized_flags.test.boo".as_bytes();
        let non_api_flags = "record_finalized_flags.test.boo".as_bytes();
        let allow_flag_package = "".as_bytes();
        let allow_flag_package = "".as_bytes();


        let exported_flags = validate_exported_flags(
        let exported_flags = validate_exported_flags(
            &all_flags_to_be_finalized[..],
            &all_flags_to_be_finalized[..],
            flags_used_with_flaggedapi_annotation,
            flags_used_with_flaggedapi_annotation,
            &already_finalized_flags[..],
            &already_finalized_flags[..],
            allow_flag_file,
            non_api_flags,
            allow_flag_package,
            allow_flag_package,
        )
        )
        .unwrap();
        .unwrap();
+32 −4
Original line number Original line Diff line number Diff line
@@ -42,6 +42,8 @@ pub(crate) fn read_flag_from_binary<R: Read>(reader: R) -> Result<HashSet<FlagId
        .lines()
        .lines()
        .map_while(Result::ok) // Ignore lines that fail to read
        .map_while(Result::ok) // Ignore lines that fail to read
        .map(|line| line.trim().to_string())
        .map(|line| line.trim().to_string())
        .filter(|line| !line.is_empty())
        .filter(|line| !line.starts_with("/"))
        .collect())
        .collect())
}
}


@@ -93,8 +95,10 @@ pub(crate) fn check_all_exported_flags(
    Ok(new_flags)
    Ok(new_flags)
}
}


pub(crate) fn filter_api_flags<R: Read>(mut cache: R, non_api_flag_file: R) -> Result<Vec<u8>> {
pub(crate) fn filter_api_flags<R: Read>(
    let non_api_flag_set = read_flag_from_binary(non_api_flag_file)?;
    mut cache: R,
    non_api_flag_set: &HashSet<FlagId>,
) -> Result<Vec<u8>> {
    let mut buffer = Vec::new();
    let mut buffer = Vec::new();
    cache.read_to_end(&mut buffer)?;
    cache.read_to_end(&mut buffer)?;
    let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
    let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
@@ -166,7 +170,9 @@ mod tests {
        record_finalized_flags.test.not_enabled
        record_finalized_flags.test.not_enabled
        "#
        "#
        .as_bytes();
        .as_bytes();
        let flags = filter_api_flags(&bytes[..], allow_flag_file).unwrap();

        let allow_flag_set = read_flag_from_binary(allow_flag_file).unwrap();
        let flags = filter_api_flags(&bytes[..], &allow_flag_set).unwrap();
        let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags).unwrap();
        let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags).unwrap();
        assert_eq!(2, parsed_flags.parsed_flag.len());
        assert_eq!(2, parsed_flags.parsed_flag.len());


@@ -190,7 +196,8 @@ mod tests {
        record_finalized_flags.test.not_enabled
        record_finalized_flags.test.not_enabled
        "#
        "#
        .as_bytes();
        .as_bytes();
        let flags = filter_api_flags(&bytes[..], allow_flag_file).unwrap();
        let allow_flag_set = read_flag_from_binary(allow_flag_file).unwrap();
        let flags = filter_api_flags(&bytes[..], &allow_flag_set).unwrap();
        let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags).unwrap();
        let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags).unwrap();
        assert_eq!(1, parsed_flags.parsed_flag.len());
        assert_eq!(1, parsed_flags.parsed_flag.len());


@@ -202,4 +209,25 @@ mod tests {
            .collect::<HashSet<FlagId>>();
            .collect::<HashSet<FlagId>>();
        assert_eq!(ret, HashSet::from_iter(vec!["record_finalized_flags.test.bar".to_string(),]));
        assert_eq!(ret, HashSet::from_iter(vec!["record_finalized_flags.test.bar".to_string(),]));
    }
    }

    #[test]
    fn test_read_flag_from_binary() {
        let test_binary_file = r#"
        // This is a comment
        //record_finalized_flags.test.not_enabled
        record_finalized_flags.test.bar

        record_finalized_flags.test.baz
        "#
        .as_bytes();
        let ret = read_flag_from_binary(test_binary_file).unwrap();
        assert_eq!(2, ret.len());
        assert_eq!(
            ret,
            HashSet::from_iter(vec![
                "record_finalized_flags.test.bar".to_string(),
                "record_finalized_flags.test.baz".to_string(),
            ])
        );
    }
}
}
Loading