More implementation

This commit is contained in:
LoveSy 2023-01-03 17:37:05 +08:00 committed by LoveSy
parent 87db036fdf
commit 9abf3f2b16
6 changed files with 82 additions and 59 deletions

View File

@ -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

View File

@ -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));

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -4,4 +4,6 @@ import org.lsposed.lspd.service.IRemotePreferenceCallback;
interface ILSPInjectedModuleService {
Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback);
ParcelFileDescriptor openRemoteFile(String path);
}