blob: 4c0f55f2bf23e15fb481693b83cb3e3c252f58c0 [file] [log] [blame]
/*
* 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 "dataloader-jni"
#include <vector>
#include "core_jni_helpers.h"
#include "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 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 DataLoaderService_OnStart(storageId);
}
static jboolean nativeStopDataLoader(JNIEnv* env,
jobject thiz,
jint storageId) {
ALOGE("nativeStopDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
storageId);
return DataLoaderService_OnStop(storageId);
}
static jboolean nativeDestroyDataLoader(JNIEnv* env,
jobject thiz,
jint storageId) {
ALOGE("nativeDestroyDataLoader: %p/%d, %d", thiz,
env->GetObjectRefType(thiz), storageId);
return 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 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 = (DataLoaderFilesystemConnectorPtr)self;
if (auto err = DataLoader_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 = (DataLoaderFilesystemConnectorPtr)self;
std::vector<char> metadata(INCFS_MAX_FILE_ATTR_SIZE);
size_t size = metadata.size();
if (DataLoader_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 = (DataLoaderStatusListenerPtr)self;
return DataLoader_StatusListener_reportStatus(listener,
(DataLoaderStatus)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_DataLoaderService(JNIEnv* env) {
return jniRegisterNativeMethods(env,
"android/service/incremental/IncrementalDataLoaderService",
dlc_method_table, NELEM(dlc_method_table));
}
} // namespace android