Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5dd91bc authored by Dennis Shen's avatar Dennis Shen
Browse files

aconfig: create first implementation of aconfig storage java read api

1, See AconfigStorageReadAPI.java to see java APIs to map storage files
and read flag values. It is using fast native annotation, in theory it
should be faster than regular JNI without much of the overhead.

2, The java api calls into Rust wrapper in srcs/lib.rs, note that
MappedByteBuffer is not copied during JNI. In the rust side
implementation we get the underlying raw pointer and buffer size and
reconstruct a rust slice. However, at current implmentation, the string
input such as package name and flag name are most likely copied. They
are converted from JStirng to JavaStr first without copy, then the
into() call to convert it to Rust string. We could potentially optimize
it to without copy.

3, Add an android_test target to lock down the API behaviors.

Bug: b/321077378
Test: atest -c
Change-Id: I8915fe70e8eb341be563c70f85e19e644e8aa6be
parent 8a402d7e
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*.java]
indent_style = tab
indent_size = 4
+6 −2
Original line number Diff line number Diff line
@@ -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"
    }
  ]
}
+25 −0
Original line number Diff line number Diff line
@@ -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",
    ],
}
+88 −0
Original line number Diff line number Diff line
/*
 * 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");
    }
}
+30 −0
Original line number Diff line number Diff line
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;
    }
}
Loading