Merge "Override map for enabling mainline module prebuilts" into main
diff --git a/core/Makefile b/core/Makefile
index f38f3a0..a215d31 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -4369,7 +4369,7 @@
INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey
INSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,pvmfw_bin)
INTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,pvmfw_img)
-INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key)
+INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key_pub_bin)
INTERNAL_PVMFW_SYMBOL := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/pvmfw
$(call declare-1p-container,$(INSTALLED_PVMFWIMAGE_TARGET),)
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 127731f..ea6ebd3 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -114,8 +114,10 @@
$(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES))
$(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))
-
-$(call add_soong_config_var_value,ANDROID,release_write_appcompat_override_system_properties,$(RELEASE_WRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES))
+ifneq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))
+ # write appcompat system properties on userdebug and eng builds
+ $(call add_soong_config_var_value,ANDROID,release_write_appcompat_override_system_properties,true)
+endif
# Enable system_server optimizations by default unless explicitly set or if
# there may be dependent runtime jars.
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 9c7e906..6e4d0d8 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -768,6 +768,10 @@
$(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem)))) \
$(eval my_compat_dist_config_$(suite) := ))
+ifneq (,$(LOCAL_SOONG_CLASSES_JAR))
+ $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
+ $(eval my_compat_api_map_$(suite) += $(LOCAL_SOONG_CLASSES_JAR)))
+endif
# Auto-generate build config.
ifeq (,$(test_config))
@@ -821,6 +825,12 @@
$(foreach dir, $(call compatibility_suite_dirs,$(suite)), \
$(s):$(dir)/$(n)))))
+ $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
+ $(eval my_compat_api_map_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \
+ $(eval p := $(subst :,$(space),$(f))) \
+ $(eval s := $(word 1,$(p))) \
+ $(if $(filter %.apk,$(s)) $(filter %.jar,$(s)),$(s),))))
+
ifneq (,$(test_config))
$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
$(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \
@@ -863,7 +873,9 @@
$(call filter-copy-pair,$(src_path),$(call append-path,$(dir),$(file)),$(my_installed_test_data)))) \
$(eval my_compat_dist_test_data_$(suite) += \
$(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \
- $(filter $(my_installed_test_data),$(call append-path,$(dir),$(file)))))))
+ $(filter $(my_installed_test_data),$(call append-path,$(dir),$(file))))) \
+ $(eval my_compat_api_map_$(suite) += \
+ $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),))))
endif
else
ifneq ($(my_test_data_file_pairs),)
@@ -873,7 +885,9 @@
$(eval file := $(word 2,$(parts))) \
$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
$(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \
- $(src_path):$(call append-path,$(dir),$(file))))))
+ $(src_path):$(call append-path,$(dir),$(file)))) \
+ $(eval my_compat_api_map_$(suite) += \
+ $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),))))
endif
endif
@@ -885,7 +899,8 @@
$(call create-suite-dependencies)
$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \
$(eval my_compat_dist_config_$(suite) := ) \
- $(eval my_compat_dist_test_data_$(suite) := ))
+ $(eval my_compat_dist_test_data_$(suite) := ) \
+ $(eval my_compat_api_map_$(suite) := ))
endif # LOCAL_UNINSTALLABLE_MODULE
diff --git a/core/definitions.mk b/core/definitions.mk
index 40b7980..eabcc68 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -3589,11 +3589,13 @@
$(if $(filter $(suite),$(ALL_COMPATIBILITY_SUITES)),,\
$(eval ALL_COMPATIBILITY_SUITES += $(suite)) \
$(eval COMPATIBILITY.$(suite).FILES :=) \
- $(eval COMPATIBILITY.$(suite).MODULES :=)) \
+ $(eval COMPATIBILITY.$(suite).MODULES :=) \
+ $(eval COMPATIBILITY.$(suite).API_MAP_FILES :=)) \
$(eval COMPATIBILITY.$(suite).FILES += \
$$(foreach f,$$(my_compat_dist_$(suite)),$$(call word-colon,2,$$(f))) \
$$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call word-colon,2,$$(f))) \
$$(my_compat_dist_test_data_$(suite))) \
+ $(eval COMPATIBILITY.$(suite).API_MAP_FILES += $$(my_compat_api_map_$(suite))) \
$(eval ALL_COMPATIBILITY_DIST_FILES += $$(my_compat_dist_$(suite))) \
$(eval COMPATIBILITY.$(suite).MODULES += $$(my_register_name))) \
$(eval $(my_all_targets) : \
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 151591e..08e2da3 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -202,6 +202,12 @@
endif
ifneq (,$(LOCAL_COMPATIBILITY_SUITE))
LOCAL_ENFORCE_USES_LIBRARIES := false
+
+ # Enable the check for WTS
+ ifneq ($(filter wts,$(LOCAL_COMPATIBILITY_SUITE)),)
+ LOCAL_ENFORCE_USES_LIBRARIES := true
+ endif
+
endif
# Disable the check if the app contains no java code.
diff --git a/core/release_config.mk b/core/release_config.mk
index 887c78b..4fdd279 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -49,9 +49,6 @@
# If this is a google source tree, restrict it to only the one file
# which has OWNERS control. If it isn't let others define their own.
-# TODO: Remove wildcard for build/release one when all branch manifests
-# have updated.
-_must_protobuf :=
config_map_files := $(wildcard build/release/release_config_map.mk) \
$(wildcard vendor/google_shared/build/release/release_config_map.mk) \
$(if $(wildcard vendor/google/release/release_config_map.mk), \
@@ -64,7 +61,7 @@
) \
)
-protobuf_map_files := $(wildcard build/release/release_config_map.textproto) \
+protobuf_map_files := build/release/release_config_map.textproto \
$(wildcard vendor/google_shared/build/release/release_config_map.textproto) \
$(if $(wildcard vendor/google/release/release_config_map.textproto), \
vendor/google/release/release_config_map.textproto, \
@@ -76,6 +73,9 @@
) \
)
+# Remove support for the legacy approach.
+_must_protobuf := true
+
# PRODUCT_RELEASE_CONFIG_MAPS is set by Soong using an initial run of product
# config to capture only the list of config maps needed by the build.
# Keep them in the order provided, but remove duplicates.
@@ -139,7 +139,7 @@
ifneq (,$(_final_product_config_pass))
# Save the final version of the config.
$(shell if ! cmp --quiet $(_flags_varmk) $(_flags_file); then cp $(_flags_varmk) $(_flags_file); fi)
- # This will also set _all_release_configs and _used_files for us.
+ # This will also set ALL_RELEASE_CONFIGS_FOR_PRODUCT and _used_files for us.
$(eval include $(_flags_file))
$(KATI_extra_file_deps $(OUT_DIR)/release-config $(protobuf_map_files) $(_flags_file))
else
@@ -155,8 +155,11 @@
$(_flags_dir)/$(_base_all_release).pb:build_flags/all_release_configs.pb \
$(_flags_dir)/$(_base_all_release).textproto:build_flags/all_release_configs.textproto \
$(_flags_dir)/$(_base_all_release).json:build_flags/all_release_configs.json \
+ $(_flags_dir)/inheritance_graph-$(TARGET_PRODUCT).dot:build_flags/inheritance_graph-$(TARGET_PRODUCT).dot \
)
# These are always created, add an empty rule for them to keep ninja happy.
+$(_flags_dir)/inheritance_graph-$(TARGET_PRODUCT).dot:
+ : created by $(OUT_DIR)/release-config
$(_flags_dir)/$(_base_all_release).pb $(_flags_dir)/$(_base_all_release).textproto $(_flags_dir)/$(_base_all_release).json:
: created by $(OUT_DIR)/release-config
_base_all_release :=
@@ -214,9 +217,9 @@
$(error declare-release-config: config $(strip $(1)) must have release config files, override another release config, or both) \
)
$(if $(strip $(4)),$(eval _all_release_configs.$(strip $(1)).ALIAS := true))
- $(eval _all_release_configs := $(sort $(_all_release_configs) $(strip $(1))))
+ $(eval ALL_RELEASE_CONFIGS_FOR_PRODUCT := $(sort $(ALL_RELEASE_CONFIGS_FOR_PRODUCT) $(strip $(1))))
$(if $(strip $(3)), \
- $(if $(filter $(_all_release_configs), $(strip $(3))),
+ $(if $(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(strip $(3))),
$(if $(filter $(_all_release_configs.$(strip $(1)).OVERRIDES),$(strip $(3))),,
$(eval _all_release_configs.$(strip $(1)).OVERRIDES := $(_all_release_configs.$(strip $(1)).OVERRIDES) $(strip $(3)))), \
$(error No release config $(strip $(3))) \
@@ -242,13 +245,13 @@
FLAG_DECLARATION_FILES :=
# Verify that all inherited/overridden release configs are declared.
-$(foreach config,$(_all_release_configs),\
+$(foreach config,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\
$(foreach r,$(all_release_configs.$(r).OVERRIDES),\
$(if $(strip $(_all_release_configs.$(r).FILES)$(_all_release_configs.$(r).OVERRIDES)),,\
$(error Release config $(config) [declared in: $(_all_release_configs.$(r).DECLARED_IN)] inherits from non-existent $(r).)\
)))
# Verify that alias configs do not have config files.
-$(foreach r,$(_all_release_configs),\
+$(foreach r,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\
$(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\
$(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\
)))
@@ -263,7 +266,7 @@
# if the variable was completely unset.
TARGET_RELEASE ?= was_unset
ifeq ($(TARGET_RELEASE),was_unset)
- $(error No release config set for target; please set TARGET_RELEASE, or if building on the command line use 'lunch <target>-<release>-<build_type>', where release is one of: $(_all_release_configs))
+ $(error No release config set for target; please set TARGET_RELEASE, or if building on the command line use 'lunch <target>-<release>-<build_type>', where release is one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))
endif
# Instead of leaving this string empty, we want to default to a valid
# setting. Full builds coming through this path is a bug, but in case
@@ -274,8 +277,8 @@
# During pass 1 of product config, using a non-existent release config is not an error.
# We can safely assume that we are doing pass 1 if DUMP_MANY_VARS=="PRODUCT_RELEASE_CONFIG_MAPS".
ifneq (,$(_final_product_config_pass))
- ifeq ($(filter $(_all_release_configs), $(TARGET_RELEASE)),)
- $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(_all_release_configs))
+ ifeq ($(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(TARGET_RELEASE)),)
+ $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))
endif
endif
@@ -324,14 +327,13 @@
.KATI_READONLY := TARGET_RELEASE
ifeq (,$(_use_protobuf))
-$(foreach config, $(_all_release_configs), \
+$(foreach config, $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), \
$(eval _all_release_configs.$(config).DECLARED_IN:= ) \
$(eval _all_release_configs.$(config).FILES:= ) \
)
applied_releases:=
# use makefiles
endif
-_all_release_configs:=
config_map_files:=
protobuf_map_files:=
@@ -378,3 +380,4 @@
_can_protobuf :=
_must_protobuf :=
_use_protobuf :=
+
diff --git a/tools/aconfig/.editorconfig b/tools/aconfig/.editorconfig
new file mode 100644
index 0000000..cc5985f
--- /dev/null
+++ b/tools/aconfig/.editorconfig
@@ -0,0 +1,9 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*.java]
+indent_style = tab
+indent_size = 4
+
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 421e94a..448d8cf 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -94,12 +94,16 @@
{
// aconfig_storage read api cpp integration tests
"name": "aconfig_storage_read_api.test.cpp"
+ },
+ {
+ // aconfig_storage file cpp integration tests
+ "name": "aconfig_storage_file.test.cpp"
}
],
"postsubmit": [
{
- // aconfig_storage file cpp integration tests
- "name": "aconfig_storage_file.test.cpp"
+ // aconfig_storage read api java integration tests
+ "name": "aconfig_storage_read_api.test.java"
}
]
}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 9b84254..3b124b1 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -111,3 +111,28 @@
"liblog",
],
}
+
+rust_ffi_shared {
+ name: "libaconfig_storage_read_api_rust_jni",
+ crate_name: "aconfig_storage_read_api_rust_jni",
+ srcs: ["srcs/lib.rs"],
+ rustlibs: [
+ "libaconfig_storage_read_api",
+ "libanyhow",
+ "libjni",
+ ],
+ prefer_rlib: true,
+}
+
+java_library {
+ name: "libaconfig_storage_read_api_java",
+ srcs: [
+ "srcs/**/*.java",
+ ],
+ required: ["libaconfig_storage_read_api_rust_jni"],
+ min_sdk_version: "UpsideDownCake",
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigStorageReadAPI.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigStorageReadAPI.java
new file mode 100644
index 0000000..7746b58
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigStorageReadAPI.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileChannel.MapMode;
+
+import android.aconfig.storage.PackageReadContext;
+import android.aconfig.storage.FlagReadContext;
+import android.aconfig.storage.BooleanFlagValue;
+
+import dalvik.annotation.optimization.FastNative;
+
+public class AconfigStorageReadAPI {
+
+ // Storage file dir on device
+ private static final String STORAGEDIR = "/metadata/aconfig";
+
+ // Stoarge file type
+ public enum StorageFileType {
+ PACKAGE_MAP,
+ FLAG_MAP,
+ FLAG_VAL,
+ FLAG_INFO
+ }
+
+ // Map a storage file given file path
+ public static MappedByteBuffer mapStorageFile(String file) throws IOException {
+ FileInputStream stream = new FileInputStream(file);
+ FileChannel channel = stream.getChannel();
+ return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+ }
+
+ // Map a storage file given container and file type
+ public static MappedByteBuffer getMappedFile(
+ String container,
+ StorageFileType type) throws IOException{
+ switch (type) {
+ case PACKAGE_MAP:
+ return mapStorageFile(STORAGEDIR + "/maps/" + container + ".package.map");
+ case FLAG_MAP:
+ return mapStorageFile(STORAGEDIR + "/maps/" + container + ".flag.map");
+ case FLAG_VAL:
+ return mapStorageFile(STORAGEDIR + "/boot/" + container + ".val");
+ case FLAG_INFO:
+ return mapStorageFile(STORAGEDIR + "/boot/" + container + ".info");
+ default:
+ throw new IOException("Invalid storage file type");
+ }
+ }
+
+ // JNI interface to get package read context
+ @FastNative
+ public static native PackageReadContext getPackageReadContext(
+ ByteBuffer mappedFile, String packageName);
+
+ // JNI interface to get flag read context
+ @FastNative
+ public static native FlagReadContext getFlagReadContext(
+ ByteBuffer mappedFile, int packageId, String flagName);
+
+ // JNI interface to get boolean flag value
+ @FastNative
+ public static native BooleanFlagValue getBooleanFlagValue(
+ ByteBuffer mappedFile, int flagIndex);
+
+ static {
+ System.loadLibrary("aconfig_storage_read_api_rust_jni");
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/BooleanFlagValue.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/BooleanFlagValue.java
new file mode 100644
index 0000000..11fe447
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/BooleanFlagValue.java
@@ -0,0 +1,30 @@
+package android.aconfig.storage;
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class BooleanFlagValue {
+ public boolean mQuerySuccess;
+ public String mErrorMessage;
+ public boolean mFlagValue;
+
+ public BooleanFlagValue(boolean querySuccess,
+ String errorMessage,
+ boolean value) {
+ mQuerySuccess = querySuccess;
+ mErrorMessage = errorMessage;
+ mFlagValue = value;
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/FlagReadContext.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/FlagReadContext.java
new file mode 100644
index 0000000..57a36ca
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/FlagReadContext.java
@@ -0,0 +1,56 @@
+package android.aconfig.storage;
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class FlagReadContext {
+ public boolean mQuerySuccess;
+ public String mErrorMessage;
+ public boolean mFlagExists;
+ public StoredFlagType mFlagType;
+ public int mFlagIndex;
+
+ public FlagReadContext(boolean querySuccess,
+ String errorMessage,
+ boolean flagExists,
+ int flagType,
+ int flagIndex) {
+ mQuerySuccess = querySuccess;
+ mErrorMessage = errorMessage;
+ mFlagExists = flagExists;
+ mFlagType = StoredFlagType.fromInteger(flagType);
+ mFlagIndex = flagIndex;
+ }
+
+ // Flag type enum, consistent with the definition in aconfig_storage_file/src/lib.rs
+ public enum StoredFlagType {
+ ReadWriteBoolean,
+ ReadOnlyBoolean,
+ FixedReadOnlyBoolean;
+
+ public static StoredFlagType fromInteger(int x) {
+ switch(x) {
+ case 0:
+ return ReadWriteBoolean;
+ case 1:
+ return ReadOnlyBoolean;
+ case 2:
+ return FixedReadOnlyBoolean;
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/PackageReadContext.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/PackageReadContext.java
new file mode 100644
index 0000000..60d6b66
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/PackageReadContext.java
@@ -0,0 +1,36 @@
+package android.aconfig.storage;
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class PackageReadContext {
+ public boolean mQuerySuccess;
+ public String mErrorMessage;
+ public boolean mPackageExists;
+ public int mPackageId;
+ public int mBooleanStartIndex;
+
+ public PackageReadContext(boolean querySuccess,
+ String errorMessage,
+ boolean packageExists,
+ int packageId,
+ int booleanStartIndex) {
+ mQuerySuccess = querySuccess;
+ mErrorMessage = errorMessage;
+ mPackageExists = packageExists;
+ mPackageId = packageId;
+ mBooleanStartIndex = booleanStartIndex;
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/lib.rs b/tools/aconfig/aconfig_storage_read_api/srcs/lib.rs
new file mode 100644
index 0000000..e195eb8
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/lib.rs
@@ -0,0 +1,203 @@
+//! aconfig storage read api java rust interlop
+
+use aconfig_storage_read_api::flag_table_query::find_flag_read_context;
+use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
+use aconfig_storage_read_api::package_table_query::find_package_read_context;
+use aconfig_storage_read_api::{FlagReadContext, PackageReadContext};
+
+use anyhow::Result;
+use jni::objects::{JByteBuffer, JClass, JString, JValue};
+use jni::sys::{jint, jobject};
+use jni::JNIEnv;
+
+/// Call rust find package read context
+fn get_package_read_context_java(
+ env: &mut JNIEnv,
+ file: JByteBuffer,
+ package: JString,
+) -> Result<Option<PackageReadContext>> {
+ // SAFETY:
+ // The safety here is ensured as the package name is guaranteed to be a java string
+ let package_name: String = unsafe { env.get_string_unchecked(&package)?.into() };
+ let buffer_ptr = env.get_direct_buffer_address(&file)?;
+ let buffer_size = env.get_direct_buffer_capacity(&file)?;
+ // SAFETY:
+ // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,
+ // so the conversion to slice is guaranteed to be valid
+ let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };
+ Ok(find_package_read_context(buffer, &package_name)?)
+}
+
+/// Create java package read context return
+fn create_java_package_read_context(
+ env: &mut JNIEnv,
+ success_query: bool,
+ error_message: String,
+ pkg_found: bool,
+ pkg_id: u32,
+ start_index: u32,
+) -> jobject {
+ let query_success = JValue::Bool(success_query as u8);
+ let errmsg = env.new_string(error_message).expect("failed to create JString");
+ let package_exists = JValue::Bool(pkg_found as u8);
+ let package_id = JValue::Int(pkg_id as i32);
+ let boolean_start_index = JValue::Int(start_index as i32);
+ let context = env.new_object(
+ "android/aconfig/storage/PackageReadContext",
+ "(ZLjava/lang/String;ZII)V",
+ &[query_success, (&errmsg).into(), package_exists, package_id, boolean_start_index],
+ );
+ context.expect("failed to call PackageReadContext constructor").into_raw()
+}
+
+/// Get package read context JNI
+#[no_mangle]
+#[allow(unused)]
+pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getPackageReadContext<
+ 'local,
+>(
+ mut env: JNIEnv<'local>,
+ class: JClass<'local>,
+ file: JByteBuffer<'local>,
+ package: JString<'local>,
+) -> jobject {
+ match get_package_read_context_java(&mut env, file, package) {
+ Ok(context_opt) => match context_opt {
+ Some(context) => create_java_package_read_context(
+ &mut env,
+ true,
+ String::from(""),
+ true,
+ context.package_id,
+ context.boolean_start_index,
+ ),
+ None => create_java_package_read_context(&mut env, true, String::from(""), false, 0, 0),
+ },
+ Err(errmsg) => {
+ create_java_package_read_context(&mut env, false, format!("{:?}", errmsg), false, 0, 0)
+ }
+ }
+}
+
+/// Call rust find flag read context
+fn get_flag_read_context_java(
+ env: &mut JNIEnv,
+ file: JByteBuffer,
+ package_id: jint,
+ flag: JString,
+) -> Result<Option<FlagReadContext>> {
+ // SAFETY:
+ // The safety here is ensured as the flag name is guaranteed to be a java string
+ let flag_name: String = unsafe { env.get_string_unchecked(&flag)?.into() };
+ let buffer_ptr = env.get_direct_buffer_address(&file)?;
+ let buffer_size = env.get_direct_buffer_capacity(&file)?;
+ // SAFETY:
+ // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,
+ // so the conversion to slice is guaranteed to be valid
+ let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };
+ Ok(find_flag_read_context(buffer, package_id as u32, &flag_name)?)
+}
+
+/// Create java flag read context return
+fn create_java_flag_read_context(
+ env: &mut JNIEnv,
+ success_query: bool,
+ error_message: String,
+ flg_found: bool,
+ flg_type: u32,
+ flg_index: u32,
+) -> jobject {
+ let query_success = JValue::Bool(success_query as u8);
+ let errmsg = env.new_string(error_message).expect("failed to create JString");
+ let flag_exists = JValue::Bool(flg_found as u8);
+ let flag_type = JValue::Int(flg_type as i32);
+ let flag_index = JValue::Int(flg_index as i32);
+ let context = env.new_object(
+ "android/aconfig/storage/FlagReadContext",
+ "(ZLjava/lang/String;ZII)V",
+ &[query_success, (&errmsg).into(), flag_exists, flag_type, flag_index],
+ );
+ context.expect("failed to call FlagReadContext constructor").into_raw()
+}
+
+/// Get flag read context JNI
+#[no_mangle]
+#[allow(unused)]
+pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getFlagReadContext<
+ 'local,
+>(
+ mut env: JNIEnv<'local>,
+ class: JClass<'local>,
+ file: JByteBuffer<'local>,
+ package_id: jint,
+ flag: JString<'local>,
+) -> jobject {
+ match get_flag_read_context_java(&mut env, file, package_id, flag) {
+ Ok(context_opt) => match context_opt {
+ Some(context) => create_java_flag_read_context(
+ &mut env,
+ true,
+ String::from(""),
+ true,
+ context.flag_type as u32,
+ context.flag_index as u32,
+ ),
+ None => create_java_flag_read_context(&mut env, true, String::from(""), false, 9999, 0),
+ },
+ Err(errmsg) => {
+ create_java_flag_read_context(&mut env, false, format!("{:?}", errmsg), false, 9999, 0)
+ }
+ }
+}
+
+/// Create java boolean flag value return
+fn create_java_boolean_flag_value(
+ env: &mut JNIEnv,
+ success_query: bool,
+ error_message: String,
+ value: bool,
+) -> jobject {
+ let query_success = JValue::Bool(success_query as u8);
+ let errmsg = env.new_string(error_message).expect("failed to create JString");
+ let flag_value = JValue::Bool(value as u8);
+ let context = env.new_object(
+ "android/aconfig/storage/BooleanFlagValue",
+ "(ZLjava/lang/String;Z)V",
+ &[query_success, (&errmsg).into(), flag_value],
+ );
+ context.expect("failed to call BooleanFlagValue constructor").into_raw()
+}
+
+/// Call rust find boolean flag value
+fn get_boolean_flag_value_java(
+ env: &mut JNIEnv,
+ file: JByteBuffer,
+ flag_index: jint,
+) -> Result<bool> {
+ let buffer_ptr = env.get_direct_buffer_address(&file)?;
+ let buffer_size = env.get_direct_buffer_capacity(&file)?;
+ // SAFETY:
+ // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,
+ // so the conversion to slice is guaranteed to be valid
+ let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };
+ Ok(find_boolean_flag_value(buffer, flag_index as u32)?)
+}
+
+/// Get flag value JNI
+#[no_mangle]
+#[allow(unused)]
+pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getBooleanFlagValue<
+ 'local,
+>(
+ mut env: JNIEnv<'local>,
+ class: JClass<'local>,
+ file: JByteBuffer<'local>,
+ flag_index: jint,
+) -> jobject {
+ match get_boolean_flag_value_java(&mut env, file, flag_index) {
+ Ok(value) => create_java_boolean_flag_value(&mut env, true, String::from(""), value),
+ Err(errmsg) => {
+ create_java_boolean_flag_value(&mut env, false, format!("{:?}", errmsg), false)
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index 98944d6..ed0c728 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -1,7 +1,16 @@
+filegroup {
+ name: "read_api_test_storage_files",
+ srcs: ["package.map",
+ "flag.map",
+ "flag.val",
+ "flag.info"
+ ],
+}
+
rust_test {
name: "aconfig_storage_read_api.test.rust",
srcs: [
- "storage_read_api_test.rs"
+ "storage_read_api_test.rs",
],
rustlibs: [
"libanyhow",
@@ -10,10 +19,7 @@
"librand",
],
data: [
- "package.map",
- "flag.map",
- "flag.val",
- "flag.info",
+ ":read_api_test_storage_files",
],
test_suites: ["general-tests"],
}
@@ -30,10 +36,7 @@
"liblog",
],
data: [
- "package.map",
- "flag.map",
- "flag.val",
- "flag.info",
+ ":read_api_test_storage_files",
],
test_suites: [
"device-tests",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigStorageReadAPITest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigStorageReadAPITest.java
new file mode 100644
index 0000000..cf4cfe6
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigStorageReadAPITest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import java.io.IOException;
+import java.nio.MappedByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import android.aconfig.storage.AconfigStorageReadAPI;
+import android.aconfig.storage.PackageReadContext;
+import android.aconfig.storage.FlagReadContext;
+import android.aconfig.storage.FlagReadContext.StoredFlagType;
+import android.aconfig.storage.BooleanFlagValue;
+
+@RunWith(JUnit4.class)
+public class AconfigStorageReadAPITest{
+
+ private String mStorageDir = "/data/local/tmp/aconfig_java_api_test";
+
+ @Test
+ public void testPackageContextQuery() {
+ MappedByteBuffer packageMap = null;
+ try {
+ packageMap = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/maps/mockup.package.map");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(packageMap != null);
+
+ PackageReadContext context = AconfigStorageReadAPI.getPackageReadContext(
+ packageMap, "com.android.aconfig.storage.test_1");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertTrue(context.mPackageExists);
+ assertEquals(context.mPackageId, 0);
+ assertEquals(context.mBooleanStartIndex, 0);
+
+ context = AconfigStorageReadAPI.getPackageReadContext(
+ packageMap, "com.android.aconfig.storage.test_2");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertTrue(context.mPackageExists);
+ assertEquals(context.mPackageId, 1);
+ assertEquals(context.mBooleanStartIndex, 3);
+
+ context = AconfigStorageReadAPI.getPackageReadContext(
+ packageMap, "com.android.aconfig.storage.test_4");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertTrue(context.mPackageExists);
+ assertEquals(context.mPackageId, 2);
+ assertEquals(context.mBooleanStartIndex, 6);
+ }
+
+ @Test
+ public void testNonExistPackageContextQuery() {
+ MappedByteBuffer packageMap = null;
+ try {
+ packageMap = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/maps/mockup.package.map");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(packageMap != null);
+
+ PackageReadContext context = AconfigStorageReadAPI.getPackageReadContext(
+ packageMap, "unknown");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertFalse(context.mPackageExists);
+ assertEquals(context.mPackageId, 0);
+ assertEquals(context.mBooleanStartIndex, 0);
+ }
+
+ @Test
+ public void testFlagContextQuery() {
+ MappedByteBuffer flagMap = null;
+ try {
+ flagMap = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/maps/mockup.flag.map");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(flagMap!= null);
+
+ class Baseline {
+ public int mPackageId;
+ public String mFlagName;
+ public StoredFlagType mFlagType;
+ public int mFlagIndex;
+
+ public Baseline(int packageId,
+ String flagName,
+ StoredFlagType flagType,
+ int flagIndex) {
+ mPackageId = packageId;
+ mFlagName = flagName;
+ mFlagType = flagType;
+ mFlagIndex = flagIndex;
+ }
+ }
+
+ List<Baseline> baselines = new ArrayList();
+ baselines.add(new Baseline(0, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 1));
+ baselines.add(new Baseline(0, "enabled_rw", StoredFlagType.ReadWriteBoolean, 2));
+ baselines.add(new Baseline(2, "enabled_rw", StoredFlagType.ReadWriteBoolean, 1));
+ baselines.add(new Baseline(1, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0));
+ baselines.add(new Baseline(1, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 1));
+ baselines.add(new Baseline(1, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 2));
+ baselines.add(new Baseline(2, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 0));
+ baselines.add(new Baseline(0, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0));
+
+ for (Baseline baseline : baselines) {
+ FlagReadContext context = AconfigStorageReadAPI.getFlagReadContext(
+ flagMap, baseline.mPackageId, baseline.mFlagName);
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertTrue(context.mFlagExists);
+ assertEquals(context.mFlagType, baseline.mFlagType);
+ assertEquals(context.mFlagIndex, baseline.mFlagIndex);
+ }
+ }
+
+ @Test
+ public void testNonExistFlagContextQuery() {
+ MappedByteBuffer flagMap = null;
+ try {
+ flagMap = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/maps/mockup.flag.map");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(flagMap!= null);
+
+ FlagReadContext context = AconfigStorageReadAPI.getFlagReadContext(
+ flagMap, 0, "unknown");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertFalse(context.mFlagExists);
+ assertEquals(context.mFlagType, null);
+ assertEquals(context.mFlagIndex, 0);
+
+ context = AconfigStorageReadAPI.getFlagReadContext(
+ flagMap, 3, "enabled_ro");
+ assertTrue(context.mQuerySuccess);
+ assertTrue(context.mErrorMessage, context.mErrorMessage.equals(""));
+ assertFalse(context.mFlagExists);
+ assertEquals(context.mFlagType, null);
+ assertEquals(context.mFlagIndex, 0);
+ }
+
+ @Test
+ public void testBooleanFlagValueQuery() {
+ MappedByteBuffer flagVal = null;
+ try {
+ flagVal = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/boot/mockup.val");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(flagVal!= null);
+
+ boolean[] baselines = {false, true, true, false, true, true, true, true};
+ for (int i = 0; i < 8; ++i) {
+ BooleanFlagValue value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, i);
+ assertTrue(value.mQuerySuccess);
+ assertTrue(value.mErrorMessage, value.mErrorMessage.equals(""));
+ assertEquals(value.mFlagValue, baselines[i]);
+ }
+ }
+
+ @Test
+ public void testInvalidBooleanFlagValueQuery() {
+ MappedByteBuffer flagVal = null;
+ try {
+ flagVal = AconfigStorageReadAPI.mapStorageFile(
+ mStorageDir + "/boot/mockup.val");
+ } catch(IOException ex){
+ assertTrue(ex.toString(), false);
+ }
+ assertTrue(flagVal!= null);
+
+ BooleanFlagValue value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, 9);
+ String expectedErrmsg = "Flag value offset goes beyond the end of the file";
+ assertFalse(value.mQuerySuccess);
+ assertTrue(value.mErrorMessage, value.mErrorMessage.contains(expectedErrmsg));
+ }
+ }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
new file mode 100644
index 0000000..d94b2b4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
@@ -0,0 +1,21 @@
+android_test {
+ name: "aconfig_storage_read_api.test.java",
+ srcs: ["AconfigStorageReadAPITest.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "libaconfig_storage_read_api_java",
+ "junit",
+ ],
+ jni_libs: [
+ "libaconfig_storage_read_api_rust_jni",
+ ],
+ data: [
+ ":read_api_test_storage_files",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: [
+ "general-tests",
+ ],
+ team: "trendy_team_android_core_experiments",
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidManifest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidManifest.xml
new file mode 100644
index 0000000..78bfb37
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.aconfig_storage.test">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.aconfig_storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml
new file mode 100644
index 0000000..99c9e25
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Config for aconfig storage read java api tests">
+ <!-- Need root to start virtualizationservice -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <!-- Prepare test directories. -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="throw-if-cmd-fail" value="true" />
+ <option name="run-command" value="mkdir -p /data/local/tmp/aconfig_java_api" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/aconfig_java_api" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="aconfig_storage_read_api.test.java.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <!-- Test data files -->
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="abort-on-push-failure" value="true" />
+ <option name="push-file" key="package.map"
+ value="/data/local/tmp/aconfig_java_api_test/maps/mockup.package.map" />
+ <option name="push-file" key="flag.map"
+ value="/data/local/tmp/aconfig_java_api_test/maps/mockup.flag.map" />
+ <option name="push-file" key="flag.val"
+ value="/data/local/tmp/aconfig_java_api_test/boot/mockup.val" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="package" value="android.aconfig_storage.test" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>