[core] Refine caching of config manager (#878)
This commit is contained in:
parent
3cd9fd1735
commit
a84935b14e
|
|
@ -3,6 +3,7 @@ import org.lsposed.lspd.models.PreLoadedApk;
|
||||||
|
|
||||||
parcelable Module {
|
parcelable Module {
|
||||||
String packageName;
|
String packageName;
|
||||||
|
int appId;
|
||||||
String apkPath;
|
String apkPath;
|
||||||
PreLoadedApk file;
|
PreLoadedApk file;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ import android.app.ActivityThread;
|
||||||
import android.app.IApplicationThread;
|
import android.app.IApplicationThread;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
@ -94,7 +96,7 @@ public class BridgeService {
|
||||||
bridgeService.unlinkToDeath(this, 0);
|
bridgeService.unlinkToDeath(this, 0);
|
||||||
bridgeService = null;
|
bridgeService = null;
|
||||||
listener.onSystemServerDied();
|
listener.onSystemServerDied();
|
||||||
new Thread(() -> sendToBridge(serviceBinder, true)).start();
|
new Handler(Looper.getMainLooper()).post(() -> sendToBridge(serviceBinder, true));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
package org.lsposed.lspd.service;
|
package org.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import static org.lsposed.lspd.service.PackageService.MATCH_ALL_FLAGS;
|
||||||
|
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 android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
|
@ -79,7 +81,6 @@ import java.util.zip.ZipFile;
|
||||||
// This config manager assume uid won't change when our service is off.
|
// This config manager assume uid won't change when our service is off.
|
||||||
// Otherwise, user should maintain it manually.
|
// Otherwise, user should maintain it manually.
|
||||||
public class ConfigManager {
|
public class ConfigManager {
|
||||||
public static final int PER_USER_RANGE = 100000;
|
|
||||||
|
|
||||||
private static final String[] MANAGER_PERMISSIONS_TO_GRANT = new String[]{
|
private static final String[] MANAGER_PERMISSIONS_TO_GRANT = new String[]{
|
||||||
"android.permission.INTERACT_ACROSS_USERS",
|
"android.permission.INTERACT_ACROSS_USERS",
|
||||||
|
|
@ -202,11 +203,8 @@ public class ConfigManager {
|
||||||
|
|
||||||
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
|
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// apkPath, PreLoadedApk
|
// packageName, Module
|
||||||
private final Map<String, PreLoadedApk> cachedApkFile = new ConcurrentHashMap<>();
|
private final Map<String, Module> cachedModule = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// appId, packageName
|
|
||||||
private final Map<Integer, String> cachedModule = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
// packageName, userId, group, key, value
|
// 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, ConcurrentHashMap<String, Object>>> cachedConfig = new ConcurrentHashMap<>();
|
||||||
|
|
@ -217,10 +215,8 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
if (sync) {
|
if (sync) {
|
||||||
cacheModules();
|
cacheModules();
|
||||||
cacheScopes();
|
|
||||||
} else {
|
} else {
|
||||||
cacheHandler.post(this::cacheModules);
|
cacheHandler.post(this::cacheModules);
|
||||||
cacheHandler.post(this::cacheScopes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,16 +255,21 @@ public class ConfigManager {
|
||||||
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
var path = cursor.getString(apkPathIdx);
|
var path = cursor.getString(apkPathIdx);
|
||||||
var file = getCachedApkFile(path);
|
var packageName = cursor.getString(pkgNameIdx);
|
||||||
if (file != null) {
|
var m = cachedModule.computeIfAbsent(packageName, p -> {
|
||||||
var module = new Module();
|
var module = new Module();
|
||||||
|
var file = loadModule(path);
|
||||||
|
if (file == null) {
|
||||||
|
Log.w(TAG, "Can not load " + path + ", skip!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
module.packageName = cursor.getString(pkgNameIdx);
|
module.packageName = cursor.getString(pkgNameIdx);
|
||||||
module.apkPath = path;
|
module.apkPath = path;
|
||||||
module.file = file;
|
module.file = file;
|
||||||
modules.add(module);
|
module.appId = -1;
|
||||||
} else {
|
return module;
|
||||||
Log.w(TAG, "Can not load " + path + ", skip!");
|
});
|
||||||
}
|
if (m != null) modules.add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modules;
|
return modules;
|
||||||
|
|
@ -438,55 +439,71 @@ public class ConfigManager {
|
||||||
if (!packageStarted) return;
|
if (!packageStarted) return;
|
||||||
if (lastModuleCacheTime >= requestModuleCacheTime) return;
|
if (lastModuleCacheTime >= requestModuleCacheTime) return;
|
||||||
else lastModuleCacheTime = SystemClock.elapsedRealtime();
|
else lastModuleCacheTime = SystemClock.elapsedRealtime();
|
||||||
cachedModule.clear();
|
try (Cursor cursor = db.query(true, "modules", new String[]{"module_pkg_name", "apk_path"},
|
||||||
try (Cursor cursor = db.query(true, "modules INNER JOIN scope ON scope.mid = modules.mid", new String[]{"module_pkg_name", "user_id"},
|
|
||||||
"enabled = 1", null, null, null, null, null)) {
|
"enabled = 1", null, null, null, null, null)) {
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
Log.e(TAG, "db cache failed");
|
Log.e(TAG, "db cache failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||||
int userIdIdx = cursor.getColumnIndex("user_id");
|
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||||
// packageName, userId, packageInfo
|
|
||||||
Map<String, Map<Integer, PackageInfo>> modules = new HashMap<>();
|
|
||||||
Set<String> obsoleteModules = new HashSet<>();
|
Set<String> obsoleteModules = new HashSet<>();
|
||||||
Set<Application> obsoleteScopes = new HashSet<>();
|
// packageName, apkPath
|
||||||
|
Map<String, String> obsoletePaths = new HashMap<>();
|
||||||
|
cachedModule.values().removeIf(m -> m.apkPath == null || !new File(m.apkPath).exists());
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
String packageName = cursor.getString(pkgNameIdx);
|
String packageName = cursor.getString(pkgNameIdx);
|
||||||
int userId = cursor.getInt(userIdIdx);
|
String apkPath = cursor.getString(apkPathIdx);
|
||||||
var pkgInfo = modules.computeIfAbsent(packageName, m -> {
|
// if still present after removeIf, this package did not change.
|
||||||
|
var oldModule = cachedModule.get(packageName);
|
||||||
|
if (oldModule != null && oldModule.appId != -1) {
|
||||||
|
Log.d(TAG, packageName + " did not change, skip caching it");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PackageInfo pkgInfo = null;
|
||||||
try {
|
try {
|
||||||
return PackageService.getPackageInfoFromAllUsers(m, 0);
|
pkgInfo = PackageService.getPackageInfo(packageName, MATCH_ALL_FLAGS, 0);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.w(TAG, "get package info of " + packageName, e);
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
}
|
||||||
});
|
if (pkgInfo == null || pkgInfo.applicationInfo == null) {
|
||||||
if (pkgInfo.isEmpty()) {
|
|
||||||
obsoleteModules.add(packageName);
|
obsoleteModules.add(packageName);
|
||||||
} else if (!pkgInfo.containsKey(userId)) {
|
continue;
|
||||||
var module = new Application();
|
}
|
||||||
|
// cache from system server, keep it and set only the appId
|
||||||
|
if (oldModule != null) {
|
||||||
|
oldModule.appId = pkgInfo.applicationInfo.uid;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var path = apkPath;
|
||||||
|
if (!new File(path).exists()) {
|
||||||
|
path = getModuleApkPath(pkgInfo.applicationInfo);
|
||||||
|
if (path == null)
|
||||||
|
obsoleteModules.add(packageName);
|
||||||
|
else
|
||||||
|
obsoletePaths.put(packageName, path);
|
||||||
|
}
|
||||||
|
var file = loadModule(path);
|
||||||
|
if (file == null) {
|
||||||
|
Log.w(TAG, "failed to load module " + packageName);
|
||||||
|
obsoleteModules.add(packageName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var module = new Module();
|
||||||
|
module.apkPath = path;
|
||||||
module.packageName = packageName;
|
module.packageName = packageName;
|
||||||
module.userId = userId;
|
module.file = file;
|
||||||
obsoleteScopes.add(module);
|
module.appId = pkgInfo.applicationInfo.uid;
|
||||||
} else {
|
cachedModule.put(packageName, module);
|
||||||
var info = pkgInfo.get(userId);
|
|
||||||
assert info != null;
|
|
||||||
cachedModule.computeIfAbsent(info.applicationInfo.uid % PER_USER_RANGE, k -> info.packageName);
|
|
||||||
}
|
}
|
||||||
}
|
obsoleteModules.forEach(this::removeModuleWithoutCache);
|
||||||
for (var obsoleteModule : obsoleteModules) {
|
obsoletePaths.forEach(this::updateModuleApkPath);
|
||||||
removeModuleWithoutCache(obsoleteModule);
|
|
||||||
}
|
|
||||||
for (var obsoleteScope : obsoleteScopes) {
|
|
||||||
removeModuleScopeWithoutCache(obsoleteScope);
|
|
||||||
}
|
|
||||||
checkCachedApkFile();
|
|
||||||
}
|
}
|
||||||
Log.d(TAG, "cached modules");
|
Log.d(TAG, "cached modules");
|
||||||
for (int uid : cachedModule.keySet()) {
|
for (String module : cachedModule.keySet()) {
|
||||||
Log.d(TAG, cachedModule.get(uid) + "/" + uid);
|
Log.d(TAG, module);
|
||||||
}
|
}
|
||||||
|
cacheScopes();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void cacheScopes() {
|
private synchronized void cacheScopes() {
|
||||||
|
|
@ -495,42 +512,48 @@ public class ConfigManager {
|
||||||
if (lastScopeCacheTime >= requestScopeCacheTime) return;
|
if (lastScopeCacheTime >= requestScopeCacheTime) return;
|
||||||
else lastScopeCacheTime = SystemClock.elapsedRealtime();
|
else lastScopeCacheTime = SystemClock.elapsedRealtime();
|
||||||
cachedScope.clear();
|
cachedScope.clear();
|
||||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "module_pkg_name", "user_id", "apk_path"},
|
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "module_pkg_name", "user_id"},
|
||||||
"enabled = 1", null, null, null, null)) {
|
"enabled = 1", null, null, null, null)) {
|
||||||
if (cursor == null) {
|
|
||||||
Log.e(TAG, "db cache failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
|
int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
|
||||||
int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||||
int userIdIdx = cursor.getColumnIndex("user_id");
|
int userIdIdx = cursor.getColumnIndex("user_id");
|
||||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
|
||||||
HashSet<Application> obsoletePackages = new HashSet<>();
|
final var obsoletePackages = new HashSet<Application>();
|
||||||
|
final var obsoleteModules = new HashSet<Application>();
|
||||||
|
final var moduleAvailability = new HashMap<Pair<String, Integer>, Boolean>();
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
Application app = new Application();
|
Application app = new Application();
|
||||||
app.packageName = cursor.getString(appPkgNameIdx);
|
app.packageName = cursor.getString(appPkgNameIdx);
|
||||||
app.userId = cursor.getInt(userIdIdx);
|
app.userId = cursor.getInt(userIdIdx);
|
||||||
|
var modulePackageName = cursor.getString(modulePkgNameIdx);
|
||||||
|
|
||||||
|
// check if module is present in this user
|
||||||
|
if (!moduleAvailability.computeIfAbsent(new Pair<>(modulePackageName, app.userId), n -> {
|
||||||
|
var available = false;
|
||||||
|
try {
|
||||||
|
available = PackageService.isPackageAvailable(n.first, n.second, true) && cachedModule.containsKey(modulePackageName);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.w(TAG, "check package availability ", e);
|
||||||
|
}
|
||||||
|
if (!available) {
|
||||||
|
var obsoleteModule = new Application();
|
||||||
|
obsoleteModule.packageName = modulePackageName;
|
||||||
|
obsoleteModule.userId = app.userId;
|
||||||
|
obsoleteModules.add(obsoleteModule);
|
||||||
|
}
|
||||||
|
return available;
|
||||||
|
})) continue;
|
||||||
|
|
||||||
// system server always loads database
|
// system server always loads database
|
||||||
if (app.packageName.equals("android")) continue;
|
if (app.packageName.equals("android")) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<ProcessScope> processesScope = getAssociatedProcesses(app);
|
List<ProcessScope> processesScope = getAssociatedProcesses(app);
|
||||||
if (processesScope.isEmpty()) {
|
if (processesScope.isEmpty()) {
|
||||||
obsoletePackages.add(app);
|
obsoletePackages.add(app);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var apkPath = cursor.getString(apkPathIdx);
|
var module = cachedModule.get(modulePackageName);
|
||||||
var modulePackageName = cursor.getString(modulePkgNameIdx);
|
|
||||||
var file = getCachedApkFile(apkPath);
|
|
||||||
Module module;
|
|
||||||
if (file != null) {
|
|
||||||
module = new Module();
|
|
||||||
module.packageName = modulePackageName;
|
|
||||||
module.apkPath = apkPath;
|
|
||||||
module.file = file;
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Can not load " + apkPath + ", skip!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (ProcessScope processScope : processesScope) {
|
for (ProcessScope processScope : processesScope) {
|
||||||
cachedScope.computeIfAbsent(processScope,
|
cachedScope.computeIfAbsent(processScope,
|
||||||
ignored -> new LinkedList<>()).add(module);
|
ignored -> new LinkedList<>()).add(module);
|
||||||
|
|
@ -553,6 +576,10 @@ public class ConfigManager {
|
||||||
Log.d(TAG, "removing obsolete package: " + obsoletePackage.packageName + "/" + obsoletePackage.userId);
|
Log.d(TAG, "removing obsolete package: " + obsoletePackage.packageName + "/" + obsoletePackage.userId);
|
||||||
removeAppWithoutCache(obsoletePackage);
|
removeAppWithoutCache(obsoletePackage);
|
||||||
}
|
}
|
||||||
|
for (Application obsoleteModule : obsoleteModules) {
|
||||||
|
Log.d(TAG, "removing obsolete module: " + obsoleteModule.packageName + "/" + obsoleteModule.userId);
|
||||||
|
removeModuleScopeWithoutCache(obsoleteModule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Log.d(TAG, "cached Scope");
|
Log.d(TAG, "cached Scope");
|
||||||
cachedScope.forEach((ps, modules) -> {
|
cachedScope.forEach((ps, modules) -> {
|
||||||
|
|
@ -616,23 +643,6 @@ public class ConfigManager {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private PreLoadedApk getCachedApkFile(String path) {
|
|
||||||
return cachedApkFile.computeIfAbsent(path, this::loadModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkCachedApkFile() {
|
|
||||||
cachedApkFile.entrySet().removeIf(entry -> {
|
|
||||||
var path = entry.getKey();
|
|
||||||
var file = entry.getValue();
|
|
||||||
if (!new File(path).exists()) {
|
|
||||||
file.preLoadedDexes.stream().parallel().forEach(SharedMemory::close);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called when a new process created, use the cached result
|
// This is called when a new process created, use the cached result
|
||||||
public List<Module> getModulesForProcess(String processName, int uid) {
|
public List<Module> getModulesForProcess(String processName, int uid) {
|
||||||
return isManager(uid) ? Collections.emptyList() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyList());
|
return isManager(uid) ? Collections.emptyList() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyList());
|
||||||
|
|
@ -669,7 +679,7 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateModuleApkPath(String packageName, ApplicationInfo info) {
|
public String getModuleApkPath(ApplicationInfo info) {
|
||||||
String[] apks;
|
String[] apks;
|
||||||
if (info.splitSourceDirs != null) {
|
if (info.splitSourceDirs != null) {
|
||||||
apks = Arrays.copyOf(info.splitSourceDirs, info.splitSourceDirs.length + 1);
|
apks = Arrays.copyOf(info.splitSourceDirs, info.splitSourceDirs.length + 1);
|
||||||
|
|
@ -682,14 +692,18 @@ public class ConfigManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}).findFirst();
|
}).findFirst();
|
||||||
if (!apkPath.isPresent()) return false;
|
return apkPath.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateModuleApkPath(String packageName, String apkPath) {
|
||||||
|
if (apkPath == null) return false;
|
||||||
if (db.inTransaction()) {
|
if (db.inTransaction()) {
|
||||||
Log.w(TAG, "update module apk path should not be called inside transaction");
|
Log.w(TAG, "update module apk path should not be called inside transaction");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("module_pkg_name", packageName);
|
values.put("module_pkg_name", packageName);
|
||||||
values.put("apk_path", apkPath.get());
|
values.put("apk_path", apkPath);
|
||||||
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
|
|
@ -811,7 +825,7 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean enableModule(String packageName, ApplicationInfo info) {
|
public boolean enableModule(String packageName, ApplicationInfo info) {
|
||||||
if (!updateModuleApkPath(packageName, info)) return false;
|
if (!updateModuleApkPath(packageName, getModuleApkPath(info))) return false;
|
||||||
int mid = getModuleId(packageName);
|
int mid = getModuleId(packageName);
|
||||||
if (mid == -1) return false;
|
if (mid == -1) return false;
|
||||||
try {
|
try {
|
||||||
|
|
@ -922,12 +936,17 @@ public class ConfigManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is slow, avoid using it
|
||||||
public boolean isModule(int uid) {
|
public boolean isModule(int uid) {
|
||||||
return cachedModule.containsKey(uid % PER_USER_RANGE);
|
for (var module : cachedModule.values()) {
|
||||||
|
if (module.appId == uid % PER_USER_RANGE) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isModule(int uid, String name) {
|
public boolean isModule(int uid, String name) {
|
||||||
return name.equals(cachedModule.getOrDefault(uid % PER_USER_RANGE, null));
|
var module = cachedModule.getOrDefault(name, null);
|
||||||
|
return module != null && module.appId == uid % PER_USER_RANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recursivelyChown(File file, int uid, int gid) throws ErrnoException {
|
private void recursivelyChown(File file, int uid, int gid) throws ErrnoException {
|
||||||
|
|
@ -939,8 +958,7 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean ensureModulePrefsPermission(int uid) {
|
public boolean ensureModulePrefsPermission(int uid, String packageName) {
|
||||||
String packageName = cachedModule.get(uid);
|
|
||||||
if (packageName == null) return false;
|
if (packageName == null) return false;
|
||||||
File path = new File(getPrefsPath(packageName, uid));
|
File path = new File(getPrefsPath(packageName, uid));
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
|
||||||
public IBinder requestModuleBinder(String name) throws RemoteException {
|
public IBinder requestModuleBinder(String name) throws RemoteException {
|
||||||
ensureRegistered();
|
ensureRegistered();
|
||||||
if (ConfigManager.getInstance().isModule(getCallingUid(), name)) {
|
if (ConfigManager.getInstance().isModule(getCallingUid(), name)) {
|
||||||
ConfigManager.getInstance().ensureModulePrefsPermission(getCallingUid());
|
ConfigManager.getInstance().ensureModulePrefsPermission(getCallingUid(), name);
|
||||||
return ServiceManager.getModuleService(name);
|
return ServiceManager.getModuleService(name);
|
||||||
} else return null;
|
} else return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.lsposed.lspd.service;
|
package org.lsposed.lspd.service;
|
||||||
|
|
||||||
import static org.lsposed.lspd.service.ConfigManager.PER_USER_RANGE;
|
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 android.app.IApplicationThread;
|
import android.app.IApplicationThread;
|
||||||
|
|
@ -113,8 +113,7 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
}
|
}
|
||||||
// when package is changed, we may need to update cache (module cache or process cache)
|
// when package is changed, we may need to update cache (module cache or process cache)
|
||||||
if (isXposedModule) {
|
if (isXposedModule) {
|
||||||
var ret = ConfigManager.getInstance().updateModuleApkPath(moduleName, applicationInfo);
|
ConfigManager.getInstance().updateCache();
|
||||||
if (ret) Log.i(TAG, "Updated module apk path: " + moduleName);
|
|
||||||
} else if (ConfigManager.getInstance().isUidHooked(uid)) {
|
} else if (ConfigManager.getInstance().isUidHooked(uid)) {
|
||||||
// it will automatically remove obsolete app from database
|
// it will automatically remove obsolete app from database
|
||||||
ConfigManager.getInstance().updateAppCache();
|
ConfigManager.getInstance().updateAppCache();
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ public class PackageService {
|
||||||
static final int INSTALL_REASON_UNKNOWN = 0;
|
static final int INSTALL_REASON_UNKNOWN = 0;
|
||||||
|
|
||||||
static final int MATCH_ALL_FLAGS = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES;
|
static final int MATCH_ALL_FLAGS = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES;
|
||||||
|
public static final int PER_USER_RANGE = 100000;
|
||||||
|
|
||||||
private static IPackageManager pm = null;
|
private static IPackageManager pm = null;
|
||||||
private static IBinder binder = null;
|
private static IBinder binder = null;
|
||||||
|
|
@ -135,7 +136,7 @@ public class PackageService {
|
||||||
if (filterNoProcess) {
|
if (filterNoProcess) {
|
||||||
res.removeIf(packageInfo -> {
|
res.removeIf(packageInfo -> {
|
||||||
try {
|
try {
|
||||||
PackageInfo pkgInfo = getPackageInfoWithComponents(packageInfo.packageName, MATCH_ALL_FLAGS, packageInfo.applicationInfo.uid / 100000);
|
PackageInfo pkgInfo = getPackageInfoWithComponents(packageInfo.packageName, MATCH_ALL_FLAGS, packageInfo.applicationInfo.uid / PER_USER_RANGE);
|
||||||
return fetchProcesses(pkgInfo).isEmpty();
|
return fetchProcesses(pkgInfo).isEmpty();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -178,6 +179,10 @@ public class PackageService {
|
||||||
return new Pair<>(fetchProcesses(pkgInfo), pkgInfo.applicationInfo.uid);
|
return new Pair<>(fetchProcesses(pkgInfo), pkgInfo.applicationInfo.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPackageAvailable(String packageName, int userId, boolean ignoreHidden) throws RemoteException {
|
||||||
|
return pm.isPackageAvailable(packageName, userId) && (!ignoreHidden || pm.getApplicationHiddenSettingAsUser(packageName, userId));
|
||||||
|
}
|
||||||
|
|
||||||
private static PackageInfo getPackageInfoWithComponents(String packageName, int flags, int userId) throws RemoteException {
|
private static PackageInfo getPackageInfoWithComponents(String packageName, int flags, int userId) throws RemoteException {
|
||||||
IPackageManager pm = getPackageManager();
|
IPackageManager pm = getPackageManager();
|
||||||
if (pm == null) return null;
|
if (pm == null) return null;
|
||||||
|
|
@ -208,7 +213,7 @@ public class PackageService {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pkgInfo == null || pkgInfo.applicationInfo == null || (!pkgInfo.packageName.equals("android") && (pkgInfo.applicationInfo.sourceDir == null || !new File(pkgInfo.applicationInfo.sourceDir).exists() || (!pm.isPackageAvailable(packageName, userId) && !pm.getApplicationHiddenSettingAsUser(packageName, userId)))))
|
if (pkgInfo == null || pkgInfo.applicationInfo == null || (!pkgInfo.packageName.equals("android") && (pkgInfo.applicationInfo.sourceDir == null || !new File(pkgInfo.applicationInfo.sourceDir).exists() || isPackageAvailable(packageName, userId, true))))
|
||||||
return null;
|
return null;
|
||||||
return pkgInfo;
|
return pkgInfo;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue