summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/service/incremental/IncrementalDataLoaderService.java563
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_service_incremental_IncrementalDataLoaderService.cpp269
4 files changed, 837 insertions, 0 deletions
diff --git a/core/java/android/service/incremental/IncrementalDataLoaderService.java b/core/java/android/service/incremental/IncrementalDataLoaderService.java
new file mode 100644
index 000000000000..c4a06c8f53db
--- /dev/null
+++ b/core/java/android/service/incremental/IncrementalDataLoaderService.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2019 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.service.incremental;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.InstallationFile;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.incremental.IncrementalDataLoaderParams;
+import android.os.incremental.IncrementalDataLoaderParamsParcel;
+import android.os.incremental.IncrementalFileSystemControlParcel;
+import android.os.incremental.NamedParcelFileDescriptor;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * The base class for implementing data loader service to control data loaders. Expecting
+ * Incremental Service to bind to a children class of this.
+ *
+ * @hide
+ *
+ * Hide for now, should be @SystemApi
+ * TODO(b/136132412): update with latest API design
+ */
+public abstract class IncrementalDataLoaderService extends Service {
+ private static final String TAG = "IncrementalDataLoaderService";
+ private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
+
+ public static final int DATA_LOADER_READY =
+ IDataLoaderStatusListener.DATA_LOADER_READY;
+ public static final int DATA_LOADER_NOT_READY =
+ IDataLoaderStatusListener.DATA_LOADER_NOT_READY;
+ public static final int DATA_LOADER_RUNNING =
+ IDataLoaderStatusListener.DATA_LOADER_RUNNING;
+ public static final int DATA_LOADER_STOPPED =
+ IDataLoaderStatusListener.DATA_LOADER_STOPPED;
+ public static final int DATA_LOADER_SLOW_CONNECTION =
+ IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION;
+ public static final int DATA_LOADER_NO_CONNECTION =
+ IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION;
+ public static final int DATA_LOADER_CONNECTION_OK =
+ IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"DATA_LOADER_"}, value = {
+ DATA_LOADER_READY,
+ DATA_LOADER_NOT_READY,
+ DATA_LOADER_RUNNING,
+ DATA_LOADER_STOPPED,
+ DATA_LOADER_SLOW_CONNECTION,
+ DATA_LOADER_NO_CONNECTION,
+ DATA_LOADER_CONNECTION_OK
+ })
+ public @interface DataLoaderStatus {
+ }
+
+ /**
+ * Incremental FileSystem block size.
+ **/
+ public static final int BLOCK_SIZE = 4096;
+
+ /**
+ * Data compression types
+ */
+ public static final int COMPRESSION_NONE = 0;
+ public static final int COMPRESSION_LZ4 = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({COMPRESSION_NONE, COMPRESSION_LZ4})
+ public @interface CompressionType {
+ }
+
+ /**
+ * Managed DataLoader interface. Each instance corresponds to a single Incremental File System
+ * instance.
+ */
+ public abstract static class DataLoader {
+ /**
+ * A virtual constructor used to do simple initialization. Not ready to serve any data yet.
+ * All heavy-lifting has to be done in onStart.
+ *
+ * @param params Data loader configuration parameters.
+ * @param connector IncFS API wrapper.
+ * @param listener Used for reporting internal state to IncrementalService.
+ * @return True if initialization of a Data Loader was successful. False will be reported to
+ * IncrementalService and can cause an unmount of an IFS instance.
+ */
+ public abstract boolean onCreate(@NonNull IncrementalDataLoaderParams params,
+ @NonNull FileSystemConnector connector,
+ @NonNull StatusListener listener);
+
+ /**
+ * Start the data loader. After this method returns data loader is considered to be ready to
+ * receive callbacks from IFS, supply data via connector and send status updates via
+ * callbacks.
+ *
+ * @return True if Data Loader was able to start. False will be reported to
+ * IncrementalService and can cause an unmount of an IFS instance.
+ */
+ public abstract boolean onStart();
+
+ /**
+ * Stop the data loader. Use to stop any additional threads and free up resources. Data
+ * loader is not longer responsible for supplying data. Start/Stop pair can be called
+ * multiple times e.g. if IFS detects corruption and data needs to be re-loaded.
+ */
+ public abstract void onStop();
+
+ /**
+ * Virtual destructor. Use to cleanup all internal state. After this method returns, the
+ * data loader can no longer use connector or callbacks. For any additional operations with
+ * this instance of IFS a new DataLoader will be created using createDataLoader method.
+ */
+ public abstract void onDestroy();
+
+ /**
+ * IFS reports a pending read each time the page needs to be loaded, e.g. missing.
+ *
+ * @param pendingReads array of blocks to load.
+ *
+ * TODO(b/136132412): avoid using collections
+ */
+ public abstract void onPendingReads(
+ @NonNull Collection<FileSystemConnector.PendingReadInfo> pendingReads);
+
+ /**
+ * IFS tracks all reads and reports them using onPageReads.
+ *
+ * @param reads array of blocks.
+ *
+ * TODO(b/136132412): avoid using collections
+ */
+ public abstract void onPageReads(@NonNull Collection<FileSystemConnector.ReadInfo> reads);
+
+ /**
+ * IFS informs data loader that a new file has been created.
+ * <p>
+ * This can be used to prepare the data loader before it starts loading data. For example,
+ * the data loader can keep a list of newly created files, so that it knows what files to
+ * download from the server.
+ *
+ * @param inode The inode value of the new file.
+ * @param metadata The metadata of the new file.
+ */
+ public abstract void onFileCreated(long inode, byte[] metadata);
+ }
+
+ /**
+ * DataLoader factory method.
+ *
+ * @return An instance of a DataLoader.
+ */
+ public abstract @Nullable DataLoader onCreateDataLoader();
+
+ /**
+ * @hide
+ */
+ public final @NonNull IBinder onBind(@NonNull Intent intent) {
+ return (IBinder) mBinder;
+ }
+
+ private class DataLoaderBinderService extends IDataLoader.Stub {
+ private int mId;
+
+ @Override
+ public void create(int id, @NonNull Bundle options,
+ @NonNull IDataLoaderStatusListener listener)
+ throws IllegalArgumentException, RuntimeException {
+ mId = id;
+ final IncrementalDataLoaderParamsParcel params = options.getParcelable("params");
+ if (params == null) {
+ throw new IllegalArgumentException("Must specify Incremental data loader params");
+ }
+ final IncrementalFileSystemControlParcel control =
+ options.getParcelable("control");
+ if (control == null) {
+ throw new IllegalArgumentException("Must specify Incremental control parcel");
+ }
+ mStatusListener = listener;
+ try {
+ if (!nativeCreateDataLoader(id, control, params, listener)) {
+ Slog.e(TAG, "Failed to create native loader for " + mId);
+ }
+ } catch (Exception ex) {
+ destroy();
+ throw new RuntimeException(ex);
+ } finally {
+ // Closing FDs.
+ if (control.cmd != null) {
+ try {
+ control.cmd.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e);
+ }
+ }
+ if (control.log != null) {
+ try {
+ control.log.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e);
+ }
+ }
+ NamedParcelFileDescriptor[] fds = params.dynamicArgs;
+ for (NamedParcelFileDescriptor nfd : fds) {
+ try {
+ nfd.fd.close();
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to close DynamicArgs parcel file descriptor " + e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void start(List<InstallationFile> fileInfos) {
+ if (!nativeStartDataLoader(mId)) {
+ Slog.e(TAG, "Failed to start loader: loader not found for " + mId);
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (!nativeStopDataLoader(mId)) {
+ Slog.w(TAG, "Failed to stop loader: loader not found for " + mId);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ if (!nativeDestroyDataLoader(mId)) {
+ Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId);
+ }
+ }
+
+ @Override
+ // TODO(b/136132412): remove this
+ public void onFileCreated(long inode, byte[] metadata) {
+ if (!nativeOnFileCreated(mId, inode, metadata)) {
+ Slog.w(TAG, "Failed to handle onFileCreated for storage:" + mId
+ + " inode:" + inode);
+ }
+ }
+ }
+
+ /**
+ * IncFs API wrapper for writing pages and getting page missing info. Non-hidden methods are
+ * expected to be called by the IncrementalDataLoaderService implemented by developers.
+ *
+ * @hide
+ *
+ * TODO(b/136132412) Should be @SystemApi
+ */
+ public static final class FileSystemConnector {
+ /**
+ * Defines a block address. A block is the unit of data chunk that IncFs operates with.
+ *
+ * @hide
+ */
+ public static class BlockAddress {
+ /**
+ * Linux inode uniquely identifies file within a single IFS instance.
+ */
+ private final long mFileIno;
+ /**
+ * Index of a 4K block within a file.
+ */
+ private final int mBlockIndex;
+
+ public BlockAddress(long fileIno, int blockIndex) {
+ this.mFileIno = fileIno;
+ this.mBlockIndex = blockIndex;
+ }
+
+ public long getFileIno() {
+ return mFileIno;
+ }
+
+ public int getBlockIndex() {
+ return mBlockIndex;
+ }
+ }
+
+ /**
+ * A block is the unit of data chunk that IncFs operates with.
+ *
+ * @hide
+ */
+ public static class Block extends BlockAddress {
+ /**
+ * Data content of the block.
+ */
+ private final @NonNull byte[] mDataBytes;
+
+ public Block(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+ super(fileIno, blockIndex);
+ this.mDataBytes = dataBytes;
+ }
+ }
+
+ /**
+ * Defines a page/block inside a file.
+ */
+ public static class DataBlock extends Block {
+ /**
+ * Compression type of the data block.
+ */
+ private final @CompressionType int mCompressionType;
+
+ public DataBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes,
+ @CompressionType int compressionType) {
+ super(fileIno, blockIndex, dataBytes);
+ this.mCompressionType = compressionType;
+ }
+ }
+
+ /**
+ * Defines a hash block for a certain file. A hash block index is the index in an array of
+ * hashes which is the 1-d representation of the hash tree. One DataBlock might be
+ * associated with multiple HashBlocks.
+ */
+ public static class HashBlock extends Block {
+ public HashBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+ super(fileIno, blockIndex, dataBytes);
+ }
+ }
+
+ /**
+ * Information about a page that is pending to be read.
+ */
+ public static class PendingReadInfo extends BlockAddress {
+ PendingReadInfo(long fileIno, int blockIndex) {
+ super(fileIno, blockIndex);
+ }
+ }
+
+ /**
+ * Information about a page that is read.
+ */
+ public static class ReadInfo extends BlockAddress {
+ /**
+ * A monotonically increasing read timestamp.
+ */
+ private final long mTimePoint;
+ /**
+ * Number of blocks read starting from blockIndex.
+ */
+ private final int mBlockCount;
+
+ ReadInfo(long timePoint, long fileIno, int firstBlockIndex, int blockCount) {
+ super(fileIno, firstBlockIndex);
+ this.mTimePoint = timePoint;
+ this.mBlockCount = blockCount;
+ }
+
+ public long getTimePoint() {
+ return mTimePoint;
+ }
+
+ public int getBlockCount() {
+ return mBlockCount;
+ }
+ }
+
+ /**
+ * Defines the dynamic information about an IncFs file.
+ */
+ public static class FileInfo {
+ /**
+ * BitSet to show if any block is available at each block index.
+ */
+ private final @NonNull
+ byte[] mBlockBitmap;
+
+ /**
+ * @hide
+ */
+ public FileInfo(@NonNull byte[] blockBitmap) {
+ this.mBlockBitmap = blockBitmap;
+ }
+ }
+
+ /**
+ * Creates a wrapper for a native instance.
+ */
+ FileSystemConnector(long nativeInstance) {
+ mNativeInstance = nativeInstance;
+ }
+
+ /**
+ * Checks whether a range in a file if loaded.
+ *
+ * @param node inode of the file.
+ * @param start The starting offset of the range.
+ * @param end The ending offset of the range.
+ * @return True if the file is fully loaded.
+ */
+ public boolean isFileRangeLoaded(long node, long start, long end) {
+ return nativeIsFileRangeLoadedNode(mNativeInstance, node, start, end);
+ }
+
+ /**
+ * Gets the metadata of a file.
+ *
+ * @param node inode of the file.
+ * @return The metadata object.
+ */
+ @NonNull
+ public byte[] getFileMetadata(long node) throws IOException {
+ final byte[] metadata = nativeGetFileMetadataNode(mNativeInstance, node);
+ if (metadata == null || metadata.length == 0) {
+ throw new IOException(
+ "IncrementalFileSystem failed to obtain metadata for node: " + node);
+ }
+ return metadata;
+ }
+
+ /**
+ * Gets the dynamic information of a file, such as page bitmaps. Can be used to get missing
+ * page indices by the FileSystemConnector.
+ *
+ * @param node inode of the file.
+ * @return Dynamic file info.
+ */
+ @NonNull
+ public FileInfo getDynamicFileInfo(long node) throws IOException {
+ final byte[] blockBitmap = nativeGetFileInfoNode(mNativeInstance, node);
+ if (blockBitmap == null || blockBitmap.length == 0) {
+ throw new IOException(
+ "IncrementalFileSystem failed to obtain dynamic file info for node: "
+ + node);
+ }
+ return new FileInfo(blockBitmap);
+ }
+
+ /**
+ * Writes a page's data and/or hashes.
+ *
+ * @param dataBlocks the DataBlock objects that contain data block index and data bytes.
+ * @param hashBlocks the HashBlock objects that contain hash indices and hash bytes.
+ *
+ * TODO(b/136132412): change API to avoid dynamic allocation of data block objects
+ */
+ public void writeMissingData(@NonNull DataBlock[] dataBlocks,
+ @Nullable HashBlock[] hashBlocks) throws IOException {
+ if (!nativeWriteMissingData(mNativeInstance, dataBlocks, hashBlocks)) {
+ throw new IOException("IncrementalFileSystem failed to write missing data.");
+ }
+ }
+
+ /**
+ * Writes the signer block of a file. Expecting the connector to call this when it got
+ * signing data from data loader.
+ *
+ * @param node the file to be written to.
+ * @param signerData the raw signer data byte array.
+ */
+ public void writeSignerData(long node, @NonNull byte[] signerData)
+ throws IOException {
+ if (!nativeWriteSignerDataNode(mNativeInstance, node, signerData)) {
+ throw new IOException(
+ "IncrementalFileSystem failed to write signer data of node " + node);
+ }
+ }
+
+ private final long mNativeInstance;
+ }
+
+ /**
+ * Wrapper for native reporting DataLoader statuses.
+ *
+ * @hide
+ *
+ * TODO(b/136132412) Should be @SystemApi
+ */
+ public static final class StatusListener {
+ /**
+ * Creates a wrapper for a native instance.
+ *
+ * @hide
+ */
+ StatusListener(long nativeInstance) {
+ mNativeInstance = nativeInstance;
+ }
+
+ /**
+ * Report the status of DataLoader. Used for system-wide notifications e.g., disabling
+ * applications which rely on this data loader to function properly.
+ *
+ * @param status status to report.
+ * @return True if status was reported successfully.
+ */
+ public boolean onStatusChanged(@DataLoaderStatus int status) {
+ return nativeReportStatus(mNativeInstance, status);
+ }
+
+ private final long mNativeInstance;
+ }
+
+ private IDataLoaderStatusListener mStatusListener = null;
+
+ /* Native methods */
+ private native boolean nativeCreateDataLoader(int storageId,
+ @NonNull IncrementalFileSystemControlParcel control,
+ @NonNull IncrementalDataLoaderParamsParcel params,
+ IDataLoaderStatusListener listener);
+
+ private native boolean nativeStartDataLoader(int storageId);
+
+ private native boolean nativeStopDataLoader(int storageId);
+
+ private native boolean nativeDestroyDataLoader(int storageId);
+
+ private static native boolean nativeOnFileCreated(int storageId,
+ long inode, byte[] metadata);
+
+ private static native boolean nativeIsFileRangeLoadedNode(
+ long nativeInstance, long node, long start, long end);
+
+ private static native boolean nativeWriteMissingData(
+ long nativeInstance, FileSystemConnector.DataBlock[] dataBlocks,
+ FileSystemConnector.HashBlock[] hashBlocks);
+
+ private static native boolean nativeWriteSignerDataNode(
+ long nativeInstance, long node, byte[] signerData);
+
+ private static native byte[] nativeGetFileMetadataNode(
+ long nativeInstance, long node);
+
+ private static native byte[] nativeGetFileInfoNode(
+ long nativeInstance, long node);
+
+ private static native boolean nativeReportStatus(long nativeInstance, int status);
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 148b0a2799b4..cd8efdb80875 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -144,6 +144,7 @@ cc_library_shared {
"android_os_VintfRuntimeInfo.cpp",
"android_net_LocalSocketImpl.cpp",
"android_net_NetUtils.cpp",
+ "android_service_incremental_IncrementalDataLoaderService.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
"android_util_StatsLog.cpp",
@@ -239,6 +240,8 @@ cc_library_shared {
"libGLESv1_CM",
"libGLESv2",
"libGLESv3",
+ "libincfs",
+ "libincremental_dataloader",
"libvulkan",
"libETC1",
"libhardware",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 378e125a3a3e..944f43fc18cf 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -150,6 +150,7 @@ extern int register_android_os_UEventObserver(JNIEnv* env);
extern int register_android_os_HidlMemory(JNIEnv* env);
extern int register_android_os_MemoryFile(JNIEnv* env);
extern int register_android_os_SharedMemory(JNIEnv* env);
+extern int register_android_service_incremental_IncrementalDataLoaderService(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
@@ -1442,6 +1443,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_NativeHandle),
REG_JNI(register_android_os_VintfObject),
REG_JNI(register_android_os_VintfRuntimeInfo),
+ REG_JNI(register_android_service_incremental_IncrementalDataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_InputApplicationHandle),
diff --git a/core/jni/android_service_incremental_IncrementalDataLoaderService.cpp b/core/jni/android_service_incremental_IncrementalDataLoaderService.cpp
new file mode 100644
index 000000000000..f518dd1a5362
--- /dev/null
+++ b/core/jni/android_service_incremental_IncrementalDataLoaderService.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "incfs-dls-jni"
+
+#include <vector>
+
+#include "core_jni_helpers.h"
+#include "incremental_dataloader_ndk.h"
+#include "jni.h"
+
+namespace android {
+namespace {
+
+struct JniIds {
+ jfieldID dataBlockFileIno;
+ jfieldID dataBlockBlockIndex;
+ jfieldID dataBlockDataBytes;
+ jfieldID dataBlockCompressionType;
+
+ JniIds(JNIEnv* env) {
+ const auto dataBlock =
+ FindClassOrDie(env,
+ "android/service/incremental/"
+ "IncrementalDataLoaderService$FileSystemConnector$DataBlock");
+ dataBlockFileIno = GetFieldIDOrDie(env, dataBlock, "mFileIno", "J");
+ dataBlockBlockIndex =
+ GetFieldIDOrDie(env, dataBlock, "mBlockIndex", "I");
+ dataBlockDataBytes = GetFieldIDOrDie(env, dataBlock, "mDataBytes", "[B");
+ dataBlockCompressionType =
+ GetFieldIDOrDie(env, dataBlock, "mCompressionType", "I");
+ }
+};
+
+const JniIds& jniIds(JNIEnv* env) {
+ static const JniIds ids(env);
+ return ids;
+}
+
+class ScopedJniArrayCritical {
+public:
+ ScopedJniArrayCritical(JNIEnv* env, jarray array) : mEnv(env), mArr(array) {
+ mPtr = array ? env->GetPrimitiveArrayCritical(array, nullptr) : nullptr;
+ }
+ ~ScopedJniArrayCritical() {
+ if (mPtr) {
+ mEnv->ReleasePrimitiveArrayCritical(mArr, mPtr, 0);
+ mPtr = nullptr;
+ }
+ }
+
+ ScopedJniArrayCritical(const ScopedJniArrayCritical&) = delete;
+ void operator=(const ScopedJniArrayCritical&) = delete;
+
+ ScopedJniArrayCritical(ScopedJniArrayCritical&& other)
+ : mEnv(other.mEnv),
+ mArr(std::exchange(mArr, nullptr)),
+ mPtr(std::exchange(mPtr, nullptr)) {}
+ ScopedJniArrayCritical& operator=(ScopedJniArrayCritical&& other) {
+ mEnv = other.mEnv;
+ mArr = std::exchange(other.mArr, nullptr);
+ mPtr = std::exchange(other.mPtr, nullptr);
+ return *this;
+ }
+
+ void* ptr() const { return mPtr; }
+ jsize size() const { return mArr ? mEnv->GetArrayLength(mArr) : 0; }
+
+private:
+ JNIEnv* mEnv;
+ jarray mArr;
+ void* mPtr;
+};
+
+static jboolean nativeCreateDataLoader(JNIEnv* env,
+ jobject thiz,
+ jint storageId,
+ jobject control,
+ jobject params,
+ jobject callback) {
+ ALOGE("nativeCreateDataLoader: %p/%d, %d, %p, %p, %p", thiz,
+ env->GetObjectRefType(thiz), storageId, params, control, callback);
+ return Incremental_DataLoaderService_OnCreate(env, thiz,
+ storageId, control, params, callback);
+}
+
+static jboolean nativeStartDataLoader(JNIEnv* env,
+ jobject thiz,
+ jint storageId) {
+ ALOGE("nativeStartDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+ storageId);
+ return Incremental_DataLoaderService_OnStart(storageId);
+}
+
+static jboolean nativeStopDataLoader(JNIEnv* env,
+ jobject thiz,
+ jint storageId) {
+ ALOGE("nativeStopDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+ storageId);
+ return Incremental_DataLoaderService_OnStop(storageId);
+}
+
+static jboolean nativeDestroyDataLoader(JNIEnv* env,
+ jobject thiz,
+ jint storageId) {
+ ALOGE("nativeDestroyDataLoader: %p/%d, %d", thiz,
+ env->GetObjectRefType(thiz), storageId);
+ return Incremental_DataLoaderService_OnDestroy(storageId);
+}
+
+
+static jboolean nativeOnFileCreated(JNIEnv* env,
+ jobject thiz,
+ jint storageId,
+ jlong inode,
+ jbyteArray metadata) {
+ ALOGE("nativeOnFileCreated: %p/%d, %d", thiz,
+ env->GetObjectRefType(thiz), storageId);
+ return Incremental_DataLoaderService_OnFileCreated(storageId, inode, metadata);
+}
+
+static jboolean nativeIsFileRangeLoadedNode(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jlong node,
+ jlong start,
+ jlong end) {
+ // TODO(b/136132412): implement this
+ return JNI_FALSE;
+}
+
+static jboolean nativeWriteMissingData(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jobjectArray data_block,
+ jobjectArray hash_blocks) {
+ const auto& jni = jniIds(env);
+ auto length = env->GetArrayLength(data_block);
+ std::vector<incfs_new_data_block> instructions(length);
+
+ // May not call back into Java after even a single jniArrayCritical, so
+ // let's collect the Java pointers to byte buffers first and lock them in
+ // memory later.
+
+ std::vector<jbyteArray> blockBuffers(length);
+ for (int i = 0; i != length; ++i) {
+ auto& inst = instructions[i];
+ auto jniBlock = env->GetObjectArrayElement(data_block, i);
+ inst.file_ino = env->GetLongField(jniBlock, jni.dataBlockFileIno);
+ inst.block_index = env->GetIntField(jniBlock, jni.dataBlockBlockIndex);
+ blockBuffers[i] = (jbyteArray)env->GetObjectField(
+ jniBlock, jni.dataBlockDataBytes);
+ inst.compression = (incfs_compression_alg)env->GetIntField(
+ jniBlock, jni.dataBlockCompressionType);
+ }
+
+ std::vector<ScopedJniArrayCritical> jniScopedArrays;
+ jniScopedArrays.reserve(length);
+ for (int i = 0; i != length; ++i) {
+ auto buffer = blockBuffers[i];
+ jniScopedArrays.emplace_back(env, buffer);
+ auto& inst = instructions[i];
+ inst.data = (uint64_t)jniScopedArrays.back().ptr();
+ inst.data_len = jniScopedArrays.back().size();
+ }
+
+ auto connector = (IncrementalFilesystemConnectorPtr)self;
+ if (auto err = Incremental_FilesystemConnector_writeBlocks(
+ connector, instructions.data(), length);
+ err < 0) {
+ jniScopedArrays.clear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+static jboolean nativeWriteSignerDataNode(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jstring relative_path,
+ jbyteArray signer_data) {
+ // TODO(b/136132412): implement this
+ return JNI_TRUE;
+}
+
+static jbyteArray nativeGetFileMetadataNode(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jlong inode) {
+ auto connector = (IncrementalFilesystemConnectorPtr)self;
+ std::vector<char> metadata(INCFS_MAX_FILE_ATTR_SIZE);
+ size_t size = metadata.size();
+ if (Incremental_FilesystemConnector_getRawMetadata(connector, inode,
+ metadata.data(), &size) < 0) {
+ size = 0;
+ }
+ metadata.resize(size);
+
+ auto buffer = env->NewByteArray(metadata.size());
+ env->SetByteArrayRegion(buffer, 0, metadata.size(),
+ (jbyte*)metadata.data());
+ return buffer;
+}
+
+static jbyteArray nativeGetFileInfoNode(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jlong inode) {
+ // TODO(b/136132412): implement this
+ return nullptr;
+}
+
+static jboolean nativeReportStatus(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jint status) {
+ auto listener = (IncrementalStatusListenerPtr)self;
+ return Incremental_StatusListener_reportStatus(listener,
+ (IncrementalDataLoaderStatus)status);
+}
+
+static const JNINativeMethod dlc_method_table[] = {
+ {"nativeCreateDataLoader",
+ "(ILandroid/os/incremental/IncrementalFileSystemControlParcel;"
+ "Landroid/os/incremental/IncrementalDataLoaderParamsParcel;"
+ "Landroid/content/pm/IDataLoaderStatusListener;)Z",
+ (void*)nativeCreateDataLoader},
+ {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
+ {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
+ {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
+ {"nativeIsFileRangeLoadedNode", "(JJJJ)Z",
+ (void*)nativeIsFileRangeLoadedNode},
+ {"nativeWriteMissingData",
+ "(J[Landroid/service/incremental/"
+ "IncrementalDataLoaderService$FileSystemConnector$DataBlock;[Landroid/service/incremental/"
+ "IncrementalDataLoaderService$FileSystemConnector$HashBlock;)Z",
+ (void*)nativeWriteMissingData},
+ {"nativeWriteSignerDataNode", "(JJ[B)Z",
+ (void*)nativeWriteSignerDataNode},
+ {"nativeGetFileMetadataNode", "(JJ)[B",
+ (void*)nativeGetFileMetadataNode},
+ {"nativeGetFileInfoNode", "(JJ)[B", (void*)nativeGetFileInfoNode},
+ {"nativeReportStatus", "(JI)Z", (void*)nativeReportStatus},
+ {"nativeOnFileCreated", "(IJ[B)Z", (void*)nativeOnFileCreated},
+};
+
+} // namespace
+
+int register_android_service_incremental_IncrementalDataLoaderService(JNIEnv* env) {
+ return jniRegisterNativeMethods(env,
+ "android/service/incremental/IncrementalDataLoaderService",
+ dlc_method_table, NELEM(dlc_method_table));
+}
+
+} // namespace android