From a384697655046637d67fa63ae65ece3bd8273207 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Thu, 1 Apr 2021 18:17:39 +0800 Subject: [PATCH] [core] Update JNI helper (#440) --- .../manager/ui/activity/LogsActivity.java | 18 +- build.gradle.kts | 2 +- core/src/main/cpp/main/include/jni_helper.h | 279 +++++++++++------- core/src/main/cpp/main/include/native_util.h | 7 +- core/src/main/cpp/main/src/service.cpp | 8 +- .../lsposed/lspd/service/BridgeService.java | 3 +- 6 files changed, 188 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java index 43614c09..ca99466c 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java @@ -91,20 +91,14 @@ public class LogsActivity extends BaseActivity { e.printStackTrace(); } AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - try { - OutputStream os = getContentResolver().openOutputStream(uri); - if (os == null) { - return; - } + try (var os = getContentResolver().openOutputStream(uri)) { ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLogs(verbose); if (parcelFileDescriptor == null) { - os.close(); return; } - InputStream is = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); - FileUtils.copy(is, os); - os.close(); - is.close(); + try (var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) { + FileUtils.copy(is, os); + } } catch (Exception e) { Snackbar.make(binding.snackbar, getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); } @@ -252,9 +246,7 @@ public class LogsActivity extends BaseActivity { now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); File cacheFile = new File(getCacheDir(), filename); - try { - OutputStream os = new FileOutputStream(cacheFile); - InputStream is = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); + try (var os = new FileOutputStream(cacheFile); var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) { FileUtils.copy(is, os); } catch (IOException e) { e.printStackTrace(); diff --git a/build.gradle.kts b/build.gradle.kts index ddece3e8..a0cd0d59 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,7 +44,7 @@ val androidTargetSdkVersion by extra(30) val androidMinSdkVersion by extra(27) val androidBuildToolsVersion by extra("30.0.3") val androidCompileSdkVersion by extra(30) -val androidCompileNdkVersion by extra("22.0.7026061") +val androidCompileNdkVersion by extra("22.1.7171670") val androidSourceCompatibility by extra(JavaVersion.VERSION_11) val androidTargetCompatibility by extra(JavaVersion.VERSION_11) val apiCode by extra(93) diff --git a/core/src/main/cpp/main/include/jni_helper.h b/core/src/main/cpp/main/include/jni_helper.h index a0296ff0..1b971e60 100644 --- a/core/src/main/cpp/main/include/jni_helper.h +++ b/core/src/main/cpp/main/include/jni_helper.h @@ -27,107 +27,6 @@ #define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz -ALWAYS_INLINE static void JNIExceptionClear(JNIEnv *env) { - if (env->ExceptionCheck()) { - env->ExceptionClear(); - } -} - -ALWAYS_INLINE static bool JNIExceptionCheck(JNIEnv *env) { - if (env->ExceptionCheck()) { - jthrowable e = env->ExceptionOccurred(); - env->Throw(e); - env->DeleteLocalRef(e); - return true; - } - return false; -} - -ALWAYS_INLINE static void JNIExceptionClearAndDescribe(JNIEnv *env) { - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } -} - -ALWAYS_INLINE static int ClearException(JNIEnv *env) { - jthrowable exception = env->ExceptionOccurred(); - if (exception != nullptr) { - env->ExceptionDescribe(); - env->ExceptionClear(); - return true; - } - return false; -} - -#define JNI_FindClass(env, name) \ - env->FindClass(name); \ - if (ClearException(env)) LOGE("FindClass " #name) - -#define JNI_GetObjectClass(env, obj) \ - env->GetObjectClass(obj); \ - if (ClearException(env)) LOGE("GetObjectClass " #obj) - -#define JNI_GetFieldID(env, class, name, sig) \ - env->GetFieldID(class, name, sig); \ - if (ClearException(env)) LOGE("GetFieldID " #name) - -#define JNI_GetObjectField(env, class, fieldId) \ - env->GetObjectField(class, fieldId); \ - if (ClearException(env)) LOGE("GetObjectField " #fieldId) - -#define JNI_GetMethodID(env, class, name, sig) \ - env->GetMethodID(class, name, sig); \ - //if (ClearException(env)) LOGE("GetMethodID " #name) - -#define JNI_CallObjectMethod(env, obj, ...) \ - env->CallObjectMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallObjectMethod " #obj " " #__VA_ARGS__) - -#define JNI_CallVoidMethod(env, obj, ...) \ - env->CallVoidMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__) - -#define JNI_CallBooleanMethod(env, obj, ...) \ - env->CallBooleanMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__) - -#define JNI_GetStaticFieldID(env, class, name, sig) \ - env->GetStaticFieldID(class, name, sig); \ - if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig) - -#define JNI_GetStaticObjectField(env, class, fieldId) \ - env->GetStaticObjectField(class, fieldId); \ - if (ClearException(env)) LOGE("GetStaticObjectField " #fieldId) - -#define JNI_GetStaticMethodID(env, class, name, sig) \ - env->GetStaticMethodID(class, name, sig); \ - if (ClearException(env)) LOGE("GetStaticMethodID " #name) - -#define JNI_CallStaticVoidMethod(env, obj, ...) \ - env->CallStaticVoidMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__) - -#define JNI_CallStaticObjectMethod(env, obj, ...) \ - env->CallStaticObjectMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticObjectMethod " #obj " " #__VA_ARGS__) - -#define JNI_CallStaticIntMethod(env, obj, ...) \ - env->CallStaticIntMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticIntMethod " #obj " " #__VA_ARGS__) - -#define JNI_GetArrayLength(env, array) \ - env->GetArrayLength(array); \ - if (ClearException(env)) LOGE("GetArrayLength " #array) - -#define JNI_NewObject(env, class, ...) \ - env->NewObject(class, __VA_ARGS__); \ - if (ClearException(env)) LOGE("NewObject " #class " " #__VA_ARGS__) - -#define JNI_RegisterNatives(env, class, methods, size) \ - env->RegisterNatives(class, methods, size); \ - if (ClearException(env)) LOGE("RegisterNatives " #class) - class JUTFString { public: inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) { @@ -181,15 +80,19 @@ private: template class ScopedLocalRef { public: - ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { + ScopedLocalRef(JNIEnv *env, T localRef) : mEnv(env), mLocalRef(localRef) { } - ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) { + + ScopedLocalRef(ScopedLocalRef &&s) noexcept: mEnv(s.mEnv), mLocalRef(s.release()) { } - explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) { + + explicit ScopedLocalRef(JNIEnv *env) : mEnv(env), mLocalRef(nullptr) { } + ~ScopedLocalRef() { reset(); } + void reset(T ptr = NULL) { if (ptr != mLocalRef) { if (mLocalRef != NULL) { @@ -198,34 +101,198 @@ public: mLocalRef = ptr; } } + T release() __attribute__((warn_unused_result)) { T localRef = mLocalRef; mLocalRef = NULL; return localRef; } + T get() const { return mLocalRef; } + // We do not expose an empty constructor as it can easily lead to errors // using common idioms, e.g.: // ScopedLocalRef<...> ref; // ref.reset(...); // Move assignment operator. - ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept { + ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { reset(s.release()); mEnv = s.mEnv; return *this; } + // Allows "if (scoped_ref == nullptr)" bool operator==(std::nullptr_t) const { return mLocalRef == nullptr; } + // Allows "if (scoped_ref != nullptr)" bool operator!=(std::nullptr_t) const { return mLocalRef != nullptr; } + private: - JNIEnv* mEnv; + JNIEnv *mEnv; T mLocalRef; DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); }; + +[[maybe_unused]] +inline void JNIExceptionClear(JNIEnv *env) { + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } +} + +[[maybe_unused]] +inline bool JNIExceptionCheck(JNIEnv *env) { + if (env->ExceptionCheck()) { + jthrowable e = env->ExceptionOccurred(); + env->Throw(e); + env->DeleteLocalRef(e); + return true; + } + return false; +} + +[[maybe_unused]] +inline void JNIExceptionClearAndDescribe(JNIEnv *env) { + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +inline jstring ClearException(JNIEnv *env) { + static jmethodID toString = env->GetMethodID(env->FindClass("java/lang/Object"), "toString", + "()Ljava/lang/String;"); + if (auto exception = env->ExceptionOccurred()) { + env->ExceptionClear(); + return (jstring) env->CallObjectMethod(exception, toString); + } + return nullptr; +} + +template +[[maybe_unused]] +inline auto unwrap_sv(T &&x) { + if constexpr (std::is_same_v, std::string_view>) return x.data(); + else return std::forward(x); +} + +template +[[maybe_unused]] +inline auto JNI_SafeInvoke(JNIEnv *env, Func f, Args &&... args) { + static_assert(std::is_member_function_pointer_v); + struct finally { + finally(JNIEnv *env) : env_(env) {} + + ~finally() { + if (auto exception = ClearException(env_)) { + LOGE("%s", JUTFString(env_, exception).get()); + } + } + + JNIEnv *env_; + } _(env); + + return (env->*f)(unwrap_sv(std::forward(args))...); +} + +[[maybe_unused]] +inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { + return JNI_SafeInvoke(env, &JNIEnv::FindClass, name); +} + +[[maybe_unused]] +inline auto JNI_GetObjectClass(JNIEnv *env, jobject obj) { + return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj); +} + +[[maybe_unused]] +inline auto JNI_GetFieldID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) { + return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, clazz, name, sig); +} + +[[maybe_unused]] +inline auto JNI_GetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldId) { + return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, clazz, fieldId); +} + +[[maybe_unused]] +inline auto +JNI_GetMethodID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) { + return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig); +} + +template +[[maybe_unused]] +inline auto JNI_CallObjectMethod(JNIEnv *env, jobject obj, Args &&... args) { + return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, std::forward(args)...); +} + +template +[[maybe_unused]] +inline auto JNI_CallVoidMethod(JNIEnv *env, jobject obj, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward(args)...); +} + +template +[[maybe_unused]] +inline auto JNI_CallBooleanMethod(JNIEnv *env, jobject obj, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward(args)...); +} + +[[maybe_unused]] +inline auto +JNI_GetStaticFieldID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) { + return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig); +} + +[[maybe_unused]] +inline auto JNI_GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldId) { + return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId); +} + +[[maybe_unused]] +inline auto +JNI_GetStaticMethodID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) { + return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig); +} + +template +[[maybe_unused]] +inline auto JNI_CallStaticVoidMethod(JNIEnv *env, jclass clazz, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward(args)...); +} + +template +[[maybe_unused]] +inline auto JNI_CallStaticObjectMethod(JNIEnv *env, jclass clazz, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward(args)...); +} + +template +[[maybe_unused]] +inline auto JNI_CallStaticIntMethod(JNIEnv *env, jclass clazz, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward(args)...); +} + +[[maybe_unused]] +inline auto JNI_GetArrayLength(JNIEnv *env, jarray array) { + return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array); +} + +template +[[maybe_unused]] +inline auto JNI_NewObject(JNIEnv *env, jclass clazz, Args &&...args) { + return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward(args)...); +} + +[[maybe_unused]] +inline auto +JNI_RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint size) { + return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size); +} diff --git a/core/src/main/cpp/main/include/native_util.h b/core/src/main/cpp/main/include/native_util.h index 3fd2dd23..759dc1e5 100644 --- a/core/src/main/cpp/main/include/native_util.h +++ b/core/src/main/cpp/main/include/native_util.h @@ -35,14 +35,15 @@ namespace lspd { const JNINativeMethod *methods, jint method_count) { - ScopedLocalRef clazz(env, - Context::GetInstance()->FindClassFromCurrentLoader(env, class_name)); + ScopedLocalRef clazz(env, + Context::GetInstance()->FindClassFromCurrentLoader(env, class_name)); if (clazz.get() == nullptr) { LOGF("Couldn't find class: %s", class_name); return false; } return JNI_RegisterNatives(env, clazz.get(), methods, method_count); } + #if defined(__cplusplus) #define _NATIVEHELPER_JNI_MACRO_CAST(to) \ reinterpret_cast @@ -60,7 +61,7 @@ namespace lspd { #endif #ifndef LSP_DEF_NATIVE_METHOD -#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \ +#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \ extern "C" ret Java_org_lsposed_lspd_nativebridge_## className ## _ ## functionName (JNI_START, ## __VA_ARGS__) #endif diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/cpp/main/src/service.cpp index b4a841da..872e88d0 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/cpp/main/src/service.cpp @@ -130,7 +130,7 @@ namespace lspd { return; } - ScopedLocalRef binderClass(env, env->FindClass("android/os/Binder")); + ScopedLocalRef binderClass(env, env->FindClass("android/os/Binder")); exec_transact_backup_methodID_ = JNI_GetMethodID(env, binderClass.get(), "execTransact", "(IJJI)Z"); auto set_table_override = reinterpret_castCallVoidMethod(reply, read_exception_method_); - if (!ClearException(env)) { - service = JNI_CallObjectMethod(env, reply, read_strong_binder_method_); - } + JNI_CallVoidMethod(env, reply, read_exception_method_); + service = JNI_CallObjectMethod(env, reply, read_strong_binder_method_); } JNI_CallVoidMethod(env, data, recycleMethod_); JNI_CallVoidMethod(env, reply, recycleMethod_); diff --git a/core/src/main/java/org/lsposed/lspd/service/BridgeService.java b/core/src/main/java/org/lsposed/lspd/service/BridgeService.java index 79dea6d8..c864aafa 100644 --- a/core/src/main/java/org/lsposed/lspd/service/BridgeService.java +++ b/core/src/main/java/org/lsposed/lspd/service/BridgeService.java @@ -265,7 +265,8 @@ public class BridgeService { try { String processName = data.readString(); IBinder heartBeat = data.readStrongBinder(); - binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid(), processName, heartBeat).asBinder(); + var applicationService = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid(), processName, heartBeat); + if (applicationService != null) binder = service.asBinder(); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(e)); }