Add EdXposed Service to update module path
This commit is contained in:
parent
fcd4b15496
commit
3389af6edc
|
|
@ -130,6 +130,7 @@ afterEvaluate {
|
|||
dependsOn cleanTemplate
|
||||
dependsOn tasks.getByPath(":dexmaker:copyDex${variantCapped}")
|
||||
dependsOn tasks.getByPath(":dalvikdx:copyDex${variantCapped}")
|
||||
dependsOn tasks.getByPath(":edxp-service:copyDex${variantCapped}")
|
||||
dependsOn tasks.getByPath(":edxp-${backendLowered}:copyDex${variantCapped}")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ namespace edxp {
|
|||
scope.emplace(std::move(app_pkg_name));
|
||||
}
|
||||
scope.insert(module_pkg_name); // Always add module itself
|
||||
if (module_pkg_name == installer_pkg_name_) scope.erase("android");
|
||||
LOGI("scope of %s is:\n%s", module_pkg_name.c_str(), ([&scope = scope]() {
|
||||
std::ostringstream join;
|
||||
std::copy(scope.begin(), scope.end(),
|
||||
|
|
@ -296,7 +297,8 @@ namespace edxp {
|
|||
fs::perms::others_exec);
|
||||
path_chown(prefs_path, uid, 0);
|
||||
}
|
||||
if (pkg_name == installer_pkg_name_ || pkg_name == kPrimaryInstallerPkgName) {
|
||||
if (pkg_name == installer_pkg_name_ || pkg_name == kPrimaryInstallerPkgName ||
|
||||
pkg_name == "android") {
|
||||
auto conf_path = GetConfigPath();
|
||||
if (!path_exists<true>(conf_path)) {
|
||||
fs::create_directories(conf_path);
|
||||
|
|
@ -307,11 +309,14 @@ namespace edxp {
|
|||
}
|
||||
fs::permissions(conf_path, fs::perms::owner_all | fs::perms::group_all);
|
||||
fs::permissions(log_path, fs::perms::owner_all | fs::perms::group_all);
|
||||
if (const auto &[r_uid, r_gid] = path_own(conf_path); r_uid != uid) {
|
||||
path_chown(conf_path, uid, 0, true);
|
||||
if (pkg_name == "android") uid = -1;
|
||||
if (const auto &[r_uid, r_gid] = path_own(conf_path);
|
||||
(uid != -1 && r_uid != uid) || r_gid != 1000u) {
|
||||
path_chown(conf_path, uid, 1000u, true);
|
||||
}
|
||||
if (const auto &[r_uid, r_gid] = path_own(log_path); r_uid != uid) {
|
||||
path_chown(log_path, uid, 0, true);
|
||||
if (const auto &[r_uid, r_gid] = path_own(log_path);
|
||||
(uid != -1 && r_uid != uid) || r_gid != 1000u) {
|
||||
path_chown(log_path, uid, 1000u, true);
|
||||
}
|
||||
|
||||
if (pkg_name == kPrimaryInstallerPkgName) {
|
||||
|
|
@ -330,7 +335,7 @@ namespace edxp {
|
|||
}
|
||||
}
|
||||
|
||||
void ConfigManager::Init(){
|
||||
void ConfigManager::Init() {
|
||||
fs::path misc_path("/data/adb/edxp/misc_path");
|
||||
try {
|
||||
RirudSocket rirud_socket{};
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ namespace edxp {
|
|||
|
||||
inline const auto &GetDataPathPrefix() const { return data_path_prefix_; }
|
||||
|
||||
inline static const auto &GetMiscPath() {return misc_path_;}
|
||||
|
||||
inline static auto GetFrameworkPath(const std::string &suffix = {}) {
|
||||
return misc_path_ / "framework" / suffix;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ namespace edxp {
|
|||
std::ifstream is(path, std::ios::binary);
|
||||
if (!is.good()) {
|
||||
LOGE("Cannot load path %s", path.c_str());
|
||||
continue;
|
||||
}
|
||||
dexes.emplace_back(std::istreambuf_iterator<char>(is),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
|
@ -88,19 +89,14 @@ namespace edxp {
|
|||
jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||
"([Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
jclass byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
||||
jmethodID byte_buffer_wrap = JNI_GetStaticMethodID(env, byte_buffer_class, "wrap",
|
||||
"([B)Ljava/nio/ByteBuffer;");
|
||||
auto buffer_array = env->NewObjectArray(dexes.size(), byte_buffer_class, nullptr);
|
||||
for (size_t i = 0; i != dexes.size(); ++i) {
|
||||
const auto dex = dexes.at(i);
|
||||
auto byte_array = env->NewByteArray(dex.size());
|
||||
env->SetByteArrayRegion(byte_array, 0, dex.size(),
|
||||
dex.data());
|
||||
auto buffer = JNI_CallStaticObjectMethod(env, byte_buffer_class, byte_buffer_wrap,
|
||||
byte_array);
|
||||
auto &dex = dexes.at(i);
|
||||
auto buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
||||
dex.size());
|
||||
env->SetObjectArrayElement(buffer_array, i, buffer);
|
||||
}
|
||||
jobject my_cl = env->NewObject(in_memory_classloader, initMid,
|
||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||
buffer_array, sys_classloader);
|
||||
env->DeleteLocalRef(classloader);
|
||||
env->DeleteLocalRef(sys_classloader);
|
||||
|
|
@ -240,8 +236,67 @@ namespace edxp {
|
|||
if (!skip_) {
|
||||
PreLoadDex(ConfigManager::GetInjectDexPaths());
|
||||
}
|
||||
ConfigManager::GetInstance()->EnsurePermission("android", 1000);
|
||||
}
|
||||
|
||||
void Context::RegisterEdxpService(JNIEnv *env) {
|
||||
auto path = ConfigManager::GetFrameworkPath("edservice.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>()};
|
||||
LOGD("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, "com.elderdrivers.riru.edxp.service.ServiceProxy"));
|
||||
if (LIKELY(service_class)) {
|
||||
jfieldID path_fid = JNI_GetStaticFieldID(env, service_class, "CONFIG_PATH",
|
||||
"Ljava/lang/String;");
|
||||
if (LIKELY(path_fid)) {
|
||||
env->SetStaticObjectField(service_class, path_fid, env->NewStringUTF(
|
||||
ConfigManager::GetMiscPath().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 EdXposed Service");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
|
||||
|
|
@ -262,11 +317,8 @@ namespace edxp {
|
|||
PrepareJavaEnv(env);
|
||||
// only do work in child since FindAndCall would print log
|
||||
FindAndCall(env, "forkSystemServerPost", "(I)V", res);
|
||||
} else {
|
||||
[[maybe_unused]] auto config_managers = ConfigManager::ReleaseInstances();
|
||||
auto context = Context::ReleaseInstance();
|
||||
LOGD("skipped android");
|
||||
}
|
||||
RegisterEdxpService(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
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ namespace edxp {
|
|||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ extract "${ZIPFILE}" 'uninstall.sh' "${MODPATH}"
|
|||
extract "${ZIPFILE}" 'system/framework/edconfig.jar' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'system/framework/eddalvikdx.dex' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'system/framework/eddexmaker.dex' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'system/framework/edservice.dex' "${MODPATH}"
|
||||
extract "${ZIPFILE}" 'system/framework/edxp.dex' "${MODPATH}"
|
||||
|
||||
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ sepolicy() {
|
|||
#fi
|
||||
|
||||
DEFAULT_BASE_PATH="${PATH_PREFIX}${EDXP_MANAGER}"
|
||||
BASE_PATH="/data/misc/$(cat /data/adb/edxp/misc_path)/0"
|
||||
BASE_PATH="/data/misc/$(cat /data/adb/edxp/misc_path)"
|
||||
|
||||
LOG_PATH="${BASE_PATH}/log"
|
||||
CONF_PATH="${BASE_PATH}/conf"
|
||||
LOG_PATH="${BASE_PATH}/0/log"
|
||||
CONF_PATH="${BASE_PATH}/0/conf"
|
||||
DISABLE_VERBOSE_LOG_FILE="${CONF_PATH}/disable_verbose_log"
|
||||
LOG_VERBOSE=true
|
||||
OLD_PATH=${PATH}
|
||||
|
|
@ -164,5 +164,6 @@ fi
|
|||
|
||||
chcon -R u:object_r:system_file:s0 "${MODDIR}"
|
||||
chcon -R ${PATH_CONTEXT} "${LOG_PATH}"
|
||||
chcon -R u:object_r:magisk_file:s0 $BASE_PATH
|
||||
chown -R ${PATH_OWNER} "${LOG_PATH}"
|
||||
chmod -R 666 "${LOG_PATH}"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
/build
|
||||
/template_override/system/framework
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
sourceCompatibility = "7"
|
||||
targetCompatibility = "7"
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.elderdrivers.riru.edxp.yahfa"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
multiDexEnabled false
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
ndkVersion androidCompileNdkVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':hiddenapi-stubs')
|
||||
implementation project(':edxp-common')
|
||||
}
|
||||
|
||||
|
||||
preBuild.doLast {
|
||||
def imlFile = file(project.name + ".iml")
|
||||
try {
|
||||
def parsedXml = (new groovy.util.XmlParser()).parse(imlFile)
|
||||
def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
|
||||
parsedXml.component[1].remove(jdkNode)
|
||||
def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
|
||||
new groovy.util.Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
|
||||
groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
|
||||
} catch (FileNotFoundException e) {
|
||||
// nop, iml not found
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xbootclasspath/p:${hiddenApiStubJarFilePath}"
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
|
||||
def variantNameCapped = variant.name.capitalize()
|
||||
def variantNameLowered = variant.name.toLowerCase()
|
||||
|
||||
task("copyDex${variantNameCapped}", type: Copy) {
|
||||
dependsOn "assemble${variantNameCapped}"
|
||||
dependsOn tasks.getByPath(":edxp-common:copyCommonProperties")
|
||||
def dexOutPath = variant.name.contains("release") ?
|
||||
"${buildDir}/intermediates/dex/${variantNameLowered}/minify${variantNameCapped}WithR8" :
|
||||
"${buildDir}/intermediates/dex/${variantNameLowered}/mergeDex${variantNameCapped}"
|
||||
from (dexOutPath){
|
||||
rename("classes.dex", "edservice.dex")
|
||||
}
|
||||
destinationDir file(templateRootPath + "system/framework/")
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# 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
|
||||
|
||||
-dontoptimize
|
||||
-dontobfuscate
|
||||
|
||||
-keep interface com.elderdrivers.riru.common.KeepAll
|
||||
-keep interface com.elderdrivers.riru.common.KeepMembers
|
||||
|
||||
-keep class * implements com.elderdrivers.riru.common.KeepAll { *; }
|
||||
-keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; }
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="com.elderdrivers.riru.edxp.service" />
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
package com.elderdrivers.riru.edxp.service;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
|
||||
import static com.elderdrivers.riru.edxp.service.ServiceProxy.CONFIG_PATH;
|
||||
|
||||
public class PackageReceiver {
|
||||
private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() {
|
||||
|
||||
private PackageManager pm = null;
|
||||
|
||||
private final String MODULES_LIST_FILENAME = "conf/modules.list";
|
||||
private final String ENABLED_MODULES_LIST_FILENAME = "conf/enabled_modules.list";
|
||||
|
||||
private String getPackageName(Intent intent) {
|
||||
Uri uri = intent.getData();
|
||||
return (uri != null) ? uri.getSchemeSpecificPart() : null;
|
||||
}
|
||||
|
||||
private void getPackageManager() {
|
||||
if (pm != null) return;
|
||||
ActivityThread activityThread = ActivityThread.currentActivityThread();
|
||||
if (activityThread == null) {
|
||||
Utils.logW("ActivityThread is null");
|
||||
return;
|
||||
}
|
||||
Context context = activityThread.getSystemContext();
|
||||
if (context == null) {
|
||||
Utils.logW("context is null");
|
||||
return;
|
||||
}
|
||||
pm = context.getPackageManager();
|
||||
}
|
||||
|
||||
private boolean isXposedModule(ApplicationInfo app) {
|
||||
return app != null && app.enabled && app.metaData != null && app.metaData.containsKey("xposedmodule");
|
||||
}
|
||||
|
||||
private PackageInfo getPackageInfo(String packageName) {
|
||||
getPackageManager();
|
||||
if (pm == null) {
|
||||
Utils.logW("PM is null");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return pm.getPackageInfo(packageName, PackageManager.GET_META_DATA);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (!enabledModules.exists()) return result;
|
||||
Scanner scanner = new Scanner(enabledModules);
|
||||
if (scanner.hasNextLine()) {
|
||||
String packageName = scanner.nextLine();
|
||||
PackageInfo info = getPackageInfo(packageName);
|
||||
if (info != null && isXposedModule(info.applicationInfo)) {
|
||||
result.put(packageName, info.applicationInfo.sourceDir);
|
||||
} else {
|
||||
Utils.logW(String.format("remove obsolete package %s", packageName));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Utils.logE("Unable to read enabled modules", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateModuleList(int uid, String packageName) {
|
||||
Map<String, String> enabledModules = loadEnabledModules(uid);
|
||||
|
||||
if (!enabledModules.containsKey(packageName)) return;
|
||||
|
||||
try {
|
||||
File moduleListFile = new File(CONFIG_PATH, uid + "/" + MODULES_LIST_FILENAME);
|
||||
moduleListFile.createNewFile();
|
||||
PrintWriter modulesList = new PrintWriter(moduleListFile);
|
||||
PrintWriter enabledModulesList = new PrintWriter(new File(CONFIG_PATH, uid + "/" + ENABLED_MODULES_LIST_FILENAME));
|
||||
for (Map.Entry<String, String> module : enabledModules.entrySet()) {
|
||||
modulesList.println(module.getValue());
|
||||
enabledModulesList.println(module.getKey());
|
||||
}
|
||||
modulesList.close();
|
||||
enabledModulesList.close();
|
||||
} catch (Throwable e) {
|
||||
Utils.logE("Fail to update module list", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Objects.requireNonNull(intent.getAction()).equals(Intent.ACTION_PACKAGE_REMOVED) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))
|
||||
// Ignore existing packages being removed in order to be updated
|
||||
return;
|
||||
String packageName = getPackageName(intent);
|
||||
if (packageName == null)
|
||||
return;
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) {
|
||||
// make sure that the change is for the complete package, not only a
|
||||
// component
|
||||
String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
|
||||
if (components != null) {
|
||||
boolean isForPackage = false;
|
||||
for (String component : components) {
|
||||
if (packageName.equals(component)) {
|
||||
isForPackage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isForPackage)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PackageInfo pkgInfo = getPackageInfo(packageName);
|
||||
|
||||
if (pkgInfo != null && !isXposedModule(pkgInfo.applicationInfo)) return;
|
||||
|
||||
try {
|
||||
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
@SuppressLint("DiscouragedPrivateApi")
|
||||
Method m = UserManager.class.getDeclaredMethod("getUsers");
|
||||
m.setAccessible(true);
|
||||
for (Object uh : (List<Object>) m.invoke(um)) {
|
||||
int uid = (int) uh.getClass().getDeclaredField("id").get(uh);
|
||||
Utils.logI("updating uid: " + uid);
|
||||
updateModuleList(uid, packageName);
|
||||
}
|
||||
Toast.makeText(context, "EdXposed: Updated " + packageName, Toast.LENGTH_SHORT).show();
|
||||
} catch (Throwable e) {
|
||||
Utils.logW("update failed", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static void register() {
|
||||
ActivityThread activityThread = ActivityThread.currentActivityThread();
|
||||
if (activityThread == null) {
|
||||
Utils.logW("ActivityThread is null");
|
||||
return;
|
||||
}
|
||||
Context context = activityThread.getSystemContext();
|
||||
if (context == null) {
|
||||
Utils.logW("context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
UserHandle userHandleAll;
|
||||
try {
|
||||
//noinspection JavaReflectionMemberAccess
|
||||
Field field = UserHandle.class.getDeclaredField("ALL");
|
||||
userHandleAll = (UserHandle) field.get(null);
|
||||
} catch (Throwable e) {
|
||||
Utils.logW("UserHandle.ALL", e);
|
||||
return;
|
||||
}
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intentFilter.addDataScheme("package");
|
||||
|
||||
HandlerThread thread = new HandlerThread("edxp-PackageReceiver");
|
||||
thread.start();
|
||||
Handler handler = new Handler(thread.getLooper());
|
||||
|
||||
try {
|
||||
@SuppressLint("DiscouragedPrivateApi")
|
||||
Method method = Context.class.getDeclaredMethod("registerReceiverAsUser", BroadcastReceiver.class, UserHandle.class, IntentFilter.class, String.class, Handler.class);
|
||||
method.invoke(context, RECEIVER, userHandleAll, intentFilter, null, handler);
|
||||
Utils.logI("registered package receiver");
|
||||
} catch (Throwable e) {
|
||||
Utils.logW("registerReceiver failed", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.elderdrivers.riru.edxp.service;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IServiceManager;
|
||||
import android.os.Looper;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepAll;
|
||||
import com.elderdrivers.riru.edxp.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;
|
||||
|
||||
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;
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package android.os;
|
||||
|
||||
public interface IServiceManager {
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
package android.os;
|
||||
|
||||
public class ServiceManager {
|
||||
private static IServiceManager sServiceManager;
|
||||
|
||||
private static IServiceManager getIServiceManager() {
|
||||
throw new IllegalArgumentException("Stub!");
|
||||
}
|
||||
|
||||
public static IBinder getService(String name) {
|
||||
throw new UnsupportedOperationException("STUB");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook'
|
||||
include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook', ':edxp-service'
|
||||
Loading…
Reference in New Issue