From e7e32351eaf0c7c1d284dd986cc854d5c1922e11 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Wed, 17 Feb 2021 01:51:27 +0800 Subject: [PATCH] [core] Add more manager service interfaces --- .../lspd/service/ILSPApplicationService.aidl | 2 +- .../de/robv/android/xposed/XposedInit.java | 2 +- .../config/LSPApplicationServiceClient.java | 17 +- .../lspd/hooker/XposedInstallerHooker.java | 18 -- .../lsposed/lspd/service/ConfigManager.java | 166 ++++++++++++------ .../lspd/service/LSPApplicationService.java | 14 +- .../lspd/service/LSPManagerService.java | 75 ++++++++ .../lsposed/lspd/ILSPManagerService.aidl | 29 +++ 8 files changed, 237 insertions(+), 86 deletions(-) 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 f1af7013..8101d4e7 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; - List getModulesList() = 6; + String[] getModulesList() = 6; String getPrefsPath(String packageName) = 7; 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 cbe53bc8..1d23d9cb 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -353,7 +353,7 @@ public final class XposedInit { topClassLoader = parent; } - List moduleList = serviceClient.getModulesList(); + String[] moduleList = serviceClient.getModulesList(); ArraySet newLoadedApk = new ArraySet<>(); for (String apk : moduleList) if (loadedModules.contains(apk)) { 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 b80f7189..b3e981f8 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 @@ -23,10 +23,15 @@ public class LSPApplicationServiceClient implements ILSPApplicationService { if (serviceClient == null && binder != null && serviceBinder == null && service == null) { serviceBinder = binder; try { - serviceBinder.linkToDeath(() -> { - serviceBinder = null; - service = null; - }, 0); + serviceBinder.linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + serviceBinder.unlinkToDeath(this, 0); + serviceBinder = null; + service = null; + } + }, 0); } catch (RemoteException e) { Utils.logE("link to death error: ", e); } @@ -84,12 +89,12 @@ public class LSPApplicationServiceClient implements ILSPApplicationService { } @Override - public List getModulesList() { + public String[] getModulesList() { try { return service.getModulesList(); } catch (RemoteException | NullPointerException ignored) { } - return Collections.emptyList(); + return new String[0]; } @Override diff --git a/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java b/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java index cf1eec77..4b323e03 100644 --- a/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java +++ b/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java @@ -23,30 +23,12 @@ package io.github.lsposed.lspd.hooker; import android.os.IBinder; import de.robv.android.xposed.XposedHelpers; -import io.github.lsposed.lspd.core.EdxpImpl; -import io.github.lsposed.lspd.core.Main; import io.github.lsposed.lspd.util.Utils; public class XposedInstallerHooker { public static void hookXposedInstaller(final ClassLoader classLoader, IBinder binder) { - final String variant; - switch (Main.getEdxpVariant()) { - case EdxpImpl.YAHFA: - variant = "YAHFA"; - break; - case EdxpImpl.SANDHOOK: - variant = "SandHook"; - break; - case EdxpImpl.NONE: - default: - variant = "Unknown"; - break; - } - Utils.logI("Found LSPosed Manager, hooking it"); - - // LSPosed Manager R try { Class serviceClass = XposedHelpers.findClass("io.github.lsposed.manager.receivers.LSPosedManagerServiceClient", classLoader); XposedHelpers.setStaticObjectField(serviceClass, "binder", binder); 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 17ba73d9..aa75bb16 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 @@ -3,9 +3,9 @@ package io.github.lsposed.lspd.service; import android.content.ContentValues; import android.content.pm.PackageInfo; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteStatement; -import android.database.sqlite.SQLiteDatabase; import android.os.FileObserver; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -21,8 +21,6 @@ import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static io.github.lsposed.lspd.service.ServiceManager.TAG; @@ -130,10 +128,12 @@ public class ConfigManager { updateManager(); } - private final SQLiteStatement createEnabledModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS enabled_modules (" + + 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" + + "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," + @@ -154,96 +154,104 @@ public class ConfigManager { updateConfig(); isPermissive = readInt(selinuxPath, 1) == 0; configObserver.startWatching(); + cacheScopes(); } private void createTables() { - createEnabledModulesTable.execute(); + createModulesTable.execute(); createScopeTable.execute(); } private synchronized void cacheScopes() { modulesForUid.clear(); - SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); - builder.setTables("scope INNER JOIN enabled_modules ON scope.mid = enabled_modules.mid"); - Cursor cursor = builder.query(db, new String[]{"scope.uid", "enabled_modules.apk_path"}, - null, null, null, null, null); - if (cursor == null) { - Log.e(TAG, "db cache failed"); - return; - } - int uid_idx = cursor.getColumnIndex("scope.uid"); - int apk_path_idx = cursor.getColumnIndex("enabled_modules.apk_path"); - 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); + try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid", "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"); + 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); + } } } // This is called when a new process created, use the cached result - public List getModulesPathForUid(int uid) { - return isManager(uid) ? new ArrayList<>() : modulesForUid.getOrDefault(uid, null); + public String[] getModulesPathForUid(int uid) { + return isManager(uid) ? new String[0] : modulesForUid.getOrDefault(uid, new ArrayList<>()).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) { + Log.d(TAG, modulesForUid.keySet().size() + ""); + for (Integer id : modulesForUid.keySet()) { + Log.d(TAG, id.toString()); + } return !modulesForUid.containsKey(uid) && !isManager(uid); } // This should only be called by manager, so we don't need to cache it - public Set getModuleScope(String packageName) { - SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); - builder.setTables("scope INNER JOIN enabled_modules ON scope.mid = enabled_modules.mid"); - Cursor cursor = builder.query(db, new String[]{"scope.uid"}, - null, null, null, null, null); - if (cursor == null) { - Log.e(TAG, "db cache failed"); - return null; + public int[] 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"}, + "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<>(); + while (cursor.moveToNext()) { + int uid = cursor.getInt(uid_idx); + result.add(uid); + } + return result.stream().mapToInt(i -> i).toArray(); } - int uid_idx = cursor.getColumnIndex("scope.uid"); - HashSet result = new HashSet<>(); - while (cursor.moveToNext()) { - int uid = cursor.getInt(uid_idx); - result.add(uid); - } - return result; } public boolean updateModuleApkPath(String packageName, String apkPath) { + if (db.inTransaction()) { + Log.w(TAG, "update module apk path should not be called inside transaction"); + return false; + } ContentValues values = new ContentValues(); + values.put("package_name", packageName); values.put("apk_path", apkPath); - int count = db.updateWithOnConflict("enabled_modules", values, "package_name = ?", new String[]{packageName}, SQLiteDatabase.CONFLICT_REPLACE); + 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); + } if (count >= 1) { cacheScopes(); - return true; } - return false; + return count >= 0; } // Only be called before updating modules. No need to cache. private int getModuleId(String packageName) { - try { - db.beginTransaction(); - SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); - Cursor cursor = builder.query(db, new String[]{"mid"}, "package_name = ?", new String[]{packageName}, null, null, null); + if (db.inTransaction()) { + 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)) { if (cursor == null) return -1; if (cursor.getCount() != 1) return -1; cursor.moveToFirst(); return cursor.getInt(cursor.getColumnIndex("mid")); - } finally { - db.setTransactionSuccessful(); - db.endTransaction(); } } - public boolean setModuleScope(String packageName, String apkPath, List uid) { - if (uid == null || uid.isEmpty()) return false; - updateModuleApkPath(packageName, apkPath); + public boolean setModuleScope(String packageName, int[] uid) { + if (uid == null || uid.length == 0) return false; + int mid = getModuleId(packageName); + if (mid == -1) return false; try { db.beginTransaction(); - int mid = getModuleId(packageName); - if (mid == -1) return false; db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)}); for (int id : uid) { ContentValues values = new ContentValues(); @@ -259,12 +267,27 @@ public class ConfigManager { return true; } + public String[] enabledModules() { + try (Cursor cursor = db.query("modules", new String[]{"package_name"}, "enabled = ?", new String[]{"1"}, null, null, null)) { + if (cursor == null) { + Log.e(TAG, "db cache failed"); + return null; + } + int pkg_idx = cursor.getColumnIndex("package_name"); + HashSet result = new HashSet<>(); + while (cursor.moveToNext()) { + result.add(cursor.getString(pkg_idx)); + } + return result.toArray(new String[0]); + } + } + public boolean removeModule(String packageName) { + int mid = getModuleId(packageName); + if (mid == -1) return false; try { db.beginTransaction(); - int mid = getModuleId(packageName); - if (mid == -1) return false; - db.delete("enabled_modules", "mid = ?", new String[]{String.valueOf(mid)}); + db.delete("modules", "mid = ?", new String[]{String.valueOf(mid)}); db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)}); } finally { db.setTransactionSuccessful(); @@ -274,6 +297,39 @@ public class ConfigManager { return true; } + public boolean disableModule(String packageName) { + int mid = getModuleId(packageName); + if (mid == -1) return false; + try { + db.beginTransaction(); + ContentValues values = new ContentValues(); + values.put("enabled", 0); + db.update("modules", values, "mid = ?", new String[]{String.valueOf(mid)}); + } finally { + db.setTransactionSuccessful(); + db.endTransaction(); + } + cacheScopes(); + return true; + } + + public boolean enableModule(String packageName, String apkPath) { + if (!updateModuleApkPath(packageName, apkPath)) return false; + int mid = getModuleId(packageName); + if (mid == -1) return false; + try { + db.beginTransaction(); + ContentValues values = new ContentValues(); + values.put("enabled", 1); + db.update("modules", values, "mid = ?", new String[]{String.valueOf(mid)}); + } finally { + db.setTransactionSuccessful(); + db.endTransaction(); + } + cacheScopes(); + return true; + } + public boolean removeApp(int uid) { int count = db.delete("scope", "uid = ?", new String[]{String.valueOf(uid)}); if (count >= 1) { 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 ce6f9997..3add1b12 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 @@ -23,10 +23,14 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { int uid = Binder.getCallingUid(); int pid = Binder.getCallingPid(); cache.add(new Pair<>(uid, pid)); - handle.linkToDeath(() -> { - Log.d(TAG, "pid=" + pid + " uid=" + uid + " is dead."); - cache.remove(new Pair<>(uid, pid)); - handles.remove(handle); + handle.linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + Log.d(TAG, "pid=" + pid + " uid=" + uid + " is dead."); + cache.remove(new Pair<>(uid, pid)); + handles.remove(handle); + handle.unlinkToDeath(this, 0); + } }, 0); } @@ -43,7 +47,7 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { } @Override - public List getModulesList() throws RemoteException { + public String[] getModulesList() throws RemoteException { ensureRegistered(); return ConfigManager.getInstance().getModulesPathForUid(Binder.getCallingUid()); } 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 b7fc0099..14859cd5 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 @@ -2,8 +2,11 @@ package io.github.lsposed.lspd.service; import android.content.pm.PackageInfo; 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.ILSPManagerService; import io.github.lsposed.lspd.utils.ParceledListSlice; @@ -27,4 +30,76 @@ public class LSPManagerService extends ILSPManagerService.Stub { public ParceledListSlice getInstalledPackagesFromAllUsers(int flags) throws RemoteException { return PackageService.getInstalledPackagesFromAllUsers(flags); } + + @Override + public String[] enabledModules() { + return ConfigManager.getInstance().enabledModules(); + } + + @Override + public boolean enableModule(String packageName) throws RemoteException { + PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, 0, 0); + if (pkgInfo == null) return false; + return ConfigManager.getInstance().enableModule(packageName, pkgInfo.applicationInfo.sourceDir); + } + + @Override + public boolean setModuleScope(String packageName, int[] uid) { + return ConfigManager.getInstance().setModuleScope(packageName, uid); + } + + @Override + public int[] getModuleScope(String packageName) { + return ConfigManager.getInstance().getModuleScope(packageName); + } + + @Override + public boolean disableModule(String packageName) { + return ConfigManager.getInstance().disableModule(packageName); + } + + @Override + public boolean isResourceHook() { + return ConfigManager.getInstance().resourceHook(); + } + + @Override + public void setResourceHook(boolean enabled) { + ConfigManager.getInstance().setResourceHook(enabled); + } + + @Override + public boolean isVerboseLog() { + return ConfigManager.getInstance().verboseLog(); + } + + @Override + public void setVerboseLog(boolean enabled) { + ConfigManager.getInstance().setVerboseLog(enabled); + } + + @Override + public int getVariant() { + return ConfigManager.getInstance().variant(); + } + + @Override + public void setVariant(int variant) { + ConfigManager.getInstance().setVariant(variant); + } + + @Override + public boolean isPermissive() { + return ConfigManager.getInstance().isPermissive(); + } + + @Override + public ParcelFileDescriptor getVerboseLog() { + return ConfigManager.getInstance().getVerboseLog(); + } + + @Override + public ParcelFileDescriptor getModulesLog() { + return ConfigManager.getInstance().getModulesLog(); + } } 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 6234a156..fbe347b7 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 @@ -4,5 +4,34 @@ import io.github.lsposed.lspd.utils.ParceledListSlice; interface ILSPManagerService { int getVersion() = 1; + ParceledListSlice getInstalledPackagesFromAllUsers(int flags) = 2; + + String[] enabledModules() = 3; + + boolean enableModule(String packageName) = 4; + + boolean disableModule(String packageName) = 5; + + boolean setModuleScope(String packageName, in int[] uid) = 6; + + int[] getModuleScope(String packageName) = 7; + + boolean isResourceHook() = 9; + + void setResourceHook(boolean enabled) = 10; + + boolean isVerboseLog() = 11; + + void setVerboseLog(boolean enabled) = 12; + + int getVariant() = 13; + + void setVariant(int variant) = 14; + + boolean isPermissive() = 15; + + ParcelFileDescriptor getVerboseLog() = 16; + + ParcelFileDescriptor getModulesLog() = 17; }