[core] Say hello to binder

This commit is contained in:
LoveSy 2021-02-13 23:51:21 +08:00 committed by tehcneko
parent 572b3e7d27
commit 3f94f49568
29 changed files with 715 additions and 266 deletions

View File

@ -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 {

View File

@ -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) -> {

View File

@ -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);
}
}

View File

@ -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'

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);
}; };
} }

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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

View File

@ -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);
} }

View File

@ -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();
}
} }

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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;
// }
}

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
package android.ddm;
public class DdmHandleAppName {
public static void setAppName(String name, int userId) {
throw new RuntimeException("STUB");
}
}

1
service/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

21
service/build.gradle Normal file
View File

@ -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
}
}

21
service/proguard-rules.pro vendored Normal file
View File

@ -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

View File

@ -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>

View File

@ -0,0 +1,5 @@
package io.github.lsposed.lspd.service;
interface ILSPosedService {
int getVersion() = 1;
}

View File

@ -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'