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:
commit
845de05ab4
13 changed files with 776 additions and 12 deletions
9
tools/aconfig/.editorconfig
Normal file
9
tools/aconfig/.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
203
tools/aconfig/aconfig_storage_read_api/srcs/lib.rs
Normal file
203
tools/aconfig/aconfig_storage_read_api/srcs/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
21
tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
Normal file
21
tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
Normal 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",
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in a new issue