Merge "aconfig: create first implementation of aconfig storage java read api" into main am: ca6602821b

Original change: https://android-review.googlesource.com/c/platform/build/+/3106837

Change-Id: I9c8280841afb2153c7e716ceea4947026744129a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Dennis Shen 2024-05-30 22:36:10 +00:00 committed by Automerger Merge Worker
commit 845de05ab4
13 changed files with 776 additions and 12 deletions

View file

@ -0,0 +1,9 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*.java]
indent_style = tab
indent_size = 4

View file

@ -94,12 +94,16 @@
{
// aconfig_storage read api cpp integration tests
"name": "aconfig_storage_read_api.test.cpp"
}
],
"postsubmit": [
},
{
// aconfig_storage file cpp integration tests
"name": "aconfig_storage_file.test.cpp"
}
],
"postsubmit": [
{
// aconfig_storage read api java integration tests
"name": "aconfig_storage_read_api.test.java"
}
]
}

View file

@ -111,3 +111,28 @@ cc_defaults {
"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",
],
}

View file

@ -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");
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}

View file

@ -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)
}
}
}

View file

@ -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 @@ rust_test {
"librand",
],
data: [
"package.map",
"flag.map",
"flag.val",
"flag.info",
":read_api_test_storage_files",
],
test_suites: ["general-tests"],
}
@ -30,10 +36,7 @@ cc_test {
"liblog",
],
data: [
"package.map",
"flag.map",
"flag.val",
"flag.info",
":read_api_test_storage_files",
],
test_suites: [
"device-tests",

View file

@ -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));
}
}

View file

@ -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",
}

View file

@ -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>

View file

@ -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>