From 6e115426e41f29bf562eea57af8ea322e86b2d4a Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 19 Feb 2021 22:29:45 +0800 Subject: [PATCH] [core] Store pkg name & user id, judge process name & uid --- .../lspd/service/ILSPApplicationService.aidl | 2 +- .../lsposed/lspd/service/ILSPosedService.aidl | 2 +- core/src/main/cpp/main/src/context.cpp | 2 +- core/src/main/cpp/main/src/service.cpp | 7 +- core/src/main/cpp/main/src/service.h | 3 +- .../de/robv/android/xposed/XposedInit.java | 1 + .../config/LSPApplicationServiceClient.java | 12 +- .../io/github/lsposed/lspd/core/Main.java | 4 +- .../lsposed/lspd/service/BridgeService.java | 71 +++++- .../lsposed/lspd/service/ConfigManager.java | 211 ++++++++++++------ .../lspd/service/LSPApplicationService.java | 8 +- .../lspd/service/LSPManagerService.java | 11 +- .../lsposed/lspd/service/LSPosedService.java | 20 +- .../lsposed/lspd/service/PackageService.java | 42 ++++ .../lsposed/lspd/service/ServiceManager.java | 16 +- .../com/android/server/LocalServices.java | 8 + .../com/android/server/SystemService.java | 4 + .../android/server/SystemServiceManager.java | 7 + .../server/am/ActivityManagerService.java | 14 ++ .../com/android/server/am/ProcessRecord.java | 5 + .../io/github/lsposed/lspd/Application.aidl | 6 + .../lsposed/lspd/ILSPManagerService.aidl | 6 +- 22 files changed, 362 insertions(+), 100 deletions(-) create mode 100644 hiddenapi-stubs/src/main/java/com/android/server/LocalServices.java create mode 100644 hiddenapi-stubs/src/main/java/com/android/server/SystemService.java create mode 100644 hiddenapi-stubs/src/main/java/com/android/server/SystemServiceManager.java create mode 100644 hiddenapi-stubs/src/main/java/com/android/server/am/ActivityManagerService.java create mode 100644 hiddenapi-stubs/src/main/java/com/android/server/am/ProcessRecord.java create mode 100644 manager-service/src/main/aidl/io/github/lsposed/lspd/Application.aidl diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl index 94e49b20..5ca70569 100644 --- a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl @@ -11,7 +11,7 @@ interface ILSPApplicationService { boolean isResourcesHookEnabled() = 5; - String[] getModulesList() = 6; + String[] getModulesList(String processName) = 6; String getPrefsPath(String packageName) = 7; diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl index 13c937fb..a692df13 100644 --- a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl @@ -3,7 +3,7 @@ package io.github.lsposed.lspd.service; import io.github.lsposed.lspd.service.ILSPApplicationService; interface ILSPosedService { - ILSPApplicationService requestApplicationService(int uid, int pid) = 1; + ILSPApplicationService requestApplicationService(int uid, int pid, String processName) = 1; oneway void dispatchPackageChanged(in Intent intent) = 2; } \ No newline at end of file diff --git a/core/src/main/cpp/main/src/context.cpp b/core/src/main/cpp/main/src/context.cpp index e9496ba0..634fba8a 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/cpp/main/src/context.cpp @@ -234,7 +234,7 @@ namespace lspd { void Context::OnNativeForkAndSpecializePost(JNIEnv *env) { const JUTFString process_name(env, nice_name_); - auto binder = skip_? nullptr : Service::instance()->RequestBinder(env); + auto binder = skip_? nullptr : Service::instance()->RequestBinder(env, nice_name_); if (binder) { LoadDex(env); InstallInlineHooks(); diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/cpp/main/src/service.cpp index ff2ad5a6..500ac951 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/cpp/main/src/service.cpp @@ -73,8 +73,8 @@ namespace lspd { write_interface_token_method_ = env->GetMethodID(parcel_class_, "writeInterfaceToken", "(Ljava/lang/String;)V"); write_int_method_ = env->GetMethodID(parcel_class_, "writeInt", "(I)V"); -// writeStringMethod_ = env->GetMethodID(parcel_class_, "writeString", -// "(Ljava/lang/String;)V"); + write_string_method_ = env->GetMethodID(parcel_class_, "writeString", + "(Ljava/lang/String;)V"); read_exception_method_ = env->GetMethodID(parcel_class_, "readException", "()V"); read_strong_binder_method_ = env->GetMethodID(parcel_class_, "readStrongBinder", "()Landroid/os/IBinder;"); @@ -126,7 +126,7 @@ namespace lspd { LOGD("Done InitService"); } - jobject Service::RequestBinder(JNIEnv *env) { + jobject Service::RequestBinder(JNIEnv *env, jstring nice_name) { if (UNLIKELY(!initialized_)) { LOGE("Service not initialized"); return nullptr; @@ -146,6 +146,7 @@ namespace lspd { auto descriptor = env->NewStringUTF(BRIDGE_SERVICE_DESCRIPTOR.data()); JNI_CallVoidMethod(env, data, write_interface_token_method_, descriptor); JNI_CallVoidMethod(env, data, write_int_method_, BRIDGE_ACTION_GET_BINDER); + JNI_CallVoidMethod(env, data, write_string_method_, nice_name); auto res = JNI_CallBooleanMethod(env, bridgeService, transact_method_, BRIDGE_TRANSACTION_CODE, diff --git a/core/src/main/cpp/main/src/service.h b/core/src/main/cpp/main/src/service.h index 94f08e5f..293d43f3 100644 --- a/core/src/main/cpp/main/src/service.h +++ b/core/src/main/cpp/main/src/service.h @@ -31,7 +31,7 @@ namespace lspd { void HookBridge(const Context& context, JNIEnv *env); - jobject RequestBinder(JNIEnv *env); + jobject RequestBinder(JNIEnv *env, jstring nice_name); jobject RequestBinderForSystemServer(JNIEnv *env); @@ -65,6 +65,7 @@ namespace lspd { jmethodID recycleMethod_ = nullptr; jmethodID write_interface_token_method_ = nullptr; jmethodID write_int_method_ = nullptr; + jmethodID write_string_method_ = nullptr; jmethodID read_exception_method_ = nullptr; jmethodID read_strong_binder_method_ = nullptr; diff --git a/core/src/main/java/de/robv/android/xposed/XposedInit.java b/core/src/main/java/de/robv/android/xposed/XposedInit.java index 1d23d9cb..061f75d9 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -353,6 +353,7 @@ public final class XposedInit { topClassLoader = parent; } + // TODO: process name String[] moduleList = serviceClient.getModulesList(); ArraySet newLoadedApk = new ArraySet<>(); for (String apk : moduleList) diff --git a/core/src/main/java/io/github/lsposed/lspd/config/LSPApplicationServiceClient.java b/core/src/main/java/io/github/lsposed/lspd/config/LSPApplicationServiceClient.java index c539e7f0..d0d72020 100644 --- a/core/src/main/java/io/github/lsposed/lspd/config/LSPApplicationServiceClient.java +++ b/core/src/main/java/io/github/lsposed/lspd/config/LSPApplicationServiceClient.java @@ -16,12 +16,14 @@ public class LSPApplicationServiceClient implements ILSPApplicationService { static String baseCachePath = null; static String basePrefsPath = null; + static String processName = null; public static LSPApplicationServiceClient serviceClient = null; - public static void Init(IBinder binder) { + public static void Init(IBinder binder, String niceName) { if (serviceClient == null && binder != null && serviceBinder == null && service == null) { serviceBinder = binder; + processName = niceName; try { serviceBinder.linkToDeath( new IBinder.DeathRecipient() { @@ -89,14 +91,18 @@ public class LSPApplicationServiceClient implements ILSPApplicationService { } @Override - public String[] getModulesList() { + public String[] getModulesList(String processName) { try { - return service.getModulesList(); + return service.getModulesList(processName); } catch (RemoteException | NullPointerException ignored) { } return new String[0]; } + public String[] getModulesList(){ + return getModulesList(processName); + } + @Override public String getPrefsPath(String packageName) { try { diff --git a/core/src/main/java/io/github/lsposed/lspd/core/Main.java b/core/src/main/java/io/github/lsposed/lspd/core/Main.java index 8a01f846..f7460c35 100644 --- a/core/src/main/java/io/github/lsposed/lspd/core/Main.java +++ b/core/src/main/java/io/github/lsposed/lspd/core/Main.java @@ -40,7 +40,7 @@ public class Main implements KeepAll { private static final Binder heartBeatBinder = new Binder(); public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) { - LSPApplicationServiceClient.Init(binder); + LSPApplicationServiceClient.Init(binder, niceName); serviceClient.registerHeartBeat(heartBeatBinder); final int variant = serviceClient.getVariant(); Impl lspd = getImpl(variant); @@ -52,7 +52,7 @@ public class Main implements KeepAll { } public static void forkSystemServerPost(IBinder binder) { - LSPApplicationServiceClient.Init(binder); + LSPApplicationServiceClient.Init(binder, "android"); serviceClient.registerHeartBeat(heartBeatBinder); final int variant = serviceClient.getVariant(); Impl lspd = getImpl(variant); diff --git a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java index 54b010cc..a7d076a6 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java @@ -8,6 +8,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import android.util.Log; import android.os.Process; @@ -15,7 +16,15 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; +import com.android.server.am.ActivityManagerService; +import com.android.server.am.ProcessRecord; + import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Map; import static hidden.HiddenApiBridge.Binder_allowBlocking; @@ -44,6 +53,7 @@ public class BridgeService { this.bridgeService = bridgeService; bridgeService.linkToDeath(this, 0); } + @Override public void binderDied() { Log.i(TAG, "service " + SERVICE_NAME + " is dead. "); @@ -222,7 +232,8 @@ public class BridgeService { case ACTION_GET_BINDER: { IBinder binder = null; try { - binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid()).asBinder(); + String processName = data.readString(); + binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid(), processName).asBinder(); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(e)); } @@ -282,11 +293,67 @@ public class BridgeService { if (binder == null) return null; try { ILSPosedService service = ILSPosedService.Stub.asInterface(binder); - ILSPApplicationService applicationService = service.requestApplicationService(Process.myUid(), Process.myPid()); + ILSPApplicationService applicationService = service.requestApplicationService(Process.myUid(), Process.myPid(), "android"); if (applicationService != null) return applicationService.asBinder(); } catch (Throwable e) { Log.e(TAG, Log.getStackTraceString(e)); } return null; } + private static void tryGetActivityManagerServiceInstance() { + try { + Log.e(TAG, "Trying to get the ams"); + Field localServiceField = LocalServices.class.getDeclaredField("sLocalServiceObjects"); + localServiceField.setAccessible(true); + ArrayMap, Object> localServiceMap = (ArrayMap, Object>) localServiceField.get(null); + Class systemServiceManagerClass = null; + for (Class clazz : localServiceMap.keySet()) { + if (clazz.getName().equals("com.android.server.SystemServiceManager")) { + systemServiceManagerClass = clazz; + } + + } + Field parentField = ClassLoader.class.getDeclaredField("parent"); + parentField.setAccessible(true); + parentField.set(BridgeService.class.getClassLoader(), systemServiceManagerClass.getClassLoader()); + SystemServiceManager systemServiceManager = LocalServices.getService(SystemServiceManager.class); + ArrayList services; + try { + Field servicesField = systemServiceManagerClass.getDeclaredField("mServices"); + servicesField.setAccessible(true); + services = (ArrayList) servicesField.get(systemServiceManager); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, Log.getStackTraceString(e)); + return; + } + + ActivityManagerService.Lifecycle lifecycle = null; + + for (SystemService service : services) { + if (service instanceof ActivityManagerService.Lifecycle) { + lifecycle = (ActivityManagerService.Lifecycle) service; + } + } + if (lifecycle == null) { + Log.e(TAG, "I cannot get the lifecycle..."); + } + ActivityManagerService activityManagerService = lifecycle.getService(); + if (activityManagerService != null) { + Log.e(TAG, "I got the ams!!!: " + activityManagerService); + } else { + Log.e(TAG, "I cannot get the ams"); + } + Method findProcessLockedMethod = ActivityManagerService.class.getDeclaredMethod("findProcessLocked", String.class, int.class, String.class); + findProcessLockedMethod.setAccessible(true); + ProcessRecord record = (ProcessRecord) findProcessLockedMethod.invoke(activityManagerService, String.valueOf(Binder.getCallingPid()), 0, "LSPosed"); + Field processNameField = ProcessRecord.class.getDeclaredField("processName"); + processNameField.setAccessible(true); + if (record != null) { + Log.e(TAG, "I got the record!!!: " + record); + Log.e(TAG, "I got the process name: " + processNameField.get(record)); + } + } catch (Throwable e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java index 177c6c1a..25cce5b9 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java @@ -21,9 +21,14 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import io.github.lsposed.lspd.Application; + import static io.github.lsposed.lspd.service.ServiceManager.TAG; // This config manager assume uid won't change when our service is off. @@ -31,35 +36,35 @@ import static io.github.lsposed.lspd.service.ServiceManager.TAG; public class ConfigManager { static ConfigManager instance = null; - final private File basePath = new File("/data/adb/lspd"); - final private File configPath = new File(basePath, "config"); - final private SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null); + static final private File basePath = new File("/data/adb/lspd"); + static final private File configPath = new File(basePath, "config"); + static final private SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null); - final private File resourceHookSwitch = new File(configPath, "enable_resources"); + static final private File resourceHookSwitch = new File(configPath, "enable_resources"); private boolean resourceHook = false; - final private File variantSwitch = new File(configPath, "variant"); + static final private File variantSwitch = new File(configPath, "variant"); private int variant = -1; - final private File verboseLogSwitch = new File(configPath, "verbose_log"); + static final private File verboseLogSwitch = new File(configPath, "verbose_log"); private boolean verboseLog = false; - final private String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager"; + static final private String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager"; - final private File managerPath = new File(configPath, "manager"); + static final private File managerPath = new File(configPath, "manager"); private String manager = null; private int managerUid = -1; - final private File miscFile = new File(basePath, "misc_path"); + static final private File miscFile = new File(basePath, "misc_path"); private String miscPath = null; - final private File selinuxPath = new File("/sys/fs/selinux/enforce"); + static final private File selinuxPath = new File("/sys/fs/selinux/enforce"); // only check on boot final private boolean isPermissive; - final private File logPath = new File(basePath, "log"); - final private File modulesLogPath = new File(logPath, "modules.log"); - final private File verboseLogPath = new File(logPath, "all.log"); + static final private File logPath = new File(basePath, "log"); + static final private File modulesLogPath = new File(logPath, "modules.log"); + static final private File verboseLogPath = new File(logPath, "all.log"); final FileObserver configObserver = new FileObserver(configPath) { @Override @@ -69,11 +74,63 @@ public class ConfigManager { } }; - private String readText(@NonNull File file) throws IOException { + static class ProcessScope { + String processName; + int uid; + + ProcessScope(@NonNull String processName, int uid) { + this.processName = processName; + this.uid = uid; + } + + @Override + public boolean equals(@Nullable Object o) { + if(o instanceof ProcessScope) { + ProcessScope p = (ProcessScope) o; + return p.processName.equals(processName) && p.uid == uid; + } + return false; + } + } + + static private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" + + "mid integer PRIMARY KEY AUTOINCREMENT," + + "module_pkg_name text NOT NULL UNIQUE," + + "apk_path text NOT NULL, " + + "enabled BOOLEAN DEFAULT 0 " + + "CHECK (enabled IN (0, 1))" + + ");"); + static private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" + + "mid integer," + + "app_pkg_name text NOT NULL," + + "user_id integer NOT NULL," + + "PRIMARY KEY (mid, app_pkg_name, user_id)" + + ");"); + + private final ConcurrentHashMap> cachedScope = new ConcurrentHashMap<>(); + + public static boolean shouldSkipSystemServer() { + try(Cursor cursor = db.query("scope", new String[]{"mid"}, "app_pkg_name=?", new String[]{"android"}, null, null, null)){ + return cursor == null || !cursor.moveToNext(); + } + } + + public static String[] getModulesPathForSystemServer() { + HashSet modules = new HashSet<>(); + try(Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"apk_path"}, "app_pkg_name=?", new String[]{"android"}, null, null, null)){ + int apkPathIdx = cursor.getColumnIndex("apk_path"); + while(cursor.moveToNext()) { + modules.add(cursor.getString(apkPathIdx)); + } + } + return modules.toArray(new String[0]); + } + + static private String readText(@NonNull File file) throws IOException { return new String(Files.readAllBytes(file.toPath())).trim(); } - private String readText(@NonNull File file, String defaultValue) { + static private String readText(@NonNull File file, String defaultValue) { try { if (!file.exists()) return defaultValue; return readText(file); @@ -83,7 +140,7 @@ public class ConfigManager { return defaultValue; } - private void writeText(@NonNull File file, String value) { + static private void writeText(@NonNull File file, String value) { try { Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE); } catch (IOException e) { @@ -91,7 +148,7 @@ public class ConfigManager { } } - private int readInt(@NonNull File file, int defaultValue) { + static private int readInt(@NonNull File file, int defaultValue) { try { if (!file.exists()) return defaultValue; return Integer.parseInt(readText(file)); @@ -101,7 +158,7 @@ public class ConfigManager { return defaultValue; } - private void writeInt(@NonNull File file, int value) { + static private void writeInt(@NonNull File file, int value) { writeText(file, String.valueOf(value)); } @@ -130,21 +187,6 @@ public class ConfigManager { updateManager(); } - private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" + - "mid integer PRIMARY KEY AUTOINCREMENT," + - "package_name text NOT NULL UNIQUE," + - "apk_path text NOT NULL, " + - "enabled BOOLEAN DEFAULT 0 " + - "CHECK (enabled IN (0, 1))" + - ");"); - private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" + - "mid integer," + - "uid integer," + - "PRIMARY KEY (mid, uid)" + - ");"); - - private final ConcurrentHashMap> modulesForUid = new ConcurrentHashMap<>(); - static ConfigManager getInstance() { if (instance == null) instance = new ConfigManager(); @@ -164,51 +206,81 @@ public class ConfigManager { createScopeTable.execute(); } + private List getAssociatedProcesses(Application app) throws RemoteException { + PackageInfo pkgInfo = PackageService.getPackageInfo(app.packageName, 0, app.userId); + List processes = new ArrayList<>(); + if (pkgInfo != null && pkgInfo.applicationInfo != null) { + for(String process: PackageService.getProcessesForUid(pkgInfo.applicationInfo.uid)){ + processes.add(new ProcessScope(process, pkgInfo.applicationInfo.uid)); + } + } + return processes; + } + private synchronized void cacheScopes() { - modulesForUid.clear(); - try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid", "apk_path"}, + cachedScope.clear(); + try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "user_id", "apk_path"}, "enabled = ?", new String[]{"1"}, null, null, null)) { if (cursor == null) { Log.e(TAG, "db cache failed"); return; } - int uid_idx = cursor.getColumnIndex("uid"); - int apk_path_idx = cursor.getColumnIndex("apk_path"); + int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name"); + int userIdIdx = cursor.getColumnIndex("user_id"); + int apkPathIdx = cursor.getColumnIndex("apk_path"); + HashSet obsoletePackages = new HashSet<>(); while (cursor.moveToNext()) { - int uid = cursor.getInt(uid_idx); - String apk_path = cursor.getString(apk_path_idx); - modulesForUid.computeIfAbsent(uid, ignored -> new ArrayList<>()).add(apk_path); + Application app = new Application(); + app.packageName = cursor.getString(appPkgNameIdx); + app.userId = cursor.getInt(userIdIdx); + String apk_path = cursor.getString(apkPathIdx); + try { + List processesScope = getAssociatedProcesses(app); + if (processesScope.isEmpty()) { + obsoletePackages.add(app); + continue; + } + for (ProcessScope processScope : processesScope) + cachedScope.computeIfAbsent(processScope, ignored -> new HashSet<>()).add(apk_path); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } + for (Application obsoletePackage : obsoletePackages) { + removeAppWithoutCache(obsoletePackage); } } } // This is called when a new process created, use the cached result - public String[] getModulesPathForUid(int uid) { - return isManager(uid) ? new String[0] : modulesForUid.getOrDefault(uid, new ArrayList<>()).toArray(new String[0]); + public String[] getModulesPathForProcess(String processName, int uid) { + return isManager(uid) ? new String[0] : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptySet()).toArray(new String[0]); } // This is called when a new process created, use the cached result - // The signature matches Riru's - public boolean shouldSkipUid(int uid) { - return !modulesForUid.containsKey(uid) && !isManager(uid); + public boolean shouldSkipProcess(ProcessScope scope) { + return !cachedScope.containsKey(scope) && !isManager(scope.uid); } // This should only be called by manager, so we don't need to cache it - public int[] getModuleScope(String packageName) { + public List getModuleScope(String packageName) { int mid = getModuleId(packageName); if (mid == -1) return null; - try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid"}, + try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "user_id"}, "scope.mid = ?", new String[]{String.valueOf(mid)}, null, null, null)) { if (cursor == null) { return null; } - int uid_idx = cursor.getColumnIndex("uid"); - HashSet result = new HashSet<>(); + int userIdIdx = cursor.getColumnIndex("user_id"); + int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name"); + ArrayList result = new ArrayList<>(); while (cursor.moveToNext()) { - int uid = cursor.getInt(uid_idx); - result.add(uid); + Application scope = new Application(); + scope.packageName = cursor.getString(appPkgNameIdx); + scope.userId = cursor.getInt(userIdIdx); + result.add(scope); } - return result.stream().mapToInt(i -> i).toArray(); + return result; } } @@ -218,11 +290,11 @@ public class ConfigManager { return false; } ContentValues values = new ContentValues(); - values.put("package_name", packageName); + values.put("module_pkg_name", packageName); values.put("apk_path", apkPath); int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE); if (count < 0) { - count = db.updateWithOnConflict("modules", values, "package_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE); + count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE); } if (count >= 1) { cacheScopes(); @@ -236,7 +308,7 @@ public class ConfigManager { Log.w(TAG, "get module id should not be called inside transaction"); return -1; } - try (Cursor cursor = db.query("modules", new String[]{"mid"}, "package_name=?", new String[]{packageName}, null, null, null)) { + try (Cursor cursor = db.query("modules", new String[]{"mid"}, "module_pkg_name=?", new String[]{packageName}, null, null, null)) { if (cursor == null) return -1; if (cursor.getCount() != 1) return -1; cursor.moveToFirst(); @@ -244,17 +316,18 @@ public class ConfigManager { } } - public boolean setModuleScope(String packageName, int[] uid) { - if (uid == null || uid.length == 0) return false; + public boolean setModuleScope(String packageName, List scopes) { + if (scopes.isEmpty()) return false; int mid = getModuleId(packageName); if (mid == -1) return false; try { db.beginTransaction(); db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)}); - for (int id : uid) { + for (Application app : scopes) { ContentValues values = new ContentValues(); values.put("mid", mid); - values.put("uid", id); + values.put("app_pkg_name", app.packageName); + values.put("user_id", app.userId); db.insertWithOnConflict("scope", null, values, SQLiteDatabase.CONFLICT_IGNORE); } } finally { @@ -266,15 +339,15 @@ public class ConfigManager { } public String[] enabledModules() { - try (Cursor cursor = db.query("modules", new String[]{"package_name"}, "enabled = ?", new String[]{"1"}, null, null, null)) { + try (Cursor cursor = db.query("modules", new String[]{"module_pkg_name"}, "enabled = ?", new String[]{"1"}, null, null, null)) { if (cursor == null) { Log.e(TAG, "query enabled modules failed"); return null; } - int pkg_idx = cursor.getColumnIndex("package_name"); + int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name"); HashSet result = new HashSet<>(); while (cursor.moveToNext()) { - result.add(cursor.getString(pkg_idx)); + result.add(cursor.getString(modulePkgNameIdx)); } return result.toArray(new String[0]); } @@ -328,9 +401,13 @@ public class ConfigManager { return true; } - public boolean removeApp(int uid) { - int count = db.delete("scope", "uid = ?", new String[]{String.valueOf(uid)}); - if (count >= 1) { + public boolean removeAppWithoutCache(Application app) { + int count = db.delete("scope", "app_pkg_name = ? AND user_id=?", new String[]{app.packageName, String.valueOf(app.userId)}); + return count >= 1; + } + + public boolean removeApp(Application scope) { + if (removeAppWithoutCache(scope)) { cacheScopes(); return true; } @@ -397,8 +474,8 @@ public class ConfigManager { } } - public boolean isManager(String package_name) { - return package_name.equals(manager); + public boolean isManager(String module_pkg_name) { + return module_pkg_name.equals(manager); } public boolean isManager(int uid) { diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java index 66a3444f..c743e530 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java @@ -48,9 +48,13 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { } @Override - public String[] getModulesList() throws RemoteException { + public String[] getModulesList(String processName) throws RemoteException { ensureRegistered(); - return ConfigManager.getInstance().getModulesPathForUid(Binder.getCallingUid()); + int callingUid = Binder.getCallingUid(); + if (callingUid == 1000 && processName.equals("android")) { + ConfigManager.getModulesPathForSystemServer(); + } + return ConfigManager.getInstance().getModulesPathForProcess(processName, callingUid); } @Override diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java index bd964db2..3a66c726 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java @@ -5,11 +5,10 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import java.util.List; - import de.robv.android.xposed.XposedBridge; import io.github.lsposed.lspd.BuildConfig; import io.github.lsposed.lspd.ILSPManagerService; +import io.github.lsposed.lspd.Application; import io.github.lsposed.lspd.utils.ParceledListSlice; public class LSPManagerService extends ILSPManagerService.Stub { @@ -55,13 +54,13 @@ public class LSPManagerService extends ILSPManagerService.Stub { } @Override - public boolean setModuleScope(String packageName, int[] uid) { - return ConfigManager.getInstance().setModuleScope(packageName, uid); + public boolean setModuleScope(String packageName, ParceledListSlice scope) { + return ConfigManager.getInstance().setModuleScope(packageName, scope.getList()); } @Override - public int[] getModuleScope(String packageName) { - return ConfigManager.getInstance().getModuleScope(packageName); + public ParceledListSlice getModuleScope(String packageName) { + return new ParceledListSlice(ConfigManager.getInstance().getModuleScope(packageName)); } @Override diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java index 4f4ecee8..a72520a7 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java @@ -8,17 +8,25 @@ import android.os.Binder; import android.os.RemoteException; import android.util.Log; +import io.github.lsposed.lspd.Application; + import static io.github.lsposed.lspd.service.ServiceManager.TAG; public class LSPosedService extends ILSPosedService.Stub { @Override - public ILSPApplicationService requestApplicationService(int uid, int pid) { + public ILSPApplicationService requestApplicationService(int uid, int pid, String processName) { if (Binder.getCallingUid() != 1000) { Log.w(TAG, "Someone else got my binder!?"); return null; } - if (ConfigManager.getInstance().shouldSkipUid(uid)) { - Log.d(TAG, "Skipped uid " + uid); + if (uid == 1000 && processName.equals("android")) { + if (ConfigManager.shouldSkipSystemServer()) + return null; + else + return ServiceManager.getApplicationService(); + } + if (ConfigManager.getInstance().shouldSkipProcess(new ConfigManager.ProcessScope(processName, uid))) { + Log.d(TAG, "Skipped " + processName); return null; } if (ServiceManager.getApplicationService().hasRegister(uid, pid)) { @@ -40,10 +48,14 @@ public class LSPosedService extends ILSPosedService.Stub { return; } int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + int userId = intent.getIntExtra(Intent.EXTRA_USER, -1); boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) && uid > 0 && !replacing) { ConfigManager.getInstance().removeModule(packageName); - ConfigManager.getInstance().removeApp(uid); + Application app = new Application(); + app.packageName = packageName; + app.userId = userId; + ConfigManager.getInstance().removeApp(app); } PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, PackageManager.GET_META_DATA, 0); boolean isXposedModule = pkgInfo != null && pkgInfo.applicationInfo != null && diff --git a/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java b/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java index 85b69e7f..c7aaa9c2 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java @@ -1,16 +1,25 @@ package io.github.lsposed.lspd.service; +import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import io.github.lsposed.lspd.utils.ParceledListSlice; +import static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS; + public class PackageService { + public static final int PER_USER_RANGE = 100000; + private static IPackageManager pm = null; private static IBinder binder = null; @@ -34,6 +43,14 @@ public class PackageService { return pm.getPackagesForUid(uid); } + public static Set getProcessesForUid(int uid) throws RemoteException { + HashSet processNames = new HashSet<>(); + for (String packageName : getPackagesForUid(uid)) { + processNames.addAll(fetchProcesses(packageName, uid / PER_USER_RANGE)); + } + return processNames; + } + public static ParceledListSlice getInstalledPackagesFromAllUsers(int flags) throws RemoteException { ArrayList res = new ArrayList<>(); IPackageManager pm = getPackageManager(); @@ -46,6 +63,31 @@ public class PackageService { public static void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException { IPackageManager pm = getPackageManager(); + if (pm == null) return; pm.grantRuntimePermission(packageName, permissionName, userId); } + + public static Set fetchProcesses(PackageInfo pkgInfo) { + HashSet processNames = new HashSet<>(); + for (ComponentInfo[] componentInfos : new ComponentInfo[][]{pkgInfo.activities, pkgInfo.receivers, pkgInfo.providers}) { + for (ComponentInfo componentInfo : componentInfos) { + processNames.add(componentInfo.processName); + } + } + for (ServiceInfo service : pkgInfo.services) { + if ((service.flags & FLAG_ISOLATED_PROCESS) == 0) { + processNames.add(service.processName); + } + } + return processNames; + } + + public static Set fetchProcesses(String packageName, int userId) throws RemoteException { + IPackageManager pm = getPackageManager(); + if (pm == null) return new HashSet<>(); + PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.MATCH_DISABLED_COMPONENTS | + PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_ACTIVITIES | + PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS, userId); + return fetchProcesses(pkgInfo); + } } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java b/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java index 077d1beb..88819f48 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java @@ -1,6 +1,7 @@ package io.github.lsposed.lspd.service; import android.content.Context; +import android.os.IBinder; import android.os.Looper; import android.util.Log; @@ -32,7 +33,7 @@ public class ServiceManager { applicationService = new LSPApplicationService(); managerService = new LSPManagerService(); - android.os.ServiceManager.addService("serial", mainService); + android.os.ServiceManager.addService("serial", (IBinder) mainService); waitSystemService("package"); waitSystemService("activity"); @@ -61,10 +62,15 @@ public class ServiceManager { Log.e(TAG, Log.getStackTraceString(e)); } - Looper.loop(); - - Log.i(TAG, "server exited"); - System.exit(0); + //noinspection InfiniteLoopStatement + while (true) { + try { + Looper.loop(); + } catch (Throwable e) { + Log.i(TAG, "server exited with " + Log.getStackTraceString(e)); + Log.i(TAG, "restarting"); + } + } } public static LSPModuleService getModuleService() { diff --git a/hiddenapi-stubs/src/main/java/com/android/server/LocalServices.java b/hiddenapi-stubs/src/main/java/com/android/server/LocalServices.java new file mode 100644 index 00000000..06717abf --- /dev/null +++ b/hiddenapi-stubs/src/main/java/com/android/server/LocalServices.java @@ -0,0 +1,8 @@ +package com.android.server; + +public class LocalServices { + + public static T getService(Class type) { + throw new UnsupportedOperationException("STUB"); + } +} diff --git a/hiddenapi-stubs/src/main/java/com/android/server/SystemService.java b/hiddenapi-stubs/src/main/java/com/android/server/SystemService.java new file mode 100644 index 00000000..2f678e8a --- /dev/null +++ b/hiddenapi-stubs/src/main/java/com/android/server/SystemService.java @@ -0,0 +1,4 @@ +package com.android.server; + +public abstract class SystemService { +} diff --git a/hiddenapi-stubs/src/main/java/com/android/server/SystemServiceManager.java b/hiddenapi-stubs/src/main/java/com/android/server/SystemServiceManager.java new file mode 100644 index 00000000..d9e2a920 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/com/android/server/SystemServiceManager.java @@ -0,0 +1,7 @@ +package com.android.server; + +import java.util.ArrayList; + +public class SystemServiceManager { + private final ArrayList mServices = new ArrayList<>(); +} diff --git a/hiddenapi-stubs/src/main/java/com/android/server/am/ActivityManagerService.java b/hiddenapi-stubs/src/main/java/com/android/server/am/ActivityManagerService.java new file mode 100644 index 00000000..2d51101f --- /dev/null +++ b/hiddenapi-stubs/src/main/java/com/android/server/am/ActivityManagerService.java @@ -0,0 +1,14 @@ +package com.android.server.am; + +import com.android.server.SystemService; + +public class ActivityManagerService { + public static final class Lifecycle extends SystemService { + public ActivityManagerService getService() { + throw new UnsupportedOperationException("STUB"); + } + private ProcessRecord findProcessLocked(String process, int userId, String callName) { + throw new UnsupportedOperationException("STUB"); + } + } +} diff --git a/hiddenapi-stubs/src/main/java/com/android/server/am/ProcessRecord.java b/hiddenapi-stubs/src/main/java/com/android/server/am/ProcessRecord.java new file mode 100644 index 00000000..8947ce02 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/com/android/server/am/ProcessRecord.java @@ -0,0 +1,5 @@ +package com.android.server.am; + +public class ProcessRecord { + String processName = null; +} diff --git a/manager-service/src/main/aidl/io/github/lsposed/lspd/Application.aidl b/manager-service/src/main/aidl/io/github/lsposed/lspd/Application.aidl new file mode 100644 index 00000000..144b33d4 --- /dev/null +++ b/manager-service/src/main/aidl/io/github/lsposed/lspd/Application.aidl @@ -0,0 +1,6 @@ +package io.github.lsposed.lspd; + +parcelable Application { + String packageName; + int userId; +} \ No newline at end of file diff --git a/manager-service/src/main/aidl/io/github/lsposed/lspd/ILSPManagerService.aidl b/manager-service/src/main/aidl/io/github/lsposed/lspd/ILSPManagerService.aidl index b3ad3f4f..c6aa03ee 100644 --- a/manager-service/src/main/aidl/io/github/lsposed/lspd/ILSPManagerService.aidl +++ b/manager-service/src/main/aidl/io/github/lsposed/lspd/ILSPManagerService.aidl @@ -1,6 +1,8 @@ package io.github.lsposed.lspd; import io.github.lsposed.lspd.utils.ParceledListSlice; +import io.github.lsposed.lspd.Application; + interface ILSPManagerService { ParceledListSlice getInstalledPackagesFromAllUsers(int flags) = 2; @@ -11,9 +13,9 @@ interface ILSPManagerService { boolean disableModule(String packageName) = 5; - boolean setModuleScope(String packageName, in int[] uid) = 6; + boolean setModuleScope(String packageName, in ParceledListSlice scope) = 6; - int[] getModuleScope(String packageName) = 7; + ParceledListSlice getModuleScope(String packageName) = 7; boolean isResourceHook() = 9;