Skip to content

libarchive base extraction + external JNI libraries #552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.16.3)
project(android-luajit-launcher LANGUAGES C CXX)

find_package(PkgConfig REQUIRED)
pkg_check_modules(LuaJIT luajit REQUIRED IMPORTED_TARGET)
pkg_check_modules(libarchive libarchive REQUIRED IMPORTED_TARGET)

set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
if(NOT Threads_FOUND OR NOT CMAKE_USE_PTHREADS_INIT)
message(FATAL_ERROR "pthread library not found!")
endif()

add_library(7z)
set_target_properties(7z PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(7z PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
target_link_libraries(7z PRIVATE PkgConfig::libarchive android log)
target_sources(7z PRIVATE jni/unarchive.cpp)

add_library(ioctl SHARED)
set_target_properties(ioctl PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(ioctl PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
target_sources(ioctl PRIVATE jni/ioctl/ioctl.cpp)

add_library(luajit-launcher SHARED)
set_target_properties(luajit-launcher PROPERTIES C_VISIBILITY_PRESET hidden)
target_include_directories(luajit-launcher PUBLIC jni/android_native_app_glue)
target_link_libraries(luajit-launcher PRIVATE PkgConfig::LuaJIT Threads::Threads android log)
target_sources(luajit-launcher PRIVATE jni/android_native_app_glue/android_native_app_glue.c jni/jni_helper.c jni/main.c)

install(TARGETS 7z)
install(TARGETS ioctl)
install(TARGETS luajit-launcher)
12 changes: 8 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ android {
buildConfigField "String", "APP_NAME", '"' + projectName + '"'
buildConfigField "boolean", "IN_APP_UPDATES", "false"
buildConfigField "boolean", "SUPPORTS_RUNTIME_CHANGES", "false"

buildConfigField "String", "SEVENZIP_LIB", '"' + sevenZipLib + '"'
}

sourceSets {
Expand All @@ -32,10 +34,12 @@ android {
includeInApk = false
}

externalNativeBuild {
ndkBuild {
path file('../jni/Android.mk')
}
if (buildJni.toBoolean()) {
externalNativeBuild {
ndkBuild {
path file('../jni/Android.mk')
}
}
}

buildFeatures {
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/koreader/launcher/Assets.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Assets {
private val tag = this::class.java.simpleName

init {
Log.i(tag, "loading lib7z")
System.loadLibrary("7z")
Log.i(tag, "loading ${BuildConfig.SEVENZIP_LIB}")
System.loadLibrary(BuildConfig.SEVENZIP_LIB)
}

fun extract(activity: Activity): Boolean {
Expand Down
4 changes: 4 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ ndkCustomPath=.
assetsPath=../assets
libsPath=../libs

# JNI libraries
buildJni=true
sevenZipLib=z

# Compilation features
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.buildconfig=true
Expand Down
3 changes: 0 additions & 3 deletions jni/android_native_app_glue/android_native_app_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,6 @@ void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
}
}

void app_dummy() {
}

static void android_app_destroy(struct android_app* android_app) {
LOGD("android_app_destroy!");
free_saved_state(android_app);
Expand Down
16 changes: 5 additions & 11 deletions jni/android_native_app_glue/android_native_app_glue.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ enum {
APP_CMD_DESTROY,
};

_Pragma("GCC visibility push(default)")

/**
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
* app command message.
Expand All @@ -329,21 +331,13 @@ void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
*/
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);

/**
* Dummy function that used to be used to prevent the linker from stripping app
* glue code. No longer necessary, since __attribute__((visibility("default")))
* does this for us.
*/
__attribute__((
deprecated("Calls to app_dummy are no longer necessary. See "
"https://github.com/android-ndk/ndk/issues/381."))) void
app_dummy();

/**
* This is the function that application code must implement, representing
* the main entry to the app.
*/
extern void android_main(struct android_app* app);
void android_main(struct android_app* app);

_Pragma("GCC visibility pop")

#ifdef __cplusplus
}
Expand Down
96 changes: 96 additions & 0 deletions jni/unarchive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
extern "C" {

#include <limits.h>
#include <stdlib.h>

#include <android/asset_manager_jni.h>
#include <android/log.h>
#include <jni.h>

#include <archive.h>
#include <archive_entry.h>

#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "unarchive", __VA_ARGS__))


static int extract_asset(struct AAssetManager *mgr, const char *name, const char *destination)
{
struct archive *in = NULL, *out = NULL;
int r = ARCHIVE_FAILED;
AAsset *asset = NULL;
char path[PATH_MAX];

asset = AAssetManager_open(mgr, name, AASSET_MODE_UNKNOWN);
if (!asset)
goto end;

in = archive_read_new();
archive_read_support_format_all(in);
archive_read_support_filter_all(in);

out = archive_write_disk_new();
archive_write_disk_set_options(out, ARCHIVE_EXTRACT_SECURE_NODOTDOT);

if ((r = archive_read_open_memory(in, AAsset_getBuffer(asset), AAsset_getLength64(asset)))) {
LOGE("%s", archive_error_string(in));
goto end;
}

for (struct archive_entry *entry; ; ) {
r = archive_read_next_header(in, &entry);
if (r == ARCHIVE_EOF)
goto end;
if (r < ARCHIVE_OK)
LOGE("%s", archive_error_string(in));
if (r < ARCHIVE_WARN)
goto end;
if (snprintf(path, sizeof (path), "%s/%s", destination, archive_entry_pathname(entry)) >= sizeof (path)) {
LOGE("path to long %s", archive_entry_pathname(entry));
r = ARCHIVE_FAILED;
goto end;
}
archive_entry_set_pathname(entry, path);
r = archive_read_extract2(in, entry, out);
if (r < ARCHIVE_OK)
LOGE("%s", archive_error_string(out));
if (r < ARCHIVE_WARN)
goto end;
}

end:
if (in) {
archive_read_close(in);
archive_read_free(in);
}

if (out) {
archive_write_close(out);
archive_write_free(out);
}

if (asset) {
AAsset_close(asset);
}

return r;
}

JNIEXPORT jint JNICALL
Java_org_koreader_launcher_Assets_extract(JNIEnv *env,
__unused jobject obj,
jobject assetsManager,
jstring payload,
jstring output)
{
const char *name = env->GetStringUTFChars(payload, nullptr);
const char *destination = env->GetStringUTFChars(output, nullptr);

int r = extract_asset(AAssetManager_fromJava(env, assetsManager), name, destination);

env->ReleaseStringUTFChars(payload, name);
env->ReleaseStringUTFChars(output, destination);

return r != ARCHIVE_OK && r != ARCHIVE_EOF;
}

}