Load module together with package name
This commit is contained in:
parent
2eefe094d9
commit
9d417fa6cf
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue