fix: 优化 manager 连接与禁用 profile 文件

修正 manager 连接失败时的回退逻辑,避免 service 为 null,统一本地服务处理。新增 disableProfile 方法,自动清理和禁用 profile 文件,防止 profile 干扰运行。
This commit is contained in:
NkBe 2025-11-20 20:29:48 +08:00
parent 4d7f56c21c
commit b076eed005
No known key found for this signature in database
GPG Key ID: 525137026FF031DF
2 changed files with 65 additions and 32 deletions

View File

@ -1,8 +1,8 @@
package org.lsposed.lspatch.metaloader;
import android.annotation.SuppressLint;
import android.app.AppComponentFactory;
import android.app.ActivityThread;
import android.app.AppComponentFactory;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
@ -76,14 +76,16 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory {
}
}
int currentUserId = Process.myUid() / 100000;
if (useManager) {
Log.i(TAG, "Bootstrap loader from manager");
var ipm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
ApplicationInfo manager;
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 {
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));
var is = zip.getInputStream(zip.getEntry(Constants.LOADER_DEX_ASSET_PATH));

View File

@ -12,6 +12,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.os.Build;
import android.os.RemoteException;
import android.system.Os;
import android.util.Log;
import com.google.gson.Gson;
@ -40,6 +41,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -72,15 +74,6 @@ public class LSPApplication {
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 {
if (isIsolated()) {
XLog.d(TAG, "Skip isolated process");
@ -94,8 +87,7 @@ public class LSPApplication {
}
Log.d(TAG, "Initialize service client");
ILSPApplicationService service = null;
ILSPApplicationService service;
if (config.useManager) {
try {
service = new RemoteApplicationService(context);
@ -107,28 +99,19 @@ public class LSPApplication {
moduleObj.put("packageName", module.packageName);
moduleArr.put(moduleObj);
}
SharedPreferences shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE);
shared.edit().putString("modules", moduleArr.toString()).apply();
Log.i(TAG, "Success update module scope from Manager");
} catch (Throwable e) {
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)");
SharedPreferences shared = context.getSharedPreferences("opatch", Context.MODE_PRIVATE);
shared.edit().putString("modules",moduleArr.toString()).commit();
Log.e(TAG, "Success update module scope");
}catch (Exception e){
Log.e(TAG, "Failed to connect to manager, fallback to fixed local service");
service = new NeoLocalApplicationService(context);
}
} else {
service = new IntegrApplicationService(context);
}
disableProfile(context);
Startup.initXposed(false, ActivityThread.currentProcessName(), context.getApplicationInfo().dataDir, service);
Startup.bootstrapXposed();
// WARN: Since it uses `XResource`, the following class should not be initialized
@ -209,6 +192,7 @@ public class LSPApplication {
appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
if (config.injectProvider){
ClassLoader loader = appLoadedApk.getClassLoader();
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() {
var fields = LoadedApk.class.getDeclaredFields();
for (Field field : fields) {