summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--jni/Android.bp81
-rw-r--r--jni/com_android_providers_media_leveldb_LevelDBInstance.cpp168
-rw-r--r--proguard.flags2
-rw-r--r--src/com/android/providers/media/leveldb/LevelDBEntry.java39
-rw-r--r--src/com/android/providers/media/leveldb/LevelDBInstance.java157
-rw-r--r--src/com/android/providers/media/leveldb/LevelDBManager.java51
-rw-r--r--src/com/android/providers/media/leveldb/LevelDBResult.java55
-rw-r--r--tests/Android.bp5
-rw-r--r--tests/src/com/android/providers/media/leveldb/LevelDBManagerTest.java83
10 files changed, 631 insertions, 11 deletions
diff --git a/Android.bp b/Android.bp
index 72462ea66..1506420c8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,7 @@ android_app {
jni_libs: [
"libfuse_jni",
"libfuse",
+ "libleveldb_jni",
],
resource_dirs: [
diff --git a/jni/Android.bp b/jni/Android.bp
index 35721d5a6..44832efcc 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -30,7 +30,7 @@ cc_library_shared {
"MediaProviderWrapper.cpp",
"ReaddirHelper.cpp",
"RedactionInfo.cpp",
- "node.cpp"
+ "node.cpp",
],
header_libs: [
@@ -44,7 +44,7 @@ cc_library_shared {
"leveldb",
"liblog",
"libfuse",
- "libandroid"
+ "libandroid",
],
static_libs: [
@@ -73,15 +73,60 @@ cc_library_shared {
min_sdk_version: "30",
}
+cc_library_shared {
+ name: "libleveldb_jni",
+
+ srcs: [
+ "com_android_providers_media_leveldb_LevelDBInstance.cpp",
+ ],
+
+ header_libs: [
+ "libnativehelper_header_only",
+ ],
+
+ apex_available: ["com.android.mediaprovider"],
+
+ shared_libs: [
+ "leveldb",
+ "liblog",
+ "libandroid",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-variable",
+ "-Wthread-safety",
+ ],
+
+ tidy: true,
+ tidy_checks: [
+ "-google-runtime-int",
+ "-performance-no-int-to-ptr",
+ ],
+
+ sdk_version: "current",
+ stl: "c++_static",
+ min_sdk_version: "30",
+}
+
cc_test {
name: "fuse_node_test",
- test_suites: ["device-tests", "mts-mediaprovider"],
+ test_suites: [
+ "device-tests",
+ "mts-mediaprovider",
+ ],
test_config: "fuse_node_test.xml",
compile_multilib: "both",
multilib: {
- lib32: { suffix: "32", },
- lib64: { suffix: "64", },
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
},
srcs: [
@@ -112,13 +157,20 @@ cc_test {
cc_test {
name: "RedactionInfoTest",
- test_suites: ["device-tests", "mts-mediaprovider"],
+ test_suites: [
+ "device-tests",
+ "mts-mediaprovider",
+ ],
test_config: "RedactionInfoTest.xml",
compile_multilib: "both",
multilib: {
- lib32: { suffix: "32", },
- lib64: { suffix: "64", },
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
},
srcs: [
@@ -144,13 +196,20 @@ cc_test {
cc_test {
name: "FuseUtilsTest",
- test_suites: ["device-tests", "mts-mediaprovider"],
+ test_suites: [
+ "device-tests",
+ "mts-mediaprovider",
+ ],
test_config: "FuseUtilsTest.xml",
compile_multilib: "both",
multilib: {
- lib32: { suffix: "32", },
- lib64: { suffix: "64", },
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
},
srcs: [
diff --git a/jni/com_android_providers_media_leveldb_LevelDBInstance.cpp b/jni/com_android_providers_media_leveldb_LevelDBInstance.cpp
new file mode 100644
index 000000000..8b40dd6ff
--- /dev/null
+++ b/jni/com_android_providers_media_leveldb_LevelDBInstance.cpp
@@ -0,0 +1,168 @@
+#include <jni.h>
+#include <nativehelper/scoped_utf_chars.h>
+
+#include "leveldb/db.h"
+
+#ifndef _Included_com_android_providers_media_leveldb_LevelDBInstance
+#define _Included_com_android_providers_media_leveldb_LevelDBInstance
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_android_providers_media_leveldb_LevelDBInstance_MAX_BULK_INSERT_ENTRIES
+#define com_android_providers_media_leveldb_LevelDBInstance_MAX_BULK_INSERT_ENTRIES 100L
+
+static std::string getStatusCode(leveldb::Status status) {
+ if (status.ok()) {
+ return "0";
+ } else if (status.IsNotFound()) {
+ return "1";
+ } else if (status.IsInvalidArgument()) {
+ return "2";
+ } else {
+ return "3";
+ }
+}
+
+static jobject createLevelDBResult(JNIEnv* env, leveldb::Status status, std::string value) {
+ // Create the object of the class LevelDBResult
+ jclass levelDbResultClass = env->FindClass("com/android/providers/media/leveldb/LevelDBResult");
+ jobject levelDbResultData = env->AllocObject(levelDbResultClass);
+
+ // Get the UserData fields to be set
+ jfieldID codeField = env->GetFieldID(levelDbResultClass, "mCode", "Ljava/lang/String;");
+ jfieldID messageField =
+ env->GetFieldID(levelDbResultClass, "mErrorMessage", "Ljava/lang/String;");
+ jfieldID valueField = env->GetFieldID(levelDbResultClass, "mValue", "Ljava/lang/String;");
+
+ std::string statusCode = getStatusCode(status);
+ env->SetObjectField(levelDbResultData, codeField, env->NewStringUTF(statusCode.c_str()));
+ env->SetObjectField(levelDbResultData, messageField,
+ env->NewStringUTF(status.ToString().c_str()));
+ env->SetObjectField(levelDbResultData, valueField, env->NewStringUTF(value.c_str()));
+ return levelDbResultData;
+}
+
+static leveldb::Status insertInLevelDB(JNIEnv* env, jobject obj, jlong leveldbptr,
+ jobject leveldbentry) {
+ jclass levelDbEntryClass = env->GetObjectClass(leveldbentry);
+ jmethodID getKeyMethodId =
+ env->GetMethodID(levelDbEntryClass, "getKey", "()Ljava/lang/String;");
+ jmethodID getValueMethodId =
+ env->GetMethodID(levelDbEntryClass, "getValue", "()Ljava/lang/String;");
+
+ jstring key = (jstring)env->CallObjectMethod(leveldbentry, getKeyMethodId);
+ jstring value = (jstring)env->CallObjectMethod(leveldbentry, getValueMethodId);
+ ScopedUtfChars utf_chars_key(env, key);
+ ScopedUtfChars utf_chars_value(env, value);
+ leveldb::DB* leveldb = reinterpret_cast<leveldb::DB*>(leveldbptr);
+ leveldb::Status status;
+ status = leveldb->Put(leveldb::WriteOptions(), utf_chars_key.c_str(), utf_chars_value.c_str());
+ return status;
+}
+
+static jobject insert(JNIEnv* env, jobject obj, jlong leveldbptr, jobject leveldbentry) {
+ return createLevelDBResult(env, insertInLevelDB(env, obj, leveldbptr, leveldbentry), "");
+}
+
+/*
+ * Class: com_android_providers_media_leveldb_LevelDBInstance
+ * Method: nativeCreateInstance
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL
+ Java_com_android_providers_media_leveldb_LevelDBInstance_nativeCreateInstance(
+ JNIEnv* env, jclass leveldbInstanceClass, jstring path) {
+ ScopedUtfChars utf_chars_path(env, path);
+ leveldb::Options options;
+ options.create_if_missing = true;
+ leveldb::DB* leveldb;
+ leveldb::Status status = leveldb::DB::Open(options, utf_chars_path.c_str(), &leveldb);
+ if (status.ok()) {
+ return reinterpret_cast<jlong>(leveldb);
+ } else {
+ long val = 0;
+ return (jlong)val;
+ }
+}
+
+/*
+ * Class: com_android_providers_media_leveldb_LevelDBInstance
+ * Method: nativeQuery
+ * Signature: (JLjava/lang/String;)Lcom/android/providers/media/leveldb/LevelDBResult;
+ */
+JNIEXPORT jobject JNICALL Java_com_android_providers_media_leveldb_LevelDBInstance_nativeQuery(
+ JNIEnv* env, jobject obj, jlong leveldbptr, jstring path) {
+ ScopedUtfChars utf_chars_path(env, path);
+ leveldb::DB* leveldb = reinterpret_cast<leveldb::DB*>(leveldbptr);
+ leveldb::Status status;
+ std::string value = "";
+ status = leveldb->Get(leveldb::ReadOptions(), utf_chars_path.c_str(), &value);
+ return createLevelDBResult(env, status, value);
+}
+
+/*
+ * Class: com_android_providers_media_leveldb_LevelDBInstance
+ * Method: nativeInsert
+ * Signature: (JLcom/android/providers/media/leveldb/LevelDBEntry;)
+ * Lcom/android/providers/media/leveldb/LevelDBResult;
+ */
+JNIEXPORT jobject JNICALL Java_com_android_providers_media_leveldb_LevelDBInstance_nativeInsert(
+ JNIEnv* env, jobject obj, jlong leveldbptr, jobject leveldbentry) {
+ return insert(env, obj, leveldbptr, leveldbentry);
+}
+
+/*
+ * Class: com_android_providers_media_leveldb_LevelDBInstance
+ * Method: nativeBulkInsert
+ * Signature: (JLjava/util/List;)Lcom/android/providers/media/leveldb/LevelDBResult;
+ */
+JNIEXPORT jobject JNICALL Java_com_android_providers_media_leveldb_LevelDBInstance_nativeBulkInsert(
+ JNIEnv* env, jobject obj, jlong leveldbptr, jobject entries) {
+ // Get the class of the list
+ jclass listClass = env->GetObjectClass(entries);
+
+ // Get the iterator method ID
+ jmethodID iteratorMethod = env->GetMethodID(listClass, "iterator", "()Ljava/util/Iterator;");
+
+ // Get the iterator object
+ jobject iterator = env->CallObjectMethod(entries, iteratorMethod);
+
+ // Get the iterator class
+ jclass iteratorClass = env->GetObjectClass(iterator);
+
+ // Get the hasNext and next method IDs
+ jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+ jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
+
+ leveldb::Status status;
+
+ // Iterate through the list
+ while (env->CallBooleanMethod(iterator, hasNextMethod)) {
+ jobject jLevelDBEntryObject = env->CallObjectMethod(iterator, nextMethod);
+ leveldb::Status status = insertInLevelDB(env, obj, leveldbptr, jLevelDBEntryObject);
+ if (!status.ok()) {
+ break;
+ }
+ }
+
+ return createLevelDBResult(env, status, "");
+}
+
+/*
+ * Class: com_android_providers_media_leveldb_LevelDBInstance
+ * Method: nativeDelete
+ * Signature: (JLjava/lang/String;)Lcom/android/providers/media/leveldb/LevelDBResult;
+ */
+JNIEXPORT jobject JNICALL Java_com_android_providers_media_leveldb_LevelDBInstance_nativeDelete(
+ JNIEnv* env, jobject obj, jlong leveldbptr, jstring key) {
+ ScopedUtfChars utf_chars_key(env, key);
+ leveldb::DB* leveldb = reinterpret_cast<leveldb::DB*>(leveldbptr);
+ leveldb::Status status;
+ status = leveldb->Delete(leveldb::WriteOptions(), utf_chars_key.c_str());
+ return createLevelDBResult(env, status, "");
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/proguard.flags b/proguard.flags
index aa234cca2..e4e4c1942 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -15,6 +15,8 @@
-keep public final class com.android.providers.media.FileOpenResult { *; }
-keep public final class com.android.providers.media.FdAccessResult { *; }
-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public final class com.android.providers.media.leveldb.LevelDBResult { *; }
+-keep public final class com.android.providers.media.leveldb.LevelDBEntry { *; }
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
diff --git a/src/com/android/providers/media/leveldb/LevelDBEntry.java b/src/com/android/providers/media/leveldb/LevelDBEntry.java
new file mode 100644
index 000000000..dcc050f12
--- /dev/null
+++ b/src/com/android/providers/media/leveldb/LevelDBEntry.java
@@ -0,0 +1,39 @@
+/*
+ * 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 com.android.providers.media.leveldb;
+
+
+/**
+ * Represents a leveldb entry which has a key and a value as string data type.
+ */
+public final class LevelDBEntry {
+ private final String mKey;
+ private final String mValue;
+
+ public LevelDBEntry(String key, String value) {
+ this.mKey = key;
+ this.mValue = value;
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+}
diff --git a/src/com/android/providers/media/leveldb/LevelDBInstance.java b/src/com/android/providers/media/leveldb/LevelDBInstance.java
new file mode 100644
index 000000000..52586542b
--- /dev/null
+++ b/src/com/android/providers/media/leveldb/LevelDBInstance.java
@@ -0,0 +1,157 @@
+/*
+ * 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 com.android.providers.media.leveldb;
+
+import java.io.File;
+import java.util.List;
+
+
+/**
+ * Represents an instance of leveldb connection.
+ */
+public final class LevelDBInstance {
+
+ // Max limit of bulk insert
+ public static final int MAX_BULK_INSERT_ENTRIES = 100;
+
+ private static boolean sIsLibraryLoaded = false;
+
+ private long mNativePtr;
+
+ private final String mLevelDBPath;
+
+ private LevelDBInstance(long ptr, String path) {
+ this.mNativePtr = ptr;
+ this.mLevelDBPath = path;
+ }
+
+ static LevelDBInstance createLevelDBInstance(String path) {
+ if (!sIsLibraryLoaded) {
+ System.loadLibrary("leveldb_jni");
+ sIsLibraryLoaded = true;
+ }
+
+ long ptr = nativeCreateInstance(path);
+ if (ptr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ return new LevelDBInstance(ptr, path);
+ }
+
+ /**
+ * Returns path of leveldb file
+ */
+ public String getLevelDBPath() {
+ return mLevelDBPath;
+ }
+
+ /**
+ * Fetch value for given key from leveldb
+ *
+ * @param key for entry
+ */
+ public LevelDBResult query(String key) {
+ synchronized (this) {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ return nativeQuery(mNativePtr, key);
+ }
+ }
+
+ /**
+ * Inserts key,value entry in leveldb.
+ *
+ * @param levelDbEntry contains key and value
+ */
+ public LevelDBResult insert(LevelDBEntry levelDbEntry) {
+ synchronized (this) {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ return nativeInsert(mNativePtr, levelDbEntry);
+ }
+ }
+
+ /**
+ * Inserts key,value entry list in leveldb.
+ *
+ * @param entryList contains list of LevelDbEntry
+ * @throws java.lang.IllegalArgumentException if entries size is zero or greater than 1000.
+ */
+ public LevelDBResult bulkInsert(List<LevelDBEntry> entryList) {
+ synchronized (this) {
+ if (entryList == null || entryList.size() == 0) {
+ throw new IllegalArgumentException("No entries provided to insert");
+ }
+
+ if (entryList.size() > MAX_BULK_INSERT_ENTRIES) {
+ throw new IllegalArgumentException(
+ "Entry size is greater than max size: " + MAX_BULK_INSERT_ENTRIES);
+ }
+
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ return nativeBulkInsert(mNativePtr, entryList);
+ }
+ }
+
+ /**
+ * Deletes entry for given key in leveldb.
+ *
+ * @param key to be deleted
+ */
+ public LevelDBResult delete(String key) {
+ synchronized (this) {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ return nativeDelete(mNativePtr, key);
+ }
+ }
+
+ /**
+ * Deletes entry for given key in leveldb.
+ *
+ */
+ public void deleteInstance() {
+ synchronized (this) {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Leveldb connection is missing");
+ }
+
+ mNativePtr = 0;
+ new File(getLevelDBPath()).delete();
+ }
+ }
+
+ private static native long nativeCreateInstance(String path);
+
+ private native LevelDBResult nativeQuery(long nativePtr, String key);
+
+ private native LevelDBResult nativeInsert(long nativePtr, LevelDBEntry levelDbEntry);
+
+ private native LevelDBResult nativeBulkInsert(long nativePtr, List<LevelDBEntry> entryList);
+
+ private native LevelDBResult nativeDelete(long nativePtr, String key);
+}
diff --git a/src/com/android/providers/media/leveldb/LevelDBManager.java b/src/com/android/providers/media/leveldb/LevelDBManager.java
new file mode 100644
index 000000000..7c75c6667
--- /dev/null
+++ b/src/com/android/providers/media/leveldb/LevelDBManager.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.android.providers.media.leveldb;
+
+import com.google.common.base.Ascii;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creates and manages connections to LevelDB.
+ */
+public final class LevelDBManager {
+ private static final Map<String, LevelDBInstance> INSTANCES = new HashMap<>();
+
+ private static final Object sLockObject = new Object();
+
+ private LevelDBManager() {}
+
+ /**
+ * Creates leveldb instance. If already exists, returns a reference to it.
+ *
+ * @param path on which instance needs to be created
+ */
+ public static LevelDBInstance getInstance(String path) {
+ synchronized (sLockObject) {
+ path = Ascii.toLowerCase(path.trim());
+ if (INSTANCES.containsKey(path)) {
+ return INSTANCES.get(path);
+ }
+
+ LevelDBInstance instance = LevelDBInstance.createLevelDBInstance(path);
+ INSTANCES.put(path, instance);
+ return instance;
+ }
+ }
+}
diff --git a/src/com/android/providers/media/leveldb/LevelDBResult.java b/src/com/android/providers/media/leveldb/LevelDBResult.java
new file mode 100644
index 000000000..56cef439b
--- /dev/null
+++ b/src/com/android/providers/media/leveldb/LevelDBResult.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.android.providers.media.leveldb;
+
+/**
+ * Container class for retrieving leveldb operation's result and status.
+ */
+public final class LevelDBResult {
+
+ private static final String SUCCESS_CODE = "0";
+ private static final String NOT_FOUND_CODE = "1";
+
+ private String mCode;
+ private String mErrorMessage;
+
+ // May be empty for update operations
+ private String mValue;
+
+ public LevelDBResult() {
+ }
+
+ public String getCode() {
+ return mCode;
+ }
+
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public boolean isSuccess() {
+ return SUCCESS_CODE.equals(mCode);
+ }
+
+ public boolean isNotFound() {
+ return NOT_FOUND_CODE.equals(mCode);
+ }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index e3b267ca6..47638cd19 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -171,6 +171,11 @@ android_test {
"framework-mediaprovider.impl",
],
+ jni_libs: [
+ // Needed to run LevelDBManagerTest
+ "libleveldb_jni",
+ ],
+
static_libs: [
"androidx.appcompat_appcompat",
"modules-utils-backgroundthread",
diff --git a/tests/src/com/android/providers/media/leveldb/LevelDBManagerTest.java b/tests/src/com/android/providers/media/leveldb/LevelDBManagerTest.java
new file mode 100644
index 000000000..f684e8ce5
--- /dev/null
+++ b/tests/src/com/android/providers/media/leveldb/LevelDBManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.android.providers.media.leveldb;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class LevelDBManagerTest {
+
+ private static final String SUCCESS_CODE = "0";
+
+ @Test
+ public void testLevelDbOperations() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ String levelDBFile = "test-leveldb";
+ final String levelDBPath = context.getFilesDir().getPath() + "/" + levelDBFile;
+ LevelDBInstance levelDBInstance = LevelDBManager.getInstance(levelDBPath);
+ LevelDBResult levelDBResult;
+ try {
+
+ List<String> fileNames = Arrays.stream(context.getFilesDir().listFiles()).map(
+ File::getName).collect(Collectors.toList());
+ assertThat(fileNames).contains(levelDBFile);
+
+ levelDBResult = levelDBInstance.insert(new LevelDBEntry("a", "1"));
+ verifySuccessResult(levelDBResult);
+
+ levelDBResult = levelDBInstance.query("a");
+ verifySuccessResult(levelDBResult);
+ assertThat(levelDBResult.getValue()).isEqualTo("1");
+
+ levelDBResult = levelDBInstance.delete("b");
+ verifySuccessResult(levelDBResult);
+
+ levelDBResult = levelDBInstance.bulkInsert(
+ Arrays.asList(new LevelDBEntry("c", "3"), new LevelDBEntry("d", "4")));
+ verifySuccessResult(levelDBResult);
+
+ assertThat(levelDBInstance.query("c").getValue()).isEqualTo("3");
+ assertThat(levelDBInstance.query("d").getValue()).isEqualTo("4");
+ assertThat(levelDBInstance.query("b").getValue()).isEqualTo("");
+ assertThat(levelDBInstance.query("a").getValue()).isEqualTo("1");
+
+ } finally {
+ // Deletes leveldb file
+ levelDBInstance.deleteInstance();
+ }
+ }
+
+ private void verifySuccessResult(LevelDBResult levelDBResult) {
+ assertThat(levelDBResult).isNotNull();
+ assertThat(levelDBResult.getCode()).isNotNull();
+ assertThat(levelDBResult.getCode()).isEqualTo(SUCCESS_CODE);
+ }
+}