From a094e36f85d7cc2192612fd7bd10c181e55042c7 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sat, 5 Dec 2020 06:05:46 +0800 Subject: [PATCH] Add EdXposed Service to update module path --- edxp-core/build.gradle | 1 + .../src/main/cpp/main/src/config_manager.cpp | 17 +- .../src/main/cpp/main/src/config_manager.h | 2 + .../src/main/cpp/main/src/edxp_context.cpp | 76 ++++++- .../src/main/cpp/main/src/edxp_context.h | 2 + edxp-core/template_override/customize.sh | 1 + edxp-core/template_override/post-fs-data.sh | 7 +- edxp-service/.gitignore | 2 + edxp-service/build.gradle | 73 ++++++ edxp-service/proguard-rules.pro | 34 +++ edxp-service/src/main/AndroidManifest.xml | 1 + .../riru/edxp/service/PackageReceiver.java | 211 ++++++++++++++++++ .../riru/edxp/service/ServiceProxy.java | 84 +++++++ .../main/java/android/os/IServiceManager.java | 4 + .../main/java/android/os/ServiceManager.java | 6 + settings.gradle | 2 +- 16 files changed, 501 insertions(+), 22 deletions(-) create mode 100644 edxp-service/.gitignore create mode 100644 edxp-service/build.gradle create mode 100644 edxp-service/proguard-rules.pro create mode 100644 edxp-service/src/main/AndroidManifest.xml create mode 100644 edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/PackageReceiver.java create mode 100644 edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/ServiceProxy.java create mode 100644 hiddenapi-stubs/src/main/java/android/os/IServiceManager.java diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index 030b43f0c..d96afc727 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -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}") } diff --git a/edxp-core/src/main/cpp/main/src/config_manager.cpp b/edxp-core/src/main/cpp/main/src/config_manager.cpp index fa95e6fa1..c10a7eb2f 100644 --- a/edxp-core/src/main/cpp/main/src/config_manager.cpp +++ b/edxp-core/src/main/cpp/main/src/config_manager.cpp @@ -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(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{}; diff --git a/edxp-core/src/main/cpp/main/src/config_manager.h b/edxp-core/src/main/cpp/main/src/config_manager.h index 7f09090da..957b1aa9b 100644 --- a/edxp-core/src/main/cpp/main/src/config_manager.h +++ b/edxp-core/src/main/cpp/main/src/config_manager.h @@ -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; } diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.cpp b/edxp-core/src/main/cpp/main/src/edxp_context.cpp index 331e18c5b..a0575b686 100644 --- a/edxp-core/src/main/cpp/main/src/edxp_context.cpp +++ b/edxp-core/src/main/cpp/main/src/edxp_context.cpp @@ -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(is), std::istreambuf_iterator()); @@ -88,16 +89,11 @@ namespace edxp { jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "", "([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(dex.data()), + dex.size()); env->SetObjectArrayElement(buffer_array, i, buffer); } jobject my_cl = env->NewObject(in_memory_classloader, initMid, @@ -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 dex{std::istreambuf_iterator(is), + std::istreambuf_iterator()}; + 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(dex.data()), + dex.size()); + + jclass in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader"); + jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "", + "(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 diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.h b/edxp-core/src/main/cpp/main/src/edxp_context.h index b8adafee8..248270bdb 100644 --- a/edxp-core/src/main/cpp/main/src/edxp_context.h +++ b/edxp-core/src/main/cpp/main/src/edxp_context.h @@ -120,6 +120,8 @@ namespace edxp { static std::tuple GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name); friend std::unique_ptr std::make_unique(); + + static void RegisterEdxpService(JNIEnv *env); }; } diff --git a/edxp-core/template_override/customize.sh b/edxp-core/template_override/customize.sh index 33b886a20..ddeccb303 100644 --- a/edxp-core/template_override/customize.sh +++ b/edxp-core/template_override/customize.sh @@ -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 diff --git a/edxp-core/template_override/post-fs-data.sh b/edxp-core/template_override/post-fs-data.sh index 8b93bfb20..2f66e1f5d 100644 --- a/edxp-core/template_override/post-fs-data.sh +++ b/edxp-core/template_override/post-fs-data.sh @@ -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}" diff --git a/edxp-service/.gitignore b/edxp-service/.gitignore new file mode 100644 index 000000000..3a66a1982 --- /dev/null +++ b/edxp-service/.gitignore @@ -0,0 +1,2 @@ +/build +/template_override/system/framework \ No newline at end of file diff --git a/edxp-service/build.gradle b/edxp-service/build.gradle new file mode 100644 index 000000000..fea020a28 --- /dev/null +++ b/edxp-service/build.gradle @@ -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 } + } + + } +} \ No newline at end of file diff --git a/edxp-service/proguard-rules.pro b/edxp-service/proguard-rules.pro new file mode 100644 index 000000000..784e5ec2b --- /dev/null +++ b/edxp-service/proguard-rules.pro @@ -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 ; +} diff --git a/edxp-service/src/main/AndroidManifest.xml b/edxp-service/src/main/AndroidManifest.xml new file mode 100644 index 000000000..c41a679e8 --- /dev/null +++ b/edxp-service/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/PackageReceiver.java b/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/PackageReceiver.java new file mode 100644 index 000000000..e9908f82f --- /dev/null +++ b/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/PackageReceiver.java @@ -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 loadEnabledModules(int uid) { + HashMap 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 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 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) 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); + } + + } +} diff --git a/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/ServiceProxy.java b/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/ServiceProxy.java new file mode 100644 index 000000000..94b12ce08 --- /dev/null +++ b/edxp-service/src/main/java/com/elderdrivers/riru/edxp/service/ServiceProxy.java @@ -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; +// } +} diff --git a/hiddenapi-stubs/src/main/java/android/os/IServiceManager.java b/hiddenapi-stubs/src/main/java/android/os/IServiceManager.java new file mode 100644 index 000000000..cfcfce016 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/os/IServiceManager.java @@ -0,0 +1,4 @@ +package android.os; + +public interface IServiceManager { +} diff --git a/hiddenapi-stubs/src/main/java/android/os/ServiceManager.java b/hiddenapi-stubs/src/main/java/android/os/ServiceManager.java index 92b6f04de..fbe5a2eea 100644 --- a/hiddenapi-stubs/src/main/java/android/os/ServiceManager.java +++ b/hiddenapi-stubs/src/main/java/android/os/ServiceManager.java @@ -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"); } diff --git a/settings.gradle b/settings.gradle index c96662e12..a4cc45acb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook' \ No newline at end of file +include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook', ':edxp-service' \ No newline at end of file