[core] Say hello to binder
This commit is contained in:
parent
572b3e7d27
commit
3f94f49568
|
|
@ -143,7 +143,8 @@ dependencies {
|
||||||
implementation 'me.zhanghai.android.appiconloader:appiconloader-glide:1.2.0'
|
implementation 'me.zhanghai.android.appiconloader:appiconloader-glide:1.2.0'
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.5'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.5'
|
||||||
implementation files('libs/WeatherView-2.0.3.aar')
|
implementation files('libs/WeatherView-2.0.3.aar')
|
||||||
compileOnly project(':hiddenapi-stubs')
|
compileOnly project(":hiddenapi-stubs")
|
||||||
|
implementation project(path: ':service')
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ import okhttp3.Cache;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import rikka.material.app.DayNightDelegate;
|
import rikka.material.app.DayNightDelegate;
|
||||||
|
|
||||||
|
import static io.github.lsposed.manager.receivers.LSPosedServiceClient.testBinder;
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
public static final String TAG = "LSPosedManager";
|
public static final String TAG = "LSPosedManager";
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
|
@ -61,6 +63,7 @@ public class App extends Application {
|
||||||
|
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
testBinder();
|
||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
try {
|
try {
|
||||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package io.github.lsposed.manager.receivers;
|
||||||
|
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import io.github.lsposed.manager.App;
|
||||||
|
import io.github.lsposed.lspd.service.ILSPosedService;
|
||||||
|
|
||||||
|
public class LSPosedServiceClient {
|
||||||
|
public static IBinder binder = null;
|
||||||
|
public static ILSPosedService service = null;
|
||||||
|
|
||||||
|
private static final IBinder.DeathRecipient DEATH_RECIPIENT = () -> {
|
||||||
|
binder = null;
|
||||||
|
service = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IBinder getBinder() {
|
||||||
|
try {
|
||||||
|
Log.e(App.TAG, "Cannot get binder");
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testBinder() {
|
||||||
|
if (binder == null && service == null) {
|
||||||
|
binder = getBinder();
|
||||||
|
}
|
||||||
|
if (binder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
binder.linkToDeath(DEATH_RECIPIENT, 0);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
service = ILSPosedService.Stub.asInterface(binder);
|
||||||
|
if (service == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int ver = -1;
|
||||||
|
try {
|
||||||
|
ver = service.getVersion();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Log.i(App.TAG, "Got version " + ver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -58,6 +58,7 @@ dependencies {
|
||||||
implementation 'rikka.ndk:riru:10'
|
implementation 'rikka.ndk:riru:10'
|
||||||
implementation 'com.android.tools.build:apksig:4.1.2'
|
implementation 'com.android.tools.build:apksig:4.1.2'
|
||||||
implementation project(path: ':sandhook-hooklib')
|
implementation project(path: ':sandhook-hooklib')
|
||||||
|
implementation project(path: ':service')
|
||||||
compileOnly project(':hiddenapi-stubs')
|
compileOnly project(':hiddenapi-stubs')
|
||||||
compileOnly project(':key-selector')
|
compileOnly project(':key-selector')
|
||||||
compileOnly 'androidx.annotation:annotation:1.1.0'
|
compileOnly 'androidx.annotation:annotation:1.1.0'
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ inline constexpr bool is64 = Is64();
|
||||||
|
|
||||||
static const auto kEntryClassName = "io.github.lsposed.lspd.core.Main"s;
|
static const auto kEntryClassName = "io.github.lsposed.lspd.core.Main"s;
|
||||||
static const auto kClassLinkerClassName = "io.github.lsposed.lspd.nativebridge.ClassLinker"s;
|
static const auto kClassLinkerClassName = "io.github.lsposed.lspd.nativebridge.ClassLinker"s;
|
||||||
|
static const auto kBridgeServiceClassName = "io.github.lsposed.lspd.service.BridgeService"s;
|
||||||
static const auto kSandHookClassName = "com.swift.sandhook.SandHook"s;
|
static const auto kSandHookClassName = "com.swift.sandhook.SandHook"s;
|
||||||
static const auto kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall"s;
|
static const auto kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall"s;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace lspd {
|
||||||
jint method_count) {
|
jint method_count) {
|
||||||
|
|
||||||
ScopedLocalRef<jclass> clazz(env,
|
ScopedLocalRef<jclass> clazz(env,
|
||||||
Context::GetInstance()->FindClassFromLoader(env, class_name));
|
Context::GetInstance()->FindClassFromCurrentLoader(env, class_name));
|
||||||
if (clazz.get() == nullptr) {
|
if (clazz.get() == nullptr) {
|
||||||
LOG(FATAL) << "Couldn't find class: " << class_name;
|
LOG(FATAL) << "Couldn't find class: " << class_name;
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
#include "jni/logger.h"
|
#include "jni/logger.h"
|
||||||
#include "jni/native_api.h"
|
#include "jni/native_api.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wunused-value"
|
#pragma clang diagnostic ignored "-Wunused-value"
|
||||||
|
|
@ -66,23 +67,19 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::PreLoadDex(const fs::path &dex_path) {
|
void Context::PreLoadDex(const fs::path &dex_path) {
|
||||||
if (LIKELY(!dexes.empty())) return;
|
if (LIKELY(!dex.empty())) return;
|
||||||
|
|
||||||
std::ifstream is(dex_path, std::ios::binary);
|
std::ifstream is(dex_path, std::ios::binary);
|
||||||
if (!is.good()) {
|
if (!is.good()) {
|
||||||
LOGE("Cannot load path %s", dex_path.c_str());
|
LOGE("Cannot load path %s", dex_path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dexes.emplace_back(std::istreambuf_iterator<char>(is),
|
dex.assign(std::istreambuf_iterator<char>(is),
|
||||||
std::istreambuf_iterator<char>());
|
std::istreambuf_iterator<char>());
|
||||||
LOGI("Loaded %s with size %zu", dex_path.c_str(), dexes.back().size());
|
LOGI("Loaded %s with size %zu", dex_path.c_str(), dex.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::InjectDexAndInit(JNIEnv *env) {
|
void Context::LoadDex(JNIEnv *env) {
|
||||||
if (LIKELY(initialized_)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
||||||
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
||||||
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
||||||
|
|
@ -93,17 +90,12 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
jclass in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
jclass in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
||||||
jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||||
"([Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||||
jclass byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
jclass byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
||||||
auto buffer_array = env->NewObjectArray(dexes.size(), byte_buffer_class, nullptr);
|
auto dex_buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
||||||
for (size_t i = 0; i != dexes.size(); ++i) {
|
|
||||||
auto &dex = dexes.at(i);
|
|
||||||
auto buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
|
||||||
dex.size());
|
dex.size());
|
||||||
env->SetObjectArrayElement(buffer_array, i, buffer);
|
|
||||||
}
|
|
||||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||||
buffer_array, sys_classloader);
|
dex_buffer, sys_classloader);
|
||||||
env->DeleteLocalRef(classloader);
|
env->DeleteLocalRef(classloader);
|
||||||
env->DeleteLocalRef(sys_classloader);
|
env->DeleteLocalRef(sys_classloader);
|
||||||
env->DeleteLocalRef(in_memory_classloader);
|
env->DeleteLocalRef(in_memory_classloader);
|
||||||
|
|
@ -118,10 +110,15 @@ namespace lspd {
|
||||||
|
|
||||||
env->DeleteLocalRef(my_cl);
|
env->DeleteLocalRef(my_cl);
|
||||||
|
|
||||||
// initialize pending methods related
|
|
||||||
env->GetJavaVM(&vm_);
|
env->GetJavaVM(&vm_);
|
||||||
|
|
||||||
|
// Make sure config manager is always working
|
||||||
|
RegisterConfigManagerMethods(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::Init(JNIEnv *env) {
|
||||||
class_linker_class_ = (jclass) env->NewGlobalRef(
|
class_linker_class_ = (jclass) env->NewGlobalRef(
|
||||||
FindClassFromLoader(env, kClassLinkerClassName));
|
FindClassFromCurrentLoader(env, kClassLinkerClassName));
|
||||||
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
||||||
"onPostFixupStaticTrampolines",
|
"onPostFixupStaticTrampolines",
|
||||||
"(Ljava/lang/Class;)V");
|
"(Ljava/lang/Class;)V");
|
||||||
|
|
@ -131,7 +128,6 @@ namespace lspd {
|
||||||
|
|
||||||
RegisterLogger(env);
|
RegisterLogger(env);
|
||||||
RegisterEdxpResourcesHook(env);
|
RegisterEdxpResourcesHook(env);
|
||||||
RegisterConfigManagerMethods(env);
|
|
||||||
RegisterArtClassLinker(env);
|
RegisterArtClassLinker(env);
|
||||||
RegisterEdxpYahfa(env);
|
RegisterEdxpYahfa(env);
|
||||||
RegisterPendingHooks(env);
|
RegisterPendingHooks(env);
|
||||||
|
|
@ -140,13 +136,12 @@ namespace lspd {
|
||||||
variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
|
variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
|
||||||
LOGI("LSP Variant: %d", variant_);
|
LOGI("LSP Variant: %d", variant_);
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
|
|
||||||
if (variant_ == SANDHOOK) {
|
if (variant_ == SANDHOOK) {
|
||||||
//for SandHook variant
|
//for SandHook variant
|
||||||
ScopedLocalRef sandhook_class(env, FindClassFromLoader(env, kSandHookClassName));
|
ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName));
|
||||||
ScopedLocalRef nevercall_class(env,
|
ScopedLocalRef nevercall_class(env,
|
||||||
FindClassFromLoader(env, kSandHookNeverCallClassName));
|
FindClassFromCurrentLoader(env,
|
||||||
|
kSandHookNeverCallClassName));
|
||||||
if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast
|
if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -158,9 +153,10 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass
|
jclass
|
||||||
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) {
|
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) {
|
||||||
jclass clz = JNI_GetObjectClass(env, class_loader);
|
if (class_loader == nullptr) return nullptr;
|
||||||
jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
static jclass clz = JNI_FindClass(env, "dalvik/system/DexClassLoader");
|
||||||
|
static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
||||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
jclass ret = nullptr;
|
jclass ret = nullptr;
|
||||||
if (!mid) {
|
if (!mid) {
|
||||||
|
|
@ -168,22 +164,17 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
if (LIKELY(mid)) {
|
if (LIKELY(mid)) {
|
||||||
jobject target = JNI_CallObjectMethod(env, class_loader, mid,
|
jobject target = JNI_CallObjectMethod(env, class_loader, mid,
|
||||||
env->NewStringUTF(class_name));
|
env->NewStringUTF(class_name.data()));
|
||||||
if (target) {
|
if (target) {
|
||||||
return (jclass) target;
|
return (jclass) target;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGE("No loadClass/findClass method found");
|
LOGE("No loadClass/findClass method found");
|
||||||
}
|
}
|
||||||
LOGE("Class %s not found", class_name);
|
LOGE("Class %s not found", class_name.data());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void Context::PrepareJavaEnv(JNIEnv *env) {
|
|
||||||
InjectDexAndInit(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Context::FindAndCall(JNIEnv *env, const char *method_name,
|
inline void Context::FindAndCall(JNIEnv *env, const char *method_name,
|
||||||
const char *method_sig, ...) const {
|
const char *method_sig, ...) const {
|
||||||
if (UNLIKELY(!entry_class_)) {
|
if (UNLIKELY(!entry_class_)) {
|
||||||
|
|
@ -222,75 +213,10 @@ namespace lspd {
|
||||||
skip_ = true;
|
skip_ = true;
|
||||||
LOGD("skip injecting into android because no module hooks it");
|
LOGD("skip injecting into android because no module hooks it");
|
||||||
}
|
}
|
||||||
if (!skip_) {
|
PreLoadDex(ConfigManager::GetInjectDexPath());
|
||||||
PreLoadDex(ConfigManager::GetInjectDexPath());
|
|
||||||
}
|
|
||||||
ConfigManager::GetInstance()->EnsurePermission("android", 1000);
|
ConfigManager::GetInstance()->EnsurePermission("android", 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::RegisterEdxpService(JNIEnv *env) {
|
|
||||||
auto path = ConfigManager::GetFrameworkPath("lspd.dex");
|
|
||||||
std::ifstream is(path, std::ios::binary);
|
|
||||||
if (!is.good()) {
|
|
||||||
LOGE("Cannot load path %s", path.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::vector<unsigned char> dex{std::istreambuf_iterator<char>(is),
|
|
||||||
std::istreambuf_iterator<char>()};
|
|
||||||
LOGI("Loaded %s with size %zu", path.c_str(), dex.size());
|
|
||||||
|
|
||||||
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
|
||||||
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
|
||||||
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
|
||||||
jobject sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid);
|
|
||||||
|
|
||||||
if (UNLIKELY(!sys_classloader)) {
|
|
||||||
LOGE("getSystemClassLoader failed!!!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// load dex
|
|
||||||
jobject bufferDex = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
|
||||||
dex.size());
|
|
||||||
|
|
||||||
jclass in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
|
||||||
jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
|
||||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
|
||||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader,
|
|
||||||
initMid,
|
|
||||||
bufferDex,
|
|
||||||
sys_classloader);
|
|
||||||
|
|
||||||
env->DeleteLocalRef(classloader);
|
|
||||||
env->DeleteLocalRef(sys_classloader);
|
|
||||||
env->DeleteLocalRef(in_memory_classloader);
|
|
||||||
|
|
||||||
if (UNLIKELY(my_cl == nullptr)) {
|
|
||||||
LOGE("InMemoryDexClassLoader creation failed!!!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto service_class = (jclass) env->NewGlobalRef(
|
|
||||||
FindClassFromLoader(env, my_cl, "io.github.lsposed.lspd.service.ServiceProxy"));
|
|
||||||
if (LIKELY(service_class)) {
|
|
||||||
jfieldID path_fid = JNI_GetStaticFieldID(env, service_class, "CONFIG_PATH",
|
|
||||||
"Ljava/lang/String;");
|
|
||||||
jfieldID pn_fid = JNI_GetStaticFieldID(env, service_class, "INSTALLER_PACKAGE_NAME",
|
|
||||||
"Ljava/lang/String;");
|
|
||||||
if (LIKELY(path_fid) && LIKELY(pn_fid)) {
|
|
||||||
env->SetStaticObjectField(service_class, path_fid, env->NewStringUTF(
|
|
||||||
ConfigManager::GetMiscPath().c_str()));
|
|
||||||
env->SetStaticObjectField(service_class, pn_fid, env->NewStringUTF(
|
|
||||||
ConfigManager::GetInstance()->GetInstallerPackageName().c_str()));
|
|
||||||
jmethodID install_mid = JNI_GetStaticMethodID(env, service_class,
|
|
||||||
"install", "()V");
|
|
||||||
if (LIKELY(install_mid)) {
|
|
||||||
JNI_CallStaticVoidMethod(env, service_class, install_mid);
|
|
||||||
LOGW("Installed LSPosed Service");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
|
Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
|
|
@ -305,14 +231,15 @@ namespace lspd {
|
||||||
munmap(buf, 1);
|
munmap(buf, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LoadDex(env);
|
||||||
if (!skip_) {
|
if (!skip_) {
|
||||||
InstallInlineHooks();
|
InstallInlineHooks();
|
||||||
PrepareJavaEnv(env);
|
Init(env);
|
||||||
// only do work in child since FindAndCall would print log
|
// only do work in child since FindAndCall would print log
|
||||||
FindAndCall(env, "forkSystemServerPost", "(II)V", res,
|
FindAndCall(env, "forkSystemServerPost", "(II)V", res,
|
||||||
variant_);
|
variant_);
|
||||||
}
|
}
|
||||||
RegisterEdxpService(env);
|
InitService(*this, env);
|
||||||
} else {
|
} else {
|
||||||
// in zygote process, res is child zygote pid
|
// in zygote process, res is child zygote pid
|
||||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
||||||
|
|
@ -414,8 +341,9 @@ namespace lspd {
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
const JUTFString process_name(env, nice_name_);
|
const JUTFString process_name(env, nice_name_);
|
||||||
if (!skip_) {
|
if (!skip_) {
|
||||||
|
LoadDex(env);
|
||||||
InstallInlineHooks();
|
InstallInlineHooks();
|
||||||
PrepareJavaEnv(env);
|
Init(env);
|
||||||
LOGD("Done prepare");
|
LOGD("Done prepare");
|
||||||
FindAndCall(env, "forkAndSpecializePost",
|
FindAndCall(env, "forkAndSpecializePost",
|
||||||
"(ILjava/lang/String;Ljava/lang/String;I)V",
|
"(ILjava/lang/String;Ljava/lang/String;I)V",
|
||||||
|
|
@ -434,6 +362,7 @@ namespace lspd {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
|
|
@ -53,8 +53,6 @@ namespace lspd {
|
||||||
|
|
||||||
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
||||||
|
|
||||||
void PrepareJavaEnv(JNIEnv *env);
|
|
||||||
|
|
||||||
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
||||||
|
|
||||||
inline auto *GetJavaVM() const { return vm_; }
|
inline auto *GetJavaVM() const { return vm_; }
|
||||||
|
|
@ -69,13 +67,9 @@ namespace lspd {
|
||||||
|
|
||||||
inline auto GetAppModulesList() const { return app_modules_list_; }
|
inline auto GetAppModulesList() const { return app_modules_list_; }
|
||||||
|
|
||||||
inline jclass FindClassFromLoader(JNIEnv *env, const std::string &className) const {
|
inline jclass FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
||||||
return FindClassFromLoader(env, className.c_str());
|
|
||||||
};
|
|
||||||
|
|
||||||
inline jclass FindClassFromLoader(JNIEnv *env, const char *className) const {
|
|
||||||
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
||||||
}
|
};
|
||||||
|
|
||||||
void OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint uid, jint gid,
|
void OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint uid, jint gid,
|
||||||
jintArray gids, jint runtime_flags, jobjectArray rlimits,
|
jintArray gids, jint runtime_flags, jobjectArray rlimits,
|
||||||
|
|
@ -93,13 +87,10 @@ namespace lspd {
|
||||||
jlong permitted_capabilities,
|
jlong permitted_capabilities,
|
||||||
jlong effective_capabilities);
|
jlong effective_capabilities);
|
||||||
|
|
||||||
inline auto IsInitialized() const { return initialized_; }
|
|
||||||
|
|
||||||
inline auto GetVariant() const { return variant_; };
|
inline auto GetVariant() const { return variant_; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline static std::unique_ptr<Context> instance_;
|
inline static std::unique_ptr<Context> instance_;
|
||||||
bool initialized_ = false;
|
|
||||||
Variant variant_ = NONE;
|
Variant variant_ = NONE;
|
||||||
jobject inject_class_loader_ = nullptr;
|
jobject inject_class_loader_ = nullptr;
|
||||||
jclass entry_class_ = nullptr;
|
jclass entry_class_ = nullptr;
|
||||||
|
|
@ -109,33 +100,29 @@ namespace lspd {
|
||||||
jclass class_linker_class_ = nullptr;
|
jclass class_linker_class_ = nullptr;
|
||||||
jmethodID post_fixup_static_mid_ = nullptr;
|
jmethodID post_fixup_static_mid_ = nullptr;
|
||||||
bool skip_ = false;
|
bool skip_ = false;
|
||||||
std::vector<std::vector<signed char>> dexes;
|
std::vector<signed char> dex{};
|
||||||
std::vector<std::string> app_modules_list_;
|
std::vector<std::string> app_modules_list_{};
|
||||||
|
|
||||||
Context() {}
|
Context() {}
|
||||||
|
|
||||||
void PreLoadDex(const std::filesystem::path &dex_paths);
|
void PreLoadDex(const std::filesystem::path &dex_paths);
|
||||||
|
|
||||||
void InjectDexAndInit(JNIEnv *env);
|
void LoadDex(JNIEnv *env);
|
||||||
|
|
||||||
inline jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
void Init(JNIEnv *env);
|
||||||
const std::string &class_name) const {
|
|
||||||
return FindClassFromLoader(env, class_loader, class_name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static jclass
|
static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||||
FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name);
|
std::string_view class_name);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, bool res,
|
ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, bool res,
|
||||||
const std::function<bool()>& empty_list,
|
const std::function<bool()> &empty_list,
|
||||||
bool is_child_zygote);
|
bool is_child_zygote);
|
||||||
|
|
||||||
static std::tuple<bool, uid_t, std::string> GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name);
|
static std::tuple<bool, uid_t, std::string>
|
||||||
|
GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name);
|
||||||
|
|
||||||
friend std::unique_ptr<Context> std::make_unique<Context>();
|
friend std::unique_ptr<Context> std::make_unique<Context>();
|
||||||
|
|
||||||
static void RegisterEdxpService(JNIEnv *env);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) {
|
jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) {
|
||||||
classXResources = Context::GetInstance()->FindClassFromLoader(env, kXResourcesClassName);
|
classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, kXResourcesClassName);
|
||||||
if (!classXResources) {
|
if (!classXResources) {
|
||||||
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
//
|
||||||
|
// Created by loves on 2/7/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <nativehelper/scoped_local_ref.h>
|
||||||
|
#include <base/object.h>
|
||||||
|
#include <dobby.h>
|
||||||
|
#include "service.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
namespace lspd {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr uint32_t BRIDGE_TRANSACTION_CODE = 1598837584;
|
||||||
|
|
||||||
|
JNINativeInterface native_interface_replace{};
|
||||||
|
jmethodID exec_transact_backup_methodID = nullptr;
|
||||||
|
|
||||||
|
jboolean (*call_boolean_method_va_backup)(JNIEnv *env, jobject obj, jmethodID methodId,
|
||||||
|
va_list args) = nullptr;
|
||||||
|
|
||||||
|
jclass bridge_service_class = nullptr;
|
||||||
|
jmethodID exec_transact_replace_methodID = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool exec_transact_replace(jboolean *res, JNIEnv *env, jobject obj, va_list args) {
|
||||||
|
jint code;
|
||||||
|
|
||||||
|
va_list copy;
|
||||||
|
va_copy(copy, args);
|
||||||
|
code = va_arg(copy, jint);
|
||||||
|
va_end(copy);
|
||||||
|
|
||||||
|
if (UNLIKELY(code == BRIDGE_TRANSACTION_CODE)) {
|
||||||
|
*res = env->CallStaticBooleanMethodV(bridge_service_class,
|
||||||
|
exec_transact_replace_methodID,
|
||||||
|
args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean
|
||||||
|
call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, va_list args) {
|
||||||
|
if (UNLIKELY(methodId == exec_transact_backup_methodID)) {
|
||||||
|
jboolean res = false;
|
||||||
|
if (LIKELY(exec_transact_replace(&res, env, obj, args))) return res;
|
||||||
|
// else fallback to backup
|
||||||
|
}
|
||||||
|
return call_boolean_method_va_backup(env, obj, methodId, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitService(const Context &context, JNIEnv *env) {
|
||||||
|
bridge_service_class = context.FindClassFromCurrentLoader(env, kBridgeServiceClassName);
|
||||||
|
if (!bridge_service_class) {
|
||||||
|
LOGE("server class not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bridge_service_class = (jclass) env->NewGlobalRef(bridge_service_class);
|
||||||
|
exec_transact_replace_methodID = env->GetStaticMethodID(bridge_service_class,
|
||||||
|
"execTransact",
|
||||||
|
"(IJJI)Z");
|
||||||
|
if (!exec_transact_replace_methodID) {
|
||||||
|
LOGE("execTransact class not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedLocalRef<jclass> binderClass(env, env->FindClass("android/os/Binder"));
|
||||||
|
exec_transact_backup_methodID = env->GetMethodID(binderClass.get(), "execTransact",
|
||||||
|
"(IJJI)Z");
|
||||||
|
auto set_table_override = reinterpret_cast<void (*)(
|
||||||
|
JNINativeInterface *)>(DobbySymbolResolver(nullptr,
|
||||||
|
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface"));
|
||||||
|
if (!set_table_override) {
|
||||||
|
LOGE("set table override not found");
|
||||||
|
}
|
||||||
|
memcpy(&native_interface_replace, env->functions, sizeof(JNINativeInterface));
|
||||||
|
|
||||||
|
call_boolean_method_va_backup = env->functions->CallBooleanMethodV;
|
||||||
|
native_interface_replace.CallBooleanMethodV = &call_boolean_method_va_replace;
|
||||||
|
|
||||||
|
if (set_table_override != nullptr) {
|
||||||
|
set_table_override(&native_interface_replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Done InitService");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Created by loves on 2/7/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LSPOSED_SERVICE_H
|
||||||
|
#define LSPOSED_SERVICE_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
namespace lspd {
|
||||||
|
void InitService(const Context & context, JNIEnv *env);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //LSPOSED_SERVICE_H
|
||||||
|
|
@ -544,28 +544,6 @@ public final class XposedBridge {
|
||||||
return invokeOriginalMethodNative(method, methodId, parameterTypes, returnType, thisObject, args);
|
return invokeOriginalMethodNative(method, methodId, parameterTypes, returnType, thisObject, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ static void setObjectClass(Object obj, Class<?> clazz) {
|
|
||||||
if (clazz.isAssignableFrom(obj.getClass())) {
|
|
||||||
throw new IllegalArgumentException("Cannot transfer object from " + obj.getClass() + " to " + clazz);
|
|
||||||
}
|
|
||||||
setObjectClassNative(obj, clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native void setObjectClassNative(Object obj, Class<?> clazz);
|
|
||||||
/*package*/ static native void dumpObjectNative(Object obj);
|
|
||||||
|
|
||||||
/*package*/ static Object cloneToSubclass(Object obj, Class<?> targetClazz) {
|
|
||||||
if (obj == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!obj.getClass().isAssignableFrom(targetClazz))
|
|
||||||
throw new ClassCastException(targetClazz + " doesn't extend " + obj.getClass());
|
|
||||||
|
|
||||||
return cloneToSubclassNative(obj, targetClazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native Object cloneToSubclassNative(Object obj, Class<?> targetClazz);
|
|
||||||
|
|
||||||
private static void removeFinalFlagNative(Class clazz) {
|
private static void removeFinalFlagNative(Class clazz) {
|
||||||
LSPdConfigGlobal.getHookProvider().removeFinalFlagNative(clazz);
|
LSPdConfigGlobal.getHookProvider().removeFinalFlagNative(clazz);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,19 @@
|
||||||
package io.github.lsposed.lspd.core;
|
package io.github.lsposed.lspd.core;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.ddm.DdmHandleAppName;
|
||||||
|
|
||||||
import io.github.lsposed.common.KeepAll;
|
import io.github.lsposed.common.KeepAll;
|
||||||
|
import io.github.lsposed.lspd.service.LSPosedService;
|
||||||
import io.github.lsposed.lspd.util.Utils;
|
import io.github.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static io.github.lsposed.lspd.service.LSPosedService.TAG;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
public class Main implements KeepAll {
|
public class Main implements KeepAll {
|
||||||
private static final AtomicReference<EdxpImpl> lspdImplRef = new AtomicReference<>(null);
|
private static final AtomicReference<EdxpImpl> lspdImplRef = new AtomicReference<>(null);
|
||||||
|
|
@ -105,4 +112,29 @@ public class Main implements KeepAll {
|
||||||
public static synchronized int getEdxpVariant() {
|
public static synchronized int getEdxpVariant() {
|
||||||
return getEdxpImpl().getVariant();
|
return getEdxpImpl().getVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void waitSystemService(String name) {
|
||||||
|
while (ServiceManager.getService(name) == null) {
|
||||||
|
try {
|
||||||
|
Log.i(TAG, "service " + name + " is not started, wait 1s.");
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.i(TAG, Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (String arg : args) {
|
||||||
|
if (arg.equals("--debug")) {
|
||||||
|
DdmHandleAppName.setAppName("lspd", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitSystemService("package");
|
||||||
|
waitSystemService("activity");
|
||||||
|
waitSystemService(Context.USER_SERVICE);
|
||||||
|
waitSystemService(Context.APP_OPS_SERVICE);
|
||||||
|
|
||||||
|
LSPosedService.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import io.github.lsposed.lspd.BuildConfig;
|
||||||
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
||||||
import io.github.lsposed.lspd.core.EdxpImpl;
|
import io.github.lsposed.lspd.core.EdxpImpl;
|
||||||
import io.github.lsposed.lspd.core.Main;
|
import io.github.lsposed.lspd.core.Main;
|
||||||
|
import io.github.lsposed.lspd.service.BridgeService;
|
||||||
import io.github.lsposed.lspd.util.Utils;
|
import io.github.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
public class XposedInstallerHooker {
|
public class XposedInstallerHooker {
|
||||||
|
|
@ -114,6 +115,13 @@ public class XposedInstallerHooker {
|
||||||
return ConfigManager.getMiscPath();
|
return ConfigManager.getMiscPath();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
XposedHelpers.findAndHookMethod("io.github.lsposed.manager.receivers.LSPosedServiceClient", classLoader, "getBinder", new XC_MethodReplacement(){
|
||||||
|
@Override
|
||||||
|
protected Object replaceHookedMethod(MethodHookParam param) {
|
||||||
|
return BridgeService.requireBinder();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Utils.logI("Hooked LSPosed Manager");
|
Utils.logI("Hooked LSPosed Manager");
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Utils.logW("Could not hook LSPosed Manager", t);
|
Utils.logW("Could not hook LSPosed Manager", t);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,291 @@
|
||||||
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
||||||
|
import io.github.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
|
import static io.github.lsposed.lspd.service.LSPosedService.TAG;
|
||||||
|
|
||||||
|
public class BridgeService {
|
||||||
|
private static final int TRANSACTION_CODE = ('_' << 24) | ('L' << 16) | ('S' << 8) | 'P';
|
||||||
|
private static final String DESCRIPTOR = "android.app.IActivityManager";
|
||||||
|
private static final String SERVICE_NAME = "activity";
|
||||||
|
|
||||||
|
private static final int ACTION_SEND_BINDER = 1;
|
||||||
|
private static final int ACTION_GET_BINDER = ACTION_SEND_BINDER + 1;
|
||||||
|
|
||||||
|
private static IBinder serviceBinder = null;
|
||||||
|
private static ILSPosedService service = null;
|
||||||
|
|
||||||
|
private static final IBinder.DeathRecipient BRIDGE_SERVICE_DEATH_RECIPIENT = () -> {
|
||||||
|
Log.i(TAG, "service " + SERVICE_NAME + " is dead. ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field field = ServiceManager.class.getDeclaredField("sServiceManager");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(null, null);
|
||||||
|
|
||||||
|
//noinspection JavaReflectionMemberAccess
|
||||||
|
field = ServiceManager.class.getDeclaredField("sCache");
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object sCache = field.get(null);
|
||||||
|
if (sCache instanceof Map) {
|
||||||
|
//noinspection rawtypes
|
||||||
|
((Map) sCache).clear();
|
||||||
|
}
|
||||||
|
Log.i(TAG, "clear ServiceManager");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.w(TAG, "clear ServiceManager: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToBridge(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final IBinder.DeathRecipient LSPSERVICE_DEATH_RECIPIENT = () -> {
|
||||||
|
serviceBinder = null;
|
||||||
|
service = null;
|
||||||
|
Log.e(TAG, "service is dead");
|
||||||
|
};
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
|
||||||
|
void onSystemServerRestarted();
|
||||||
|
|
||||||
|
void onResponseFromBridgeService(boolean response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Listener listener;
|
||||||
|
|
||||||
|
private static PackageManager pm = null;
|
||||||
|
|
||||||
|
private static void sendToBridge(boolean isRestart) {
|
||||||
|
IBinder bridgeService;
|
||||||
|
do {
|
||||||
|
bridgeService = ServiceManager.getService(SERVICE_NAME);
|
||||||
|
if (bridgeService != null && bridgeService.pingBinder()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "service " + SERVICE_NAME + " is not started, wait 1s.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
//noinspection BusyWait
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.w(TAG, "sleep" + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
if (isRestart && listener != null) {
|
||||||
|
listener.onSystemServerRestarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
bridgeService.linkToDeath(BRIDGE_SERVICE_DEATH_RECIPIENT, 0);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.w(TAG, "linkToDeath " + Log.getStackTraceString(e));
|
||||||
|
sendToBridge(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
boolean res = false;
|
||||||
|
// try at most three times
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
try {
|
||||||
|
data.writeInterfaceToken(DESCRIPTOR);
|
||||||
|
data.writeInt(ACTION_SEND_BINDER);
|
||||||
|
Log.v(TAG, "binder " + serviceBinder.toString());
|
||||||
|
data.writeStrongBinder(serviceBinder);
|
||||||
|
res = bridgeService.transact(TRANSACTION_CODE, data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "send binder " + Log.getStackTraceString(e));
|
||||||
|
} finally {
|
||||||
|
data.recycle();
|
||||||
|
reply.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res) break;
|
||||||
|
|
||||||
|
Log.w(TAG, "no response from bridge, retry in 1s");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onResponseFromBridgeService(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void receiveFromBridge(IBinder binder) {
|
||||||
|
if (binder == null) {
|
||||||
|
Log.e(TAG, "received empty binder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceBinder == null) {
|
||||||
|
PackageReceiver.register();
|
||||||
|
} else {
|
||||||
|
serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceBinder = binder;
|
||||||
|
service = ILSPosedService.Stub.asInterface(serviceBinder);
|
||||||
|
try {
|
||||||
|
serviceBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "binder received");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(LSPosedService service, Listener listener) {
|
||||||
|
BridgeService.listener = listener;
|
||||||
|
BridgeService.service = service;
|
||||||
|
BridgeService.serviceBinder = service.asBinder();
|
||||||
|
sendToBridge(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILSPosedService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PackageManager getPackageManager() {
|
||||||
|
if (pm != null) return pm;
|
||||||
|
ActivityThread activityThread = ActivityThread.currentActivityThread();
|
||||||
|
if (activityThread == null) {
|
||||||
|
Utils.logW("ActivityThread is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Context context = activityThread.getSystemContext();
|
||||||
|
if (context == null) {
|
||||||
|
Utils.logW("context is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
pm = context.getPackageManager();
|
||||||
|
return pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBinder requireBinder() {
|
||||||
|
IBinder binder = ServiceManager.getService(SERVICE_NAME);
|
||||||
|
if (binder == null) return null;
|
||||||
|
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
data.writeInterfaceToken(DESCRIPTOR);
|
||||||
|
data.writeInt(ACTION_GET_BINDER);
|
||||||
|
binder.transact(TRANSACTION_CODE, data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
IBinder received = reply.readStrongBinder();
|
||||||
|
if (received != null) {
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
data.recycle();
|
||||||
|
reply.recycle();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused", "RedundantSuppression"})
|
||||||
|
public static boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) {
|
||||||
|
data.enforceInterface(DESCRIPTOR);
|
||||||
|
|
||||||
|
int action = data.readInt();
|
||||||
|
Log.d(TAG, "onTransact: action=" + action + ", callingUid=" + Binder.getCallingUid() + ", callingPid=" + Binder.getCallingPid());
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case ACTION_SEND_BINDER: {
|
||||||
|
if (Binder.getCallingUid() == 0) {
|
||||||
|
receiveFromBridge(data.readStrongBinder());
|
||||||
|
if (reply != null) {
|
||||||
|
reply.writeNoException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ACTION_GET_BINDER: {
|
||||||
|
String InstallerPackageName = ConfigManager.getInstallerPackageName();
|
||||||
|
boolean isInstaller = false;
|
||||||
|
PackageManager pm = getPackageManager();
|
||||||
|
if (pm == null) return false;
|
||||||
|
for (String pkg : pm.getPackagesForUid(Binder.getCallingUid())) {
|
||||||
|
isInstaller = isInstaller || InstallerPackageName.equals(pkg);
|
||||||
|
}
|
||||||
|
if (!isInstaller) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply != null) {
|
||||||
|
reply.writeNoException();
|
||||||
|
Log.d(TAG, "saved binder is " + serviceBinder.toString());
|
||||||
|
reply.writeStrongBinder(serviceBinder);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean execTransact(int code, long dataObj, long replyObj, int flags) {
|
||||||
|
Log.d(TAG, String.valueOf(code));
|
||||||
|
if (code != TRANSACTION_CODE) return false;
|
||||||
|
|
||||||
|
Parcel data = ParcelUtils.fromNativePointer(dataObj);
|
||||||
|
Parcel reply = ParcelUtils.fromNativePointer(replyObj);
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean res = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String descriptor = ParcelUtils.readInterfaceDescriptor(data);
|
||||||
|
data.setDataPosition(0);
|
||||||
|
|
||||||
|
if (descriptor.equals(DESCRIPTOR)) {
|
||||||
|
res = onTransact(code, data, reply, flags);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if ((flags & IBinder.FLAG_ONEWAY) != 0) {
|
||||||
|
Log.w(TAG, "Caught a Exception from the binder stub implementation. " + Log.getStackTraceString(e));
|
||||||
|
} else {
|
||||||
|
reply.setDataPosition(0);
|
||||||
|
reply.writeException(e);
|
||||||
|
}
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
if (data != null) data.recycle();
|
||||||
|
if (reply != null) reply.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
public class LSPosedService extends ILSPosedService.Stub {
|
||||||
|
public static final String TAG = "LSPosedService";
|
||||||
|
|
||||||
|
// call by ourselves
|
||||||
|
public static void start() {
|
||||||
|
Log.i(TAG, "starting server...");
|
||||||
|
|
||||||
|
Looper.prepare();
|
||||||
|
new LSPosedService();
|
||||||
|
Looper.loop();
|
||||||
|
|
||||||
|
Log.i(TAG, "server exited");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LSPosedService() {
|
||||||
|
BridgeService.send(this, new BridgeService.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onSystemServerRestarted() {
|
||||||
|
Log.w(TAG, "system restarted...");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponseFromBridgeService(boolean response) {
|
||||||
|
if (response) {
|
||||||
|
Log.i(TAG, "sent service to bridge");
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "no response from bridge");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder asBinder() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return XposedBridge.getXposedVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,10 +32,13 @@ import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
||||||
import io.github.lsposed.lspd.util.Utils;
|
import io.github.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -50,8 +53,6 @@ import java.util.Scanner;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
|
||||||
import static io.github.lsposed.lspd.service.ServiceProxy.CONFIG_PATH;
|
|
||||||
|
|
||||||
public class PackageReceiver {
|
public class PackageReceiver {
|
||||||
private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() {
|
private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() {
|
||||||
|
|
||||||
|
|
@ -102,7 +103,7 @@ public class PackageReceiver {
|
||||||
private Map<String, String> loadEnabledModules(int uid) {
|
private Map<String, String> loadEnabledModules(int uid) {
|
||||||
HashMap<String, String> result = new HashMap<>();
|
HashMap<String, String> result = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
File enabledModules = new File(CONFIG_PATH, uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
File enabledModules = new File(ConfigManager.getMiscPath(), uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
||||||
if (!enabledModules.exists()) return result;
|
if (!enabledModules.exists()) return result;
|
||||||
Scanner scanner = new Scanner(enabledModules);
|
Scanner scanner = new Scanner(enabledModules);
|
||||||
while (scanner.hasNextLine()) {
|
while (scanner.hasNextLine()) {
|
||||||
|
|
@ -125,8 +126,8 @@ public class PackageReceiver {
|
||||||
if (!enabledModules.containsKey(packageName)) return false;
|
if (!enabledModules.containsKey(packageName)) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File moduleListFile = new File(CONFIG_PATH, uid + "/" + MODULES_LIST_FILENAME);
|
File moduleListFile = new File(ConfigManager.getMiscPath(), uid + "/" + MODULES_LIST_FILENAME);
|
||||||
File enabledModuleListFile = new File(CONFIG_PATH, uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
File enabledModuleListFile = new File(ConfigManager.getMiscPath(), uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
||||||
if (moduleListFile.exists() && !moduleListFile.canWrite()) {
|
if (moduleListFile.exists() && !moduleListFile.canWrite()) {
|
||||||
moduleListFile.delete();
|
moduleListFile.delete();
|
||||||
moduleListFile.createNewFile();
|
moduleListFile.createNewFile();
|
||||||
|
|
@ -144,7 +145,7 @@ public class PackageReceiver {
|
||||||
enabledModulesList.println(module.getKey());
|
enabledModulesList.println(module.getKey());
|
||||||
} else {
|
} else {
|
||||||
Utils.logI(String.format("remove obsolete package %s", packageName));
|
Utils.logI(String.format("remove obsolete package %s", packageName));
|
||||||
File prefsDir = new File(CONFIG_PATH, uid + "/prefs/" + packageName);
|
File prefsDir = new File(ConfigManager.getMiscPath(), uid + "/prefs/" + packageName);
|
||||||
File[] fileList = prefsDir.listFiles();
|
File[] fileList = prefsDir.listFiles();
|
||||||
if (fileList != null) {
|
if (fileList != null) {
|
||||||
for (File childFile : fileList) {
|
for (File childFile : fileList) {
|
||||||
|
|
@ -212,9 +213,9 @@ public class PackageReceiver {
|
||||||
Intent broadCast = new Intent(activated ? MODULE_UPDATED : MODULE_NOT_ACTIVATAED);
|
Intent broadCast = new Intent(activated ? MODULE_UPDATED : MODULE_NOT_ACTIVATAED);
|
||||||
broadCast.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | 0x01000000);
|
broadCast.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | 0x01000000);
|
||||||
broadCast.setData(intent.getData());
|
broadCast.setData(intent.getData());
|
||||||
broadCast.setPackage(ServiceProxy.INSTALLER_PACKAGE_NAME);
|
broadCast.setPackage(ConfigManager.getInstallerPackageName());
|
||||||
XposedHelpers.callMethod(context, "sendBroadcastAsUser", broadCast, userHandle);
|
XposedHelpers.callMethod(context, "sendBroadcastAsUser", broadCast, userHandle);
|
||||||
Utils.logI("broadcast to " + ServiceProxy.INSTALLER_PACKAGE_NAME);
|
Utils.logI("broadcast to " + ConfigManager.getInstallerPackageName());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Utils.logW("send broadcast failed", t);
|
Utils.logW("send broadcast failed", t);
|
||||||
Toast.makeText(context, "LSPosed: Updated " + packageName, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, "LSPosed: Updated " + packageName, Toast.LENGTH_SHORT).show();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class ParcelUtils {
|
||||||
|
|
||||||
|
public static String readInterfaceDescriptor(Parcel parcel) {
|
||||||
|
parcel.readInt();
|
||||||
|
if (Build.VERSION.SDK_INT >= 29) {
|
||||||
|
parcel.readInt();
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
parcel.readInt();
|
||||||
|
}
|
||||||
|
return parcel.readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method obtainMethod;
|
||||||
|
|
||||||
|
public static Parcel fromNativePointer(long ptr) {
|
||||||
|
if (ptr == 0) return null;
|
||||||
|
|
||||||
|
if (obtainMethod == null) {
|
||||||
|
try {
|
||||||
|
//noinspection JavaReflectionMemberAccess
|
||||||
|
obtainMethod = Parcel.class.getDeclaredMethod("obtain", long.class);
|
||||||
|
obtainMethod.setAccessible(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Parcel) obtainMethod.invoke(null, ptr);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.lsposed.lspd.service;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.IServiceManager;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.ServiceManager;
|
|
||||||
|
|
||||||
import io.github.lsposed.common.KeepAll;
|
|
||||||
import io.github.lsposed.lspd.util.Utils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
|
|
||||||
public class ServiceProxy implements InvocationHandler, KeepAll {
|
|
||||||
|
|
||||||
public static String CONFIG_PATH = null;
|
|
||||||
public static String INSTALLER_PACKAGE_NAME = null;
|
|
||||||
|
|
||||||
private static IServiceManager original;
|
|
||||||
|
|
||||||
public synchronized static void install() throws ReflectiveOperationException {
|
|
||||||
if (original != null) return;
|
|
||||||
|
|
||||||
Method method = ServiceManager.class.getDeclaredMethod("getIServiceManager");
|
|
||||||
Field field = ServiceManager.class.getDeclaredField("sServiceManager");
|
|
||||||
|
|
||||||
method.setAccessible(true);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
original = (IServiceManager) method.invoke(null);
|
|
||||||
field.set(null, Proxy.newProxyInstance(
|
|
||||||
ServiceProxy.class.getClassLoader(),
|
|
||||||
new Class[]{IServiceManager.class},
|
|
||||||
new ServiceProxy()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
switch (method.getName()) {
|
|
||||||
case "addService": {
|
|
||||||
if (args.length > 1 && args[0] instanceof String && args[1] instanceof IBinder) {
|
|
||||||
final String name = (String) args[0];
|
|
||||||
final IBinder service = (IBinder) args[1];
|
|
||||||
args[1] = onAddService(name, service);
|
|
||||||
}
|
|
||||||
return method.invoke(original, args);
|
|
||||||
}
|
|
||||||
// case "getService":
|
|
||||||
// if(args.length == 1 && args[0] instanceof String && method.getReturnType() == IBinder.class) {
|
|
||||||
// final String name = (String) args[0];
|
|
||||||
// final IBinder service = (IBinder)method.invoke(original, args);
|
|
||||||
// return onGetService(name, service);
|
|
||||||
// }
|
|
||||||
// return method.invoke(original, args);
|
|
||||||
default:
|
|
||||||
return method.invoke(original, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IBinder onAddService(String name, IBinder service) {
|
|
||||||
if ("activity".equals(name)) {
|
|
||||||
try {
|
|
||||||
Handler handler = new Handler(Looper.getMainLooper());
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
PackageReceiver.register();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Utils.logW("Error in registering package receiver", e);
|
|
||||||
}
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected IBinder onGetService(String name, IBinder service) {
|
|
||||||
// return service;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
@ -170,6 +170,7 @@ extract "${ZIPFILE}" 'module.prop' "${MODPATH}"
|
||||||
extract "${ZIPFILE}" 'system.prop' "${MODPATH}"
|
extract "${ZIPFILE}" 'system.prop' "${MODPATH}"
|
||||||
extract "${ZIPFILE}" 'sepolicy.rule' "${MODPATH}"
|
extract "${ZIPFILE}" 'sepolicy.rule' "${MODPATH}"
|
||||||
extract "${ZIPFILE}" 'post-fs-data.sh' "${MODPATH}"
|
extract "${ZIPFILE}" 'post-fs-data.sh' "${MODPATH}"
|
||||||
|
extract "${ZIPFILE}" 'service.sh' "${MODPATH}"
|
||||||
extract "${ZIPFILE}" 'uninstall.sh' "${MODPATH}"
|
extract "${ZIPFILE}" 'uninstall.sh' "${MODPATH}"
|
||||||
|
|
||||||
extract "${ZIPFILE}" 'system/framework/lspd.dex' "${MODPATH}"
|
extract "${ZIPFILE}" 'system/framework/lspd.dex' "${MODPATH}"
|
||||||
|
|
|
||||||
|
|
@ -165,5 +165,6 @@ if [[ ! -z "${MISC_PATH}" ]]; then
|
||||||
print_log_head "${LOG_PATH}/modules.log"
|
print_log_head "${LOG_PATH}/modules.log"
|
||||||
# start_verbose_log_catcher
|
# start_verbose_log_catcher
|
||||||
start_log_catcher all "LSPosed:V XSharedPreferences:V LSPosed-Bridge:V LSPosedManager:V *:F" true ${LOG_VERBOSE}
|
start_log_catcher all "LSPosed:V XSharedPreferences:V LSPosed-Bridge:V LSPosedManager:V *:F" true ${LOG_VERBOSE}
|
||||||
|
echo 'starting service'
|
||||||
fi
|
fi
|
||||||
rm -f /data/adb/lspd/new_install
|
rm -f /data/adb/lspd/new_install
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,9 @@ MODDIR=${0%/*}
|
||||||
if [[ -f "${MODDIR}/reboot_twice_flag" ]]; then
|
if [[ -f "${MODDIR}/reboot_twice_flag" ]]; then
|
||||||
rm -f "${MODDIR}/reboot_twice_flag"
|
rm -f "${MODDIR}/reboot_twice_flag"
|
||||||
reboot
|
reboot
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
MISC_PATH=$(cat /data/adb/lspd/misc_path)
|
||||||
|
BASE_PATH="/data/misc/$MISC_PATH"
|
||||||
|
|
||||||
|
/system/bin/app_process -Djava.class.path=${BASE_PATH}/framework/lspd.dex /system/bin --nice-name=lspd io.github.lsposed.lspd.core.Main
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package android.ddm;
|
||||||
|
|
||||||
|
public class DdmHandleAppName {
|
||||||
|
public static void setAppName(String name, int userId) {
|
||||||
|
throw new RuntimeException("STUB");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||||
|
ndkVersion androidCompileNdkVersion
|
||||||
|
buildToolsVersion androidBuildToolsVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion androidMinSdkVersion.toInteger()
|
||||||
|
targetSdkVersion androidTargetSdkVersion.toInteger()
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="io.github.lsposed.lspd.service">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
|
interface ILSPosedService {
|
||||||
|
int getVersion() = 1;
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
rootProject.name = "LSPosed"
|
rootProject.name = "LSPosed"
|
||||||
|
include ':service'
|
||||||
include ':core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector'
|
include ':core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue