Load module together with package name

This commit is contained in:
LoveSy 2021-05-21 05:10:09 +08:00 committed by LoveSy
parent 2eefe094d9
commit 9d417fa6cf
5 changed files with 46 additions and 40 deletions

View File

@ -7,7 +7,7 @@ interface ILSPApplicationService {
boolean isResourcesHookEnabled() = 5; boolean isResourcesHookEnabled() = 5;
String[] getModulesList(String processName) = 6; Map getModulesList(String processName) = 6;
String getPrefsPath(String packageName) = 7; String getPrefsPath(String packageName) = 7;

View File

@ -219,25 +219,25 @@ public final class XposedInit {
return false; return false;
} }
synchronized (moduleLoadLock) { synchronized (moduleLoadLock) {
// TODO: process name var moduleList = serviceClient.getModulesList();
String[] moduleList = serviceClient.getModulesList();
ArraySet<String> newLoadedApk = new ArraySet<>(); ArraySet<String> newLoadedApk = new ArraySet<>();
for (String apk : moduleList) moduleList.forEach((name, apk) -> {
if (loadedModules.contains(apk)) { if (loadedModules.contains(apk)) {
newLoadedApk.add(apk); newLoadedApk.add(apk);
} else { } else {
loadedModules.add(apk); // temporarily add it for XSharedPreference loadedModules.add(apk); // temporarily add it for XSharedPreference
boolean loadSuccess = loadModule(apk); boolean loadSuccess = loadModule(name, apk);
if (loadSuccess) { if (loadSuccess) {
newLoadedApk.add(apk); newLoadedApk.add(apk);
} }
} }
loadedModules.clear(); loadedModules.clear();
loadedModules.addAll(newLoadedApk); loadedModules.addAll(newLoadedApk);
// refresh callback according to current loaded module list // refresh callback according to current loaded module list
pruneCallbacks(loadedModules); pruneCallbacks(loadedModules);
});
} }
return true; return true;
} }
@ -276,7 +276,7 @@ public final class XposedInit {
* Load all so from an APK by reading <code>assets/native_init</code>. * Load all so from an APK by reading <code>assets/native_init</code>.
* It will only store the so names but not doing anything. * It will only store the so names but not doing anything.
*/ */
private static boolean initNativeModule(ClassLoader mcl, String apk) { private static boolean initNativeModule(ClassLoader mcl, String name) {
InputStream is = mcl.getResourceAsStream("assets/native_init"); InputStream is = mcl.getResourceAsStream("assets/native_init");
if (is == null) return true; if (is == null) return true;
BufferedReader moduleLibraryReader = new BufferedReader(new InputStreamReader(is)); BufferedReader moduleLibraryReader = new BufferedReader(new InputStreamReader(is));
@ -288,7 +288,7 @@ public final class XposedInit {
} }
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, " Failed to load native library list from " + apk, e); Log.e(TAG, " Failed to load native library list from " + name, e);
return false; return false;
} finally { } finally {
closeSilently(is); closeSilently(is);
@ -297,7 +297,7 @@ public final class XposedInit {
} }
private static boolean initModule(ClassLoader mcl, String apk) { private static boolean initModule(ClassLoader mcl, String name, String apk) {
InputStream is = mcl.getResourceAsStream("assets/xposed_init"); InputStream is = mcl.getResourceAsStream("assets/xposed_init");
if (is == null) { if (is == null) {
return true; return true;
@ -346,7 +346,7 @@ public final class XposedInit {
} }
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, " Failed to load module from " + apk, e); Log.e(TAG, " Failed to load module " + name + " from " + apk, e);
return false; return false;
} finally { } finally {
closeSilently(is); closeSilently(is);
@ -359,8 +359,8 @@ public final class XposedInit {
* in <code>assets/xposed_init</code>. * in <code>assets/xposed_init</code>.
*/ */
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")
private static boolean loadModule(String apk) { private static boolean loadModule(String name, String apk) {
Log.i(TAG, "Loading modules from " + apk); Log.i(TAG, "Loading module " + name + " from " + apk);
if (!new File(apk).exists()) { if (!new File(apk).exists()) {
Log.e(TAG, " File does not exist"); Log.e(TAG, " File does not exist");
@ -378,7 +378,7 @@ public final class XposedInit {
try { try {
if (mcl.loadClass(XposedBridge.class.getName()).getClassLoader() != initLoader) { if (mcl.loadClass(XposedBridge.class.getName()).getClassLoader() != initLoader) {
Log.e(TAG, " Cannot load module:"); Log.e(TAG, " Cannot load module: " + name);
Log.e(TAG, " The Xposed API classes are compiled into the module's APK."); Log.e(TAG, " The Xposed API classes are compiled into the module's APK.");
Log.e(TAG, " This may cause strange issues and must be fixed by the module developer."); Log.e(TAG, " This may cause strange issues and must be fixed by the module developer.");
Log.e(TAG, " For details, see: http://api.xposed.info/using.html"); Log.e(TAG, " For details, see: http://api.xposed.info/using.html");
@ -387,7 +387,7 @@ public final class XposedInit {
} catch (ClassNotFoundException ignored) { } catch (ClassNotFoundException ignored) {
} }
return initNativeModule(mcl, apk) && initModule(mcl, apk); return initNativeModule(mcl, apk) && initModule(mcl, name, apk);
} }
public final static HashSet<String> loadedPackagesInProcess = new HashSet<>(1); public final static HashSet<String> loadedPackagesInProcess = new HashSet<>(1);

View File

@ -24,6 +24,8 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import java.io.File; import java.io.File;
import java.util.Collections;
import java.util.Map;
import org.lsposed.lspd.service.ILSPApplicationService; import org.lsposed.lspd.service.ILSPApplicationService;
import org.lsposed.lspd.util.Utils; import org.lsposed.lspd.util.Utils;
@ -86,15 +88,16 @@ public class LSPApplicationServiceClient implements ILSPApplicationService {
} }
@Override @Override
public String[] getModulesList(String processName) { public Map<String, String> getModulesList(String processName) {
try { try {
//noinspection unchecked
return service.getModulesList(processName); return service.getModulesList(processName);
} catch (RemoteException | NullPointerException ignored) { } catch (RemoteException | NullPointerException ignored) {
} }
return new String[0]; return Collections.emptyMap();
} }
public String[] getModulesList() { public Map<String, String> getModulesList() {
return getModulesList(processName); return getModulesList(processName);
} }

View File

@ -113,7 +113,7 @@ public class ConfigManager {
private final Handler cacheHandler; private final Handler cacheHandler;
private final ConcurrentHashMap<String, SharedMemory> moduleDexes = new ConcurrentHashMap<>(); private final Map<String, SharedMemory> moduleDexes = new ConcurrentHashMap<>();
private long lastModuleCacheTime = 0; private long lastModuleCacheTime = 0;
private long requestModuleCacheTime = 0; private long requestModuleCacheTime = 0;
@ -161,9 +161,9 @@ public class ConfigManager {
"PRIMARY KEY (mid, app_pkg_name, user_id)" + "PRIMARY KEY (mid, app_pkg_name, user_id)" +
");"); ");");
private final ConcurrentHashMap<ProcessScope, Set<String>> cachedScope = new ConcurrentHashMap<>(); private final Map<ProcessScope, Map<String, String>> cachedScope = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, String> cachedModule = new ConcurrentHashMap<>(); private final Map<Integer, String> cachedModule = new ConcurrentHashMap<>();
private void updateCaches(boolean sync) { private void updateCaches(boolean sync) {
synchronized (this) { synchronized (this) {
@ -206,15 +206,16 @@ public class ConfigManager {
} }
} }
public String[] getModulesPathForSystemServer() { public Map<String, String> getModulesForSystemServer() {
HashSet<String> modules = new HashSet<>(); HashMap<String, String> modules = new HashMap<>();
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"apk_path"}, "app_pkg_name=? AND enabled=1", new String[]{"android"}, null, null, null)) { 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 apkPathIdx = cursor.getColumnIndex("apk_path");
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
modules.add(cursor.getString(apkPathIdx)); modules.put(cursor.getString(pkgNameIdx), cursor.getString(apkPathIdx));
} }
} }
return modules.toArray(new String[0]); return modules;
} }
private static String readText(@NonNull File file) throws IOException { private static String readText(@NonNull File file) throws IOException {
@ -388,13 +389,14 @@ 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", "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", "apk_path"},
"enabled = 1", null, null, null, null)) { "enabled = 1", 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 appPkgNameIdx = cursor.getColumnIndex("app_pkg_name"); int appPkgNameIdx = cursor.getColumnIndex("app_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"); int apkPathIdx = cursor.getColumnIndex("apk_path");
HashSet<Application> obsoletePackages = new HashSet<>(); HashSet<Application> obsoletePackages = new HashSet<>();
@ -405,6 +407,7 @@ public class ConfigManager {
// system server always loads database // system server always loads database
if (app.packageName.equals("android")) continue; if (app.packageName.equals("android")) continue;
String apk_path = cursor.getString(apkPathIdx); String apk_path = cursor.getString(apkPathIdx);
String module_pkg = cursor.getString(modulePkgNameIdx);
try { try {
List<ProcessScope> processesScope = getAssociatedProcesses(app); List<ProcessScope> processesScope = getAssociatedProcesses(app);
if (processesScope.isEmpty()) { if (processesScope.isEmpty()) {
@ -412,7 +415,7 @@ public class ConfigManager {
continue; continue;
} }
for (ProcessScope processScope : processesScope) for (ProcessScope processScope : processesScope)
cachedScope.computeIfAbsent(processScope, ignored -> new HashSet<>()).add(apk_path); cachedScope.computeIfAbsent(processScope, ignored -> new HashMap<>()).put(module_pkg, apk_path);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(e)); Log.e(TAG, Log.getStackTraceString(e));
} }
@ -423,17 +426,17 @@ public class ConfigManager {
} }
} }
Log.d(TAG, "cached Scope"); Log.d(TAG, "cached Scope");
for (ProcessScope ps : cachedScope.keySet()) { cachedScope.forEach((ps, module) -> {
Log.d(TAG, ps.processName + "/" + ps.uid); Log.d(TAG, ps.processName + "/" + ps.uid);
for (String apk : cachedScope.get(ps)) { module.forEach((pkg_name, apk_path) -> {
Log.d(TAG, "\t" + apk); Log.d(TAG, "\t" + pkg_name);
} });
} });
} }
// 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 String[] getModulesPathForProcess(String processName, int uid) { public Map<String, String> getModulesForProcess(String processName, int uid) {
return isManager(uid) ? new String[0] : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptySet()).toArray(new String[0]); return isManager(uid) ? Collections.emptyMap() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyMap());
} }
// This is called when a new process created, use the cached result // This is called when a new process created, use the cached result

View File

@ -67,13 +67,13 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
} }
@Override @Override
public String[] getModulesList(String processName) throws RemoteException { public Map<String, String> getModulesList(String processName) throws RemoteException {
ensureRegistered(); ensureRegistered();
int callingUid = getCallingUid(); int callingUid = getCallingUid();
if (callingUid == 1000 && processName.equals("android")) { if (callingUid == 1000 && processName.equals("android")) {
return ConfigManager.getInstance().getModulesPathForSystemServer(); return ConfigManager.getInstance().getModulesForSystemServer();
} }
return ConfigManager.getInstance().getModulesPathForProcess(processName, callingUid); return ConfigManager.getInstance().getModulesForProcess(processName, callingUid);
} }
@Override @Override