Preparation for Module api (#506)
Co-authored-by: vvb2060 <vvb2060@gmail.com>
This commit is contained in:
parent
a70e2595bd
commit
10a2ae56a6
|
|
@ -1,16 +1,20 @@
|
|||
package org.lsposed.lspd.service;
|
||||
|
||||
import org.lsposed.lspd.service.Module;
|
||||
|
||||
interface ILSPApplicationService {
|
||||
IBinder requestModuleBinder();
|
||||
IBinder requestModuleBinder(String name);
|
||||
|
||||
//TODO: after array copy bug fixed, use array instead of list
|
||||
boolean requestManagerBinder(String packageName, String path, out List<IBinder> binder);
|
||||
|
||||
boolean isResourcesHookEnabled();
|
||||
|
||||
Map getModulesList(String processName);
|
||||
List<Module> getModulesList(String processName);
|
||||
|
||||
String getPrefsPath(String packageName);
|
||||
|
||||
ParcelFileDescriptor getModuleLogger();
|
||||
|
||||
Bundle requestRemotePreference(String packageName, int userId, IBinder callback);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package org.lsposed.lspd.service;
|
||||
import org.lsposed.lspd.service.ModuleConfig;
|
||||
|
||||
parcelable Module {
|
||||
String name;
|
||||
String apk;
|
||||
ModuleConfig config;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package org.lsposed.lspd.service;
|
||||
|
||||
parcelable ModuleConfig {
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package de.robv.android.xposed;
|
||||
|
||||
import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.github.xposed.xposedservice.IXRemotePreferenceCallback;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class XRemotePreference implements SharedPreferences {
|
||||
|
||||
private Map<String, Object> mMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Object CONTENT = new Object();
|
||||
final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<>();
|
||||
|
||||
IXRemotePreferenceCallback callback = new IXRemotePreferenceCallback.Stub() {
|
||||
@Override
|
||||
synchronized public void onUpdate(Bundle bundle) {
|
||||
if (bundle.containsKey("map"))
|
||||
mMap = (ConcurrentHashMap<String, Object>) bundle.getSerializable("map");
|
||||
if (bundle.containsKey("diff")) {
|
||||
for (var key : bundle.getStringArrayList("diff")) {
|
||||
synchronized (mListeners) {
|
||||
mListeners.forEach((listener, __) -> {
|
||||
listener.onSharedPreferenceChanged(XRemotePreference.this, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public XRemotePreference(String packageName) {
|
||||
this(packageName, 0);
|
||||
}
|
||||
|
||||
public XRemotePreference(String packageName, int userId) {
|
||||
try {
|
||||
Bundle output = serviceClient.requestRemotePreference(packageName, userId, callback.asBinder());
|
||||
callback.onUpdate(output);
|
||||
} catch (RemoteException e) {
|
||||
XposedBridge.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getAll() {
|
||||
return new TreeMap<>(mMap);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getString(String key, @Nullable String defValue) {
|
||||
var v = (String) mMap.getOrDefault(key, defValue);
|
||||
if (v != null) return v;
|
||||
return defValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
|
||||
var v = (Set<String>) mMap.getOrDefault(key, defValues);
|
||||
if (v != null) return v;
|
||||
return defValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defValue) {
|
||||
var v = (Integer) mMap.getOrDefault(key, defValue);
|
||||
if (v != null) return v;
|
||||
return defValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defValue) {
|
||||
var v = (Long) mMap.getOrDefault(key, defValue);
|
||||
if (v != null) return v;
|
||||
return defValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String key, float defValue) {
|
||||
var v = (Float) mMap.getOrDefault(key, defValue);
|
||||
if (v != null) return v;
|
||||
return defValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defValue) {
|
||||
var v = (Boolean) mMap.getOrDefault(key, defValue);
|
||||
if (v != null) return v;
|
||||
return defValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return mMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor edit() {
|
||||
throw new UnsupportedOperationException("Read only implementation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
synchronized (mListeners) {
|
||||
mListeners.put(listener, CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
synchronized (mListeners) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -221,7 +221,9 @@ public final class XposedInit {
|
|||
synchronized (moduleLoadLock) {
|
||||
var moduleList = serviceClient.getModulesList();
|
||||
ArraySet<String> newLoadedApk = new ArraySet<>();
|
||||
moduleList.forEach((name, apk) -> {
|
||||
moduleList.forEach(module -> {
|
||||
var apk = module.apk;
|
||||
var name = module.name;
|
||||
if (loadedModules.contains(apk)) {
|
||||
newLoadedApk.add(apk);
|
||||
} else {
|
||||
|
|
@ -386,7 +388,7 @@ public final class XposedInit {
|
|||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
|
||||
return initNativeModule(mcl, apk) && initModule(mcl, name, apk);
|
||||
return initNativeModule(mcl, name) && initModule(mcl, name, apk);
|
||||
}
|
||||
|
||||
public final static HashSet<String> loadedPackagesInProcess = new HashSet<>(1);
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ import android.os.IBinder;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import org.lsposed.lspd.service.ILSPApplicationService;
|
||||
|
||||
import org.lsposed.lspd.service.Module;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
abstract public class ApplicationServiceClient implements ILSPApplicationService {
|
||||
|
||||
public static ApplicationServiceClient serviceClient = null;
|
||||
|
||||
@Override
|
||||
abstract public IBinder requestModuleBinder();
|
||||
abstract public IBinder requestModuleBinder(String name);
|
||||
|
||||
@Override
|
||||
abstract public boolean requestManagerBinder(String packageName, String path, List<IBinder> binder);
|
||||
|
|
@ -22,9 +21,9 @@ abstract public class ApplicationServiceClient implements ILSPApplicationService
|
|||
abstract public boolean isResourcesHookEnabled();
|
||||
|
||||
@Override
|
||||
abstract public Map getModulesList(String processName);
|
||||
abstract public List getModulesList(String processName);
|
||||
|
||||
abstract public Map<String, String> getModulesList();
|
||||
abstract public List<Module> getModulesList();
|
||||
|
||||
@Override
|
||||
abstract public String getPrefsPath(String packageName);
|
||||
|
|
|
|||
|
|
@ -19,16 +19,17 @@
|
|||
|
||||
package org.lsposed.lspd.config;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.lsposed.lspd.service.ILSPApplicationService;
|
||||
import org.lsposed.lspd.service.Module;
|
||||
import org.lsposed.lspd.util.Utils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LSPApplicationServiceClient extends ApplicationServiceClient {
|
||||
static ILSPApplicationService service = null;
|
||||
|
|
@ -60,9 +61,9 @@ public class LSPApplicationServiceClient extends ApplicationServiceClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBinder requestModuleBinder() {
|
||||
public IBinder requestModuleBinder(String name) {
|
||||
try {
|
||||
return service.requestModuleBinder();
|
||||
return service.requestModuleBinder(name);
|
||||
} catch (RemoteException | NullPointerException ignored) {
|
||||
}
|
||||
return null;
|
||||
|
|
@ -87,17 +88,15 @@ public class LSPApplicationServiceClient extends ApplicationServiceClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getModulesList(String processName) {
|
||||
public List<Module> getModulesList(String processName) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return service.getModulesList(processName);
|
||||
} catch (RemoteException | NullPointerException ignored) {
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getModulesList() {
|
||||
public List<Module> getModulesList() {
|
||||
return getModulesList(processName);
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +118,11 @@ public class LSPApplicationServiceClient extends ApplicationServiceClient {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle requestRemotePreference(String packageName, int userId, IBinder callback) throws RemoteException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
return serviceBinder;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class LoadedApkGetCLHooker extends XC_MethodHook {
|
|||
lpparam.appInfo = loadedApk.getApplicationInfo();
|
||||
lpparam.isFirstApplication = this.isFirstApplication;
|
||||
|
||||
IBinder moduleBinder = serviceClient.requestModuleBinder();
|
||||
IBinder moduleBinder = serviceClient.requestModuleBinder(lpparam.packageName);
|
||||
if (moduleBinder != null) {
|
||||
hookNewXSP(lpparam);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,14 +42,17 @@ import android.util.Pair;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
import org.lsposed.lspd.BuildConfig;
|
||||
import org.lsposed.lspd.models.Application;
|
||||
import org.lsposed.lspd.service.Module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.file.Files;
|
||||
|
|
@ -61,6 +64,7 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
@ -184,11 +188,21 @@ public class ConfigManager {
|
|||
"user_id integer NOT NULL," +
|
||||
"PRIMARY KEY (mid, app_pkg_name, user_id)" +
|
||||
");");
|
||||
private static final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS config (" +
|
||||
"module_pkg_name text NOT NULL," +
|
||||
"user_id integer NOT NULL," +
|
||||
"`group` text NOT NULL," +
|
||||
"`key` text NOT NULL," +
|
||||
"data blob NOT NULL," +
|
||||
"PRIMARY KEY (module_pkg_name, user_id)" +
|
||||
");");
|
||||
|
||||
private final Map<ProcessScope, Map<String, String>> cachedScope = new ConcurrentHashMap<>();
|
||||
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Integer, String> cachedModule = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Pair<String, Integer>, Map<String, ConcurrentHashMap<String, Object>>> cachedConfig = new ConcurrentHashMap<>();
|
||||
|
||||
private void updateCaches(boolean sync) {
|
||||
synchronized (this) {
|
||||
requestScopeCacheTime = requestModuleCacheTime = SystemClock.elapsedRealtime();
|
||||
|
|
@ -230,13 +244,17 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getModulesForSystemServer() {
|
||||
HashMap<String, String> modules = new HashMap<>();
|
||||
public List<Module> getModulesForSystemServer() {
|
||||
List<Module> modules = new LinkedList<>();
|
||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"module_pkg_name", "apk_path"}, "app_pkg_name=? AND enabled=1", new String[]{"android"}, null, null, null)) {
|
||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||
while (cursor.moveToNext()) {
|
||||
modules.put(cursor.getString(pkgNameIdx), cursor.getString(apkPathIdx));
|
||||
var module = new Module();
|
||||
module.name = cursor.getString(pkgNameIdx);
|
||||
module.apk = cursor.getString(apkPathIdx);
|
||||
module.config = null;
|
||||
modules.add(module);
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
|
|
@ -343,6 +361,7 @@ public class ConfigManager {
|
|||
private void createTables() {
|
||||
createModulesTable.execute();
|
||||
createScopeTable.execute();
|
||||
createConfigTable.execute();
|
||||
}
|
||||
|
||||
private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {
|
||||
|
|
@ -354,6 +373,52 @@ public class ConfigManager {
|
|||
return processes;
|
||||
}
|
||||
|
||||
private @NonNull
|
||||
Map<String, ConcurrentHashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
||||
var config = new ConcurrentHashMap<String, ConcurrentHashMap<String, Object>>();
|
||||
|
||||
try (Cursor cursor = db.query("config", new String[]{"group", "key", "data"},
|
||||
"module_pkg_name = ? and user_id = ?", new String[]{name, String.valueOf(user_id)}, null, null, null)) {
|
||||
if (cursor == null) {
|
||||
Log.e(TAG, "db cache failed");
|
||||
return config;
|
||||
}
|
||||
int groupIdx = cursor.getColumnIndex("group");
|
||||
int keyIdx = cursor.getColumnIndex("key");
|
||||
int dataIdx = cursor.getColumnIndex("data");
|
||||
while (cursor.moveToNext()) {
|
||||
var group = cursor.getString(groupIdx);
|
||||
var key = cursor.getString(keyIdx);
|
||||
var data = cursor.getBlob(dataIdx);
|
||||
var object = SerializationUtils.deserialize(data);
|
||||
if (object == null) continue;
|
||||
config.computeIfAbsent(group, g -> new ConcurrentHashMap<>()).put(key, object);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void updateModulePrefs(String moduleName, int userId, String group, String key, Object value) {
|
||||
var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));
|
||||
var prefs = config.computeIfAbsent(group, g -> new ConcurrentHashMap<>());
|
||||
if (value instanceof Serializable) {
|
||||
prefs.put(key, value);
|
||||
var values = new ContentValues();
|
||||
values.put("group", group);
|
||||
values.put("key", key);
|
||||
values.put("value", SerializationUtils.serialize((Serializable) value));
|
||||
db.updateWithOnConflict("config", values, "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)}, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
} else {
|
||||
prefs.remove(key);
|
||||
db.delete("config", "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)});
|
||||
}
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, Object> getModulePrefs(String moduleName, int userId, String group) {
|
||||
var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));
|
||||
return config.getOrDefault(group, null);
|
||||
}
|
||||
|
||||
private synchronized void cacheModules() {
|
||||
// skip caching when pm is not yet available
|
||||
if (!packageStarted) return;
|
||||
|
|
@ -440,12 +505,16 @@ public class ConfigManager {
|
|||
continue;
|
||||
}
|
||||
for (ProcessScope processScope : processesScope) {
|
||||
cachedScope.computeIfAbsent(processScope, ignored -> new HashMap<>()).put(module_pkg, apk_path);
|
||||
var module = new Module();
|
||||
module.name = module_pkg;
|
||||
module.apk = apk_path;
|
||||
module.config = null;
|
||||
cachedScope.computeIfAbsent(processScope, ignored -> new LinkedList<>()).add(module);
|
||||
if (module_pkg.equals(app.packageName)) {
|
||||
var appId = processScope.uid % PER_USER_RANGE;
|
||||
for (var user : UserService.getUsers()) {
|
||||
cachedScope.computeIfAbsent(new ProcessScope(processScope.processName, user.id * PER_USER_RANGE + appId),
|
||||
ignored -> new HashMap<>()).put(module_pkg, apk_path);
|
||||
ignored -> new LinkedList<>()).add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -459,15 +528,15 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
Log.d(TAG, "cached Scope");
|
||||
cachedScope.forEach((ps, module) -> {
|
||||
cachedScope.forEach((ps, modules) -> {
|
||||
Log.d(TAG, ps.processName + "/" + ps.uid);
|
||||
module.forEach((pkg_name, apk_path) -> Log.d(TAG, "\t" + pkg_name));
|
||||
modules.forEach(module -> Log.d(TAG, "\t" + module.name));
|
||||
});
|
||||
}
|
||||
|
||||
// This is called when a new process created, use the cached result
|
||||
public Map<String, String> getModulesForProcess(String processName, int uid) {
|
||||
return isManager(uid) ? Collections.emptyMap() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyMap());
|
||||
public List<Module> getModulesForProcess(String processName, int uid) {
|
||||
return isManager(uid) ? Collections.emptyList() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyList());
|
||||
}
|
||||
|
||||
// This is called when a new process created, use the cached result
|
||||
|
|
@ -759,8 +828,8 @@ public class ConfigManager {
|
|||
return cachedModule.containsKey(uid % PER_USER_RANGE);
|
||||
}
|
||||
|
||||
public boolean isModule(String packageName) {
|
||||
return cachedModule.containsValue(packageName);
|
||||
public boolean isModule(int uid, String name) {
|
||||
return name.equals(cachedModule.getOrDefault(uid % PER_USER_RANGE, null));
|
||||
}
|
||||
|
||||
private void recursivelyChown(File file, int uid, int gid) throws ErrnoException {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.lsposed.lspd.service;
|
||||
|
||||
import android.os.Bundle;
|
||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
import android.os.IBinder;
|
||||
|
|
@ -71,7 +72,7 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getModulesList(String processName) throws RemoteException {
|
||||
public List<Module> getModulesList(String processName) throws RemoteException {
|
||||
ensureRegistered();
|
||||
int callingUid = getCallingUid();
|
||||
if (callingUid == 1000 && processName.equals("android")) {
|
||||
|
|
@ -93,15 +94,20 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBinder requestModuleBinder() throws RemoteException {
|
||||
public Bundle requestRemotePreference(String packageName, int userId, IBinder callback) throws RemoteException {
|
||||
ensureRegistered();
|
||||
if (ConfigManager.getInstance().isModule(getCallingUid())) {
|
||||
ConfigManager.getInstance().ensureModulePrefsPermission(getCallingUid());
|
||||
return ServiceManager.getModuleService();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder requestModuleBinder(String name) throws RemoteException {
|
||||
ensureRegistered();
|
||||
if (ConfigManager.getInstance().isModule(getCallingUid(), name)) {
|
||||
ConfigManager.getInstance().ensureModulePrefsPermission(getCallingUid());
|
||||
return ServiceManager.getModuleService(name);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestManagerBinder(String packageName, String path, List<IBinder> binder) throws RemoteException {
|
||||
ensureRegistered();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ import io.github.xposed.xposedservice.IXposedService;
|
|||
|
||||
public class LSPModuleService extends IXposedService.Stub {
|
||||
|
||||
public LSPModuleService() {
|
||||
final private String name;
|
||||
|
||||
public LSPModuleService(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -35,11 +35,12 @@ import com.android.internal.os.BinderInternal;
|
|||
|
||||
import org.lsposed.lspd.BuildConfig;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import hidden.HiddenApiBridge;
|
||||
|
||||
public class ServiceManager {
|
||||
private static LSPosedService mainService = null;
|
||||
private static LSPModuleService moduleService = null;
|
||||
final private static ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
||||
private static LSPApplicationService applicationService = null;
|
||||
private static LSPManagerService managerService = null;
|
||||
private static LSPSystemServerService systemServerService = null;
|
||||
|
|
@ -81,7 +82,6 @@ public class ServiceManager {
|
|||
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
||||
Looper.prepareMainLooper();
|
||||
mainService = new LSPosedService();
|
||||
moduleService = new LSPModuleService();
|
||||
applicationService = new LSPApplicationService();
|
||||
managerService = new LSPManagerService();
|
||||
systemServerService = new LSPSystemServerService();
|
||||
|
|
@ -129,8 +129,8 @@ public class ServiceManager {
|
|||
throw new RuntimeException("Main thread loop unexpectedly exited");
|
||||
}
|
||||
|
||||
public static LSPModuleService getModuleService() {
|
||||
return moduleService;
|
||||
public static LSPModuleService getModuleService(String module) {
|
||||
return moduleServices.computeIfAbsent(module, LSPModuleService::new);
|
||||
}
|
||||
|
||||
public static LSPApplicationService getApplicationService() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue