fix: 优化 manager 连接与禁用 profile 文件
修正 manager 连接失败时的回退逻辑,避免 service 为 null,统一本地服务处理。新增 disableProfile 方法,自动清理和禁用 profile 文件,防止 profile 干扰运行。
This commit is contained in:
parent
4d7f56c21c
commit
b076eed005
|
|
@ -1,8 +1,8 @@
|
||||||
package org.lsposed.lspatch.metaloader;
|
package org.lsposed.lspatch.metaloader;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AppComponentFactory;
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
@ -76,14 +76,16 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int currentUserId = Process.myUid() / 100000;
|
||||||
|
|
||||||
if (useManager) {
|
if (useManager) {
|
||||||
Log.i(TAG, "Bootstrap loader from manager");
|
Log.i(TAG, "Bootstrap loader from manager");
|
||||||
var ipm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
var ipm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
||||||
ApplicationInfo manager;
|
ApplicationInfo manager;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
manager = (ApplicationInfo) HiddenApiBypass.invoke(IPackageManager.class, ipm, "getApplicationInfo", Constants.MANAGER_PACKAGE_NAME, 0L, Process.myUid() / 100000);
|
manager = (ApplicationInfo) HiddenApiBypass.invoke(IPackageManager.class, ipm, "getApplicationInfo", Constants.MANAGER_PACKAGE_NAME, 0L, currentUserId);
|
||||||
} else {
|
} else {
|
||||||
manager = ipm.getApplicationInfo(Constants.MANAGER_PACKAGE_NAME, 0, Process.myUid() / 100000);
|
manager = ipm.getApplicationInfo(Constants.MANAGER_PACKAGE_NAME, 0, currentUserId);
|
||||||
}
|
}
|
||||||
try (var zip = new ZipFile(new File(manager.sourceDir));
|
try (var zip = new ZipFile(new File(manager.sourceDir));
|
||||||
var is = zip.getInputStream(zip.getEntry(Constants.LOADER_DEX_ASSET_PATH));
|
var is = zip.getInputStream(zip.getEntry(Constants.LOADER_DEX_ASSET_PATH));
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.CompatibilityInfo;
|
import android.content.res.CompatibilityInfo;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.system.Os;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
@ -40,6 +41,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -72,15 +74,6 @@ public class LSPApplication {
|
||||||
return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID;
|
return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasEmbeddedModules(Context context) {
|
|
||||||
try {
|
|
||||||
String[] list = context.getAssets().list("lspatch/modules");
|
|
||||||
return list != null && list.length > 0;
|
|
||||||
} catch (IOException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onLoad() throws RemoteException, IOException {
|
public static void onLoad() throws RemoteException, IOException {
|
||||||
if (isIsolated()) {
|
if (isIsolated()) {
|
||||||
XLog.d(TAG, "Skip isolated process");
|
XLog.d(TAG, "Skip isolated process");
|
||||||
|
|
@ -94,8 +87,7 @@ public class LSPApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Initialize service client");
|
Log.d(TAG, "Initialize service client");
|
||||||
ILSPApplicationService service = null;
|
ILSPApplicationService service;
|
||||||
|
|
||||||
if (config.useManager) {
|
if (config.useManager) {
|
||||||
try {
|
try {
|
||||||
service = new RemoteApplicationService(context);
|
service = new RemoteApplicationService(context);
|
||||||
|
|
@ -107,28 +99,19 @@ public class LSPApplication {
|
||||||
moduleObj.put("packageName", module.packageName);
|
moduleObj.put("packageName", module.packageName);
|
||||||
moduleArr.put(moduleObj);
|
moduleArr.put(moduleObj);
|
||||||
}
|
}
|
||||||
SharedPreferences shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE);
|
SharedPreferences shared = context.getSharedPreferences("opatch", Context.MODE_PRIVATE);
|
||||||
shared.edit().putString("modules", moduleArr.toString()).apply();
|
shared.edit().putString("modules",moduleArr.toString()).commit();
|
||||||
Log.i(TAG, "Success update module scope from Manager");
|
Log.e(TAG, "Success update module scope");
|
||||||
|
}catch (Exception e){
|
||||||
} catch (Throwable e) {
|
Log.e(TAG, "Failed to connect to manager, fallback to fixed local service");
|
||||||
Log.w(TAG, "Failed to connect to manager: " + e.getMessage());
|
|
||||||
service = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (service == null) {
|
|
||||||
|
|
||||||
if (hasEmbeddedModules(context)) {
|
|
||||||
Log.i(TAG, "Using Integrated Service (Embedded Modules Found)");
|
|
||||||
service = new IntegrApplicationService(context);
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Using NeoLocal Service (Cached Config)");
|
|
||||||
service = new NeoLocalApplicationService(context);
|
service = new NeoLocalApplicationService(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
service = new IntegrApplicationService(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableProfile(context);
|
||||||
Startup.initXposed(false, ActivityThread.currentProcessName(), context.getApplicationInfo().dataDir, service);
|
Startup.initXposed(false, ActivityThread.currentProcessName(), context.getApplicationInfo().dataDir, service);
|
||||||
Startup.bootstrapXposed();
|
Startup.bootstrapXposed();
|
||||||
// WARN: Since it uses `XResource`, the following class should not be initialized
|
// WARN: Since it uses `XResource`, the following class should not be initialized
|
||||||
|
|
@ -209,6 +192,7 @@ public class LSPApplication {
|
||||||
appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config.injectProvider){
|
if (config.injectProvider){
|
||||||
ClassLoader loader = appLoadedApk.getClassLoader();
|
ClassLoader loader = appLoadedApk.getClassLoader();
|
||||||
Object dexPathList = XposedHelpers.getObjectField(loader, "pathList");
|
Object dexPathList = XposedHelpers.getObjectField(loader, "pathList");
|
||||||
|
|
@ -268,6 +252,53 @@ public class LSPApplication {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void disableProfile(Context context) {
|
||||||
|
final ArrayList<String> codePaths = new ArrayList<>();
|
||||||
|
var appInfo = context.getApplicationInfo();
|
||||||
|
var pkgName = context.getPackageName();
|
||||||
|
if (appInfo == null) return;
|
||||||
|
if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
|
||||||
|
codePaths.add(appInfo.sourceDir);
|
||||||
|
}
|
||||||
|
if (appInfo.splitSourceDirs != null) {
|
||||||
|
Collections.addAll(codePaths, appInfo.splitSourceDirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codePaths.isEmpty()) {
|
||||||
|
// If there are no code paths there's no need to setup a profile file and register with
|
||||||
|
// the runtime,
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileDir = HiddenApiBridge.Environment_getDataProfilesDePackageDirectory(appInfo.uid / PER_USER_RANGE, pkgName);
|
||||||
|
|
||||||
|
var attrs = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("r--------"));
|
||||||
|
|
||||||
|
for (int i = codePaths.size() - 1; i >= 0; i--) {
|
||||||
|
String splitName = i == 0 ? null : appInfo.splitNames[i - 1];
|
||||||
|
File curProfileFile = new File(profileDir, splitName == null ? "primary.prof" : splitName + ".split.prof").getAbsoluteFile();
|
||||||
|
Log.d(TAG, "Processing " + curProfileFile.getAbsolutePath());
|
||||||
|
try {
|
||||||
|
if (!curProfileFile.canWrite() && Files.size(curProfileFile.toPath()) == 0) {
|
||||||
|
Log.d(TAG, "Skip profile " + curProfileFile.getAbsolutePath());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (curProfileFile.exists() && !curProfileFile.delete()) {
|
||||||
|
try (var writer = new FileOutputStream(curProfileFile)) {
|
||||||
|
Log.d(TAG, "Failed to delete, try to clear content " + curProfileFile.getAbsolutePath());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "Failed to delete and clear profile file " + curProfileFile.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
Os.chmod(curProfileFile.getAbsolutePath(), 00400);
|
||||||
|
} else {
|
||||||
|
Files.createFile(curProfileFile.toPath(), attrs);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "Failed to disable profile file " + curProfileFile.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void switchAllClassLoader() {
|
private static void switchAllClassLoader() {
|
||||||
var fields = LoadedApk.class.getDeclaredFields();
|
var fields = LoadedApk.class.getDeclaredFields();
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue