Add more module service interfaces

This commit is contained in:
LoveSy 2023-01-03 00:58:20 +08:00 committed by LoveSy
parent acaf40ca44
commit ffc20e2e72
14 changed files with 205 additions and 59 deletions

View File

@ -84,15 +84,6 @@ public class ApplicationServiceClient implements ILSPApplicationService, IBinder
return null; 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 @Override
public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) { public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) {
try { try {

View File

@ -36,6 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.lsposed.lspd.models.Module; import org.lsposed.lspd.models.Module;
import org.lsposed.lspd.service.ILSPInjectedModuleService;
import org.lsposed.lspd.util.LspModuleClassLoader; import org.lsposed.lspd.util.LspModuleClassLoader;
import java.io.File; import java.io.File;
@ -70,12 +71,14 @@ public class LSPosedContext extends XposedContext {
private final Context mBase; private final Context mBase;
private final String mPackageName; private final String mPackageName;
private final String mApkPath; private final String mApkPath;
private final ILSPInjectedModuleService service;
private final Map<String, SharedPreferences> mRemotePrefs = new ConcurrentHashMap<>(); private final Map<String, SharedPreferences> mRemotePrefs = new ConcurrentHashMap<>();
LSPosedContext(Context base, String packageName, String apkPath) { LSPosedContext(Context base, String packageName, String apkPath, ILSPInjectedModuleService service) {
this.mBase = base; this.mBase = base;
this.mPackageName = packageName; this.mPackageName = packageName;
this.mApkPath = apkPath; this.mApkPath = apkPath;
this.service = service;
} }
public static void callOnPackageLoaded(XposedModuleInterface.PackageLoadedParam param) { public static void callOnPackageLoaded(XposedModuleInterface.PackageLoadedParam param) {
@ -143,7 +146,7 @@ public class LSPosedContext extends XposedContext {
} }
args[i] = null; 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) { for (var entry : module.file.moduleClassNames) {
var moduleClass = ctx.getClassLoader().loadClass(entry); var moduleClass = ctx.getClassLoader().loadClass(entry);
Log.d(TAG, " Loading class " + moduleClass); Log.d(TAG, " Loading class " + moduleClass);
@ -267,7 +270,7 @@ public class LSPosedContext extends XposedContext {
@Override @Override
public SharedPreferences getSharedPreferences(String name, int mode) { public SharedPreferences getSharedPreferences(String name, int mode) {
if (name == null) throw new IllegalArgumentException("name must not be null"); 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 @Override

View File

@ -1,13 +1,14 @@
package org.lsposed.lspd.impl; package org.lsposed.lspd.impl;
import static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.lsposed.lspd.service.ILSPInjectedModuleService;
import org.lsposed.lspd.service.IRemotePreferenceCallback;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
@ -29,6 +30,14 @@ public class LSPosedRemotePreferences implements SharedPreferences {
synchronized public void onUpdate(Bundle bundle) { synchronized public void onUpdate(Bundle bundle) {
if (bundle.containsKey("map")) if (bundle.containsKey("map"))
mMap.putAll((Map<String, ?>) bundle.getSerializable("map")); mMap.putAll((Map<String, ?>) 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")) { if (bundle.containsKey("diff")) {
for (var key : bundle.getStringArrayList("diff")) { for (var key : bundle.getStringArrayList("diff")) {
synchronized (mListeners) { 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 { try {
Bundle output = serviceClient.requestRemotePreferences(packageName, userId, group, callback.asBinder()); Bundle output = service.requestRemotePreferences(group, callback);
callback.onUpdate(output); callback.onUpdate(output);
} catch (RemoteException e) { } catch (RemoteException e) {
XposedBridge.log(e); XposedBridge.log(e);

View File

@ -26,6 +26,7 @@ import android.content.res.AssetManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.Process; import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux; import android.os.SELinux;
import android.os.SharedMemory; import android.os.SharedMemory;
import android.system.ErrnoException; import android.system.ErrnoException;
@ -77,6 +78,7 @@ import hidden.HiddenApiBridge;
public class ConfigFileManager { public class ConfigFileManager {
static final Path basePath = Paths.get("/data/adb/lspd"); 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 daemonApkPath = Paths.get(System.getProperty("java.class.path", null));
static final Path managerApkPath = basePath.resolve("manager.apk"); static final Path managerApkPath = basePath.resolve("manager.apk");
static final File magiskDbPath = new File("/data/adb/magisk.db"); static final File magiskDbPath = new File("/data/adb/magisk.db");
@ -427,6 +429,19 @@ public class ConfigFileManager {
return preloadDex; 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 static class FileLocker {
private final FileChannel lockChannel; private final FileChannel lockChannel;
private final FileLock locker; private final FileLock locker;

View File

@ -229,6 +229,7 @@ public class ConfigManager {
} catch (PackageParser.PackageParserException e) { } catch (PackageParser.PackageParserException e) {
Log.w(TAG, "failed to parse parse " + module.apkPath, e); Log.w(TAG, "failed to parse parse " + module.apkPath, e);
} }
module.service = new LSPInjectedModuleService(module);
modules.add(module); modules.add(module);
} }
} }
@ -1003,9 +1004,9 @@ public class ConfigManager {
} }
// this is slow, avoid using it // this is slow, avoid using it
public String getModule(int uid) { public Module getModule(int uid) {
for (var module : cachedModule.values()) { 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; return null;
} }

View File

@ -21,7 +21,6 @@ package org.lsposed.lspd.service;
import static org.lsposed.lspd.service.ServiceManager.TAG; import static org.lsposed.lspd.service.ServiceManager.TAG;
import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@ -142,15 +141,6 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
return ConfigManager.getInstance().getPrefsPath(packageName, getCallingUid()); 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 @Override
public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) throws RemoteException { public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) throws RemoteException {
var processInfo = ensureRegistered(); var processInfo = ensureRegistered();

View File

@ -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<String, Set<IRemotePreferenceCallback>> 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);
}
}
}
}
}

View File

@ -19,13 +19,19 @@
package org.lsposed.lspd.service; package org.lsposed.lspd.service;
import android.app.IUidObserver;
import android.content.AttributionSource; import android.content.AttributionSource;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log; 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.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -37,15 +43,16 @@ public class LSPModuleService extends IXposedService.Stub {
private final static Set<Integer> uidSet = ConcurrentHashMap.newKeySet(); private final static Set<Integer> uidSet = ConcurrentHashMap.newKeySet();
private final static Map<Integer, LSPModuleService> serviceMap = new ConcurrentHashMap<>();
private final int uid; private final Module loadedModule;
private final String packageName;
static void uidStarts(int uid) { static void uidStarts(int uid) {
if (!uidSet.contains(uid)) { if (!uidSet.contains(uid)) {
uidSet.add(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); uidSet.remove(uid);
} }
private static void sendBinder(LSPModuleService service) { private void sendBinder(int uid) {
if (service == null) return; var name = loadedModule.packageName;
var uid = service.uid;
var name = service.packageName;
try { try {
int userId = uid / PackageService.PER_USER_RANGE; int userId = uid / PackageService.PER_USER_RANGE;
var authority = name + AUTHORITY_SUFFIX; var authority = name + AUTHORITY_SUFFIX;
@ -66,11 +71,10 @@ public class LSPModuleService extends IXposedService.Stub {
return; return;
} }
var extra = new Bundle(); var extra = new Bundle();
extra.putBinder("binder", service.asBinder()); extra.putBinder("binder", asBinder());
Bundle reply = null; Bundle reply = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
reply = provider.call(new AttributionSource.Builder(1000).setPackageName("android").build(), reply = provider.call(new AttributionSource.Builder(1000).setPackageName("android").build(), authority, SEND_BINDER, null, extra);
authority, SEND_BINDER, null, extra);
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
reply = provider.call("android", null, authority, SEND_BINDER, null, extra); reply = provider.call("android", null, authority, SEND_BINDER, null, extra);
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { } 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) { LSPModuleService(Module module) {
var module = ConfigManager.getInstance().getModule(uid); loadedModule = module;
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;
} }
@Override @Override
public long getAPIVersion() { public long getAPIVersion() {
return API; 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<String> getScope() {
ArrayList<String> 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());
}
}
} }

View File

@ -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.TAG;
import static org.lsposed.lspd.service.ServiceManager.getExecutorService; import static org.lsposed.lspd.service.ServiceManager.getExecutorService;
import android.app.ActivityManager;
import android.app.IApplicationThread; import android.app.IApplicationThread;
import android.app.IUidObserver; import android.app.IUidObserver;
import android.content.IIntentReceiver; import android.content.IIntentReceiver;
@ -96,7 +95,7 @@ public class LSPosedService extends ILSPosedService.Stub {
var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false); var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);
if (userId == USER_NULL) userId = uid % PER_USER_RANGE; if (userId == USER_NULL) userId = uid % PER_USER_RANGE;
Uri uri = intent.getData(); 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; ApplicationInfo applicationInfo = null;
if (moduleName != null) { if (moduleName != null) {

View File

@ -5,5 +5,21 @@ interface IXposedService {
const String AUTHORITY_SUFFIX = ".XposedService"; const String AUTHORITY_SUFFIX = ".XposedService";
const String SEND_BINDER = "SendBinder"; const String SEND_BINDER = "SendBinder";
// framework details
long getAPIVersion() = 1; long getAPIVersion() = 1;
String implementationName() = 2;
String implementationVersion() = 3;
long implementationVersionCode() = 4;
// scope utilities
List<String> 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;
} }

View File

@ -1,5 +1,6 @@
package org.lsposed.lspd.models; package org.lsposed.lspd.models;
import org.lsposed.lspd.models.PreLoadedApk; import org.lsposed.lspd.models.PreLoadedApk;
import org.lsposed.lspd.service.ILSPInjectedModuleService;
parcelable Module { parcelable Module {
String packageName; String packageName;
@ -7,4 +8,5 @@ parcelable Module {
String apkPath; String apkPath;
PreLoadedApk file; PreLoadedApk file;
ApplicationInfo applicationInfo; ApplicationInfo applicationInfo;
ILSPInjectedModuleService service;
} }

View File

@ -9,7 +9,5 @@ interface ILSPApplicationService {
String getPrefsPath(String packageName); String getPrefsPath(String packageName);
Bundle requestRemotePreferences(String packageName, int userId, String group, IBinder callback);
ParcelFileDescriptor requestInjectedManagerBinder(out List<IBinder> binder); ParcelFileDescriptor requestInjectedManagerBinder(out List<IBinder> binder);
} }

View File

@ -0,0 +1,7 @@
package org.lsposed.lspd.service;
import org.lsposed.lspd.service.IRemotePreferenceCallback;
interface ILSPInjectedModuleService {
Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback);
}

View File

@ -1,4 +1,4 @@
package org.lsposed.lspd.impl; package org.lsposed.lspd.service;
interface IRemotePreferenceCallback { interface IRemotePreferenceCallback {
oneway void onUpdate(in Bundle map); oneway void onUpdate(in Bundle map);