diff options
29 files changed, 729 insertions, 90 deletions
diff --git a/core/Makefile b/core/Makefile index e563873798..00577edfa5 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1477,13 +1477,14 @@ boototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K) ifeq ($(BOARD_16K_OTA_MOVE_VENDOR),true) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip)) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip)) +ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip +ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip else $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT)/boot_otas/boot_ota_4k.zip)) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT)/boot_otas/boot_ota_16k.zip)) -endif # BOARD_16K_OTA_MOVE_VENDOR == true - ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_4k.zip ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_16k.zip +endif # BOARD_16K_OTA_MOVE_VENDOR == true endif diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk index d9446153c4..364fed4efc 100644 --- a/target/product/aosp_arm64.mk +++ b/target/product/aosp_arm64.mk @@ -55,7 +55,8 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # All components inherited here go to vendor or vendor_boot image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk) -$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk) +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS ?= system # # Special settings for GSI releasing diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk index 4344f50cae..595940d9d1 100644 --- a/target/product/aosp_x86_64.mk +++ b/target/product/aosp_x86_64.mk @@ -57,7 +57,8 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk) -$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk) +AB_OTA_UPDATER := true +AB_OTA_PARTITIONS ?= system # # Special settings for GSI releasing diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING index b7ff8eff17..421e94a8a6 100644 --- a/tools/aconfig/TEST_MAPPING +++ b/tools/aconfig/TEST_MAPPING @@ -97,5 +97,9 @@ } ], "postsubmit": [ + { + // aconfig_storage file cpp integration tests + "name": "aconfig_storage_file.test.cpp" + } ] } diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs index 90b3951ce9..6945fd4649 100644 --- a/tools/aconfig/aconfig/src/commands.rs +++ b/tools/aconfig/aconfig/src/commands.rs @@ -202,7 +202,11 @@ pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Ve generate_java_code(&package, modified_parsed_flags.into_iter(), codegen_mode) } -pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> { +pub fn create_cpp_lib( + mut input: Input, + codegen_mode: CodegenMode, + allow_instrumentation: bool, +) -> Result<Vec<OutputFile>> { // TODO(327420679): Enable export mode for native flag library ensure!( codegen_mode != CodegenMode::Exported, @@ -215,7 +219,13 @@ pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec }; let package = package.to_string(); let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; - generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode, flag_ids, false) + generate_cpp_code( + &package, + modified_parsed_flags.into_iter(), + codegen_mode, + flag_ids, + allow_instrumentation, + ) } pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> { diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs index 69f54581cd..72be1c9896 100644 --- a/tools/aconfig/aconfig/src/main.rs +++ b/tools/aconfig/aconfig/src/main.rs @@ -83,6 +83,12 @@ fn cli() -> Command { .long("mode") .value_parser(EnumValueParser::<CodegenMode>::new()) .default_value("production"), + ) + .arg( + Arg::new("allow-instrumentation") + .long("allow-instrumentation") + .value_parser(clap::value_parser!(bool)) + .default_value("false"), ), ) .subcommand( @@ -241,8 +247,10 @@ fn main() -> Result<()> { Some(("create-cpp-lib", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?; - let generated_files = - commands::create_cpp_lib(cache, *mode).context("failed to create cpp lib")?; + let allow_instrumentation = + get_required_arg::<bool>(sub_matches, "allow-instrumentation")?; + let generated_files = commands::create_cpp_lib(cache, *mode, *allow_instrumentation) + .context("failed to create cpp lib")?; let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?); generated_files .iter() diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template index 7646015af9..62664bc566 100644 --- a/tools/aconfig/aconfig/templates/cpp_source_file.template +++ b/tools/aconfig/aconfig/templates/cpp_source_file.template @@ -3,7 +3,6 @@ {{ if allow_instrumentation }} #include <sys/stat.h> #include "aconfig_storage/aconfig_storage_read_api.hpp" -#include <protos/aconfig_storage_metadata.pb.h> #include <android/log.h> #define ALOGI(msg, ...) \ diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp index 08c00b0d6d..e066e31ac1 100644 --- a/tools/aconfig/aconfig_storage_file/Android.bp +++ b/tools/aconfig/aconfig_storage_file/Android.bp @@ -12,6 +12,7 @@ rust_defaults { "libtempfile", "libprotobuf", "libclap", + "libcxx", "libaconfig_storage_protos", ], } @@ -77,3 +78,62 @@ cc_library { product_available: true, double_loadable: true, } + +// cxx source codegen from rust api +genrule { + name: "libcxx_aconfig_storage_file_bridge_code", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) > $(out)", + srcs: ["src/lib.rs"], + out: ["aconfig_storage/lib.rs.cc"], +} + +// cxx header codegen from rust api +genrule { + name: "libcxx_aconfig_storage_file_bridge_header", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) --header > $(out)", + srcs: ["src/lib.rs"], + out: ["aconfig_storage/lib.rs.h"], +} + +// a static cc lib based on generated code +rust_ffi_static { + name: "libaconfig_storage_file_cxx_bridge", + crate_name: "aconfig_storage_file_cxx_bridge", + host_supported: true, + vendor_available: true, + product_available: true, + srcs: ["src/lib.rs"], + defaults: ["aconfig_storage_file.defaults"], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + min_sdk_version: "29", +} + +// storage file parse api cc interface +cc_library { + name: "libaconfig_storage_file_cc", + srcs: ["aconfig_storage_file.cpp"], + generated_headers: [ + "cxx-bridge-header", + "libcxx_aconfig_storage_file_bridge_header", + ], + generated_sources: ["libcxx_aconfig_storage_file_bridge_code"], + whole_static_libs: ["libaconfig_storage_file_cxx_bridge"], + export_include_dirs: ["include"], + host_supported: true, + vendor_available: true, + product_available: true, + shared_libs: [ + "libbase", + ], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + min_sdk_version: "29", + double_loadable: true, +} diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml index 641f481ed3..192dfad40a 100644 --- a/tools/aconfig/aconfig_storage_file/Cargo.toml +++ b/tools/aconfig/aconfig_storage_file/Cargo.toml @@ -13,6 +13,7 @@ protobuf = "3.2.0" tempfile = "3.9.0" thiserror = "1.0.56" clap = { version = "4.1.8", features = ["derive"] } +cxx = "1.0" [[bin]] name = "aconfig-storage" diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp new file mode 100644 index 0000000000..548078f48f --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp @@ -0,0 +1,38 @@ +#include "rust/cxx.h" +#include "aconfig_storage/lib.rs.h" + +#include "aconfig_storage/aconfig_storage_file.hpp" + +using namespace android::base; + +namespace aconfig_storage { + +Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info( + const std::string& package_map, + const std::string& flag_map, + const std::string& flag_val, + const std::string& flag_info) { + auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()), + rust::Str(flag_map.c_str()), + rust::Str(flag_val.c_str()), + rust::Str(flag_info.c_str())); + if (flag_list_cxx.query_success) { + auto flag_list = std::vector<FlagValueAndInfoSummary>(); + for (const auto& flag_cxx : flag_list_cxx.flags) { + auto flag = FlagValueAndInfoSummary(); + flag.package_name = std::string(flag_cxx.package_name); + flag.flag_name = std::string(flag_cxx.flag_name); + flag.flag_value = std::string(flag_cxx.flag_value); + flag.value_type = std::string(flag_cxx.value_type); + flag.is_readwrite = flag_cxx.is_readwrite; + flag.has_server_override = flag_cxx.has_server_override; + flag.has_local_override = flag_cxx.has_local_override; + flag_list.push_back(flag); + } + return flag_list; + } else { + return Error() << flag_list_cxx.error_message; + } +} + +} // namespace aconfig_storage diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs index 1feeb60677..e0ade2aba0 100644 --- a/tools/aconfig/aconfig_storage_file/build.rs +++ b/tools/aconfig/aconfig_storage_file/build.rs @@ -14,4 +14,6 @@ fn main() { .inputs(proto_files) .cargo_out_dir("aconfig_storage_protos") .run_from_script(); + + let _ = cxx_build::bridge("src/lib.rs"); } diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp new file mode 100644 index 0000000000..5044a4dbfc --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <vector> +#include <string> +#include <android-base/result.h> + +namespace aconfig_storage { + +/// Flag value and info summary for a flag +struct FlagValueAndInfoSummary { + std::string package_name; + std::string flag_name; + std::string flag_value; + std::string value_type; + bool is_readwrite; + bool has_server_override; + bool has_local_override; +}; + +/// List all flag values with their flag info +/// \input package_map: package map file +/// \input flag_map: flag map file +/// \input flag_val: flag value file +/// \input flag_info: flag info file +android::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info( + const std::string& package_map, + const std::string& flag_map, + const std::string& flag_val, + const std::string& flag_info); + +}// namespace aconfig_storage diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs index 2acfc7dbc7..80602bbfb0 100644 --- a/tools/aconfig/aconfig_storage_file/src/lib.rs +++ b/tools/aconfig/aconfig_storage_file/src/lib.rs @@ -278,12 +278,21 @@ pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageErro Ok(buffer) } +/// Flag value summary +#[derive(Debug, PartialEq)] +pub struct FlagValueSummary { + pub package_name: String, + pub flag_name: String, + pub flag_value: String, + pub value_type: StoredFlagType, +} + /// List flag values from storage files pub fn list_flags( package_map: &str, flag_map: &str, flag_val: &str, -) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> { +) -> Result<Vec<FlagValueSummary>, 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)?)?; @@ -295,30 +304,155 @@ pub fn list_flags( 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 flag_offset = package_offset + node.flag_index as u32; - let flag_value = flag_value_list.booleans[flag_offset as usize]; - flags.push(( - String::from(package_name), - node.flag_name.clone(), - node.flag_type, - flag_value, - )); + let (package_name, boolean_start_index) = package_info[node.package_id as usize]; + let flag_index = boolean_start_index + node.flag_index as u32; + let flag_value = flag_value_list.booleans[flag_index as usize]; + flags.push(FlagValueSummary { + package_name: String::from(package_name), + flag_name: node.flag_name.clone(), + flag_value: flag_value.to_string(), + value_type: node.flag_type, + }); } - flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) { - Ordering::Equal => v1.1.cmp(&v2.1), + flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) { + Ordering::Equal => v1.flag_name.cmp(&v2.flag_name), other => other, }); Ok(flags) } +/// Flag value and info summary +#[derive(Debug, PartialEq)] +pub struct FlagValueAndInfoSummary { + pub package_name: String, + pub flag_name: String, + pub flag_value: String, + pub value_type: StoredFlagType, + pub is_readwrite: bool, + pub has_server_override: bool, + pub has_local_override: bool, +} + +/// List flag values and info from storage files +pub fn list_flags_with_info( + package_map: &str, + flag_map: &str, + flag_val: &str, + flag_info: &str, +) -> Result<Vec<FlagValueAndInfoSummary>, 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 flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?; + + 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_start_index); + } + + let mut flags = Vec::new(); + for node in flag_table.nodes.iter() { + let (package_name, boolean_start_index) = package_info[node.package_id as usize]; + let flag_index = boolean_start_index + node.flag_index as u32; + let flag_value = flag_value_list.booleans[flag_index as usize]; + let flag_attribute = flag_info.nodes[flag_index as usize].attributes; + flags.push(FlagValueAndInfoSummary { + package_name: String::from(package_name), + flag_name: node.flag_name.clone(), + flag_value: flag_value.to_string(), + value_type: node.flag_type, + is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0, + has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0, + has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0, + }); + } + + flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) { + Ordering::Equal => v1.flag_name.cmp(&v2.flag_name), + other => other, + }); + Ok(flags) +} + +// *************************************** // +// CC INTERLOP +// *************************************** // + +// Exported rust data structure and methods, c++ code will be generated +#[cxx::bridge] +mod ffi { + /// flag value and info summary cxx return + pub struct FlagValueAndInfoSummaryCXX { + pub package_name: String, + pub flag_name: String, + pub flag_value: String, + pub value_type: String, + pub is_readwrite: bool, + pub has_server_override: bool, + pub has_local_override: bool, + } + + /// list flag result cxx return + pub struct ListFlagValueAndInfoResultCXX { + pub query_success: bool, + pub error_message: String, + pub flags: Vec<FlagValueAndInfoSummaryCXX>, + } + + // Rust export to c++ + extern "Rust" { + pub fn list_flags_with_info_cxx( + package_map: &str, + flag_map: &str, + flag_val: &str, + flag_info: &str, + ) -> ListFlagValueAndInfoResultCXX; + } +} + +/// implement flag value and info summary cxx return type +impl ffi::FlagValueAndInfoSummaryCXX { + pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self { + Self { + package_name: summary.package_name, + flag_name: summary.flag_name, + flag_value: summary.flag_value, + value_type: format!("{:?}", summary.value_type), + is_readwrite: summary.is_readwrite, + has_server_override: summary.has_server_override, + has_local_override: summary.has_local_override, + } + } +} + +/// implement list flag with info cxx interlop +pub fn list_flags_with_info_cxx( + package_map: &str, + flag_map: &str, + flag_val: &str, + flag_info: &str, +) -> ffi::ListFlagValueAndInfoResultCXX { + match list_flags_with_info(package_map, flag_map, flag_val, flag_info) { + Ok(summary) => ffi::ListFlagValueAndInfoResultCXX { + query_success: true, + error_message: String::new(), + flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(), + }, + Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX { + query_success: false, + error_message: format!("{:?}", errmsg), + flags: Vec::new(), + }, + } +} + #[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, + create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list, + create_test_package_table, write_bytes_to_temp_file, }; #[test] @@ -337,54 +471,154 @@ mod tests { 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"), - String::from("disabled_rw"), - StoredFlagType::ReadWriteBoolean, - false, - ), - ( - String::from("com.android.aconfig.storage.test_1"), - String::from("enabled_ro"), - StoredFlagType::ReadOnlyBoolean, - true, - ), - ( - String::from("com.android.aconfig.storage.test_1"), - String::from("enabled_rw"), - StoredFlagType::ReadWriteBoolean, - true, - ), - ( - String::from("com.android.aconfig.storage.test_2"), - String::from("disabled_rw"), - StoredFlagType::ReadWriteBoolean, - false, - ), - ( - String::from("com.android.aconfig.storage.test_2"), - String::from("enabled_fixed_ro"), - StoredFlagType::FixedReadOnlyBoolean, - true, - ), - ( - String::from("com.android.aconfig.storage.test_2"), - String::from("enabled_ro"), - StoredFlagType::ReadOnlyBoolean, - true, - ), - ( - String::from("com.android.aconfig.storage.test_4"), - String::from("enabled_fixed_ro"), - StoredFlagType::FixedReadOnlyBoolean, - true, - ), - ( - String::from("com.android.aconfig.storage.test_4"), - String::from("enabled_rw"), - StoredFlagType::ReadWriteBoolean, - true, - ), + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("disabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("false"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("enabled_ro"), + value_type: StoredFlagType::ReadOnlyBoolean, + flag_value: String::from("true"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("enabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("true"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("disabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("false"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("enabled_fixed_ro"), + value_type: StoredFlagType::FixedReadOnlyBoolean, + flag_value: String::from("true"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("enabled_ro"), + value_type: StoredFlagType::ReadOnlyBoolean, + flag_value: String::from("true"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_4"), + flag_name: String::from("enabled_fixed_ro"), + value_type: StoredFlagType::FixedReadOnlyBoolean, + flag_value: String::from("true"), + }, + FlagValueSummary { + package_name: String::from("com.android.aconfig.storage.test_4"), + flag_name: String::from("enabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("true"), + }, + ]; + assert_eq!(flags, expected); + } + + #[test] + // this test point locks down the flag list with info api + fn test_list_flag_with_info() { + let package_table = + write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap(); + let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap(); + let flag_value_list = + write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap(); + let flag_info_list = + write_bytes_to_temp_file(&create_test_flag_info_list().into_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 flag_info_list_path = flag_info_list.path().display().to_string(); + + let flags = list_flags_with_info( + &package_table_path, + &flag_table_path, + &flag_value_list_path, + &flag_info_list_path, + ) + .unwrap(); + let expected = [ + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("disabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("false"), + is_readwrite: true, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("enabled_ro"), + value_type: StoredFlagType::ReadOnlyBoolean, + flag_value: String::from("true"), + is_readwrite: false, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_1"), + flag_name: String::from("enabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("true"), + is_readwrite: true, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("disabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("false"), + is_readwrite: true, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("enabled_fixed_ro"), + value_type: StoredFlagType::FixedReadOnlyBoolean, + flag_value: String::from("true"), + is_readwrite: false, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_2"), + flag_name: String::from("enabled_ro"), + value_type: StoredFlagType::ReadOnlyBoolean, + flag_value: String::from("true"), + is_readwrite: false, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_4"), + flag_name: String::from("enabled_fixed_ro"), + value_type: StoredFlagType::FixedReadOnlyBoolean, + flag_value: String::from("true"), + is_readwrite: false, + has_server_override: false, + has_local_override: false, + }, + FlagValueAndInfoSummary { + package_name: String::from("com.android.aconfig.storage.test_4"), + flag_name: String::from("enabled_rw"), + value_type: StoredFlagType::ReadWriteBoolean, + flag_value: String::from("true"), + is_readwrite: true, + has_server_override: false, + has_local_override: false, + }, ]; assert_eq!(flags, expected); } diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs index b686274a2d..8b9e38da02 100644 --- a/tools/aconfig/aconfig_storage_file/src/main.rs +++ b/tools/aconfig/aconfig_storage_file/src/main.rs @@ -17,8 +17,8 @@ //! `aconfig-storage` is a debugging tool to parse storage files use aconfig_storage_file::{ - list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList, - PackageTable, StorageFileType, + list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList, + FlagTable, FlagValueList, PackageTable, StorageFileType, }; use clap::{builder::ArgAction, Arg, Command}; @@ -45,7 +45,10 @@ fn cli() -> Command { .action(ArgAction::Set), ) .arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set)) - .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)), + .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)) + .arg( + Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set), + ), ) } @@ -87,9 +90,27 @@ fn main() -> Result<(), AconfigStorageError> { let package_map = sub_matches.get_one::<String>("package-map").unwrap(); let flag_map = sub_matches.get_one::<String>("flag-map").unwrap(); let flag_val = sub_matches.get_one::<String>("flag-val").unwrap(); - let flags = list_flags(package_map, flag_map, flag_val)?; - for (package_name, flag_name, flag_type, flag_value) in flags.iter() { - println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value); + let flag_info = sub_matches.get_one::<String>("flag-info"); + match flag_info { + Some(info_file) => { + let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?; + for flag in flags.iter() { + println!( + "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}", + flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, + flag.is_readwrite, flag.has_server_override, flag.has_local_override, + ); + } + } + None => { + let flags = list_flags(package_map, flag_map, flag_val)?; + for flag in flags.iter() { + println!( + "{} {} {} {:?}", + flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, + ); + } + } } } _ => unreachable!(), diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp new file mode 100644 index 0000000000..26b7800877 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp @@ -0,0 +1,23 @@ + +cc_test { + name: "aconfig_storage_file.test.cpp", + team: "trendy_team_android_core_experiments", + srcs: [ + "storage_file_test.cpp", + ], + static_libs: [ + "libgmock", + "libaconfig_storage_file_cc", + "libbase", + ], + data: [ + "package.map", + "flag.map", + "flag.val", + "flag.info", + ], + test_suites: [ + "device-tests", + "general-tests", + ], +} diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.info b/tools/aconfig/aconfig_storage_file/tests/flag.info Binary files differnew file mode 100644 index 0000000000..6223edf369 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/flag.info diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map Binary files differnew file mode 100644 index 0000000000..e868f53d7e --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/flag.map diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val Binary files differnew file mode 100644 index 0000000000..ed203d4d13 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/flag.val diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map Binary files differnew file mode 100644 index 0000000000..6c46a0339c --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/package.map diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp new file mode 100644 index 0000000000..eccbca53d5 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> +#include <vector> +#include <android-base/file.h> +#include <android-base/result.h> +#include <gtest/gtest.h> +#include "aconfig_storage/aconfig_storage_file.hpp" + +using namespace android::base; +using namespace aconfig_storage; + + +void verify_flag(const FlagValueAndInfoSummary& flag, + const std::string& package_name, + const std::string& flag_name, + const std::string& flag_val, + const std::string& value_type, + bool is_readwrite, + bool has_server_override, + bool has_local_override) { + ASSERT_EQ(flag.package_name, package_name); + ASSERT_EQ(flag.flag_name, flag_name); + ASSERT_EQ(flag.flag_value, flag_val); + ASSERT_EQ(flag.value_type, value_type); + ASSERT_EQ(flag.is_readwrite, is_readwrite); + ASSERT_EQ(flag.has_server_override, has_server_override); + ASSERT_EQ(flag.has_local_override, has_local_override); +} + +TEST(AconfigStorageFileTest, test_list_flag_with_info) { + auto const test_dir = GetExecutableDirectory(); + auto const package_map = test_dir + "/package.map"; + auto const flag_map = test_dir + "/flag.map"; + auto const flag_val = test_dir + "/flag.val"; + auto const flag_info = test_dir + "/flag.info"; + auto flag_list_result = aconfig_storage::list_flags_with_info( + package_map, flag_map, flag_val, flag_info); + ASSERT_TRUE(flag_list_result.ok()); + + auto const& flag_list = *flag_list_result; + ASSERT_EQ(flag_list.size(), 8); + verify_flag(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", + "false", "ReadWriteBoolean", true, false, false); + verify_flag(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", + "true", "ReadOnlyBoolean", false, false, false); + verify_flag(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", + "true", "ReadWriteBoolean", true, false, false); + verify_flag(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", + "false", "ReadWriteBoolean", true, false, false); + verify_flag(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", + "true", "FixedReadOnlyBoolean", false, false, false); + verify_flag(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", + "true", "ReadOnlyBoolean", false, false, false); + verify_flag(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", + "true", "FixedReadOnlyBoolean", false, false, false); + verify_flag(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", + "true", "ReadWriteBoolean", true, false, false); +} diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp index c89107fddd..217104b6b2 100644 --- a/tools/aconfig/aconfig_storage_read_api/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/Android.bp @@ -109,3 +109,11 @@ cc_library { }, double_loadable: true, } + +cc_defaults { + name: "aconfig_lib_cc_static_link.defaults", + shared_libs: [ + "libaconfig_storage_read_api_cc", + "liblog", + ], +} diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs index e65dcfbc69..e4192066d5 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs @@ -504,7 +504,7 @@ files {{ let pb_file_path = pb_file.path().display().to_string(); let flag_info_file = unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() }; - let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false]; + let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true]; for (offset, expected_value) in is_rw.into_iter().enumerate() { let attribute = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap(); diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info Binary files differindex 820d839363..6223edf369 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/flag.info +++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp index b499c1c279..cfd128d123 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp @@ -232,7 +232,7 @@ TEST_F(AconfigStorageTest, test_boolean_flag_info_query) { ASSERT_TRUE(mapped_file.ok()); auto expected_value = std::vector<bool>{ - true, false, true, false, false, false, false, false}; + true, false, true, true, false, false, false, true}; for (int index = 0; index < 8; ++index) { auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index); ASSERT_TRUE(attribute.ok()); diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs index ce9c0186dd..ecba573d82 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs @@ -188,7 +188,7 @@ files {{ // The safety here is ensured as the test process will not write to temp storage file let flag_info_file = unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() }; - let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false]; + let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true]; for (offset, expected_value) in is_rw.into_iter().enumerate() { let attribute = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap(); diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs index a3ca221d9e..c21c5424bb 100644 --- a/tools/aconfig/aflags/src/aconfig_storage_source.rs +++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs @@ -27,13 +27,13 @@ impl FlagSource for AconfigStorageSource { let container = file_info.container.ok_or(anyhow!("storage file is missing container"))?; - for (package, name, _flag_type, val) in + for listed_flag in aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)? { result.push(Flag { - name: name.to_string(), - package: package.to_string(), - value: FlagValue::try_from(val.to_string().as_str())?, + name: listed_flag.flag_name, + package: listed_flag.package_name, + value: FlagValue::try_from(listed_flag.flag_value.as_str())?, container: container.to_string(), // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI. diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt index 62c9cbb832..0569bfd71b 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt @@ -31,8 +31,9 @@ private val API_SIGNATURE = // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public final class Clazz { - ctor public Clazz(); + ctor @FlaggedApi("android.flag.foo") public Clazz(); field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1 + method @FlaggedApi("android.flag.foo") public int getErrorCode(); } @FlaggedApi("android.flag.bar") public static class Clazz.Builder { } @@ -47,6 +48,7 @@ private val API_VERSIONS = <class name="android/Clazz" since="1"> <method name="<init>()V"/> <field name="FOO"/> + <method name="getErrorCode()I"/> </class> <class name="android/Clazz${"$"}Builder" since="2"> </class> @@ -88,7 +90,9 @@ class CheckFlaggedApisTest { val expected = setOf( Pair(Symbol("android.Clazz"), Flag("android.flag.foo")), + Pair(Symbol("android.Clazz.Clazz()"), Flag("android.flag.foo")), Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")), + Pair(Symbol("android.Clazz.getErrorCode()"), Flag("android.flag.foo")), Pair(Symbol("android.Clazz.Builder"), Flag("android.flag.bar")), ) val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()) @@ -108,7 +112,9 @@ class CheckFlaggedApisTest { val expected: Set<Symbol> = setOf( Symbol("android.Clazz"), + Symbol("android.Clazz.Clazz()"), Symbol("android.Clazz.FOO"), + Symbol("android.Clazz.getErrorCode()"), Symbol("android.Clazz.Builder"), ) val actual = parseApiVersions(API_VERSIONS.byteInputStream()) @@ -131,8 +137,12 @@ class CheckFlaggedApisTest { val expected = setOf<ApiError>( DisabledFlaggedApiIsPresentError(Symbol("android.Clazz"), Flag("android.flag.foo")), + DisabledFlaggedApiIsPresentError( + Symbol("android.Clazz.Clazz()"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( + Symbol("android.Clazz.getErrorCode()"), Flag("android.flag.foo")), + DisabledFlaggedApiIsPresentError( Symbol("android.Clazz.Builder"), Flag("android.flag.bar")), ) val actual = diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt index 918a5d9bf3..0c078a0b93 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt @@ -22,6 +22,7 @@ import com.android.tools.metalava.model.BaseItemVisitor import com.android.tools.metalava.model.ClassItem import com.android.tools.metalava.model.FieldItem import com.android.tools.metalava.model.Item +import com.android.tools.metalava.model.MethodItem import com.android.tools.metalava.model.text.ApiFile import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.ProgramResult @@ -48,7 +49,7 @@ import org.w3c.dom.Node @JvmInline internal value class Symbol(val name: String) { companion object { - private val FORBIDDEN_CHARS = listOf('/', '#', '$') + private val FORBIDDEN_CHARS = listOf('#', '$') /** Create a new Symbol from a String that may include delimiters other than dot. */ fun create(name: String): Symbol { @@ -187,6 +188,25 @@ internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbo } } + override fun visitMethod(method: MethodItem) { + getFlagOrNull(method)?.let { flag -> + val name = buildString { + append(method.containingClass().qualifiedName()) + append(".") + append(method.name()) + append("(") + // TODO(334870672): replace this early return with proper parsing of the command line + // arguments, followed by translation to Lname/of/class; + III format + if (!method.parameters().isEmpty()) { + return + } + append(")") + } + val symbol = Symbol.create(name) + output.add(Pair(symbol, flag)) + } + } + private fun getFlagOrNull(item: Item): Flag? { return item.modifiers .findAnnotation("android.annotation.FlaggedApi") @@ -223,7 +243,7 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> { requireNotNull(cls.getAttribute("name")) { "Bad XML: <class> element without name attribute" } - output.add(Symbol.create(className)) + output.add(Symbol.create(className.replace("/", "."))) } val fields = document.getElementsByTagName("field") @@ -235,9 +255,31 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> { "Bad XML: <field> element without name attribute" } val className = - requireNotNull(field.getParentNode()) { "Bad XML: top level <field> element" } - .getAttribute("name") - output.add(Symbol.create("$className.$fieldName")) + requireNotNull(field.getParentNode()?.getAttribute("name")) { "Bad XML: top level <field> element" } + output.add(Symbol.create("${className.replace("/", ".")}.$fieldName")) + } + + val methods = document.getElementsByTagName("method") + // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead + for (i in 0.rangeUntil(methods.getLength())) { + val method = methods.item(i) + val methodSignature = + requireNotNull(method.getAttribute("name")) { + "Bad XML: <method> element without name attribute" + } + val methodSignatureParts = methodSignature.split(Regex("\\(|\\)")) + if (methodSignatureParts.size != 3) { + throw Exception("Bad XML: method signature '$methodSignature'") + } + var (methodName, methodArgs, methodReturnValue) = methodSignatureParts + val packageAndClassName = + requireNotNull(method.getParentNode()?.getAttribute("name")) { + "Bad XML: top level <method> element, or <class> element missing name attribute" + } + if (methodName == "<init>") { + methodName = packageAndClassName.split("/").last() + } + output.add(Symbol.create("${packageAndClassName.replace("/", ".")}.$methodName($methodArgs)")) } return output diff --git a/tools/lunchable b/tools/lunchable new file mode 100755 index 0000000000..fce2c2719d --- /dev/null +++ b/tools/lunchable @@ -0,0 +1,72 @@ +#!/bin/bash + +# TODO: Currently only checks trunk_staging. Should check trunk_staging first, +# then use the product-specfic releases. Only applies to -c though. + +function Help() { +cat <<@EOF@ +Usage: lunchable [options] + +Lists products that have no functioning lunch combo. + +options: +-c prints all failing lunch combos for all targets; +-w why? Prints the error message after each failed lunch combo. Only + works with -c + +@EOF@ +} + +complete=0 +why=0 +while getopts "cwh" option; do + case $option in + c) + complete=1;; + w) + why=1;; + h) + Help + exit;; + esac +done + +# Getting all named products can fail if we haven't lunched anything +source $(pwd)/build/envsetup.sh &> /dev/null +all_named_products=( $(get_build_var all_named_products 2> /dev/null) ) +if [[ $? -ne 0 ]]; then + echo "get_build_var all_named_products failed. Lunch something first?" >&2 + exit 1 +fi +total_products=${#all_named_products[@]} +current_product=0 + +for product in "${all_named_products[@]}"; do + (( current_product += 1 )) + single_pass=0 + printf " Checking ${current_product}/${total_products} \r" >&2 + for release in trunk_staging; do + for variant in eng user userdebug; do + lunchcombo="${product}-${release}-${variant}" + lunch_error="$(lunch $lunchcombo 2>&1 > /dev/null)" + if [[ $? -ne 0 ]]; then + # Lunch failed + if [[ $complete -eq 1 ]]; then + echo -e "${product} : ${lunchcombo}" + if [[ $why -eq 1 ]]; then + echo -e "$(sed 's/^/ /g' <<<$lunch_error)" + fi + fi + elif [[ $complete -ne 1 ]]; then + single_pass=1 + break # skip variant + fi + done + if [[ $single_pass -eq 1 ]]; then + break # skip release + fi + done + if [[ $complete -eq 0 ]] && [[ $single_pass -eq 0 ]]; then + echo "${product}" + fi +done |