More implementation
This commit is contained in:
parent
87db036fdf
commit
9abf3f2b16
|
|
@ -28,6 +28,7 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
|
@ -42,7 +43,9 @@ import org.lsposed.lspd.util.LspModuleClassLoader;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -285,8 +288,12 @@ public class LSPosedContext extends XposedContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public FileInputStream openFileInput(String name) {
|
||||
throw new AbstractMethodError();
|
||||
public FileInputStream openFileInput(String name) throws FileNotFoundException {
|
||||
try {
|
||||
return new FileInputStream(service.openRemoteFile(name).getFileDescriptor());
|
||||
} catch (RemoteException e) {
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import android.content.pm.PackageParser;
|
|||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
|
@ -44,7 +45,6 @@ import android.os.SystemClock;
|
|||
import android.permission.IPermissionManager;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ public class ConfigManager {
|
|||
private final Map<String, Module> cachedModule = new ConcurrentHashMap<>();
|
||||
|
||||
// packageName, userId, group, key, value
|
||||
private final Map<Pair<String, Integer>, Map<String, ConcurrentHashMap<String, Object>>> cachedConfig = new ConcurrentHashMap<>();
|
||||
private final Map<Pair<String, Integer>, Map<String, HashMap<String, Object>>> cachedConfig = new ConcurrentHashMap<>();
|
||||
|
||||
private void updateCaches(boolean sync) {
|
||||
synchronized (cacheHandler) {
|
||||
|
|
@ -423,8 +423,8 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
private @NonNull
|
||||
Map<String, ConcurrentHashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
||||
var config = new ConcurrentHashMap<String, ConcurrentHashMap<String, Object>>();
|
||||
Map<String, HashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
||||
var config = new ConcurrentHashMap<String, HashMap<String, Object>>();
|
||||
|
||||
try (Cursor cursor = db.query("configs", new String[]{"`group`", "`key`", "data"},
|
||||
"module_pkg_name = ? and user_id = ?", new String[]{name, String.valueOf(user_id)}, null, null, null)) {
|
||||
|
|
@ -441,7 +441,7 @@ public class ConfigManager {
|
|||
var data = cursor.getBlob(dataIdx);
|
||||
var object = SerializationUtils.deserialize(data);
|
||||
if (object == null) continue;
|
||||
config.computeIfAbsent(group, g -> new ConcurrentHashMap<>()).put(key, object);
|
||||
config.computeIfAbsent(group, g -> new HashMap<>()).put(key, object);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
|
|
@ -455,33 +455,49 @@ public class ConfigManager {
|
|||
|
||||
public void updateModulePrefs(String moduleName, int userId, String group, Map<String, Object> values) {
|
||||
var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));
|
||||
var prefs = config.computeIfAbsent(group, g -> new ConcurrentHashMap<>());
|
||||
executeInTransaction(() -> {
|
||||
var contents = new ContentValues();
|
||||
for (var entry : values.entrySet()) {
|
||||
var key = entry.getKey();
|
||||
var value = entry.getValue();
|
||||
if (value instanceof Serializable) {
|
||||
prefs.put(key, value);
|
||||
contents.put("`group`", group);
|
||||
contents.put("`key`", key);
|
||||
contents.put("data", SerializationUtils.serialize((Serializable) value));
|
||||
contents.put("module_pkg_name", moduleName);
|
||||
contents.put("user_id", String.valueOf(userId));
|
||||
} else {
|
||||
prefs.remove(key);
|
||||
db.delete("configs", "module_pkg_name=? and user_id=? and `group`=? and `key`=?", new String[]{moduleName, String.valueOf(userId), group, key});
|
||||
config.compute(group, (g, prefs) -> {
|
||||
HashMap<String, Object> newPrefs = prefs == null ? new HashMap<>() : new HashMap<>(prefs);
|
||||
executeInTransaction(() -> {
|
||||
var contents = new ContentValues();
|
||||
for (var entry : values.entrySet()) {
|
||||
var key = entry.getKey();
|
||||
var value = entry.getValue();
|
||||
if (value instanceof Serializable) {
|
||||
newPrefs.put(key, value);
|
||||
contents.put("`group`", group);
|
||||
contents.put("`key`", key);
|
||||
contents.put("data", SerializationUtils.serialize((Serializable) value));
|
||||
contents.put("module_pkg_name", moduleName);
|
||||
contents.put("user_id", String.valueOf(userId));
|
||||
} else {
|
||||
newPrefs.remove(key);
|
||||
db.delete("configs", "module_pkg_name=? and user_id=? and `group`=? and `key`=?", new String[]{moduleName, String.valueOf(userId), group, key});
|
||||
}
|
||||
if (contents.size() > 0) {
|
||||
db.insertWithOnConflict("configs", null, contents, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
}
|
||||
if (contents.size() > 0) {
|
||||
db.insertWithOnConflict("configs", null, contents, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
var bundle = new Bundle();
|
||||
bundle.putSerializable("config", (Serializable) config);
|
||||
if (bundle.size() > 1024 * 1024) {
|
||||
throw new IllegalArgumentException("Preference too large");
|
||||
}
|
||||
}
|
||||
});
|
||||
return newPrefs;
|
||||
});
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, Object> getModulePrefs(String moduleName, int userId, String group) {
|
||||
public void deleteModulePrefs(String moduleName, int userId, String group) {
|
||||
db.delete("configs", "module_pkg_name=? and user_id=? and `group`=?", new String[]{moduleName, String.valueOf(userId), group});
|
||||
var config = cachedConfig.getOrDefault(new Pair<>(moduleName, userId), null);
|
||||
if (config != null) {
|
||||
config.remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
public HashMap<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, new ConcurrentHashMap<>());
|
||||
return config.getOrDefault(group, new HashMap<>());
|
||||
}
|
||||
|
||||
private synchronized void clearCache() {
|
||||
|
|
@ -1026,11 +1042,6 @@ public class ConfigManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
public boolean isModule(int uid, String name) {
|
||||
var module = cachedModule.getOrDefault(name, null);
|
||||
return module != null && module.appId == uid % PER_USER_RANGE;
|
||||
}
|
||||
|
||||
private void walkFileTree(Path rootDir, Consumer<Path> action) throws IOException {
|
||||
if (Files.notExists(rootDir)) return;
|
||||
Files.walkFileTree(rootDir, new SimpleFileVisitor<>() {
|
||||
|
|
@ -1048,24 +1059,6 @@ public class ConfigManager {
|
|||
});
|
||||
}
|
||||
|
||||
public void ensureModulePrefsPermission(int uid, String packageName) {
|
||||
if (packageName == null) return;
|
||||
var path = Paths.get(getPrefsPath(packageName, uid));
|
||||
try {
|
||||
var perms = PosixFilePermissions.fromString("rwx--x--x");
|
||||
Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms));
|
||||
walkFileTree(path, p -> {
|
||||
try {
|
||||
Os.chown(p.toString(), uid, uid);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeModulePrefs(int uid, String packageName) throws IOException {
|
||||
if (packageName == null) return;
|
||||
var path = Paths.get(getPrefsPath(packageName, uid));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;
|
|||
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.lsposed.lspd.models.Module;
|
||||
|
|
@ -40,6 +41,16 @@ public class LSPInjectedModuleService extends ILSPInjectedModuleService.Stub {
|
|||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openRemoteFile(String path) throws RemoteException {
|
||||
try {
|
||||
var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path);
|
||||
return ParcelFileDescriptor.open(absolutePath.toFile(), ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
} catch (Throwable e) {
|
||||
throw new RemoteException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void onUpdateRemotePreferences(String group, Bundle diff) {
|
||||
var groupCallbacks = callbacks.get(group);
|
||||
if (groupCallbacks != null) {
|
||||
|
|
|
|||
|
|
@ -102,11 +102,12 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
loadedModule = module;
|
||||
}
|
||||
|
||||
private void ensureModule() throws RemoteException {
|
||||
var appId = Binder.getCallingUid() % PackageService.PER_USER_RANGE;
|
||||
private int ensureModule() throws RemoteException {
|
||||
var appId = Binder.getCallingUid() % PER_USER_RANGE;
|
||||
if (loadedModule.appId != appId) {
|
||||
throw new RemoteException("Module " + loadedModule.packageName + " is not for uid " + Binder.getCallingUid());
|
||||
}
|
||||
return Binder.getCallingUid() / PER_USER_RANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -153,17 +154,15 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
|
||||
@Override
|
||||
public Bundle requestRemotePreferences(String group) throws RemoteException {
|
||||
ensureModule();
|
||||
var userId = ensureModule();
|
||||
var bundle = new Bundle();
|
||||
var userId = Binder.getCallingUid() % PER_USER_RANGE;
|
||||
bundle.putSerializable("map", ConfigManager.getInstance().getModulePrefs(loadedModule.packageName, userId, group));
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRemotePreferences(String group, Bundle diff) throws RemoteException {
|
||||
ensureModule();
|
||||
var userId = Binder.getCallingUid() / PackageService.PER_USER_RANGE;
|
||||
var userId = ensureModule();
|
||||
Map<String, Object> values = new ArrayMap<>();
|
||||
if (diff.containsKey("delete")) {
|
||||
var deletes = diff.getStringArrayList("delete");
|
||||
|
|
@ -181,8 +180,18 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
Log.e(TAG, "updateRemotePreferences: ", e);
|
||||
}
|
||||
}
|
||||
ConfigManager.getInstance().updateModulePrefs(loadedModule.packageName, userId, group, values);
|
||||
((LSPInjectedModuleService) loadedModule.service).onUpdateRemotePreferences(group, diff);
|
||||
try {
|
||||
ConfigManager.getInstance().updateModulePrefs(loadedModule.packageName, userId, group, values);
|
||||
((LSPInjectedModuleService) loadedModule.service).onUpdateRemotePreferences(group, diff);
|
||||
} catch (Throwable e) {
|
||||
throw new RemoteException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRemotePreferences(String group) throws RemoteException {
|
||||
var userId = ensureModule();
|
||||
ConfigManager.getInstance().deleteModulePrefs(loadedModule.packageName, userId, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ interface IXposedService {
|
|||
// remote preference utilities
|
||||
Bundle requestRemotePreferences(String group) = 20;
|
||||
void updateRemotePreferences(String group, in Bundle diff) = 21;
|
||||
void deleteRemotePreferences(String group) = 22;
|
||||
|
||||
// remote file utilities
|
||||
ParcelFileDescriptor openRemoteFile(String path, int mode) = 30;
|
||||
|
|
|
|||
|
|
@ -4,4 +4,6 @@ import org.lsposed.lspd.service.IRemotePreferenceCallback;
|
|||
|
||||
interface ILSPInjectedModuleService {
|
||||
Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback);
|
||||
|
||||
ParcelFileDescriptor openRemoteFile(String path);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue