diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java
index 00bb6946..d0da0634 100644
--- a/app/src/main/java/org/lsposed/manager/ConfigManager.java
+++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java
@@ -27,6 +27,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
+import org.lsposed.lspd.ILSPManagerService;
import org.lsposed.lspd.models.Application;
import org.lsposed.lspd.models.UserInfo;
import org.lsposed.manager.adapters.ScopeAdapter;
@@ -35,13 +36,9 @@ import org.lsposed.manager.receivers.LSPManagerServiceHolder;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import io.github.xposed.xposedservice.utils.ParceledListSlice;
-
public class ConfigManager {
public static boolean isBinderAlive() {
@@ -360,12 +357,12 @@ public class ConfigManager {
}
}
- public static boolean dex2oatWrapperAlive() {
+ public static int getDex2OatWrapperCompatibility() {
try {
- return LSPManagerServiceHolder.getService().dex2oatWrapperAlive();
+ return LSPManagerServiceHolder.getService().getDex2OatWrapperCompatibility();
} catch (RemoteException e) {
Log.e(App.TAG, Log.getStackTraceString(e));
- return false;
+ return ILSPManagerService.DEX2OAT_CRASHED;
}
}
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java
index 76933a6f..72b02759 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import androidx.core.text.HtmlCompat;
import androidx.fragment.app.DialogFragment;
+import org.lsposed.lspd.ILSPManagerService;
import org.lsposed.manager.BuildConfig;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
@@ -107,7 +108,7 @@ public class HomeFragment extends BaseFragment {
} else {
binding.updateCard.setVisibility(View.GONE);
}
- boolean dex2oatAbnormal = !ConfigManager.dex2oatWrapperAlive() && !ConfigManager.dex2oatFlagsLoaded();
+ boolean dex2oatAbnormal = ConfigManager.getDex2OatWrapperCompatibility() != ILSPManagerService.DEX2OAT_OK && !ConfigManager.dex2oatFlagsLoaded();
if (!ConfigManager.isSepolicyLoaded() || !ConfigManager.systemServerRequested() || dex2oatAbnormal) {
binding.statusTitle.setText(R.string.partial_activated);
binding.statusIcon.setImageResource(R.drawable.ic_round_warning_24);
@@ -158,11 +159,23 @@ public class HomeFragment extends BaseFragment {
binding.api.setText(ConfigManager.getApi());
binding.frameworkVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s (%2$d)", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode()));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- binding.dex2oatWrapper.setText(R.string.unsupported_android_version);
- } else if (ConfigManager.dex2oatWrapperAlive()) {
- binding.dex2oatWrapper.setText(R.string.supported);
- } else {
- binding.dex2oatWrapper.setText(R.string.unsupported_crashed);
+ binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.android_version_unsatisfied)));
+ } else switch (ConfigManager.getDex2OatWrapperCompatibility()) {
+ case ILSPManagerService.DEX2OAT_OK:
+ binding.dex2oatWrapper.setText(R.string.supported);
+ break;
+ case ILSPManagerService.DEX2OAT_CRASHED:
+ binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.crashed)));
+ break;
+ case ILSPManagerService.DEX2OAT_MOUNT_FAILED:
+ binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.mount_failed)));
+ break;
+ case ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE:
+ binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.selinux_permissive)));
+ break;
+ case ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT:
+ binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.sepolicy_incorrect)));
+ break;
}
} else {
binding.apiVersion.setText(R.string.not_installed);
@@ -187,6 +200,10 @@ public class HomeFragment extends BaseFragment {
"\n" +
binding.api.getText() +
"\n\n" +
+ activity.getString(R.string.info_dex2oat_wrapper) +
+ "\n" +
+ binding.dex2oatWrapper.getText() +
+ "\n\n" +
activity.getString(R.string.info_framework_version) +
"\n" +
binding.frameworkVersion.getText() +
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b125c77f..8f9e259b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -63,10 +63,14 @@
Device
System ABI
Injection Interface
- Optimizer Wrapper
+ Dex Optimizer Wrapper
Supported
- Unsupported (Android version unsatisfied)
- Unsupported (Crashed)
+ Unsupported
+ Android version unsatisfied
+ Crashed
+ Mount failed
+ SELinux is permissive
+ SELinux policy is incorrect
Parasitic Manager Recommended
LSPosed now supports system parasitization to avoid detection. You can safely uninstall the manager after successfully creating a shortcut of the parasitic manager. In any case you can install the manager back from /data/adb/lspd/manager.apk.
Create shortcut
diff --git a/daemon/proguard-rules.pro b/daemon/proguard-rules.pro
index 39516ba3..74c73f81 100644
--- a/daemon/proguard-rules.pro
+++ b/daemon/proguard-rules.pro
@@ -4,6 +4,13 @@
-keepclasseswithmembers class org.lsposed.lspd.Main {
public static void main(java.lang.String[]);
}
+-keepclasseswithmembers class org.lsposed.lspd.service.Dex2OatService {
+ private java.lang.String devTmpDir;
+ private java.lang.String magiskPath;
+ private java.lang.String fakeBin32;
+ private java.lang.String fakeBin64;
+ private java.lang.String[] dex2oatBinaries;
+}
-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {
private int refreshFd(boolean);
}
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java
index e8e1b67a..8e67b9f7 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java
@@ -19,12 +19,17 @@
package org.lsposed.lspd.service;
+import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_CRASHED;
+import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_MOUNT_FAILED;
+import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_OK;
+import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE;
+import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT;
+
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
+import android.os.FileObserver;
import android.os.SELinux;
import android.system.ErrnoException;
import android.system.Os;
@@ -32,88 +37,184 @@ import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Locale;
+import java.util.Objects;
public class Dex2OatService {
public static final String PROP_NAME = "dalvik.vm.dex2oat-flags";
public static final String PROP_VALUE = "--inline-max-code-units=0";
private static final String TAG = "LSPosedDex2Oat";
- private static final String DEX2OAT_32 = "/apex/com.android.art/bin/dex2oat32";
- private static final String DEX2OAT_64 = "/apex/com.android.art/bin/dex2oat64";
- private final Thread thread;
+ private String devTmpDir, magiskPath, fakeBin32, fakeBin64;
+ private String[] dex2oatBinaries;
+ private FileDescriptor[] stockFds;
private LocalSocket serverSocket = null;
private LocalServerSocket server = null;
- private FileDescriptor stockFd32 = null, stockFd64 = null;
+ private int compatibility = DEX2OAT_OK;
+
+ private final FileObserver selinuxObserver = new FileObserver("/sys/fs/selinux/enforce", FileObserver.MODIFY) {
+ @Override
+ public void onEvent(int i, @Nullable String s) {
+ Log.d(TAG, "SELinux status changed");
+ synchronized (this) {
+ if (compatibility == DEX2OAT_CRASHED) stopWatching();
+ boolean enforcing = false;
+ try (var is = Files.newInputStream(Paths.get("/sys/fs/selinux/enforce"))) {
+ enforcing = is.read() == '1';
+ } catch (IOException ignored) {
+ }
+ if (!enforcing) {
+ if (compatibility == DEX2OAT_OK) setEnabled(false);
+ compatibility = DEX2OAT_SELINUX_PERMISSIVE;
+ } else if (SELinux.checkSELinuxAccess("u:r:untrusted_app:s0", "u:object_r:dex2oat_exec:s0", "file", "execute")
+ || SELinux.checkSELinuxAccess("u:r:untrusted_app:s0", "u:object_r:dex2oat_exec:s0", "file", "execute_no_trans")) {
+ if (compatibility == DEX2OAT_OK) setEnabled(false);
+ compatibility = DEX2OAT_SEPOLICY_INCORRECT;
+ } else {
+ if (compatibility != DEX2OAT_OK) {
+ setEnabled(true);
+ if (checkMount()) compatibility = DEX2OAT_OK;
+ else {
+ setEnabled(false);
+ compatibility = DEX2OAT_MOUNT_FAILED;
+ stopWatching();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void stopWatching() {
+ super.stopWatching();
+ Log.w(TAG, "SELinux observer stopped");
+ }
+ };
@RequiresApi(Build.VERSION_CODES.Q)
public Dex2OatService() {
- thread = new Thread(() -> {
- var devPath = Paths.get(getDevPath());
+ initNative();
+ try {
+ Files.walk(Paths.get(magiskPath).resolve("dex2oat")).forEach(path -> {
+ SELinux.setFileContext(path.toString(), "u:object_r:magisk_file:s0");
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "Error setting sepolicy", e);
+ }
+ if (Arrays.stream(dex2oatBinaries).noneMatch(Objects::nonNull)) {
+ Log.e(TAG, "Failed to find dex2oat binaries");
+ compatibility = DEX2OAT_MOUNT_FAILED;
+ return;
+ }
+
+ if (!checkMount()) { // Already mounted when restart daemon
+ setEnabled(true);
+ if (!checkMount()) {
+ setEnabled(false);
+ compatibility = DEX2OAT_MOUNT_FAILED;
+ return;
+ }
+ }
+
+ Thread daemonThread = new Thread(() -> {
+ var devPath = Paths.get(devTmpDir);
var sockPath = devPath.resolve("dex2oat.sock");
try {
- Log.i(TAG, "dex2oat daemon start");
+ Log.i(TAG, "Daemon start");
if (setSocketCreateContext("u:r:dex2oat:s0")) {
- Log.d(TAG, "set socket context to u:r:dex2oat:s0");
+ Log.d(TAG, "Set socket context to u:r:dex2oat:s0");
} else {
- throw new IOException("failed to set socket context");
+ throw new IOException("Failed to set socket context");
}
Files.createDirectories(devPath);
- Log.d(TAG, "dev path: " + devPath);
+ Log.d(TAG, "Dev path: " + devPath);
serverSocket = new LocalSocket(LocalSocket.SOCKET_STREAM);
serverSocket.bind(new LocalSocketAddress(sockPath.toString(), LocalSocketAddress.Namespace.FILESYSTEM));
server = new LocalServerSocket(serverSocket.getFileDescriptor());
SELinux.setFileContext(sockPath.toString(), "u:object_r:magisk_file:s0");
- if (new File(DEX2OAT_32).exists()) stockFd32 = Os.open(DEX2OAT_32, OsConstants.O_RDONLY, 0);
- if (new File(DEX2OAT_64).exists()) stockFd64 = Os.open(DEX2OAT_64, OsConstants.O_RDONLY, 0);
+ stockFds = new FileDescriptor[dex2oatBinaries.length];
+ for (int i = 0; i < dex2oatBinaries.length; i++) {
+ if (dex2oatBinaries[i] != null) {
+ stockFds[i] = Os.open(dex2oatBinaries[i], OsConstants.O_RDONLY, 0);
+ }
+ }
while (true) {
var client = server.accept();
try (var is = client.getInputStream();
var os = client.getOutputStream()) {
- var lp = is.read();
- if (lp == 32) client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd32});
- else client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd64});
+ var id = is.read();
+ client.setFileDescriptorsForSend(new FileDescriptor[]{stockFds[id]});
os.write(1);
- Log.d(TAG, "sent fd" + lp);
+ Log.d(TAG, String.format("Sent stock fd: is64 = %b, isDebug = %b", (id & 0b10) != 0, (id & 0b01) != 0));
}
}
} catch (Throwable e) {
- Log.e(TAG, "dex2oat daemon crashed", e);
+ Log.e(TAG, "Daemon crashed", e);
try {
server.close();
Files.delete(sockPath);
} catch (IOException ignored) {
}
try {
- if (stockFd32 != null && stockFd32.valid()) Os.close(stockFd32);
- if (stockFd64 != null && stockFd64.valid()) Os.close(stockFd64);
+ for (var fd : stockFds) {
+ if (fd != null && fd.valid()) Os.close(fd);
+ }
} catch (ErrnoException ignored) {
}
- new Handler(Looper.getMainLooper()).post(Dex2OatService::fallback);
+ synchronized (this) {
+ selinuxObserver.stopWatching();
+ if (compatibility == DEX2OAT_OK) {
+ setEnabled(false);
+ compatibility = DEX2OAT_CRASHED;
+ }
+ }
}
});
- thread.start();
+
+ daemonThread.start();
+ selinuxObserver.startWatching();
}
@RequiresApi(Build.VERSION_CODES.Q)
- public boolean isAlive() {
- return thread.isAlive();
+ public int getCompatibility() {
+ return compatibility;
}
- private static native String getDevPath();
+ private native void initNative();
- private static native void fallback();
+ private native void setEnabled(boolean enabled);
+
+ private boolean checkMount() {
+ for (int i = 0; i < dex2oatBinaries.length; i++) {
+ var bin = dex2oatBinaries[i];
+ if (bin == null) continue;
+ try {
+ var apex = Os.stat("/proc/1/root" + bin);
+ var fake = Os.stat(i < 2 ? fakeBin32 : fakeBin64);
+ if (apex.st_ino != fake.st_ino) {
+ Log.w(TAG, "Check mount failed for " + bin);
+ return false;
+ }
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Check mount failed for " + bin, e);
+ return false;
+ }
+ }
+ Log.d(TAG, "Check mount succeeded");
+ return true;
+ }
private boolean setSocketCreateContext(String context) {
FileDescriptor fd = null;
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java
index e2762e3e..9c22d3aa 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java
@@ -777,11 +777,11 @@ public class LSPManagerService extends ILSPManagerService.Stub {
}
@Override
- public boolean dex2oatWrapperAlive() {
+ public int getDex2OatWrapperCompatibility() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- return ServiceManager.getDex2OatService().isAlive();
+ return ServiceManager.getDex2OatService().getCompatibility();
} else {
- return false;
+ return 0;
}
}
}
diff --git a/daemon/src/main/jni/CMakeLists.txt b/daemon/src/main/jni/CMakeLists.txt
index 8ff0318a..6f63d431 100644
--- a/daemon/src/main/jni/CMakeLists.txt
+++ b/daemon/src/main/jni/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(${EXTERNAL_ROOT} external)
set(SOURCES
- dex2oat.c
+ dex2oat.cpp
logcat.cpp
obfuscation.cpp
)
diff --git a/daemon/src/main/jni/dex2oat.cpp b/daemon/src/main/jni/dex2oat.cpp
new file mode 100644
index 00000000..11e693ea
--- /dev/null
+++ b/daemon/src/main/jni/dex2oat.cpp
@@ -0,0 +1,141 @@
+/*
+ * This file is part of LSPosed.
+ *
+ * LSPosed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LSPosed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LSPosed. If not, see .
+ *
+ * Copyright (C) 2022 LSPosed Contributors
+ */
+
+//
+// Created by Nullptr on 2022/4/2.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "logging.h"
+#include "utils.h"
+
+using namespace std::string_literals;
+
+const char *kDex2oat32Path, *kDex2oatDebug32Path;
+const char *kDex2oat64Path, *kDex2oatDebug64Path;
+
+char kFakeBin32[PATH_MAX], kFakeBin64[PATH_MAX];
+char kTmpDir[] = "placeholder_/dev/0123456789abcdef\0";
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_org_lsposed_lspd_service_Dex2OatService_initNative(JNIEnv *env, jobject thiz) {
+ char magisk_path[PATH_MAX], cwd[PATH_MAX], *module_name;
+ FILE *fp = popen("magisk --path", "r");
+ fscanf(fp, "%s", magisk_path);
+ fclose(fp);
+ getcwd(cwd, PATH_MAX);
+ module_name = cwd + std::string_view(cwd).find_last_of('/') + 1;
+ sprintf(kFakeBin32, "%s/.magisk/modules/%s/bin/dex2oat32", magisk_path, module_name);
+ sprintf(kFakeBin64, "%s/.magisk/modules/%s/bin/dex2oat64", magisk_path, module_name);
+
+ if (GetAndroidApiLevel() == 29) {
+ kDex2oat32Path = "/apex/com.android.runtime/bin/dex2oat";
+ kDex2oatDebug32Path = "/apex/com.android.runtime/bin/dex2oatd";
+ } else {
+ kDex2oat32Path = "/apex/com.android.art/bin/dex2oat32";
+ kDex2oatDebug32Path = "/apex/com.android.art/bin/dex2oatd32";
+ kDex2oat64Path = "/apex/com.android.art/bin/dex2oat64";
+ kDex2oatDebug64Path = "/apex/com.android.art/bin/dex2oatd64";
+ }
+
+ std::string copy_dir = magisk_path + "/dex2oat"s;
+ mkdir(copy_dir.c_str(), 0755);
+ auto CopyAndMount = [&](const char *src, const std::string &target) -> bool {
+ int stock = open(src, O_RDONLY);
+ if (stock == -1) return false;
+ struct stat st{};
+ fstat(stock, &st);
+ int copy = open(target.c_str(), O_WRONLY | O_CREAT, st.st_mode);
+ sendfile(copy, stock, nullptr, st.st_size);
+ close(stock);
+ close(copy);
+ mount(target.c_str(), src, nullptr, MS_BIND, nullptr);
+ return true;
+ };
+ bool done[4] = {false};
+ done[0] = CopyAndMount(kDex2oat32Path, copy_dir + "/dex2oat32");
+ done[1] = CopyAndMount(kDex2oatDebug32Path, copy_dir + "/dex2oatd32");
+ if (GetAndroidApiLevel() >= 30) {
+ done[2] = CopyAndMount(kDex2oat64Path, copy_dir + "/dex2oat64");
+ done[3] = CopyAndMount(kDex2oatDebug64Path, copy_dir + "/dex2oatd64");
+ }
+
+ auto clazz = env->GetObjectClass(thiz);
+ auto dev_path_field = env->GetFieldID(clazz, "devTmpDir", "Ljava/lang/String;");
+ auto magisk_path_field = env->GetFieldID(clazz, "magiskPath", "Ljava/lang/String;");
+ auto fake_bin32_field = env->GetFieldID(clazz, "fakeBin32", "Ljava/lang/String;");
+ auto fake_bin64_field = env->GetFieldID(clazz, "fakeBin64", "Ljava/lang/String;");
+ auto binaries_field = env->GetFieldID(clazz, "dex2oatBinaries", "[Ljava/lang/String;");
+ env->SetObjectField(thiz, dev_path_field, env->NewStringUTF(kTmpDir + 12));
+ env->SetObjectField(thiz, magisk_path_field, env->NewStringUTF(magisk_path));
+ env->SetObjectField(thiz, fake_bin32_field, env->NewStringUTF(kFakeBin32));
+ env->SetObjectField(thiz, fake_bin64_field, env->NewStringUTF(kFakeBin64));
+ auto arr = env->NewObjectArray(4, env->FindClass("java/lang/String"), nullptr);
+ if (done[0]) env->SetObjectArrayElement(arr, 0, env->NewStringUTF(kDex2oat32Path));
+ if (done[1]) env->SetObjectArrayElement(arr, 1, env->NewStringUTF(kDex2oatDebug32Path));
+ if (GetAndroidApiLevel() >= 30) {
+ if (done[2]) env->SetObjectArrayElement(arr, 2, env->NewStringUTF(kDex2oat64Path));
+ if (done[3]) env->SetObjectArrayElement(arr, 3, env->NewStringUTF(kDex2oatDebug64Path));
+ }
+ env->SetObjectField(thiz, binaries_field, arr);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_org_lsposed_lspd_service_Dex2OatService_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
+ if (vfork() == 0) {
+ int ns = open("/proc/1/ns/mnt", O_RDONLY);
+ setns(ns, CLONE_NEWNS);
+ close(ns);
+ if (enabled) {
+ LOGI("Enable dex2oat wrapper");
+ mount(kFakeBin32, kDex2oat32Path, nullptr, MS_BIND, nullptr);
+ mount(kFakeBin32, kDex2oatDebug32Path, nullptr, MS_BIND, nullptr);
+ if (GetAndroidApiLevel() >= 30) {
+ mount(kFakeBin64, kDex2oat64Path, nullptr, MS_BIND, nullptr);
+ mount(kFakeBin64, kDex2oatDebug64Path, nullptr, MS_BIND, nullptr);
+ }
+ execlp("resetprop", "resetprop", "--delete", "dalvik.vm.dex2oat-flags", nullptr);
+ } else {
+ LOGI("Disable dex2oat wrapper");
+ umount(kDex2oat32Path);
+ umount(kDex2oatDebug32Path);
+ if (GetAndroidApiLevel() >= 30) {
+ umount(kDex2oat64Path);
+ umount(kDex2oatDebug64Path);
+ }
+ execlp("resetprop", "resetprop", "dalvik.vm.dex2oat-flags", "--inline-max-code-units=0", nullptr);
+ }
+ PLOGE("Failed to resetprop");
+ exit(0);
+ }
+}
diff --git a/daemon/src/main/jni/dex2oat.c b/daemon/src/main/jni/utils.h
similarity index 52%
rename from daemon/src/main/jni/dex2oat.c
rename to daemon/src/main/jni/utils.h
index d3d5e0d1..fb0eaf12 100644
--- a/daemon/src/main/jni/dex2oat.c
+++ b/daemon/src/main/jni/utils.h
@@ -18,26 +18,18 @@
*/
//
-// Created by Nullptr on 2022/4/2.
+// Created by Nullptr on 2022/4/20.
//
-#include
-#include
-#include
-#include
+#pragma once
-#include "logging.h"
-
-char kTmpDir[] = "placeholder_/dev/0123456789abcdef";
-
-JNIEXPORT jstring JNICALL
-Java_org_lsposed_lspd_service_Dex2OatService_getDevPath(JNIEnv *env, jclass clazz) {
- return (*env)->NewStringUTF(env, kTmpDir + 12);
-}
-
-JNIEXPORT void JNICALL
-Java_org_lsposed_lspd_service_Dex2OatService_fallback(JNIEnv *env, jclass clazz) {
- LOGI("do fallback");
- system("nsenter -m -t 1 umount /apex/com.android.art/bin/dex2oat*");
- __system_property_set("dalvik.vm.dex2oat-flags", "--inline-max-code-units=0");
+inline int32_t GetAndroidApiLevel() {
+ static int32_t api_level = []() {
+ char prop_value[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.sdk", prop_value);
+ int base = atoi(prop_value);
+ __system_property_get("ro.build.version.preview_sdk", prop_value);
+ return base + atoi(prop_value);
+ }();
+ return api_level;
}
diff --git a/dex2oat/src/main/cpp/dex2oat.c b/dex2oat/src/main/cpp/dex2oat.c
index 67c97d9e..aa622853 100644
--- a/dex2oat/src/main/cpp/dex2oat.c
+++ b/dex2oat/src/main/cpp/dex2oat.c
@@ -36,6 +36,8 @@
# define LP_SELECT(lp32, lp64) lp32
#endif
+#define ID_VEC(is64, is_debug) (((is64) << 1) | (is_debug))
+
char kTmpDir[] = "placeholder_/dev/0123456789abcdef";
static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
@@ -106,7 +108,7 @@ int main(int argc, char **argv) {
PLOGE("failed to connect to %s", sock.sun_path);
return 1;
}
- write_int(sock_fd, LP_SELECT(32, 64));
+ write_int(sock_fd, ID_VEC(LP_SELECT(0, 1), strstr(argv[0], "dex2oatd") != NULL));
int stock_fd = recv_fd(sock_fd);
read_int(sock_fd);
close(sock_fd);
diff --git a/magisk-loader/magisk_module/daemon b/magisk-loader/magisk_module/daemon
index f7cf399e..8187d2aa 100644
--- a/magisk-loader/magisk_module/daemon
+++ b/magisk-loader/magisk_module/daemon
@@ -40,15 +40,5 @@ if [ ! -S "/dev/socket/zygote" ]; then
fi
$debug && log -p d -t "LSPosed" "start $flavor daemon $*"
-if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then
- umount /apex/com.android.art/bin/dex2oat*
- mkdir "$magiskPath/dex2oat"
- cp -p /apex/com.android.art/bin/dex2oat32 "$magiskPath/dex2oat/dex2oat32"
- cp -p /apex/com.android.art/bin/dex2oat64 "$magiskPath/dex2oat/dex2oat64"
- chcon -R u:object_r:magisk_file:s0 "$magiskPath/dex2oat"
- mount --bind "$magiskPath/dex2oat/dex2oat32" /apex/com.android.art/bin/dex2oat32
- mount --bind "$magiskPath/dex2oat/dex2oat64" /apex/com.android.art/bin/dex2oat64
-fi
-
# shellcheck disable=SC2086
exec /system/bin/app_process $java_options /system/bin --nice-name=lspd org.lsposed.lspd.Main "$@" >/dev/null 2>&1
diff --git a/magisk-loader/magisk_module/post-fs-data.sh b/magisk-loader/magisk_module/post-fs-data.sh
index c26cb98f..6a205643 100644
--- a/magisk-loader/magisk_module/post-fs-data.sh
+++ b/magisk-loader/magisk_module/post-fs-data.sh
@@ -18,15 +18,8 @@
#
MODDIR=${0%/*}
-MODNAME=${MODDIR##*/}
-MAGISK_PATH=$(magisk --path)
rm -f "/data/local/tmp/daemon.apk"
cd "$MODDIR"
-if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then
- mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat32" "/apex/com.android.art/bin/dex2oat32"
- mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat64" "/apex/com.android.art/bin/dex2oat64"
-fi
-
unshare -m sh -c "$MODDIR/daemon &"
diff --git a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl
index dc899994..1b0b17a1 100644
--- a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl
+++ b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl
@@ -6,6 +6,12 @@ import org.lsposed.lspd.models.Application;
interface ILSPManagerService {
+ const int DEX2OAT_OK = 0;
+ const int DEX2OAT_CRASHED = 1;
+ const int DEX2OAT_MOUNT_FAILED = 2;
+ const int DEX2OAT_SELINUX_PERMISSIVE = 3;
+ const int DEX2OAT_SEPOLICY_INCORRECT = 4;
+
String getApi() = 1;
ParceledListSlice getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) = 2;
@@ -80,5 +86,5 @@ interface ILSPManagerService {
void setDexObfuscate(boolean enable) = 43;
- boolean dex2oatWrapperAlive() = 44;
+ int getDex2OatWrapperCompatibility() = 44;
}