Merge "Add tool to determine if products can be lunched" into main
diff --git a/cogsetup.sh b/cogsetup.sh
index 44538f2..ef1485d 100644
--- a/cogsetup.sh
+++ b/cogsetup.sh
@@ -52,7 +52,9 @@
# it with this function. If the user is running repo within a Cog workspace,
# we'll fail with an error, otherwise, we run the original repo command with
# the given args.
- ORIG_REPO_PATH=`which repo`
+ if ! ORIG_REPO_PATH=`which repo`; then
+ return 0
+ fi
function repo {
if [[ "${PWD}" == /google/cog/* ]]; then
echo "\e[01;31mERROR:\e[0mrepo command is disallowed within Cog workspaces."
diff --git a/core/Makefile b/core/Makefile
index e6a5bc0..00577ed 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1477,13 +1477,14 @@
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
@@ -6919,6 +6920,7 @@
--verbose \
--path $(HOST_OUT) \
$(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
+ $(if $(BOOT_VAR_OTA_CONFIG), --boot_variable_file $(BOOT_VAR_OTA_CONFIG)) \
$(2) \
$(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1)
endef
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 08311ca..26b8b17 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -123,22 +123,28 @@
$(call dist-for-goals, droidcore, $(boot_zip))
-ifneq (,$(filter true,$(ART_MODULE_BUILD_FROM_SOURCE) $(MODULE_BUILD_FROM_SOURCE)))
# Build the system_server.zip which contains the Apex system server jars and standalone system server jars
+system_server_dex2oat_dir := $(SOONG_OUT_DIR)/system_server_dexjars
system_server_zip := $(PRODUCT_OUT)/system_server.zip
+# non_updatable_system_server_jars contains jars in /system and /system_ext that are not part of an apex.
+non_updatable_system_server_jars := \
+ $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),\
+ $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
+
apex_system_server_jars := \
$(foreach m,$(PRODUCT_APEX_SYSTEM_SERVER_JARS),\
- $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+ $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
apex_standalone_system_server_jars := \
$(foreach m,$(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS),\
- $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+ $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
standalone_system_server_jars := \
$(foreach m,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS),\
- $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+ $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
-$(system_server_zip): PRIVATE_SYSTEM_SERVER_JARS := $(system_server_jars)
+$(system_server_zip): PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR := $(system_server_dex2oat_dir)
+$(system_server_zip): PRIVATE_SYSTEM_SERVER_JARS := $(non_updatable_system_server_jars)
$(system_server_zip): PRIVATE_APEX_SYSTEM_SERVER_JARS := $(apex_system_server_jars)
$(system_server_zip): PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS := $(apex_standalone_system_server_jars)
$(system_server_zip): PRIVATE_STANDALONE_SYSTEM_SERVER_JARS := $(standalone_system_server_jars)
@@ -146,14 +152,13 @@
@echo "Create system server package: $@"
rm -f $@
$(SOONG_ZIP) -o $@ \
- -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
- -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_SYSTEM_SERVER_JARS)) \
- -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS)) \
- -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_STANDALONE_SYSTEM_SERVER_JARS))
+ -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
+ -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_APEX_SYSTEM_SERVER_JARS)) \
+ -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS)) \
+ -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_STANDALONE_SYSTEM_SERVER_JARS))
$(call dist-for-goals, droidcore, $(system_server_zip))
-endif #ART_MODULE_BUILD_FROM_SOURCE || MODULE_BUILD_FROM_SOURCE
endif #PRODUCT_USES_DEFAULT_ART_CONFIG
endif #WITH_DEXPREOPT_ART_BOOT_IMG_ONLY
endif #WITH_DEXPREOPT
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 99ecd83..c630bcc 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -49,6 +49,41 @@
$(eval $(call declare-1p-copy-files,device/google/coral,audio_policy_configuration.xml))
$(eval $(call declare-1p-copy-files,device/google/coral,display_19260504575090817.xml))
+# Moved here from device/google/gs101/Android.mk
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,p2p_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+
+$(eval $(call declare-1p-copy-files,device/google/gs101,audio_policy_configuration.xml))
+
+# Move here from device/google/raviole/Android.mk
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci-raven.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+
+$(eval $(call declare-1p-copy-files,device/google/raviole,audio_policy_configuration.xml))
+
# Moved here from device/sample/Android.mk
$(eval $(call declare-1p-copy-files,device/sample,))
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index bf9aa41..3c401f3 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -82,8 +82,9 @@
KeyChain \
Telecom \
+PRODUCT_PACKAGES += framework-audio_effects.xml
+
PRODUCT_COPY_FILES += \
- frameworks/av/media/libeffects/data/audio_effects.xml:system/etc/audio_effects.xml \
frameworks/native/data/etc/android.software.window_magnification.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/android.software.window_magnification.xml \
PRODUCT_VENDOR_PROPERTIES += \
diff --git a/teams/Android.bp b/teams/Android.bp
index a02a573..78efa61 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4371,3 +4371,10 @@
// go/trendy/manage/engineers/5810097836621824
trendy_team_id: "5810097836621824",
}
+
+team {
+ name: "trendy_team_adte",
+
+ // go/trendy/manage/engineers/5551098528825344
+ trendy_team_id: "5551098528825344",
+}
diff --git a/tools/aconfig/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
index cd71b10..e743b2f 100644
--- a/tools/aconfig/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -16,6 +16,7 @@
use anyhow::{ensure, Result};
use serde::Serialize;
+use std::collections::HashMap;
use std::path::PathBuf;
use tinytemplate::TinyTemplate;
@@ -29,13 +30,15 @@
package: &str,
parsed_flags_iter: I,
codegen_mode: CodegenMode,
+ flag_ids: HashMap<String, u16>,
+ allow_instrumentation: bool,
) -> Result<Vec<OutputFile>>
where
I: Iterator<Item = ProtoParsedFlag>,
{
let mut readwrite_count = 0;
let class_elements: Vec<ClassElement> = parsed_flags_iter
- .map(|pf| create_class_element(package, &pf, &mut readwrite_count))
+ .map(|pf| create_class_element(package, &pf, flag_ids.clone(), &mut readwrite_count))
.collect();
let readwrite = readwrite_count > 0;
let has_fixed_read_only = class_elements.iter().any(|item| item.is_fixed_read_only);
@@ -53,6 +56,7 @@
readwrite_count,
is_test_mode: codegen_mode == CodegenMode::Test,
class_elements,
+ allow_instrumentation,
};
let files = [
@@ -96,6 +100,7 @@
pub readwrite_count: i32,
pub is_test_mode: bool,
pub class_elements: Vec<ClassElement>,
+ pub allow_instrumentation: bool,
}
#[derive(Serialize)]
@@ -106,11 +111,18 @@
pub default_value: String,
pub flag_name: String,
pub flag_macro: String,
+ pub flag_offset: u16,
pub device_config_namespace: String,
pub device_config_flag: String,
+ pub container: String,
}
-fn create_class_element(package: &str, pf: &ProtoParsedFlag, rw_count: &mut i32) -> ClassElement {
+fn create_class_element(
+ package: &str,
+ pf: &ProtoParsedFlag,
+ flag_ids: HashMap<String, u16>,
+ rw_count: &mut i32,
+) -> ClassElement {
ClassElement {
readwrite_idx: if pf.permission() == ProtoFlagPermission::READ_WRITE {
let index = *rw_count;
@@ -128,9 +140,11 @@
},
flag_name: pf.name().to_string(),
flag_macro: pf.name().to_uppercase(),
+ flag_offset: *flag_ids.get(pf.name()).expect("values checked at flag parse time"),
device_config_namespace: pf.namespace().to_string(),
device_config_flag: codegen::create_device_config_ident(package, pf.name())
.expect("values checked at flag parse time"),
+ container: pf.container().to_string(),
}
}
@@ -1162,18 +1176,27 @@
return true;
}
"#;
+ use crate::commands::assign_flag_ids;
fn test_generate_cpp_code(
parsed_flags: ProtoParsedFlags,
mode: CodegenMode,
expected_header: &str,
expected_src: &str,
+ allow_instrumentation: bool,
) {
let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
- let generated =
- generate_cpp_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
- .unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let generated = generate_cpp_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ allow_instrumentation,
+ )
+ .unwrap();
let mut generated_files_map = HashMap::new();
for file in generated {
generated_files_map.insert(
@@ -1211,6 +1234,7 @@
CodegenMode::Production,
EXPORTED_PROD_HEADER_EXPECTED,
PROD_SOURCE_FILE_EXPECTED,
+ false,
);
}
@@ -1222,6 +1246,7 @@
CodegenMode::Test,
EXPORTED_TEST_HEADER_EXPECTED,
TEST_SOURCE_FILE_EXPECTED,
+ false,
);
}
@@ -1233,6 +1258,7 @@
CodegenMode::Exported,
EXPORTED_EXPORTED_HEADER_EXPECTED,
EXPORTED_SOURCE_FILE_EXPECTED,
+ false,
);
}
@@ -1244,6 +1270,7 @@
CodegenMode::ForceReadOnly,
EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED,
FORCE_READ_ONLY_SOURCE_FILE_EXPECTED,
+ false,
);
}
@@ -1255,6 +1282,7 @@
CodegenMode::Production,
READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED,
READ_ONLY_PROD_SOURCE_FILE_EXPECTED,
+ false,
);
}
}
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 18a4be5..3360ddd 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -64,20 +64,27 @@
include_str!("../../templates/FeatureFlags.java.template"),
)?;
template.add_template(
+ "CustomFeatureFlags.java",
+ include_str!("../../templates/CustomFeatureFlags.java.template"),
+ )?;
+ template.add_template(
"FakeFeatureFlagsImpl.java",
include_str!("../../templates/FakeFeatureFlagsImpl.java.template"),
)?;
let path: PathBuf = package.split('.').collect();
- ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
- .iter()
- .map(|file| {
- Ok(OutputFile {
- contents: template.render(file, &context)?.into(),
- path: path.join(file),
- })
- })
- .collect::<Result<Vec<OutputFile>>>()
+ [
+ "Flags.java",
+ "FeatureFlags.java",
+ "FeatureFlagsImpl.java",
+ "CustomFeatureFlags.java",
+ "FakeFeatureFlagsImpl.java",
+ ]
+ .iter()
+ .map(|file| {
+ Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file) })
+ })
+ .collect::<Result<Vec<OutputFile>>>()
}
fn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec<NamespaceFlags> {
@@ -292,76 +299,82 @@
}
"#;
- const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
+ const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#"
package com.android.aconfig.test;
+
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
import java.util.Arrays;
- import java.util.HashMap;
import java.util.HashSet;
- import java.util.Map;
+ import java.util.List;
import java.util.Set;
+ import java.util.function.BiPredicate;
+ import java.util.function.Predicate;
+
/** @hide */
- public class FakeFeatureFlagsImpl implements FeatureFlags {
- public FakeFeatureFlagsImpl() {
- resetAll();
+ public class CustomFeatureFlags implements FeatureFlags {
+
+ private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+ public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+ mGetValueImpl = getValueImpl;
}
+
@Override
@UnsupportedAppUsage
public boolean disabledRo() {
- return getValue(Flags.FLAG_DISABLED_RO);
+ return getValue(Flags.FLAG_DISABLED_RO,
+ FeatureFlags::disabledRo);
}
@Override
@UnsupportedAppUsage
public boolean disabledRw() {
- return getValue(Flags.FLAG_DISABLED_RW);
+ return getValue(Flags.FLAG_DISABLED_RW,
+ FeatureFlags::disabledRw);
}
@Override
@UnsupportedAppUsage
public boolean disabledRwExported() {
- return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+ return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
+ FeatureFlags::disabledRwExported);
}
@Override
@UnsupportedAppUsage
public boolean disabledRwInOtherNamespace() {
- return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
+ return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+ FeatureFlags::disabledRwInOtherNamespace);
}
@Override
@UnsupportedAppUsage
public boolean enabledFixedRo() {
- return getValue(Flags.FLAG_ENABLED_FIXED_RO);
+ return getValue(Flags.FLAG_ENABLED_FIXED_RO,
+ FeatureFlags::enabledFixedRo);
}
@Override
@UnsupportedAppUsage
public boolean enabledFixedRoExported() {
- return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
+ return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+ FeatureFlags::enabledFixedRoExported);
}
@Override
@UnsupportedAppUsage
public boolean enabledRo() {
- return getValue(Flags.FLAG_ENABLED_RO);
+ return getValue(Flags.FLAG_ENABLED_RO,
+ FeatureFlags::enabledRo);
}
@Override
@UnsupportedAppUsage
public boolean enabledRoExported() {
- return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
+ return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
+ FeatureFlags::enabledRoExported);
}
@Override
@UnsupportedAppUsage
public boolean enabledRw() {
- return getValue(Flags.FLAG_ENABLED_RW);
+ return getValue(Flags.FLAG_ENABLED_RW,
+ FeatureFlags::enabledRw);
}
- public void setFlag(String flagName, boolean value) {
- if (!this.mFlagMap.containsKey(flagName)) {
- throw new IllegalArgumentException("no such flag " + flagName);
- }
- this.mFlagMap.put(flagName, value);
- }
- public void resetAll() {
- for (Map.Entry entry : mFlagMap.entrySet()) {
- entry.setValue(null);
- }
- }
+
public boolean isFlagReadOnlyOptimized(String flagName) {
if (mReadOnlyFlagsSet.contains(flagName) &&
isOptimizationEnabled()) {
@@ -369,30 +382,30 @@
}
return false;
}
+
@com.android.aconfig.annotations.AssumeTrueForR8
private boolean isOptimizationEnabled() {
return false;
}
- private boolean getValue(String flagName) {
- Boolean value = this.mFlagMap.get(flagName);
- if (value == null) {
- throw new IllegalArgumentException(flagName + " is not set");
- }
- return value;
+
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+ return mGetValueImpl.test(flagName, getter);
}
- private Map<String, Boolean> mFlagMap = new HashMap<>(
- Map.ofEntries(
- Map.entry(Flags.FLAG_DISABLED_RO, false),
- Map.entry(Flags.FLAG_DISABLED_RW, false),
- Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
- Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
- Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
- Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
- Map.entry(Flags.FLAG_ENABLED_RO, false),
- Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false),
- Map.entry(Flags.FLAG_ENABLED_RW, false)
- )
- );
+
+ public List<String> getFlagNames() {
+ return Arrays.asList(
+ Flags.FLAG_DISABLED_RO,
+ Flags.FLAG_DISABLED_RW,
+ Flags.FLAG_DISABLED_RW_EXPORTED,
+ Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+ Flags.FLAG_ENABLED_FIXED_RO,
+ Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+ Flags.FLAG_ENABLED_RO,
+ Flags.FLAG_ENABLED_RO_EXPORTED,
+ Flags.FLAG_ENABLED_RW
+ );
+ }
+
private Set<String> mReadOnlyFlagsSet = new HashSet<>(
Arrays.asList(
Flags.FLAG_DISABLED_RO,
@@ -406,6 +419,58 @@
}
"#;
+ const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
+ package com.android.aconfig.test;
+
+ import java.util.HashMap;
+ import java.util.Map;
+ import java.util.function.Predicate;
+
+ /** @hide */
+ public class FakeFeatureFlagsImpl extends CustomFeatureFlags {
+ private final Map<String, Boolean> mFlagMap = new HashMap<>();
+ private final FeatureFlags mDefaults;
+
+ public FakeFeatureFlagsImpl() {
+ this(null);
+ }
+
+ public FakeFeatureFlagsImpl(FeatureFlags defaults) {
+ super(null);
+ mDefaults = defaults;
+ // Initialize the map with null values
+ for (String flagName : getFlagNames()) {
+ mFlagMap.put(flagName, null);
+ }
+ }
+
+ @Override
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+ Boolean value = this.mFlagMap.get(flagName);
+ if (value != null) {
+ return value;
+ }
+ if (mDefaults != null) {
+ return getter.test(mDefaults);
+ }
+ throw new IllegalArgumentException(flagName + " is not set");
+ }
+
+ public void setFlag(String flagName, boolean value) {
+ if (!this.mFlagMap.containsKey(flagName)) {
+ throw new IllegalArgumentException("no such flag " + flagName);
+ }
+ this.mFlagMap.put(flagName, value);
+ }
+
+ public void resetAll() {
+ for (Map.Entry entry : mFlagMap.entrySet()) {
+ entry.setValue(null);
+ }
+ }
+ }
+ "#;
+
#[test]
fn test_generate_java_code_production() {
let parsed_flags = crate::test::parse_test_flags();
@@ -549,6 +614,10 @@
("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
(
+ "com/android/aconfig/test/CustomFeatureFlags.java",
+ EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
+ ),
+ (
"com/android/aconfig/test/FakeFeatureFlagsImpl.java",
EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
),
@@ -671,55 +740,53 @@
}
}"#;
- let expect_fake_feature_flags_impl_content = r#"
+ let expect_custom_feature_flags_content = r#"
package com.android.aconfig.test;
+
import java.util.Arrays;
- import java.util.HashMap;
import java.util.HashSet;
- import java.util.Map;
+ import java.util.List;
import java.util.Set;
+ import java.util.function.BiPredicate;
+ import java.util.function.Predicate;
+
/** @hide */
- public class FakeFeatureFlagsImpl implements FeatureFlags {
- public FakeFeatureFlagsImpl() {
- resetAll();
+ public class CustomFeatureFlags implements FeatureFlags {
+
+ private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+ public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+ mGetValueImpl = getValueImpl;
}
+
@Override
public boolean disabledRwExported() {
- return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+ return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
+ FeatureFlags::disabledRwExported);
}
@Override
public boolean enabledFixedRoExported() {
- return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
+ return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+ FeatureFlags::enabledFixedRoExported);
}
@Override
public boolean enabledRoExported() {
- return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
+ return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
+ FeatureFlags::enabledRoExported);
}
- public void setFlag(String flagName, boolean value) {
- if (!this.mFlagMap.containsKey(flagName)) {
- throw new IllegalArgumentException("no such flag " + flagName);
- }
- this.mFlagMap.put(flagName, value);
+
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+ return mGetValueImpl.test(flagName, getter);
}
- public void resetAll() {
- for (Map.Entry entry : mFlagMap.entrySet()) {
- entry.setValue(null);
- }
+
+ public List<String> getFlagNames() {
+ return Arrays.asList(
+ Flags.FLAG_DISABLED_RW_EXPORTED,
+ Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+ Flags.FLAG_ENABLED_RO_EXPORTED
+ );
}
- private boolean getValue(String flagName) {
- Boolean value = this.mFlagMap.get(flagName);
- if (value == null) {
- throw new IllegalArgumentException(flagName + " is not set");
- }
- return value;
- }
- private Map<String, Boolean> mFlagMap = new HashMap<>(
- Map.ofEntries(
- Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
- Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
- Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false)
- )
- );
+
private Set<String> mReadOnlyFlagsSet = new HashSet<>(
Arrays.asList(
""
@@ -733,8 +800,12 @@
("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content),
("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content),
(
+ "com/android/aconfig/test/CustomFeatureFlags.java",
+ expect_custom_feature_flags_content,
+ ),
+ (
"com/android/aconfig/test/FakeFeatureFlagsImpl.java",
- expect_fake_feature_flags_impl_content,
+ EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
),
]);
@@ -854,6 +925,10 @@
("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
(
+ "com/android/aconfig/test/CustomFeatureFlags.java",
+ EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
+ ),
+ (
"com/android/aconfig/test/FakeFeatureFlagsImpl.java",
EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
),
@@ -1020,61 +1095,64 @@
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
}"#;
- let expect_fakefeatureflags_content = r#"
+ let expect_customfeatureflags_content = r#"
package com.android.aconfig.test;
+
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
import java.util.Arrays;
- import java.util.HashMap;
import java.util.HashSet;
- import java.util.Map;
+ import java.util.List;
import java.util.Set;
+ import java.util.function.BiPredicate;
+ import java.util.function.Predicate;
+
/** @hide */
- public class FakeFeatureFlagsImpl implements FeatureFlags {
- public FakeFeatureFlagsImpl() {
- resetAll();
+ public class CustomFeatureFlags implements FeatureFlags {
+
+ private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+ public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+ mGetValueImpl = getValueImpl;
}
+
@Override
@UnsupportedAppUsage
public boolean disabledRo() {
- return getValue(Flags.FLAG_DISABLED_RO);
+ return getValue(Flags.FLAG_DISABLED_RO,
+ FeatureFlags::disabledRo);
}
@Override
@UnsupportedAppUsage
public boolean disabledRw() {
- return getValue(Flags.FLAG_DISABLED_RW);
+ return getValue(Flags.FLAG_DISABLED_RW,
+ FeatureFlags::disabledRw);
}
@Override
@UnsupportedAppUsage
public boolean disabledRwInOtherNamespace() {
- return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
+ return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+ FeatureFlags::disabledRwInOtherNamespace);
}
@Override
@UnsupportedAppUsage
public boolean enabledFixedRo() {
- return getValue(Flags.FLAG_ENABLED_FIXED_RO);
+ return getValue(Flags.FLAG_ENABLED_FIXED_RO,
+ FeatureFlags::enabledFixedRo);
}
@Override
@UnsupportedAppUsage
public boolean enabledRo() {
- return getValue(Flags.FLAG_ENABLED_RO);
+ return getValue(Flags.FLAG_ENABLED_RO,
+ FeatureFlags::enabledRo);
}
@Override
@UnsupportedAppUsage
public boolean enabledRw() {
- return getValue(Flags.FLAG_ENABLED_RW);
+ return getValue(Flags.FLAG_ENABLED_RW,
+ FeatureFlags::enabledRw);
}
- public void setFlag(String flagName, boolean value) {
- if (!this.mFlagMap.containsKey(flagName)) {
- throw new IllegalArgumentException("no such flag " + flagName);
- }
- this.mFlagMap.put(flagName, value);
- }
- public void resetAll() {
- for (Map.Entry entry : mFlagMap.entrySet()) {
- entry.setValue(null);
- }
- }
+
public boolean isFlagReadOnlyOptimized(String flagName) {
if (mReadOnlyFlagsSet.contains(flagName) &&
isOptimizationEnabled()) {
@@ -1082,27 +1160,27 @@
}
return false;
}
+
@com.android.aconfig.annotations.AssumeTrueForR8
private boolean isOptimizationEnabled() {
return false;
}
- private boolean getValue(String flagName) {
- Boolean value = this.mFlagMap.get(flagName);
- if (value == null) {
- throw new IllegalArgumentException(flagName + " is not set");
- }
- return value;
+
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+ return mGetValueImpl.test(flagName, getter);
}
- private Map<String, Boolean> mFlagMap = new HashMap<>(
- Map.ofEntries(
- Map.entry(Flags.FLAG_DISABLED_RO, false),
- Map.entry(Flags.FLAG_DISABLED_RW, false),
- Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
- Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
- Map.entry(Flags.FLAG_ENABLED_RO, false),
- Map.entry(Flags.FLAG_ENABLED_RW, false)
- )
- );
+
+ public List<String> getFlagNames() {
+ return Arrays.asList(
+ Flags.FLAG_DISABLED_RO,
+ Flags.FLAG_DISABLED_RW,
+ Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+ Flags.FLAG_ENABLED_FIXED_RO,
+ Flags.FLAG_ENABLED_RO,
+ Flags.FLAG_ENABLED_RW
+ );
+ }
+
private Set<String> mReadOnlyFlagsSet = new HashSet<>(
Arrays.asList(
Flags.FLAG_DISABLED_RO,
@@ -1116,11 +1194,16 @@
);
}
"#;
+
let mut file_set = HashMap::from([
("com/android/aconfig/test/Flags.java", expect_flags_content),
("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content),
- ("com/android/aconfig/test/FakeFeatureFlagsImpl.java", expect_fakefeatureflags_content),
+ ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content),
+ (
+ "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+ EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+ ),
]);
for file in generated_files {
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 9cb40bf..6945fd4 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -202,7 +202,11 @@
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,
@@ -214,8 +218,14 @@
bail!("no parsed flags, or the parsed flags use different packages");
};
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)
+ let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
+ 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> {
@@ -239,10 +249,8 @@
container: &str,
file: &StorageFileType,
) -> Result<Vec<u8>> {
- let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
- .into_iter()
- .map(|mut input| input.try_parse_flags())
- .collect::<Result<Vec<_>>>()?;
+ let parsed_flags_vec: Vec<ProtoParsedFlags> =
+ caches.into_iter().map(|mut input| input.try_parse_flags()).collect::<Result<Vec<_>>>()?;
generate_storage_file(container, parsed_flags_vec.iter(), file)
}
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index 69f5458..72be1c9 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -83,6 +83,12 @@
.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 @@
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/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 855ed02..73339f2 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -189,14 +189,14 @@
assert_eq!(packages[1].package_id, 1);
assert_eq!(packages[1].flag_names.len(), 3);
assert!(packages[1].flag_names.contains("enabled_ro"));
- assert!(packages[1].flag_names.contains("disabled_ro"));
+ assert!(packages[1].flag_names.contains("disabled_rw"));
assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
assert_eq!(packages[1].boolean_start_index, 3);
assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
assert_eq!(packages[2].package_id, 2);
assert_eq!(packages[2].flag_names.len(), 2);
- assert!(packages[2].flag_names.contains("enabled_ro"));
+ assert!(packages[2].flag_names.contains("enabled_rw"));
assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
assert_eq!(packages[2].boolean_start_index, 6);
}
diff --git a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
new file mode 100644
index 0000000..b82b9cb
--- /dev/null
+++ b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
@@ -0,0 +1,70 @@
+package {package_name};
+
+{{ if not library_exported- }}
+// TODO(b/303773055): Remove the annotation after access issue is resolved.
+import android.compat.annotation.UnsupportedAppUsage;
+{{ -endif }}
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+/** @hide */
+public class CustomFeatureFlags implements FeatureFlags \{
+
+ private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+ public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) \{
+ mGetValueImpl = getValueImpl;
+ }
+
+{{ -for item in flag_elements}}
+ @Override
+{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }}
+ public boolean {item.method_name}() \{
+ return getValue(Flags.FLAG_{item.flag_name_constant_suffix},
+ FeatureFlags::{item.method_name});
+ }
+{{ endfor }}
+
+{{ -if not library_exported }}
+ public boolean isFlagReadOnlyOptimized(String flagName) \{
+ if (mReadOnlyFlagsSet.contains(flagName) &&
+ isOptimizationEnabled()) \{
+ return true;
+ }
+ return false;
+ }
+
+ @com.android.aconfig.annotations.AssumeTrueForR8
+ private boolean isOptimizationEnabled() \{
+ return false;
+ }
+{{ -endif }}
+
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
+ return mGetValueImpl.test(flagName, getter);
+ }
+
+ public List<String> getFlagNames() \{
+ return Arrays.asList(
+ {{ -for item in flag_elements }}
+ Flags.FLAG_{item.flag_name_constant_suffix}
+ {{ -if not @last }},{{ endif }}
+ {{ -endfor }}
+ );
+ }
+
+ private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+ Arrays.asList(
+ {{ -for item in flag_elements }}
+ {{ -if not item.is_read_write }}
+ Flags.FLAG_{item.flag_name_constant_suffix},
+ {{ -endif }}
+ {{ -endfor }}
+ ""{# The empty string here is to resolve the ending comma #}
+ )
+ );
+}
diff --git a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
index 177e711..290d2c4 100644
--- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -1,27 +1,39 @@
package {package_name};
-{{ if not library_exported- }}
-// TODO(b/303773055): Remove the annotation after access issue is resolved.
-import android.compat.annotation.UnsupportedAppUsage;
-{{ -endif }}
-import java.util.Arrays;
+
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
+import java.util.function.Predicate;
/** @hide */
-public class FakeFeatureFlagsImpl implements FeatureFlags \{
+public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{
+ private final Map<String, Boolean> mFlagMap = new HashMap<>();
+ private final FeatureFlags mDefaults;
+
public FakeFeatureFlagsImpl() \{
- resetAll();
+ this(null);
}
-{{ for item in flag_elements}}
- @Override
-{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }}
- public boolean {item.method_name}() \{
- return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
+ public FakeFeatureFlagsImpl(FeatureFlags defaults) \{
+ super(null);
+ mDefaults = defaults;
+ // Initialize the map with null values
+ for (String flagName : getFlagNames()) \{
+ mFlagMap.put(flagName, null);
+ }
}
-{{ endfor}}
+
+ @Override
+ protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
+ Boolean value = this.mFlagMap.get(flagName);
+ if (value != null) \{
+ return value;
+ }
+ if (mDefaults != null) \{
+ return getter.test(mDefaults);
+ }
+ throw new IllegalArgumentException(flagName + " is not set");
+ }
+
public void setFlag(String flagName, boolean value) \{
if (!this.mFlagMap.containsKey(flagName)) \{
throw new IllegalArgumentException("no such flag " + flagName);
@@ -34,46 +46,4 @@
entry.setValue(null);
}
}
-{{ if not library_exported }}
- public boolean isFlagReadOnlyOptimized(String flagName) \{
- if (mReadOnlyFlagsSet.contains(flagName) &&
- isOptimizationEnabled()) \{
- return true;
- }
- return false;
- }
-
- @com.android.aconfig.annotations.AssumeTrueForR8
- private boolean isOptimizationEnabled() \{
- return false;
- }
-{{ -endif }}
- private boolean getValue(String flagName) \{
- Boolean value = this.mFlagMap.get(flagName);
- if (value == null) \{
- throw new IllegalArgumentException(flagName + " is not set");
- }
- return value;
- }
-
-
- private Map<String, Boolean> mFlagMap = new HashMap<>(
- Map.ofEntries(
- {{ -for item in flag_elements }}
- Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false)
- {{ -if not @last }},{{ endif }}
- {{ -endfor }}
- )
- );
-
- private Set<String> mReadOnlyFlagsSet = new HashSet<>(
- Arrays.asList(
- {{ -for item in flag_elements }}
- {{ -if not item.is_read_write }}
- Flags.FLAG_{item.flag_name_constant_suffix},
- {{ -endif }}
- {{ -endfor }}
- ""{# The empty string here is to resolve the ending comma #}
- )
- );
}
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index 4bcd1b7..7646015 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -1,5 +1,16 @@
#include "{header}.h"
+{{ 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, ...) \
+ __android_log_print(ANDROID_LOG_INFO, "AconfigTestMission1", (msg), __VA_ARGS__)
+
+{{ endif }}
+
{{ if readwrite- }}
#include <server_configurable_flags/get_flags.h>
{{ endif }}
@@ -97,6 +108,58 @@
{{ -if item.readwrite }}
return {cpp_namespace}::{item.flag_name}();
{{ -else }}
+ {{ if allow_instrumentation }}
+ auto result =
+ {{ if item.is_fixed_read_only }}
+ {package_macro}_{item.flag_macro}
+ {{ else }}
+ {item.default_value}
+ {{ endif }};
+
+ struct stat buffer;
+ if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) != 0) \{
+ return result;
+ }
+
+ auto package_map_file = aconfig_storage::get_mapped_file(
+ "{item.container}",
+ aconfig_storage::StorageFileType::package_map);
+ if (!package_map_file.ok()) \{
+ ALOGI("error: failed to get package map file: %s", package_map_file.error().message().c_str());
+ return result;
+ }
+
+ auto package_read_context = aconfig_storage::get_package_read_context(
+ *package_map_file, "{package}");
+ if (!package_read_context.ok()) \{
+ ALOGI("error: failed to get package read context: %s", package_map_file.error().message().c_str());
+ return result;
+ }
+
+ auto flag_val_map = aconfig_storage::get_mapped_file(
+ "{item.container}",
+ aconfig_storage::StorageFileType::flag_val);
+ if (!flag_val_map.ok()) \{
+ ALOGI("error: failed to get flag val map: %s", package_map_file.error().message().c_str());
+ return result;
+ }
+
+ auto value = aconfig_storage::get_boolean_flag_value(
+ *flag_val_map,
+ package_read_context->package_id + {item.flag_offset});
+ if (!value.ok()) \{
+ ALOGI("error: failed to get flag val: %s", package_map_file.error().message().c_str());
+ return result;
+ }
+
+ if (*value != result) \{
+ ALOGI("error: new storage value '%d' does not match current value '%d'", *value, result);
+ } else \{
+ ALOGI("success: new storage value was '%d, legacy storage was '%d'", *value, result);
+ }
+
+ return result;
+ {{ else }}
{{ -if item.is_fixed_read_only }}
return {package_macro}_{item.flag_macro};
{{ -else }}
@@ -104,6 +167,7 @@
{{ -endif }}
{{ -endif }}
{{ -endif }}
+ {{ -endif }}
}
{{ -if is_test_mode }}
@@ -119,3 +183,4 @@
}
{{ -endif }}
+
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.aconfig b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
index bb14fd1..db77f7a 100644
--- a/tools/aconfig/aconfig/tests/storage_test_2.aconfig
+++ b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
@@ -9,7 +9,7 @@
}
flag {
- name: "disabled_ro"
+ name: "disabled_rw"
namespace: "aconfig_test"
description: "This flag is DISABLED + READ_ONLY"
bug: "123"
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.values b/tools/aconfig/aconfig/tests/storage_test_2.values
index a7bb0b1..b650721 100644
--- a/tools/aconfig/aconfig/tests/storage_test_2.values
+++ b/tools/aconfig/aconfig/tests/storage_test_2.values
@@ -6,9 +6,9 @@
}
flag_value {
package: "com.android.aconfig.storage.test_2"
- name: "disabled_ro"
+ name: "disabled_rw"
state: DISABLED
- permission: READ_ONLY
+ permission: READ_WRITE
}
flag_value {
package: "com.android.aconfig.storage.test_2"
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.aconfig b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
index 333fe09..5802a73 100644
--- a/tools/aconfig/aconfig/tests/storage_test_4.aconfig
+++ b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
@@ -2,7 +2,7 @@
container: "system"
flag {
- name: "enabled_ro"
+ name: "enabled_rw"
namespace: "aconfig_test"
description: "This flag is ENABLED + READ_ONLY"
bug: "abc"
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.values b/tools/aconfig/aconfig/tests/storage_test_4.values
index fa21317..784b744 100644
--- a/tools/aconfig/aconfig/tests/storage_test_4.values
+++ b/tools/aconfig/aconfig/tests/storage_test_4.values
@@ -1,8 +1,8 @@
flag_value {
package: "com.android.aconfig.storage.test_4"
- name: "enabled_ro"
+ name: "enabled_rw"
state: ENABLED
- permission: READ_ONLY
+ permission: READ_WRITE
}
flag_value {
package: "com.android.aconfig.storage.test_4"
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 070a3cf..2acfc7d 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -357,8 +357,8 @@
),
(
String::from("com.android.aconfig.storage.test_2"),
- String::from("disabled_ro"),
- StoredFlagType::ReadOnlyBoolean,
+ String::from("disabled_rw"),
+ StoredFlagType::ReadWriteBoolean,
false,
),
(
@@ -381,8 +381,8 @@
),
(
String::from("com.android.aconfig.storage.test_4"),
- String::from("enabled_ro"),
- StoredFlagType::ReadOnlyBoolean,
+ String::from("enabled_rw"),
+ StoredFlagType::ReadWriteBoolean,
true,
),
];
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 608563c..106666c 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -92,8 +92,8 @@
None,
None,
None,
- Some(178),
None,
+ Some(177),
Some(204),
None,
Some(262),
@@ -108,8 +108,8 @@
let nodes = vec![
FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
FlagTableNode::new_expected(0, "enabled_rw", 0, 2, Some(151)),
- FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
- FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(2, "enabled_rw", 0, 1, None),
+ FlagTableNode::new_expected(1, "disabled_rw", 0, 0, None),
FlagTableNode::new_expected(1, "enabled_fixed_ro", 2, 1, Some(236)),
FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
FlagTableNode::new_expected(2, "enabled_fixed_ro", 2, 0, None),
@@ -140,7 +140,7 @@
num_flags: 8,
boolean_flag_offset: 27,
};
- let is_flag_rw = [true, false, true, false, false, false, false, false];
+ let is_flag_rw = [true, false, true, true, false, false, false, true];
let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();
FlagInfoList { header, nodes }
}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 3fb6a53..c89107f 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -102,5 +102,10 @@
"//apex_available:anyapex",
],
min_sdk_version: "29",
+ target: {
+ linux: {
+ version_script: "libaconfig_storage_read_api_cc.map",
+ },
+ },
double_loadable: true,
}
diff --git a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
index ff2f38e..8fe42ce 100644
--- a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
@@ -65,8 +65,24 @@
return Error() << "Unable to find storage files for container " << container;;
}
+namespace private_internal_api {
+
+/// Get mapped file implementation.
+Result<MappedStorageFile> get_mapped_file_impl(
+ std::string const& pb_file,
+ std::string const& container,
+ StorageFileType file_type) {
+ auto file_result = find_storage_file(pb_file, container, file_type);
+ if (!file_result.ok()) {
+ return Error() << file_result.error();
+ }
+ return map_storage_file(*file_result);
+}
+
+} // namespace private internal api
+
/// Map a storage file
-static Result<MappedStorageFile> map_storage_file(std::string const& file) {
+Result<MappedStorageFile> map_storage_file(std::string const& file) {
int fd = open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
if (fd == -1) {
return ErrnoError() << "failed to open " << file;
@@ -90,22 +106,6 @@
return mapped_file;
}
-namespace private_internal_api {
-
-/// Get mapped file implementation.
-Result<MappedStorageFile> get_mapped_file_impl(
- std::string const& pb_file,
- std::string const& container,
- StorageFileType file_type) {
- auto file_result = find_storage_file(pb_file, container, file_type);
- if (!file_result.ok()) {
- return Error() << file_result.error();
- }
- return map_storage_file(*file_result);
-}
-
-} // namespace private internal api
-
/// Map from StoredFlagType to FlagValueType
android::base::Result<FlagValueType> map_to_flag_value_type(
StoredFlagType stored_type) {
diff --git a/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
index 0a4d5b4..2bd84fc 100644
--- a/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
+++ b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
@@ -67,6 +67,11 @@
} // namespace private_internal_api
+/// Map a storage file
+android::base::Result<MappedStorageFile> map_storage_file(
+ std::string const& file);
+
+
/// Map from StoredFlagType to FlagValueType
/// \input stored_type: stored flag type in the storage file
/// \returns the flag value type enum
diff --git a/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map b/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map
new file mode 100644
index 0000000..7d47e0b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map
@@ -0,0 +1,11 @@
+LIBACONFIG_STORAGE_READ_API_CC {
+ # Export everything in the aconfig_storage namespace. This includes both the
+ # public API and library internals.
+ global:
+ extern "C++" {
+ aconfig_storage::*;
+ };
+ # Hide everything else.
+ local:
+ *;
+};
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
index 0087feb..6d03377 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
@@ -70,7 +70,7 @@
// this test point locks down query if flag is readwrite
fn test_is_flag_readwrite() {
let flag_info_list = create_test_flag_info_list().into_bytes();
- let baseline: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+ let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true];
for offset in 0..8 {
let attribute =
find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
index 55fdcb7..a1a4793 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
@@ -82,8 +82,8 @@
let baseline = vec![
(0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
(0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
- (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
- (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+ (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
(1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
(1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
(2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index fa8dad6..e65dcfb 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -468,8 +468,8 @@
let baseline = vec![
(0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
(0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
- (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
- (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+ (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
(1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
(1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
(2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
index d26e00f..e868f53 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/flag.map
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
Binary files differ
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 5b4d32c..b499c1c 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
@@ -167,8 +167,8 @@
auto baseline = std::vector<std::tuple<int, std::string, api::StoredFlagType, int>>{
{0, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
{0, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 2},
- {1, "disabled_ro", api::StoredFlagType::ReadOnlyBoolean, 0},
- {2, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
+ {2, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 1},
+ {1, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
{1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1},
{1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2},
{2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0},
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 1a8af75..ce9c018 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
@@ -118,8 +118,8 @@
let baseline = vec![
(0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
(0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
- (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
- (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+ (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
(1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
(1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
(2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
diff --git a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
index f031d01..cd57f4f 100644
--- a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -17,78 +17,6 @@
namespace aconfig_storage {
-/// Storage location pb file
-static constexpr char kPersistStorageRecordsPb[] =
- "/metadata/aconfig/persistent_storage_file_records.pb";
-
-/// Read aconfig storage records pb file
-static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_file) {
- auto records = storage_records_pb();
- auto content = std::string();
- if (!ReadFileToString(pb_file, &content)) {
- return ErrnoError() << "ReadFileToString failed";
- }
-
- if (!records.ParseFromString(content)) {
- return ErrnoError() << "Unable to parse persistent storage records protobuf";
- }
- return records;
-}
-
-/// Get storage file path
-static Result<std::string> find_storage_file(
- std::string const& pb_file,
- std::string const& container,
- StorageFileType file_type) {
- auto records_pb = read_storage_records_pb(pb_file);
- if (!records_pb.ok()) {
- return Error() << "Unable to read storage records from " << pb_file
- << " : " << records_pb.error();
- }
-
- for (auto& entry : records_pb->files()) {
- if (entry.container() == container) {
- switch(file_type) {
- case StorageFileType::package_map:
- return entry.package_map();
- case StorageFileType::flag_map:
- return entry.flag_map();
- case StorageFileType::flag_val:
- return entry.flag_val();
- case StorageFileType::flag_info:
- return entry.flag_info();
- default:
- return Error() << "Invalid file type " << file_type;
- }
- }
- }
-
- return Error() << "Unable to find storage files for container " << container;
-}
-
-
-namespace private_internal_api {
-
-/// Get mutable mapped file implementation.
-Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
- std::string const& pb_file,
- std::string const& container,
- StorageFileType file_type) {
- if (file_type != StorageFileType::flag_val &&
- file_type != StorageFileType::flag_info) {
- return Error() << "Cannot create mutable mapped file for this file type";
- }
-
- auto file_result = find_storage_file(pb_file, container, file_type);
- if (!file_result.ok()) {
- return Error() << file_result.error();
- }
-
- return map_mutable_storage_file(*file_result);
-}
-
-} // namespace private internal api
-
/// Map a storage file
Result<MutableMappedStorageFile> map_mutable_storage_file(std::string const& file) {
struct stat file_stat;
@@ -120,14 +48,6 @@
return mapped_file;
}
-/// Get mutable mapped file
-Result<MutableMappedStorageFile> get_mutable_mapped_file(
- std::string const& container,
- StorageFileType file_type) {
- return private_internal_api::get_mutable_mapped_file_impl(
- kPersistStorageRecordsPb, container, file_type);
-}
-
/// Set boolean flag value
Result<void> set_boolean_flag_value(
const MutableMappedStorageFile& file,
diff --git a/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
index 12b0ecc..7148396 100644
--- a/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
+++ b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
@@ -16,25 +16,10 @@
size_t file_size;
};
-/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
-namespace private_internal_api {
-
-Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
- std::string const& pb_file,
- std::string const& container,
- StorageFileType file_type);
-
-} // namespace private_internal_api
-
/// Map a storage file
Result<MutableMappedStorageFile> map_mutable_storage_file(
std::string const& file);
-/// Get mapped writeable storage file
-Result<MutableMappedStorageFile> get_mutable_mapped_file(
- std::string const& container,
- StorageFileType file_type);
-
/// Set boolean flag value
Result<void> set_boolean_flag_value(
const MutableMappedStorageFile& file,
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
index bcddce1..aec28de 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -34,15 +34,9 @@
use std::fs::File;
use std::io::{Read, Write};
-/// Storage file location pb file
-pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
-
/// Get read write mapped storage files.
///
-/// \input container: the flag package container
-/// \input file_type: storage file type enum
-/// \return a result of read write mapped file
-///
+/// \input file_path: path to the storage file
///
/// # Safety
///
@@ -50,11 +44,8 @@
/// file not thru this memory mapped file or there are concurrent writes to this
/// memory mapped file. Ensure all writes to the underlying file are thru this memory
/// mapped file and there are no concurrent writes.
-pub unsafe fn get_mapped_storage_file(
- container: &str,
- file_type: StorageFileType,
-) -> Result<MmapMut, AconfigStorageError> {
- unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
+pub unsafe fn map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
+ crate::mapped_file::map_file(file_path)
}
/// Set boolean flag value thru mapped file and flush the change to file
@@ -344,7 +335,6 @@
mod tests {
use super::*;
use crate::test_utils::copy_to_temp_file;
- use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
use aconfig_storage_file::test_utils::{
create_test_flag_info_list, create_test_flag_table, create_test_package_table,
write_bytes_to_temp_file,
@@ -367,33 +357,12 @@
fn test_set_boolean_flag_value() {
let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
let flag_value_path = flag_value_file.path().display().to_string();
- let text_proto = format!(
- r#"
-files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "{}"
- flag_info: "some_flag.info"
- timestamp: 12345
-}}
-"#,
- flag_value_path
- );
- let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is guaranteed as only this single threaded test process will
// write to this file
unsafe {
- let mut file = crate::mapped_file::get_mapped_file(
- &record_pb_path,
- "system",
- StorageFileType::FlagVal,
- )
- .unwrap();
+ let mut file = map_mutable_storage_file(&flag_value_path).unwrap();
for i in 0..8 {
set_boolean_flag_value(&mut file, i, true).unwrap();
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
@@ -417,33 +386,12 @@
fn test_set_flag_has_server_override() {
let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
let flag_info_path = flag_info_file.path().display().to_string();
- let text_proto = format!(
- r#"
- files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "some_flag.val"
- flag_info: "{}"
- timestamp: 12345
- }}
- "#,
- flag_info_path
- );
- let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is guaranteed as only this single threaded test process will
// write to this file
unsafe {
- let mut file = crate::mapped_file::get_mapped_file(
- &record_pb_path,
- "system",
- StorageFileType::FlagInfo,
- )
- .unwrap();
+ let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
for i in 0..8 {
set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
let attribute =
@@ -461,33 +409,12 @@
fn test_set_flag_has_local_override() {
let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
let flag_info_path = flag_info_file.path().display().to_string();
- let text_proto = format!(
- r#"
- files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "some_flag.val"
- flag_info: "{}"
- timestamp: 12345
- }}
- "#,
- flag_info_path
- );
- let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is guaranteed as only this single threaded test process will
// write to this file
unsafe {
- let mut file = crate::mapped_file::get_mapped_file(
- &record_pb_path,
- "system",
- StorageFileType::FlagInfo,
- )
- .unwrap();
+ let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
for i in 0..8 {
set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
let attribute =
diff --git a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
index ea9ac19..401d6b7 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
@@ -17,11 +17,8 @@
use anyhow::anyhow;
use memmap2::MmapMut;
use std::fs::{self, OpenOptions};
-use std::io::Read;
use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};
-use aconfig_storage_file::StorageFileType;
-use aconfig_storage_read_api::mapped_file::find_container_storage_location;
/// Get the mutable memory mapping of a storage file
///
@@ -31,7 +28,7 @@
/// file not thru this memory mapped file or there are concurrent writes to this
/// memory mapped file. Ensure all writes to the underlying file are thru this memory
/// mapped file and there are no concurrent writes.
-unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
+pub(crate) unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
// make sure file has read write permission
let perms = fs::metadata(file_path).unwrap().permissions();
if perms.readonly() {
@@ -51,57 +48,18 @@
}
}
-/// Get a mapped storage file given the container and file type
-///
-/// # Safety
-///
-/// The memory mapped file may have undefined behavior if there are writes to this
-/// file not thru this memory mapped file or there are concurrent writes to this
-/// memory mapped file. Ensure all writes to the underlying file are thru this memory
-/// mapped file and there are no concurrent writes.
-pub unsafe fn get_mapped_file(
- location_pb_file: &str,
- container: &str,
- file_type: StorageFileType,
-) -> Result<MmapMut, AconfigStorageError> {
- let files_location = find_container_storage_location(location_pb_file, container)?;
- match file_type {
- StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
- StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
- _ => Err(MapFileFail(anyhow!(
- "Cannot map file type {:?} as writeable memory mapped files.",
- file_type
- ))),
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::copy_to_temp_file;
- use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+ use std::io::Read;
#[test]
fn test_mapped_file_contents() {
let mut rw_val_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
let mut rw_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
- let text_proto = format!(
- r#"
-files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "{}"
- flag_info: "{}"
- timestamp: 12345
-}}
-"#,
- rw_val_file.path().display().to_string(),
- rw_info_file.path().display().to_string()
- );
- let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
- let storage_record_file_path = storage_record_file.path().display().to_string();
+ let flag_val = rw_val_file.path().display().to_string();
+ let flag_info = rw_info_file.path().display().to_string();
let mut content = Vec::new();
rw_val_file.read_to_end(&mut content).unwrap();
@@ -109,9 +67,7 @@
// SAFETY:
// The safety here is guaranteed here as no writes happens to this temp file
unsafe {
- let mmaped_file =
- get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
- .unwrap();
+ let mmaped_file = map_file(&flag_val).unwrap();
assert_eq!(mmaped_file[..], content[..]);
}
@@ -121,90 +77,23 @@
// SAFETY:
// The safety here is guaranteed here as no writes happens to this temp file
unsafe {
- let mmaped_file =
- get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagInfo)
- .unwrap();
+ let mmaped_file = map_file(&flag_info).unwrap();
assert_eq!(mmaped_file[..], content[..]);
}
}
#[test]
fn test_mapped_read_only_file() {
- let ro_file = copy_to_temp_file("./tests/flag.val", true).unwrap();
- let text_proto = format!(
- r#"
-files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "{}"
- flag_info: "some_flag.info"
- timestamp: 12345
-}}
-"#,
- ro_file.path().display().to_string()
- );
- let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
- let storage_record_file_path = storage_record_file.path().display().to_string();
+ let ro_val_file = copy_to_temp_file("./tests/flag.val", true).unwrap();
+ let flag_val = ro_val_file.path().display().to_string();
// SAFETY:
// The safety here is guaranteed here as no writes happens to this temp file
unsafe {
- let error =
- get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
- .unwrap_err();
+ let error = map_file(&flag_val).unwrap_err();
assert_eq!(
format!("{:?}", error),
- format!(
- "MapFileFail(fail to map non read write storage file {})",
- ro_file.path().display().to_string()
- )
- );
- }
- }
-
- #[test]
- fn test_mapped_not_supported_file() {
- let text_proto = format!(
- r#"
-files {{
- version: 0
- container: "system"
- package_map: "some_package.map"
- flag_map: "some_flag.map"
- flag_val: "some_flag.val"
- flag_info: "some_flag.info"
- timestamp: 12345
-}}
-"#,
- );
- let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
- let storage_record_file_path = storage_record_file.path().display().to_string();
-
- // SAFETY:
- // The safety here is guaranteed here as no writes happens to this temp file
- unsafe {
- let error =
- get_mapped_file(&storage_record_file_path, "system", StorageFileType::PackageMap)
- .unwrap_err();
- assert_eq!(
- format!("{:?}", error),
- format!(
- "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
- StorageFileType::PackageMap
- )
- );
-
- let error =
- get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagMap)
- .unwrap_err();
- assert_eq!(
- format!("{:?}", error),
- format!(
- "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
- StorageFileType::FlagMap
- )
+ format!("MapFileFail(fail to map non read write storage file {})", flag_val)
);
}
}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.info b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
index 820d839..6223edf 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/flag.info
+++ b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
index ab5f0a7..bd39e9e 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
@@ -50,88 +50,36 @@
return temp_file;
}
- Result<std::string> write_storage_location_pb_file(std::string const& flag_val,
- std::string const& flag_info) {
- auto temp_file = std::tmpnam(nullptr);
- auto proto = storage_files();
- auto* info = proto.add_files();
- info->set_version(0);
- info->set_container("mockup");
- info->set_package_map("some_package.map");
- info->set_flag_map("some_flag.map");
- info->set_flag_val(flag_val);
- info->set_flag_info(flag_info);
- info->set_timestamp(12345);
-
- auto content = std::string();
- proto.SerializeToString(&content);
- if (!WriteStringToFile(content, temp_file)) {
- return Error() << "failed to write storage records pb file";
- }
- return temp_file;
- }
-
void SetUp() override {
auto const test_dir = android::base::GetExecutableDirectory();
flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info");
- storage_record_pb = *write_storage_location_pb_file(flag_val, flag_info);
}
void TearDown() override {
std::remove(flag_val.c_str());
std::remove(flag_info.c_str());
- std::remove(storage_record_pb.c_str());
}
std::string flag_val;
std::string flag_info;
- std::string storage_record_pb;
};
-/// Negative test to lock down the error when mapping none exist storage files
-TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "vendor", api::StorageFileType::flag_val);
- ASSERT_FALSE(mapped_file_result.ok());
- ASSERT_EQ(mapped_file_result.error().message(),
- "Unable to find storage files for container vendor");
-}
-
/// Negative test to lock down the error when mapping a non writeable storage file
TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {
ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_val);
+ auto mapped_file_result = api::map_mutable_storage_file(flag_val);
ASSERT_FALSE(mapped_file_result.ok());
auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
}
-/// Negative test to lock down the error when mapping a file type that cannot be modified
-TEST_F(AconfigStorageTest, test_invalid_storage_file_type_mapping) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::package_map);
- ASSERT_FALSE(mapped_file_result.ok());
- auto it = mapped_file_result.error().message().find(
- "Cannot create mutable mapped file for this file type");
- ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
-
- mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_map);
- ASSERT_FALSE(mapped_file_result.ok());
- it = mapped_file_result.error().message().find(
- "Cannot create mutable mapped file for this file type");
- ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
-}
-
/// Test to lock down storage flag value update api
TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_val);
+ auto mapped_file_result = api::map_mutable_storage_file(flag_val);
ASSERT_TRUE(mapped_file_result.ok());
- auto mapped_file = *mapped_file_result;
+ auto mapped_file = *mapped_file_result;
for (int offset = 0; offset < 8; ++offset) {
auto update_result = api::set_boolean_flag_value(mapped_file, offset, true);
ASSERT_TRUE(update_result.ok());
@@ -146,10 +94,10 @@
/// Negative test to lock down the error when querying flag value out of range
TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_val);
+ auto mapped_file_result = api::map_mutable_storage_file(flag_val);
ASSERT_TRUE(mapped_file_result.ok());
auto mapped_file = *mapped_file_result;
+
auto update_result = api::set_boolean_flag_value(mapped_file, 8, true);
ASSERT_FALSE(update_result.ok());
ASSERT_EQ(update_result.error().message(),
@@ -158,15 +106,14 @@
/// Test to lock down storage flag has server override update api
TEST_F(AconfigStorageTest, test_flag_has_server_override_update) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ auto mapped_file_result = api::map_mutable_storage_file(flag_info);
ASSERT_TRUE(mapped_file_result.ok());
auto mapped_file = *mapped_file_result;
for (int offset = 0; offset < 8; ++offset) {
auto update_result = api::set_flag_has_server_override(
mapped_file, api::FlagValueType::Boolean, offset, true);
- ASSERT_TRUE(update_result.ok());
+ ASSERT_TRUE(update_result.ok()) << update_result.error();
auto ro_mapped_file = api::MappedStorageFile();
ro_mapped_file.file_ptr = mapped_file.file_ptr;
ro_mapped_file.file_size = mapped_file.file_size;
@@ -189,8 +136,7 @@
/// Test to lock down storage flag has local override update api
TEST_F(AconfigStorageTest, test_flag_has_local_override_update) {
- auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
- storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ auto mapped_file_result = api::map_mutable_storage_file(flag_info);
ASSERT_TRUE(mapped_file_result.ok());
auto mapped_file = *mapped_file_result;
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
index a087c1a..367569d 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
@@ -1,44 +1,17 @@
#[cfg(not(feature = "cargo"))]
mod aconfig_storage_write_api_test {
- use aconfig_storage_file::protos::ProtoStorageFiles;
- use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType};
+ use aconfig_storage_file::{FlagInfoBit, FlagValueType};
use aconfig_storage_read_api::flag_info_query::find_flag_attribute;
use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
use aconfig_storage_write_api::{
- mapped_file::get_mapped_file, set_boolean_flag_value, set_flag_has_local_override,
+ map_mutable_storage_file, set_boolean_flag_value, set_flag_has_local_override,
set_flag_has_server_override,
};
- use protobuf::Message;
use std::fs::{self, File};
- use std::io::{Read, Write};
+ use std::io::Read;
use tempfile::NamedTempFile;
- /// Write storage location record pb to a temp file
- fn write_storage_record_file(flag_val: &str, flag_info: &str) -> NamedTempFile {
- let text_proto = format!(
- r#"
-files {{
- version: 0
- container: "mockup"
- package_map: "some_package_map"
- flag_map: "some_flag_map"
- flag_val: "{}"
- flag_info: "{}"
- timestamp: 12345
-}}
-"#,
- flag_val, flag_info
- );
- let storage_files: ProtoStorageFiles =
- protobuf::text_format::parse_from_str(&text_proto).unwrap();
- let mut binary_proto_bytes = Vec::new();
- storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
- let mut file = NamedTempFile::new().unwrap();
- file.write_all(&binary_proto_bytes).unwrap();
- file
- }
-
/// Create temp file copy
fn copy_to_temp_rw_file(source_file: &str) -> NamedTempFile {
let file = NamedTempFile::new().unwrap();
@@ -66,18 +39,12 @@
/// Test to lock down flag value update api
fn test_boolean_flag_value_update() {
let flag_value_file = copy_to_temp_rw_file("./flag.val");
- let flag_info_file = copy_to_temp_rw_file("./flag.info");
let flag_value_path = flag_value_file.path().display().to_string();
- let flag_info_path = flag_info_file.path().display().to_string();
- let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as only this single threaded test process will
// write to this file
- let mut file = unsafe {
- get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagVal).unwrap()
- };
+ let mut file = unsafe { map_mutable_storage_file(&flag_value_path).unwrap() };
for i in 0..8 {
set_boolean_flag_value(&mut file, i, true).unwrap();
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
@@ -92,19 +59,13 @@
#[test]
/// Test to lock down flag has server override update api
fn test_set_flag_has_server_override() {
- let flag_value_file = copy_to_temp_rw_file("./flag.val");
let flag_info_file = copy_to_temp_rw_file("./flag.info");
- let flag_value_path = flag_value_file.path().display().to_string();
let flag_info_path = flag_info_file.path().display().to_string();
- let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as only this single threaded test process will
// write to this file
- let mut file = unsafe {
- get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
- };
+ let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };
for i in 0..8 {
set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
let attribute =
@@ -120,19 +81,13 @@
#[test]
/// Test to lock down flag has local override update api
fn test_set_flag_has_local_override() {
- let flag_value_file = copy_to_temp_rw_file("./flag.val");
let flag_info_file = copy_to_temp_rw_file("./flag.info");
- let flag_value_path = flag_value_file.path().display().to_string();
let flag_info_path = flag_info_file.path().display().to_string();
- let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
- let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as only this single threaded test process will
// write to this file
- let mut file = unsafe {
- get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
- };
+ let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };
for i in 0..8 {
set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
let attribute =
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 5d87a4c..0569bfd 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
@@ -16,6 +16,8 @@
package com.android.checkflaggedapis
import android.aconfig.Aconfig
+import android.aconfig.Aconfig.flag_state.DISABLED
+import android.aconfig.Aconfig.flag_state.ENABLED
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
@@ -28,9 +30,12 @@
"""
// Signature format: 2.0
package android {
- public final class Clazz {
- ctor public Clazz();
+ @FlaggedApi("android.flag.foo") public final class 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 {
}
}
"""
@@ -43,13 +48,19 @@
<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>
</api>
"""
.trim()
-private fun generateFlagsProto(fooState: Aconfig.flag_state): InputStream {
- val parsed_flag =
+private fun generateFlagsProto(
+ fooState: Aconfig.flag_state,
+ barState: Aconfig.flag_state
+): InputStream {
+ val fooFlag =
Aconfig.parsed_flag
.newBuilder()
.setPackage("android.flag")
@@ -57,9 +68,18 @@
.setState(fooState)
.setPermission(Aconfig.flag_permission.READ_ONLY)
.build()
- val parsed_flags = Aconfig.parsed_flags.newBuilder().addParsedFlag(parsed_flag).build()
+ val barFlag =
+ Aconfig.parsed_flag
+ .newBuilder()
+ .setPackage("android.flag")
+ .setName("bar")
+ .setState(barState)
+ .setPermission(Aconfig.flag_permission.READ_ONLY)
+ .build()
+ val flags =
+ Aconfig.parsed_flags.newBuilder().addParsedFlag(fooFlag).addParsedFlag(barFlag).build()
val binaryProto = ByteArrayOutputStream()
- parsed_flags.writeTo(binaryProto)
+ flags.writeTo(binaryProto)
return ByteArrayInputStream(binaryProto.toByteArray())
}
@@ -67,21 +87,36 @@
class CheckFlaggedApisTest {
@Test
fun testParseApiSignature() {
- val expected = setOf(Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
+ 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())
assertEquals(expected, actual)
}
@Test
fun testParseFlagValues() {
- val expected: Map<Flag, Boolean> = mapOf(Flag("android.flag.foo") to true)
- val actual = parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED))
+ val expected: Map<Flag, Boolean> =
+ mapOf(Flag("android.flag.foo") to true, Flag("android.flag.bar") to true)
+ val actual = parseFlagValues(generateFlagsProto(ENABLED, ENABLED))
assertEquals(expected, actual)
}
@Test
fun testParseApiVersions() {
- val expected: Set<Symbol> = setOf(Symbol("android.Clazz.FOO"))
+ 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())
assertEquals(expected, actual)
}
@@ -92,7 +127,7 @@
val actual =
findErrors(
parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
- parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED)),
+ parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
parseApiVersions(API_VERSIONS.byteInputStream()))
assertEquals(expected, actual)
}
@@ -101,11 +136,19 @@
fun testFindErrorsDisabledFlaggedApiIsPresent() {
val expected =
setOf<ApiError>(
- DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
+ 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 =
findErrors(
parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
- parseFlagValues(generateFlagsProto(Aconfig.flag_state.DISABLED)),
+ parseFlagValues(generateFlagsProto(DISABLED, DISABLED)),
parseApiVersions(API_VERSIONS.byteInputStream()))
assertEquals(expected, 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 84564ba..0c078a0 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
@@ -19,7 +19,10 @@
import android.aconfig.Aconfig
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
@@ -46,7 +49,7 @@
@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 {
@@ -167,22 +170,50 @@
}
internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
- // TODO(334870672): add support for classes and metods
+ // TODO(334870672): add support for metods
val output = mutableSetOf<Pair<Symbol, Flag>>()
val visitor =
object : BaseItemVisitor() {
- override fun visitField(field: FieldItem) {
- val flag =
- field.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value() as? String
- if (flag != null) {
- val symbol = Symbol.create(field.baselineElementId())
- output.add(Pair(symbol, Flag(flag)))
+ override fun visitClass(cls: ClassItem) {
+ getFlagOrNull(cls)?.let { flag ->
+ val symbol = Symbol.create(cls.baselineElementId())
+ output.add(Pair(symbol, flag))
}
}
+
+ override fun visitField(field: FieldItem) {
+ getFlagOrNull(field)?.let { flag ->
+ val symbol = Symbol.create(field.baselineElementId())
+ output.add(Pair(symbol, flag))
+ }
+ }
+
+ 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")
+ ?.findAttribute("value")
+ ?.value
+ ?.let { Flag(it.value() as String) }
+ }
}
val codebase = ApiFile.parseApi(path, input)
codebase.accept(visitor)
@@ -203,16 +234,54 @@
val factory = DocumentBuilderFactory.newInstance()
val parser = factory.newDocumentBuilder()
val document = parser.parse(input)
+
+ val classes = document.getElementsByTagName("class")
+ // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
+ for (i in 0.rangeUntil(classes.getLength())) {
+ val cls = classes.item(i)
+ val className =
+ requireNotNull(cls.getAttribute("name")) {
+ "Bad XML: <class> element without name attribute"
+ }
+ output.add(Symbol.create(className.replace("/", ".")))
+ }
+
val fields = document.getElementsByTagName("field")
// ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
for (i in 0.rangeUntil(fields.getLength())) {
val field = fields.item(i)
- val fieldName = field.getAttribute("name")
+ val fieldName =
+ requireNotNull(field.getAttribute("name")) {
+ "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/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 23762bb..432ea19 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -195,6 +195,8 @@
ro.product.* properties are overridden by the 'import' statement.
The file expects one property per line, and each line has the following
format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro'
+ The path specified can either be relative to the current working directory
+ or the path to a file inside of input_target_files.
--skip_postinstall
Skip the postinstall hooks when generating an A/B OTA package (default:
@@ -1048,6 +1050,10 @@
from check_target_files_vintf import CheckVintfIfTrebleEnabled
CheckVintfIfTrebleEnabled(target_file, target_info)
+ # Allow boot_variable_file to also exist in target-files
+ if OPTIONS.boot_variable_file:
+ if not os.path.isfile(OPTIONS.boot_variable_file):
+ OPTIONS.boot_variable_file = os.path.join(target_file, OPTIONS.boot_variable_file)
# Metadata to comply with Android OTA package format.
metadata = GetPackageMetadata(target_info, source_info)
# Generate payload.
diff --git a/tools/tool_event_logger/Android.bp b/tools/tool_event_logger/Android.bp
new file mode 100644
index 0000000..7a1d2aa
--- /dev/null
+++ b/tools/tool_event_logger/Android.bp
@@ -0,0 +1,67 @@
+// Copyright 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.
+
+// Set of error prone rules to ensure code quality
+// PackageLocation check requires the androidCompatible=false otherwise it does not do anything.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_adte",
+}
+
+python_library_host {
+ name: "tool_event_proto",
+ srcs: [
+ "proto/tool_event.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
+
+python_binary_host {
+ name: "tool_event_logger",
+ pkg_path: "tool_event_logger",
+ srcs: [
+ "tool_event_logger.py",
+ ],
+ libs: [
+ "asuite_cc_client",
+ "tool_event_proto",
+ ],
+ main: "tool_event_logger.py",
+}
+
+python_test_host {
+ name: "tool_event_logger_test",
+ main: "tool_event_logger_test.py",
+ pkg_path: "tool_event_logger",
+ srcs: [
+ "tool_event_logger.py",
+ "tool_event_logger_test.py",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ libs: [
+ "asuite_cc_client",
+ "tool_event_proto",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ enabled: true,
+ },
+ },
+}
diff --git a/tools/tool_event_logger/OWNERS b/tools/tool_event_logger/OWNERS
new file mode 100644
index 0000000..b692c9e
--- /dev/null
+++ b/tools/tool_event_logger/OWNERS
@@ -0,0 +1,4 @@
+include platform/tools/asuite:/OWNERS
+
+zhuoyao@google.com
+hzalek@google.com
\ No newline at end of file
diff --git a/tools/tool_event_logger/proto/tool_event.proto b/tools/tool_event_logger/proto/tool_event.proto
new file mode 100644
index 0000000..61e28a2
--- /dev/null
+++ b/tools/tool_event_logger/proto/tool_event.proto
@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package tools.asuite.tool_event_logger;
+
+message ToolEvent {
+ // Occurs immediately upon execution of the tool.
+ message InvocationStarted {
+ string command_args = 1;
+ string cwd = 2;
+ string os = 3;
+ }
+
+ // Occurs when tool exits for any reason.
+ message InvocationStopped {
+ int32 exit_code = 2;
+ string exit_log = 3;
+ }
+
+ // ------------------------
+ // FIELDS FOR ToolEvent
+ // ------------------------
+ // Random string generated to identify the invocation.
+ string invocation_id = 1;
+ // Internal user name.
+ string user_name = 2;
+ // The root of Android source.
+ string source_root = 3;
+ // Name of the tool used.
+ string tool_tag = 6;
+
+ oneof event {
+ InvocationStarted invocation_started = 4;
+ InvocationStopped invocation_stopped = 5;
+ }
+}
diff --git a/tools/tool_event_logger/tool_event_logger.py b/tools/tool_event_logger/tool_event_logger.py
new file mode 100644
index 0000000..65a9696
--- /dev/null
+++ b/tools/tool_event_logger/tool_event_logger.py
@@ -0,0 +1,229 @@
+# Copyright 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.
+
+
+import argparse
+import datetime
+import getpass
+import logging
+import os
+import platform
+import sys
+import tempfile
+import uuid
+
+from atest.metrics import clearcut_client
+from atest.proto import clientanalytics_pb2
+from proto import tool_event_pb2
+
+LOG_SOURCE = 2395
+
+
+class ToolEventLogger:
+ """Logs tool events to Sawmill through Clearcut."""
+
+ def __init__(
+ self,
+ tool_tag: str,
+ invocation_id: str,
+ user_name: str,
+ source_root: str,
+ platform_version: str,
+ python_version: str,
+ client: clearcut_client.Clearcut,
+ ):
+ self.tool_tag = tool_tag
+ self.invocation_id = invocation_id
+ self.user_name = user_name
+ self.source_root = source_root
+ self.platform_version = platform_version
+ self.python_version = python_version
+ self._clearcut_client = client
+
+ @classmethod
+ def create(cls, tool_tag: str):
+ return ToolEventLogger(
+ tool_tag=tool_tag,
+ invocation_id=str(uuid.uuid4()),
+ user_name=getpass.getuser(),
+ source_root=os.environ.get('ANDROID_BUILD_TOP', ''),
+ platform_version=platform.platform(),
+ python_version=platform.python_version(),
+ client=clearcut_client.Clearcut(LOG_SOURCE),
+ )
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.flush()
+
+ def log_invocation_started(self, event_time: datetime, command_args: str):
+ """Creates an event log with invocation started info."""
+ event = self._create_tool_event()
+ event.invocation_started.CopyFrom(
+ tool_event_pb2.ToolEvent.InvocationStarted(
+ command_args=command_args,
+ os=f'{self.platform_version}:{self.python_version}',
+ )
+ )
+
+ logging.debug('Log invocation_started: %s', event)
+ self._log_clearcut_event(event, event_time)
+
+ def log_invocation_stopped(
+ self,
+ event_time: datetime,
+ exit_code: int,
+ exit_log: str,
+ ):
+ """Creates an event log with invocation stopped info."""
+ event = self._create_tool_event()
+ event.invocation_stopped.CopyFrom(
+ tool_event_pb2.ToolEvent.InvocationStopped(
+ exit_code=exit_code,
+ exit_log=exit_log,
+ )
+ )
+
+ logging.debug('Log invocation_stopped: %s', event)
+ self._log_clearcut_event(event, event_time)
+
+ def flush(self):
+ """Sends all batched events to Clearcut."""
+ logging.debug('Sending events to Clearcut.')
+ self._clearcut_client.flush_events()
+
+ def _create_tool_event(self):
+ return tool_event_pb2.ToolEvent(
+ tool_tag=self.tool_tag,
+ invocation_id=self.invocation_id,
+ user_name=self.user_name,
+ source_root=self.source_root,
+ )
+
+ def _log_clearcut_event(
+ self, tool_event: tool_event_pb2.ToolEvent, event_time: datetime
+ ):
+ log_event = clientanalytics_pb2.LogEvent(
+ event_time_ms=int(event_time.timestamp() * 1000),
+ source_extension=tool_event.SerializeToString(),
+ )
+ self._clearcut_client.log(log_event)
+
+
+class ArgumentParserWithLogging(argparse.ArgumentParser):
+
+ def error(self, message):
+ logging.error('Failed to parse args with error: %s', message)
+ super().error(message)
+
+
+def create_arg_parser():
+ """Creates an instance of the default ToolEventLogger arg parser."""
+
+ parser = ArgumentParserWithLogging(
+ description='Build and upload logs for Android dev tools',
+ add_help=True,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+
+ parser.add_argument(
+ '--tool_tag',
+ type=str,
+ required=True,
+ help='Name of the tool.',
+ )
+
+ parser.add_argument(
+ '--start_timestamp',
+ type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),
+ required=True,
+ help=(
+ 'Timestamp when the tool starts. The timestamp should have the format'
+ '%s.%N which represents the seconds elapses since epoch.'
+ ),
+ )
+
+ parser.add_argument(
+ '--end_timestamp',
+ type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),
+ required=True,
+ help=(
+ 'Timestamp when the tool exits. The timestamp should have the format'
+ '%s.%N which represents the seconds elapses since epoch.'
+ ),
+ )
+
+ parser.add_argument(
+ '--tool_args',
+ type=str,
+ help='Parameters that are passed to the tool.',
+ )
+
+ parser.add_argument(
+ '--exit_code',
+ type=int,
+ required=True,
+ help='Tool exit code.',
+ )
+
+ parser.add_argument(
+ '--exit_log',
+ type=str,
+ help='Logs when tool exits.',
+ )
+
+ parser.add_argument(
+ '--dry_run',
+ action='store_true',
+ help='Dry run the tool event logger if set.',
+ )
+
+ return parser
+
+
+def configure_logging():
+ root_logging_dir = tempfile.mkdtemp(prefix='tool_event_logger_')
+
+ log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
+ date_fmt = '%Y-%m-%d %H:%M:%S'
+ _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')
+
+ logging.basicConfig(
+ filename=log_path, level=logging.DEBUG, format=log_fmt, datefmt=date_fmt
+ )
+
+
+def main(argv: list[str]):
+ args = create_arg_parser().parse_args(argv[1:])
+
+ if args.dry_run:
+ logging.debug('This is a dry run.')
+ return
+
+ try:
+ with ToolEventLogger.create(args.tool_tag) as logger:
+ logger.log_invocation_started(args.start_timestamp, args.tool_args)
+ logger.log_invocation_stopped(
+ args.end_timestamp, args.exit_code, args.exit_log
+ )
+ except Exception as e:
+ logging.error('Log failed with unexpected error: %s', e)
+ raise
+
+
+if __name__ == '__main__':
+ configure_logging()
+ main(sys.argv)
diff --git a/tools/tool_event_logger/tool_event_logger_test.py b/tools/tool_event_logger/tool_event_logger_test.py
new file mode 100644
index 0000000..34b6c35
--- /dev/null
+++ b/tools/tool_event_logger/tool_event_logger_test.py
@@ -0,0 +1,209 @@
+# Copyright 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.
+
+"""Unittests for ToolEventLogger."""
+
+import datetime
+import logging
+import unittest
+from unittest import mock
+
+from atest.metrics import clearcut_client
+from proto import tool_event_pb2
+from tool_event_logger import tool_event_logger
+
+TEST_INVOCATION_ID = 'test_invocation_id'
+TEST_USER_NAME = 'test_user'
+TEST_TOOL_TAG = 'test_tool'
+TEST_SOURCE_ROOT = 'test_source_root'
+TEST_PLATFORM_VERSION = 'test_platform_version'
+TEST_PYTHON_VERSION = 'test_python_version'
+TEST_EVENT_TIMESTAMP = datetime.datetime.now()
+
+
+class ToolEventLoggerTest(unittest.TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.clearcut_client = FakeClearcutClient()
+ self.logger = tool_event_logger.ToolEventLogger(
+ TEST_TOOL_TAG,
+ TEST_INVOCATION_ID,
+ TEST_USER_NAME,
+ TEST_SOURCE_ROOT,
+ TEST_PLATFORM_VERSION,
+ TEST_PYTHON_VERSION,
+ client=self.clearcut_client,
+ )
+
+ def test_log_event_timestamp(self):
+ with self.logger:
+ self.logger.log_invocation_started(
+ datetime.datetime.fromtimestamp(100.101), 'test_command'
+ )
+
+ self.assertEqual(
+ self.clearcut_client.get_last_sent_event().event_time_ms, 100101
+ )
+
+ def test_log_event_basic_information(self):
+ with self.logger:
+ self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+
+ sent_event = self.clearcut_client.get_last_sent_event()
+ log_event = tool_event_pb2.ToolEvent.FromString(sent_event.source_extension)
+ self.assertEqual(log_event.invocation_id, TEST_INVOCATION_ID)
+ self.assertEqual(log_event.user_name, TEST_USER_NAME)
+ self.assertEqual(log_event.tool_tag, TEST_TOOL_TAG)
+ self.assertEqual(log_event.source_root, TEST_SOURCE_ROOT)
+
+ def test_log_invocation_started(self):
+ expected_invocation_started = tool_event_pb2.ToolEvent.InvocationStarted(
+ command_args='test_command',
+ os=TEST_PLATFORM_VERSION + ':' + TEST_PYTHON_VERSION,
+ )
+
+ with self.logger:
+ self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+
+ self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)
+ sent_event = self.clearcut_client.get_last_sent_event()
+ self.assertEqual(
+ expected_invocation_started,
+ tool_event_pb2.ToolEvent.FromString(
+ sent_event.source_extension
+ ).invocation_started,
+ )
+
+ def test_log_invocation_stopped(self):
+ expected_invocation_stopped = tool_event_pb2.ToolEvent.InvocationStopped(
+ exit_code=0,
+ exit_log='exit_log',
+ )
+
+ with self.logger:
+ self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')
+
+ self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)
+ sent_event = self.clearcut_client.get_last_sent_event()
+ self.assertEqual(
+ expected_invocation_stopped,
+ tool_event_pb2.ToolEvent.FromString(
+ sent_event.source_extension
+ ).invocation_stopped,
+ )
+
+ def test_log_multiple_events(self):
+ with self.logger:
+ self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+ self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')
+
+ self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 2)
+
+
+class MainTest(unittest.TestCase):
+
+ REQUIRED_ARGS = [
+ '',
+ '--tool_tag',
+ 'test_tool',
+ '--start_timestamp',
+ '1',
+ '--end_timestamp',
+ '2',
+ '--exit_code',
+ '0',
+ ]
+
+ def test_log_and_exit_with_missing_required_args(self):
+ with self.assertLogs() as logs:
+ with self.assertRaises(SystemExit) as ex:
+ tool_event_logger.main(['', '--tool_tag', 'test_tool'])
+
+ with self.subTest('Verify exception code'):
+ self.assertEqual(ex.exception.code, 2)
+
+ with self.subTest('Verify log messages'):
+ self.assertIn(
+ 'the following arguments are required',
+ '\n'.join(logs.output),
+ )
+
+ def test_log_and_exit_with_invalid_args(self):
+ with self.assertLogs() as logs:
+ with self.assertRaises(SystemExit) as ex:
+ tool_event_logger.main(['', '--start_timestamp', 'test'])
+
+ with self.subTest('Verify exception code'):
+ self.assertEqual(ex.exception.code, 2)
+
+ with self.subTest('Verify log messages'):
+ self.assertIn(
+ '--start_timestamp: invalid',
+ '\n'.join(logs.output),
+ )
+
+ def test_log_and_exit_with_dry_run(self):
+ with self.assertLogs(level=logging.DEBUG) as logs:
+ tool_event_logger.main(self.REQUIRED_ARGS + ['--dry_run'])
+
+ with self.subTest('Verify log messages'):
+ self.assertIn('dry run', '\n'.join(logs.output))
+
+ @mock.patch.object(clearcut_client, 'Clearcut')
+ def test_log_and_exit_with_unexpected_exception(self, mock_cc):
+ mock_cc.return_value = FakeClearcutClient(raise_log_exception=True)
+
+ with self.assertLogs() as logs:
+ with self.assertRaises(Exception) as ex:
+ tool_event_logger.main(self.REQUIRED_ARGS)
+
+ with self.subTest('Verify log messages'):
+ self.assertIn('unexpected error', '\n'.join(logs.output))
+
+ @mock.patch.object(clearcut_client, 'Clearcut')
+ def test_success(self, mock_cc):
+ mock_clear_cut_client = FakeClearcutClient()
+ mock_cc.return_value = mock_clear_cut_client
+
+ tool_event_logger.main(self.REQUIRED_ARGS)
+
+ self.assertEqual(mock_clear_cut_client.get_number_of_sent_events(), 2)
+
+
+class FakeClearcutClient:
+
+ def __init__(self, raise_log_exception=False):
+ self.pending_log_events = []
+ self.sent_log_events = []
+ self.raise_log_exception = raise_log_exception
+
+ def log(self, log_event):
+ if self.raise_log_exception:
+ raise Exception('unknown exception')
+ self.pending_log_events.append(log_event)
+
+ def flush_events(self):
+ self.sent_log_events.extend(self.pending_log_events)
+ self.pending_log_events.clear()
+
+ def get_number_of_sent_events(self):
+ return len(self.sent_log_events)
+
+ def get_last_sent_event(self):
+ return self.sent_log_events[-1]
+
+
+if __name__ == '__main__':
+ unittest.main()