[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.fastscroll:library:1.1.5'
|
||||
implementation files('libs/WeatherView-2.0.3.aar')
|
||||
compileOnly project(':hiddenapi-stubs')
|
||||
compileOnly project(":hiddenapi-stubs")
|
||||
implementation project(path: ':service')
|
||||
}
|
||||
|
||||
configurations {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import okhttp3.Cache;
|
|||
import okhttp3.OkHttpClient;
|
||||
import rikka.material.app.DayNightDelegate;
|
||||
|
||||
import static io.github.lsposed.manager.receivers.LSPosedServiceClient.testBinder;
|
||||
|
||||
public class App extends Application {
|
||||
public static final String TAG = "LSPosedManager";
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
|
@ -61,6 +63,7 @@ public class App extends Application {
|
|||
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
testBinder();
|
||||
if (!BuildConfig.DEBUG) {
|
||||
try {
|
||||
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 'com.android.tools.build:apksig:4.1.2'
|
||||
implementation project(path: ':sandhook-hooklib')
|
||||
implementation project(path: ':service')
|
||||
compileOnly project(':hiddenapi-stubs')
|
||||
compileOnly project(':key-selector')
|
||||
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 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 kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall"s;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace lspd {
|
|||
jint method_count) {
|
||||
|
||||
ScopedLocalRef<jclass> clazz(env,
|
||||
Context::GetInstance()->FindClassFromLoader(env, class_name));
|
||||
Context::GetInstance()->FindClassFromCurrentLoader(env, class_name));
|
||||
if (clazz.get() == nullptr) {
|
||||
LOG(FATAL) << "Couldn't find class: " << class_name;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "native_hook.h"
|
||||
#include "jni/logger.h"
|
||||
#include "jni/native_api.h"
|
||||
#include "service.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-value"
|
||||
|
|
@ -66,23 +67,19 @@ namespace lspd {
|
|||
}
|
||||
|
||||
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);
|
||||
if (!is.good()) {
|
||||
LOGE("Cannot load path %s", dex_path.c_str());
|
||||
return;
|
||||
}
|
||||
dexes.emplace_back(std::istreambuf_iterator<char>(is),
|
||||
std::istreambuf_iterator<char>());
|
||||
LOGI("Loaded %s with size %zu", dex_path.c_str(), dexes.back().size());
|
||||
dex.assign(std::istreambuf_iterator<char>(is),
|
||||
std::istreambuf_iterator<char>());
|
||||
LOGI("Loaded %s with size %zu", dex_path.c_str(), dex.size());
|
||||
}
|
||||
|
||||
void Context::InjectDexAndInit(JNIEnv *env) {
|
||||
if (LIKELY(initialized_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Context::LoadDex(JNIEnv *env) {
|
||||
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
||||
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
||||
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
|
|
@ -93,17 +90,12 @@ namespace lspd {
|
|||
}
|
||||
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");
|
||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
jclass byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
||||
auto buffer_array = env->NewObjectArray(dexes.size(), byte_buffer_class, nullptr);
|
||||
for (size_t i = 0; i != dexes.size(); ++i) {
|
||||
auto &dex = dexes.at(i);
|
||||
auto buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
||||
auto dex_buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
||||
dex.size());
|
||||
env->SetObjectArrayElement(buffer_array, i, buffer);
|
||||
}
|
||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||
buffer_array, sys_classloader);
|
||||
dex_buffer, sys_classloader);
|
||||
env->DeleteLocalRef(classloader);
|
||||
env->DeleteLocalRef(sys_classloader);
|
||||
env->DeleteLocalRef(in_memory_classloader);
|
||||
|
|
@ -118,10 +110,15 @@ namespace lspd {
|
|||
|
||||
env->DeleteLocalRef(my_cl);
|
||||
|
||||
// initialize pending methods related
|
||||
env->GetJavaVM(&vm_);
|
||||
|
||||
// Make sure config manager is always working
|
||||
RegisterConfigManagerMethods(env);
|
||||
}
|
||||
|
||||
void Context::Init(JNIEnv *env) {
|
||||
class_linker_class_ = (jclass) env->NewGlobalRef(
|
||||
FindClassFromLoader(env, kClassLinkerClassName));
|
||||
FindClassFromCurrentLoader(env, kClassLinkerClassName));
|
||||
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
||||
"onPostFixupStaticTrampolines",
|
||||
"(Ljava/lang/Class;)V");
|
||||
|
|
@ -131,7 +128,6 @@ namespace lspd {
|
|||
|
||||
RegisterLogger(env);
|
||||
RegisterEdxpResourcesHook(env);
|
||||
RegisterConfigManagerMethods(env);
|
||||
RegisterArtClassLinker(env);
|
||||
RegisterEdxpYahfa(env);
|
||||
RegisterPendingHooks(env);
|
||||
|
|
@ -140,13 +136,12 @@ namespace lspd {
|
|||
variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
|
||||
LOGI("LSP Variant: %d", variant_);
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
if (variant_ == SANDHOOK) {
|
||||
//for SandHook variant
|
||||
ScopedLocalRef sandhook_class(env, FindClassFromLoader(env, kSandHookClassName));
|
||||
ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName));
|
||||
ScopedLocalRef nevercall_class(env,
|
||||
FindClassFromLoader(env, kSandHookNeverCallClassName));
|
||||
FindClassFromCurrentLoader(env,
|
||||
kSandHookNeverCallClassName));
|
||||
if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast
|
||||
return;
|
||||
}
|
||||
|
|
@ -158,9 +153,10 @@ namespace lspd {
|
|||
}
|
||||
|
||||
jclass
|
||||
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) {
|
||||
jclass clz = JNI_GetObjectClass(env, class_loader);
|
||||
jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
||||
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) {
|
||||
if (class_loader == nullptr) return nullptr;
|
||||
static jclass clz = JNI_FindClass(env, "dalvik/system/DexClassLoader");
|
||||
static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
jclass ret = nullptr;
|
||||
if (!mid) {
|
||||
|
|
@ -168,22 +164,17 @@ namespace lspd {
|
|||
}
|
||||
if (LIKELY(mid)) {
|
||||
jobject target = JNI_CallObjectMethod(env, class_loader, mid,
|
||||
env->NewStringUTF(class_name));
|
||||
env->NewStringUTF(class_name.data()));
|
||||
if (target) {
|
||||
return (jclass) target;
|
||||
}
|
||||
} else {
|
||||
LOGE("No loadClass/findClass method found");
|
||||
}
|
||||
LOGE("Class %s not found", class_name);
|
||||
LOGE("Class %s not found", class_name.data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
inline void Context::PrepareJavaEnv(JNIEnv *env) {
|
||||
InjectDexAndInit(env);
|
||||
}
|
||||
|
||||
inline void Context::FindAndCall(JNIEnv *env, const char *method_name,
|
||||
const char *method_sig, ...) const {
|
||||
if (UNLIKELY(!entry_class_)) {
|
||||
|
|
@ -222,75 +213,10 @@ namespace lspd {
|
|||
skip_ = true;
|
||||
LOGD("skip injecting into android because no module hooks it");
|
||||
}
|
||||
if (!skip_) {
|
||||
PreLoadDex(ConfigManager::GetInjectDexPath());
|
||||
}
|
||||
PreLoadDex(ConfigManager::GetInjectDexPath());
|
||||
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
|
||||
Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
|
||||
if (res == 0) {
|
||||
|
|
@ -305,14 +231,15 @@ namespace lspd {
|
|||
munmap(buf, 1);
|
||||
}
|
||||
}
|
||||
LoadDex(env);
|
||||
if (!skip_) {
|
||||
InstallInlineHooks();
|
||||
PrepareJavaEnv(env);
|
||||
Init(env);
|
||||
// only do work in child since FindAndCall would print log
|
||||
FindAndCall(env, "forkSystemServerPost", "(II)V", res,
|
||||
variant_);
|
||||
}
|
||||
RegisterEdxpService(env);
|
||||
InitService(*this, env);
|
||||
} else {
|
||||
// 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
|
||||
|
|
@ -414,8 +341,9 @@ namespace lspd {
|
|||
if (res == 0) {
|
||||
const JUTFString process_name(env, nice_name_);
|
||||
if (!skip_) {
|
||||
LoadDex(env);
|
||||
InstallInlineHooks();
|
||||
PrepareJavaEnv(env);
|
||||
Init(env);
|
||||
LOGD("Done prepare");
|
||||
FindAndCall(env, "forkAndSpecializePost",
|
||||
"(ILjava/lang/String;Ljava/lang/String;I)V",
|
||||
|
|
@ -434,6 +362,7 @@ namespace lspd {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
|
@ -53,8 +53,6 @@ namespace lspd {
|
|||
|
||||
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
||||
|
||||
void PrepareJavaEnv(JNIEnv *env);
|
||||
|
||||
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
||||
|
||||
inline auto *GetJavaVM() const { return vm_; }
|
||||
|
|
@ -69,13 +67,9 @@ namespace lspd {
|
|||
|
||||
inline auto GetAppModulesList() const { return app_modules_list_; }
|
||||
|
||||
inline jclass FindClassFromLoader(JNIEnv *env, const std::string &className) const {
|
||||
return FindClassFromLoader(env, className.c_str());
|
||||
};
|
||||
|
||||
inline jclass FindClassFromLoader(JNIEnv *env, const char *className) const {
|
||||
inline jclass FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
||||
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
||||
}
|
||||
};
|
||||
|
||||
void OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint uid, jint gid,
|
||||
jintArray gids, jint runtime_flags, jobjectArray rlimits,
|
||||
|
|
@ -93,13 +87,10 @@ namespace lspd {
|
|||
jlong permitted_capabilities,
|
||||
jlong effective_capabilities);
|
||||
|
||||
inline auto IsInitialized() const { return initialized_; }
|
||||
|
||||
inline auto GetVariant() const { return variant_; };
|
||||
|
||||
private:
|
||||
inline static std::unique_ptr<Context> instance_;
|
||||
bool initialized_ = false;
|
||||
Variant variant_ = NONE;
|
||||
jobject inject_class_loader_ = nullptr;
|
||||
jclass entry_class_ = nullptr;
|
||||
|
|
@ -109,33 +100,29 @@ namespace lspd {
|
|||
jclass class_linker_class_ = nullptr;
|
||||
jmethodID post_fixup_static_mid_ = nullptr;
|
||||
bool skip_ = false;
|
||||
std::vector<std::vector<signed char>> dexes;
|
||||
std::vector<std::string> app_modules_list_;
|
||||
std::vector<signed char> dex{};
|
||||
std::vector<std::string> app_modules_list_{};
|
||||
|
||||
Context() {}
|
||||
|
||||
void PreLoadDex(const std::filesystem::path &dex_paths);
|
||||
|
||||
void InjectDexAndInit(JNIEnv *env);
|
||||
void LoadDex(JNIEnv *env);
|
||||
|
||||
inline jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||
const std::string &class_name) const {
|
||||
return FindClassFromLoader(env, class_loader, class_name.c_str());
|
||||
}
|
||||
void Init(JNIEnv *env);
|
||||
|
||||
static jclass
|
||||
FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name);
|
||||
static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||
std::string_view class_name);
|
||||
|
||||
static bool
|
||||
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);
|
||||
|
||||
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>();
|
||||
|
||||
static void RegisterEdxpService(JNIEnv *env);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ namespace lspd {
|
|||
}
|
||||
|
||||
jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) {
|
||||
classXResources = Context::GetInstance()->FindClassFromLoader(env, kXResourcesClassName);
|
||||
classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, kXResourcesClassName);
|
||||
if (!classXResources) {
|
||||
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
||||
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);
|
||||
}
|
||||
|
||||
/*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) {
|
||||
LSPdConfigGlobal.getHookProvider().removeFinalFlagNative(clazz);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,19 @@
|
|||
package io.github.lsposed.lspd.core;
|
||||
|
||||
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.lspd.service.LSPosedService;
|
||||
import io.github.lsposed.lspd.util.Utils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static io.github.lsposed.lspd.service.LSPosedService.TAG;
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public class Main implements KeepAll {
|
||||
private static final AtomicReference<EdxpImpl> lspdImplRef = new AtomicReference<>(null);
|
||||
|
|
@ -105,4 +112,29 @@ public class Main implements KeepAll {
|
|||
public static synchronized int getEdxpVariant() {
|
||||
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.core.EdxpImpl;
|
||||
import io.github.lsposed.lspd.core.Main;
|
||||
import io.github.lsposed.lspd.service.BridgeService;
|
||||
import io.github.lsposed.lspd.util.Utils;
|
||||
|
||||
public class XposedInstallerHooker {
|
||||
|
|
@ -114,6 +115,13 @@ public class XposedInstallerHooker {
|
|||
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");
|
||||
} catch (Throwable 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.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
||||
import io.github.lsposed.lspd.util.Utils;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -50,8 +53,6 @@ import java.util.Scanner;
|
|||
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
|
||||
import static io.github.lsposed.lspd.service.ServiceProxy.CONFIG_PATH;
|
||||
|
||||
public class PackageReceiver {
|
||||
private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() {
|
||||
|
||||
|
|
@ -102,7 +103,7 @@ public class PackageReceiver {
|
|||
private Map<String, String> loadEnabledModules(int uid) {
|
||||
HashMap<String, String> result = new HashMap<>();
|
||||
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;
|
||||
Scanner scanner = new Scanner(enabledModules);
|
||||
while (scanner.hasNextLine()) {
|
||||
|
|
@ -125,8 +126,8 @@ public class PackageReceiver {
|
|||
if (!enabledModules.containsKey(packageName)) return false;
|
||||
|
||||
try {
|
||||
File moduleListFile = new File(CONFIG_PATH, uid + "/" + MODULES_LIST_FILENAME);
|
||||
File enabledModuleListFile = new File(CONFIG_PATH, uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
||||
File moduleListFile = new File(ConfigManager.getMiscPath(), uid + "/" + MODULES_LIST_FILENAME);
|
||||
File enabledModuleListFile = new File(ConfigManager.getMiscPath(), uid + "/" + ENABLED_MODULES_LIST_FILENAME);
|
||||
if (moduleListFile.exists() && !moduleListFile.canWrite()) {
|
||||
moduleListFile.delete();
|
||||
moduleListFile.createNewFile();
|
||||
|
|
@ -144,7 +145,7 @@ public class PackageReceiver {
|
|||
enabledModulesList.println(module.getKey());
|
||||
} else {
|
||||
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();
|
||||
if (fileList != null) {
|
||||
for (File childFile : fileList) {
|
||||
|
|
@ -212,9 +213,9 @@ public class PackageReceiver {
|
|||
Intent broadCast = new Intent(activated ? MODULE_UPDATED : MODULE_NOT_ACTIVATAED);
|
||||
broadCast.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | 0x01000000);
|
||||
broadCast.setData(intent.getData());
|
||||
broadCast.setPackage(ServiceProxy.INSTALLER_PACKAGE_NAME);
|
||||
broadCast.setPackage(ConfigManager.getInstallerPackageName());
|
||||
XposedHelpers.callMethod(context, "sendBroadcastAsUser", broadCast, userHandle);
|
||||
Utils.logI("broadcast to " + ServiceProxy.INSTALLER_PACKAGE_NAME);
|
||||
Utils.logI("broadcast to " + ConfigManager.getInstallerPackageName());
|
||||
} catch (Throwable t) {
|
||||
Utils.logW("send broadcast failed", t);
|
||||
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}" 'sepolicy.rule' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'post-fs-data.sh' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'service.sh' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'uninstall.sh' "${MODPATH}"
|
||||
|
||||
extract "${ZIPFILE}" 'system/framework/lspd.dex' "${MODPATH}"
|
||||
|
|
|
|||
|
|
@ -165,5 +165,6 @@ if [[ ! -z "${MISC_PATH}" ]]; then
|
|||
print_log_head "${LOG_PATH}/modules.log"
|
||||
# start_verbose_log_catcher
|
||||
start_log_catcher all "LSPosed:V XSharedPreferences:V LSPosed-Bridge:V LSPosedManager:V *:F" true ${LOG_VERBOSE}
|
||||
echo 'starting service'
|
||||
fi
|
||||
rm -f /data/adb/lspd/new_install
|
||||
|
|
|
|||
|
|
@ -25,4 +25,9 @@ MODDIR=${0%/*}
|
|||
if [[ -f "${MODDIR}/reboot_twice_flag" ]]; then
|
||||
rm -f "${MODDIR}/reboot_twice_flag"
|
||||
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"
|
||||
include ':service'
|
||||
include ':core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector'
|
||||
|
|
|
|||
Loading…
Reference in New Issue