diff --git a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java index ca631b9e..6cb02868 100644 --- a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java +++ b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java @@ -84,15 +84,6 @@ public class ApplicationServiceClient implements ILSPApplicationService, IBinder return null; } - @Override - public Bundle requestRemotePreferences(String packageName, int userId, String group, IBinder callback) { - try { - return service.requestRemotePreferences(packageName, userId, group, callback); - } catch (RemoteException | NullPointerException ignored) { - } - return null; - } - @Override public ParcelFileDescriptor requestInjectedManagerBinder(List binder) { try { diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java index 8777cf05..5ca48dd8 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java @@ -36,6 +36,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.lsposed.lspd.models.Module; +import org.lsposed.lspd.service.ILSPInjectedModuleService; import org.lsposed.lspd.util.LspModuleClassLoader; import java.io.File; @@ -70,12 +71,14 @@ public class LSPosedContext extends XposedContext { private final Context mBase; private final String mPackageName; private final String mApkPath; + private final ILSPInjectedModuleService service; private final Map mRemotePrefs = new ConcurrentHashMap<>(); - LSPosedContext(Context base, String packageName, String apkPath) { + LSPosedContext(Context base, String packageName, String apkPath, ILSPInjectedModuleService service) { this.mBase = base; this.mPackageName = packageName; this.mApkPath = apkPath; + this.service = service; } public static void callOnPackageLoaded(XposedModuleInterface.PackageLoadedParam param) { @@ -143,7 +146,7 @@ public class LSPosedContext extends XposedContext { } args[i] = null; } - var ctx = new LSPosedContext((Context) ctor.newInstance(args), module.packageName, module.apkPath); + var ctx = new LSPosedContext((Context) ctor.newInstance(args), module.packageName, module.apkPath, module.service); for (var entry : module.file.moduleClassNames) { var moduleClass = ctx.getClassLoader().loadClass(entry); Log.d(TAG, " Loading class " + moduleClass); @@ -267,7 +270,7 @@ public class LSPosedContext extends XposedContext { @Override public SharedPreferences getSharedPreferences(String name, int mode) { if (name == null) throw new IllegalArgumentException("name must not be null"); - return mRemotePrefs.computeIfAbsent(name, __ -> new LSPosedRemotePreferences(mPackageName, Process.myPid() / PER_USER_RANGE, name)); + return mRemotePrefs.computeIfAbsent(name, __ -> new LSPosedRemotePreferences(service, name)); } @Override diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedRemotePreferences.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedRemotePreferences.java index 6e3ac32d..54876845 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedRemotePreferences.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedRemotePreferences.java @@ -1,13 +1,14 @@ package org.lsposed.lspd.impl; -import static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient; - import android.content.SharedPreferences; import android.os.Bundle; import android.os.RemoteException; import androidx.annotation.Nullable; +import org.lsposed.lspd.service.ILSPInjectedModuleService; +import org.lsposed.lspd.service.IRemotePreferenceCallback; + import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -29,6 +30,14 @@ public class LSPosedRemotePreferences implements SharedPreferences { synchronized public void onUpdate(Bundle bundle) { if (bundle.containsKey("map")) mMap.putAll((Map) bundle.getSerializable("map")); + if (bundle.containsKey("delete")) { + for (var key : bundle.getStringArrayList("delete")) { + mMap.remove(key); + synchronized (mListeners) { + mListeners.forEach((listener, __) -> listener.onSharedPreferenceChanged(LSPosedRemotePreferences.this, key)); + } + } + } if (bundle.containsKey("diff")) { for (var key : bundle.getStringArrayList("diff")) { synchronized (mListeners) { @@ -39,9 +48,9 @@ public class LSPosedRemotePreferences implements SharedPreferences { } }; - public LSPosedRemotePreferences(String packageName, int userId, String group) { + public LSPosedRemotePreferences(ILSPInjectedModuleService service, String group) { try { - Bundle output = serviceClient.requestRemotePreferences(packageName, userId, group, callback.asBinder()); + Bundle output = service.requestRemotePreferences(group, callback); callback.onUpdate(output); } catch (RemoteException e) { XposedBridge.log(e); diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index eb31126d..cb222926 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -26,6 +26,7 @@ import android.content.res.AssetManager; import android.content.res.Resources; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.os.SELinux; import android.os.SharedMemory; import android.system.ErrnoException; @@ -77,6 +78,7 @@ import hidden.HiddenApiBridge; public class ConfigFileManager { static final Path basePath = Paths.get("/data/adb/lspd"); + static final Path modulePath = basePath.resolve("modules"); static final Path daemonApkPath = Paths.get(System.getProperty("java.class.path", null)); static final Path managerApkPath = basePath.resolve("manager.apk"); static final File magiskDbPath = new File("/data/adb/magisk.db"); @@ -427,6 +429,19 @@ public class ConfigFileManager { return preloadDex; } + static Path resolveModulePath(String packageName, String path) throws IOException { + var requestPath = Paths.get(path); + if (requestPath.isAbsolute()) { + throw new IOException("path must be relative"); + } + var moduleDir = modulePath.resolve(packageName).normalize(); + var absolutePath = moduleDir.resolve(requestPath).normalize(); + if (!absolutePath.startsWith(moduleDir)) { + throw new IOException("path must be in module dir"); + } + return absolutePath; + } + private static class FileLocker { private final FileChannel lockChannel; private final FileLock locker; diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java index 4e57ca80..7ddccc3a 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -229,6 +229,7 @@ public class ConfigManager { } catch (PackageParser.PackageParserException e) { Log.w(TAG, "failed to parse parse " + module.apkPath, e); } + module.service = new LSPInjectedModuleService(module); modules.add(module); } } @@ -1003,9 +1004,9 @@ public class ConfigManager { } // this is slow, avoid using it - public String getModule(int uid) { + public Module getModule(int uid) { for (var module : cachedModule.values()) { - if (module.appId == uid % PER_USER_RANGE) return module.packageName; + if (module.appId == uid % PER_USER_RANGE) return module; } return null; } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java index c2b70316..e2e43f78 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java @@ -21,7 +21,6 @@ package org.lsposed.lspd.service; import static org.lsposed.lspd.service.ServiceManager.TAG; -import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; @@ -142,15 +141,6 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { return ConfigManager.getInstance().getPrefsPath(packageName, getCallingUid()); } - @Override - public Bundle requestRemotePreferences(String packageName, int userId, String group, IBinder callback) throws RemoteException { - ensureRegistered(); - // TODO: Handle callback - var bundle = new Bundle(); - bundle.putSerializable("map", ConfigManager.getInstance().getModulePrefs(packageName, userId, group)); - return bundle; - } - @Override public ParcelFileDescriptor requestInjectedManagerBinder(List binder) throws RemoteException { var processInfo = ensureRegistered(); diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java new file mode 100644 index 00000000..3102ce30 --- /dev/null +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java @@ -0,0 +1,56 @@ +package org.lsposed.lspd.service; + +import static org.lsposed.lspd.service.PackageService.PER_USER_RANGE; + +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; + +import org.lsposed.lspd.models.Module; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class LSPInjectedModuleService extends ILSPInjectedModuleService.Stub { + private final Module loadedModule; + private final LSPModuleService moduleService; + + Map> callbacks = new ConcurrentHashMap<>(); + + LSPInjectedModuleService(Module module) { + loadedModule = module; + moduleService = new LSPModuleService(module); + } + + LSPModuleService getModuleService() { + return moduleService; + } + + @Override + public Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback) throws RemoteException { + var bundle = new Bundle(); + var userId = Binder.getCallingUid() % PER_USER_RANGE; + bundle.putSerializable("map", ConfigManager.getInstance().getModulePrefs(loadedModule.packageName, userId, group)); + if (callback != null) { + var groupCallbacks = callbacks.computeIfAbsent(group, k -> ConcurrentHashMap.newKeySet()); + groupCallbacks.add(callback); + callback.asBinder().unlinkToDeath(() -> groupCallbacks.remove(callback), 0); + } + return bundle; + } + + void onUpdateRemotePreferences(String group, Bundle diff) { + var groupCallbacks = callbacks.get(group); + if (groupCallbacks != null) { + for (var callback : groupCallbacks) { + try { + callback.onUpdate(diff); + } catch (RemoteException e) { + groupCallbacks.remove(callback); + } + } + } + } +} diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java index c655dca0..c98a3ac0 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java @@ -19,13 +19,19 @@ package org.lsposed.lspd.service; -import android.app.IUidObserver; import android.content.AttributionSource; import android.os.Build; import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.util.Log; -import java.util.Map; +import org.lsposed.daemon.BuildConfig; +import org.lsposed.lspd.models.Module; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -37,15 +43,16 @@ public class LSPModuleService extends IXposedService.Stub { private final static Set uidSet = ConcurrentHashMap.newKeySet(); - private final static Map serviceMap = new ConcurrentHashMap<>(); - private final int uid; - private final String packageName; + private final Module loadedModule; static void uidStarts(int uid) { if (!uidSet.contains(uid)) { uidSet.add(uid); - sendBinder(getService(uid)); + var module = ConfigManager.getInstance().getModule(uid); + if (module != null) { + ((LSPInjectedModuleService) module.service).getModuleService().sendBinder(uid); + } } } @@ -53,10 +60,8 @@ public class LSPModuleService extends IXposedService.Stub { uidSet.remove(uid); } - private static void sendBinder(LSPModuleService service) { - if (service == null) return; - var uid = service.uid; - var name = service.packageName; + private void sendBinder(int uid) { + var name = loadedModule.packageName; try { int userId = uid / PackageService.PER_USER_RANGE; var authority = name + AUTHORITY_SUFFIX; @@ -66,11 +71,10 @@ public class LSPModuleService extends IXposedService.Stub { return; } var extra = new Bundle(); - extra.putBinder("binder", service.asBinder()); + extra.putBinder("binder", asBinder()); Bundle reply = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - reply = provider.call(new AttributionSource.Builder(1000).setPackageName("android").build(), - authority, SEND_BINDER, null, extra); + reply = provider.call(new AttributionSource.Builder(1000).setPackageName("android").build(), authority, SEND_BINDER, null, extra); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { reply = provider.call("android", null, authority, SEND_BINDER, null, extra); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { @@ -86,23 +90,78 @@ public class LSPModuleService extends IXposedService.Stub { } } - public static LSPModuleService getService(int uid) { - var module = ConfigManager.getInstance().getModule(uid); - if (module == null) return null; - return serviceMap.computeIfAbsent(uid, __ -> new LSPModuleService(module, uid)); - } - - public static void removeService(int uid) { - serviceMap.remove(uid); - } - - private LSPModuleService(String name, int uid) { - this.uid = uid; - this.packageName = name; + LSPModuleService(Module module) { + loadedModule = module; } @Override public long getAPIVersion() { return API; } + + @Override + public String implementationName() { + return "LSPosed"; + } + + @Override + public String implementationVersion() throws RemoteException { + return BuildConfig.VERSION_NAME; + } + + @Override + public long implementationVersionCode() throws RemoteException { + return BuildConfig.VERSION_CODE; + } + + @Override + public List getScope() { + ArrayList res = new ArrayList<>(); + var scope = ConfigManager.getInstance().getModuleScope(loadedModule.packageName); + if (scope == null) return res; + for (var s : scope) { + res.add(s.packageName); + } + return res; + } + + @Override + public void requestScope(String packageName) { + // TODO + } + + @Override + public Bundle requestRemotePreferences(String group) throws RemoteException { + // TODO + return null; + } + + @Override + public void updateRemotePreferences(String group, Bundle diff) throws RemoteException { + // TODO + ((LSPInjectedModuleService) loadedModule.service).onUpdateRemotePreferences(group, diff); + } + + @Override + public ParcelFileDescriptor openRemoteFile(String path, int mode) throws RemoteException { + try { + var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path); + if (!absolutePath.getParent().toFile().mkdirs()) { + throw new IOException("failed to create parent dir"); + } + return ParcelFileDescriptor.open(absolutePath.toFile(), mode); + } catch (Throwable e) { + throw new RemoteException(e.getMessage()); + } + } + + @Override + public boolean deleteRemoteFile(String path) throws RemoteException { + try { + var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path); + return absolutePath.toFile().delete(); + } catch (Throwable e) { + throw new RemoteException(e.getMessage()); + } + } } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java index 86b20458..50716cdd 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java @@ -24,7 +24,6 @@ import static org.lsposed.lspd.service.PackageService.PER_USER_RANGE; import static org.lsposed.lspd.service.ServiceManager.TAG; import static org.lsposed.lspd.service.ServiceManager.getExecutorService; -import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.IUidObserver; import android.content.IIntentReceiver; @@ -96,7 +95,7 @@ public class LSPosedService extends ILSPosedService.Stub { var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false); if (userId == USER_NULL) userId = uid % PER_USER_RANGE; Uri uri = intent.getData(); - String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : ConfigManager.getInstance().getModule(uid); + String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : ConfigManager.getInstance().getModule(uid).packageName; ApplicationInfo applicationInfo = null; if (moduleName != null) { diff --git a/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl b/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl index bc1f1d4a..91f6d636 100644 --- a/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl +++ b/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl @@ -5,5 +5,21 @@ interface IXposedService { const String AUTHORITY_SUFFIX = ".XposedService"; const String SEND_BINDER = "SendBinder"; + // framework details long getAPIVersion() = 1; + String implementationName() = 2; + String implementationVersion() = 3; + long implementationVersionCode() = 4; + + // scope utilities + List getScope() = 10; + oneway void requestScope(String packageName) = 11; + + // remote preference utilities + Bundle requestRemotePreferences(String group) = 20; + void updateRemotePreferences(String group, in Bundle diff) = 21; + + // remote file utilities + ParcelFileDescriptor openRemoteFile(String path, int mode) = 30; + boolean deleteRemoteFile(String path) = 31; } diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/Module.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/Module.aidl index b27d833e..d2886f90 100644 --- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/Module.aidl +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/Module.aidl @@ -1,5 +1,6 @@ package org.lsposed.lspd.models; import org.lsposed.lspd.models.PreLoadedApk; +import org.lsposed.lspd.service.ILSPInjectedModuleService; parcelable Module { String packageName; @@ -7,4 +8,5 @@ parcelable Module { String apkPath; PreLoadedApk file; ApplicationInfo applicationInfo; + ILSPInjectedModuleService service; } diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl index 9c656e72..4f01ab0e 100644 --- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl @@ -9,7 +9,5 @@ interface ILSPApplicationService { String getPrefsPath(String packageName); - Bundle requestRemotePreferences(String packageName, int userId, String group, IBinder callback); - ParcelFileDescriptor requestInjectedManagerBinder(out List binder); } diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPInjectedModuleService.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPInjectedModuleService.aidl new file mode 100644 index 00000000..fd441240 --- /dev/null +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPInjectedModuleService.aidl @@ -0,0 +1,7 @@ +package org.lsposed.lspd.service; + +import org.lsposed.lspd.service.IRemotePreferenceCallback; + +interface ILSPInjectedModuleService { + Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback); +} diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/impl/IRemotePreferenceCallback.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/IRemotePreferenceCallback.aidl similarity index 70% rename from services/daemon-service/src/main/aidl/org/lsposed/lspd/impl/IRemotePreferenceCallback.aidl rename to services/daemon-service/src/main/aidl/org/lsposed/lspd/service/IRemotePreferenceCallback.aidl index 268347c8..259c5def 100644 --- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/impl/IRemotePreferenceCallback.aidl +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/IRemotePreferenceCallback.aidl @@ -1,4 +1,4 @@ -package org.lsposed.lspd.impl; +package org.lsposed.lspd.service; interface IRemotePreferenceCallback { oneway void onUpdate(in Bundle map);