diff --git a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java
new file mode 100644
index 00000000..f4f01c05
--- /dev/null
+++ b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java
@@ -0,0 +1,152 @@
+/*
+ * 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
+ */
+
+package org.lsposed.lspd.service;
+
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.SELinux;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.text.TextUtils;
+import android.util.Log;
+
+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.Locale;
+
+public class Dex2OatService {
+
+ private static final String TAG = "Dex2OatService";
+ 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 Thread thread = null;
+
+ public void start() {
+ try {
+ var devPath = Files.readAllLines(Paths.get("/data/adb/lspd/dev_path")).get(0);
+ Log.d(TAG, "dev path: " + devPath);
+ daemon(devPath);
+ } catch (IOException e) {
+ Log.e(TAG, "dex2oat daemon failed to start", e);
+ }
+ }
+
+ public boolean isAlive() {
+ return thread.isAlive();
+ }
+
+ private void daemon(String devPath) {
+ thread = new Thread(() -> {
+ try {
+ Log.i(TAG, "dex2oat daemon start");
+ if (setSocketCreateContext("u:r:dex2oat:s0")) {
+ Log.d(TAG, "set socket context to u:r:dex2oat:s0");
+ } else {
+ Log.e(TAG, "failed to set socket context");
+ }
+ var sockPath = devPath + "/dex2oat.sock";
+ var serverSocket = new LocalSocket(LocalSocket.SOCKET_STREAM);
+ serverSocket.bind(new LocalSocketAddress(sockPath, LocalSocketAddress.Namespace.FILESYSTEM));
+ var server = new LocalServerSocket(serverSocket.getFileDescriptor());
+ SELinux.setFileContext(sockPath, "u:object_r:magisk_file:s0");
+ FileDescriptor stockFd32 = null, stockFd64 = null;
+ 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);
+ 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});
+ os.write(1);
+ Log.d(TAG, "sent fd" + lp);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "dex2oat daemon crashed", e);
+ }
+ });
+ thread.start();
+ }
+
+ private boolean setSocketCreateContext(String context) {
+ FileDescriptor fd = null;
+ try {
+ fd = Os.open("/proc/thread-self/attr/sockcreate", OsConstants.O_RDWR, 0);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.ENOENT) {
+ int tid = Os.gettid();
+ try {
+ fd = Os.open(String.format(Locale.ENGLISH, "/proc/self/task/%d/attr/sockcreate", tid), OsConstants.O_RDWR, 0);
+ } catch (ErrnoException ignored) {
+ }
+ }
+ }
+
+ if (fd == null) {
+ return false;
+ }
+
+ byte[] bytes;
+ int length;
+ int remaining;
+ if (!TextUtils.isEmpty(context)) {
+ byte[] stringBytes = context.getBytes();
+ bytes = new byte[stringBytes.length + 1];
+ System.arraycopy(stringBytes, 0, bytes, 0, stringBytes.length);
+ bytes[stringBytes.length] = '\0';
+
+ length = bytes.length;
+ remaining = bytes.length;
+ } else {
+ bytes = null;
+ length = 0;
+ remaining = 0;
+ }
+
+ do {
+ try {
+ remaining -= Os.write(fd, bytes, length - remaining, remaining);
+ if (remaining <= 0) {
+ break;
+ }
+ } catch (ErrnoException e) {
+ break;
+ } catch (InterruptedIOException e) {
+ remaining -= e.bytesTransferred;
+ }
+ } while (true);
+
+ try {
+ Os.close(fd);
+ } catch (ErrnoException e) {
+ Log.w(TAG, Log.getStackTraceString(e));
+ }
+ return true;
+ }
+}
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 2f603710..fa5a0fe6 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java
@@ -688,7 +688,11 @@ public class LSPManagerService extends ILSPManagerService.Stub {
@Override
public boolean dex2oatFlagsLoaded() {
- return SystemProperties.get(PROP_NAME).contains(PROP_VALUE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return ServiceManager.getDex2OatService().isAlive();
+ } else {
+ return SystemProperties.get(PROP_NAME).contains(PROP_VALUE);
+ }
}
@Override
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
index 90a0adaf..182bb24d 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
@@ -22,6 +22,7 @@ package org.lsposed.lspd.service;
import android.app.ActivityThread;
import android.content.Context;
import android.ddm.DdmHandleAppName;
+import android.os.Build;
import android.os.IBinder;
import android.os.IServiceManager;
import android.os.Looper;
@@ -49,9 +50,14 @@ public class ServiceManager {
private static LSPManagerService managerService = null;
private static LSPSystemServerService systemServerService = null;
private static LogcatService logcatService = null;
+ private static Dex2OatService dex2OatService = null;
private static final ExecutorService executorService = Executors.newCachedThreadPool();
+ public static Dex2OatService getDex2OatService() {
+ return dex2OatService;
+ }
+
public static ExecutorService getExecutorService() {
return executorService;
}
@@ -98,6 +104,11 @@ public class ServiceManager {
logcatService = new LogcatService();
logcatService.start();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ dex2OatService = new Dex2OatService();
+ dex2OatService.start();
+ }
+
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
Looper.prepareMainLooper();
diff --git a/dex2oat/.gitignore b/dex2oat/.gitignore
new file mode 100644
index 00000000..9ff2a377
--- /dev/null
+++ b/dex2oat/.gitignore
@@ -0,0 +1,2 @@
+/build
+/.cxx
diff --git a/dex2oat/build.gradle.kts b/dex2oat/build.gradle.kts
new file mode 100644
index 00000000..0309cece
--- /dev/null
+++ b/dex2oat/build.gradle.kts
@@ -0,0 +1,42 @@
+/*
+ * 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
+ */
+
+plugins {
+ id("com.android.library")
+}
+
+android {
+ namespace = "org.lsposed.dex2oat"
+
+ buildFeatures {
+ androidResources = false
+ buildConfig = false
+ prefab = true
+ }
+
+ defaultConfig {
+ minSdk = 29
+ }
+
+ externalNativeBuild {
+ cmake {
+ path("src/main/cpp/CMakeLists.txt")
+ }
+ }
+}
diff --git a/dex2oat/src/main/cpp/CMakeLists.txt b/dex2oat/src/main/cpp/CMakeLists.txt
new file mode 100644
index 00000000..b63eaad3
--- /dev/null
+++ b/dex2oat/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,19 @@
+project(dex2oat)
+cmake_minimum_required(VERSION 3.4.1)
+
+add_subdirectory(${EXTERNAL_ROOT} external)
+
+add_executable(dex2oat dex2oat.c)
+
+target_link_libraries(dex2oat cxx log)
+
+if (DEFINED DEBUG_SYMBOLS_PATH)
+ message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
+ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}
+ COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $
+ ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
+ COMMAND ${CMAKE_STRIP} --strip-all $
+ COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
+ $)
+endif()
diff --git a/dex2oat/src/main/cpp/dex2oat.c b/dex2oat/src/main/cpp/dex2oat.c
new file mode 100644
index 00000000..121dc68c
--- /dev/null
+++ b/dex2oat/src/main/cpp/dex2oat.c
@@ -0,0 +1,114 @@
+/*
+ * 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/1.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "logging.h"
+
+#if defined(__LP64__)
+# define LP_SELECT(lp32, lp64) lp64
+#else
+# define LP_SELECT(lp32, lp64) lp32
+#endif
+
+char kTmpDir[] = "placeholder_/dev/0123456789abcdef";
+
+static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
+ int rec = recvmsg(sockfd, msg, flags);
+ if (rec < 0) {
+ PLOGE("recvmsg");
+ }
+ return rec;
+}
+
+static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {
+ struct iovec iov = {
+ .iov_base = &cnt,
+ .iov_len = sizeof(cnt),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsgbuf,
+ .msg_controllen = bufsz
+ };
+
+ xrecvmsg(sockfd, &msg, MSG_WAITALL);
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+
+ if (msg.msg_controllen != bufsz ||
+ cmsg == NULL ||
+ cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS) {
+ return NULL;
+ }
+
+ return CMSG_DATA(cmsg);
+}
+
+static void write_int(int fd, int val) {
+ if (fd < 0) return;
+ write(fd, &val, sizeof(val));
+}
+
+static int recv_fd(int sockfd) {
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);
+ if (data == NULL)
+ return -1;
+
+ int result;
+ memcpy(&result, data, sizeof(int));
+ return result;
+}
+
+int main(int argc, char **argv) {
+ LOGI("dex2oat wrapper");
+ struct sockaddr_un sock;
+ sock.sun_family = AF_UNIX;
+ snprintf(sock.sun_path, sizeof(sock.sun_path), "%s/dex2oat.sock", kTmpDir + 12);
+ int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (-1 == connect(sock_fd, (struct sockaddr *) &sock, sizeof(sock))) {
+ PLOGE("failed to connect to %s", sock.sun_path);
+ return 1;
+ }
+ write_int(sock_fd, LP_SELECT(32, 64));
+ int stock_fd = recv_fd(sock_fd);
+ close(sock_fd);
+ LOGD("sock: %s %d", sock.sun_path, stock_fd);
+
+ const char *new_argv[argc + 2];
+ for (int i = 0; i < argc; i++) new_argv[i] = argv[i];
+ new_argv[argc] = "--inline-max-code-units=0";
+ new_argv[argc + 1] = NULL;
+ fexecve(stock_fd, (char **) new_argv, environ);
+ PLOGE("fexecve failed");
+ return 2;
+}
diff --git a/dex2oat/src/main/cpp/logging.h b/dex2oat/src/main/cpp/logging.h
new file mode 100644
index 00000000..cb810dc7
--- /dev/null
+++ b/dex2oat/src/main/cpp/logging.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include
+
+#ifndef LOG_TAG
+#define LOG_TAG "dex2oat-wrapper"
+#endif
+
+#ifdef LOG_DISABLED
+#define LOGD(...)
+#define LOGV(...)
+#define LOGI(...)
+#define LOGW(...)
+#define LOGE(...)
+#else
+#ifndef NDEBUG
+#define LOGD(fmt, ...) \
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, \
+ "%s:%d#%s" \
+ ": " fmt, \
+ __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
+#define LOGV(fmt, ...) \
+ __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, \
+ "%s:%d#%s" \
+ ": " fmt, \
+ __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
+#else
+#define LOGD(...)
+#define LOGV(...)
+#endif
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
+#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
+#endif
diff --git a/magisk-loader/build.gradle.kts b/magisk-loader/build.gradle.kts
index 85e1c0ed..bc48d1ba 100644
--- a/magisk-loader/build.gradle.kts
+++ b/magisk-loader/build.gradle.kts
@@ -143,7 +143,8 @@ fun afterEval() = android.applicationVariants.forEach { variant ->
dependsOn(
"assemble$variantCapped",
":app:package$buildTypeCapped",
- ":daemon:package$buildTypeCapped"
+ ":daemon:package$buildTypeCapped",
+ ":dex2oat:merge${buildTypeCapped}NativeLibs"
)
into(magiskDir)
from("${rootProject.projectDir}/README.md")
@@ -205,6 +206,11 @@ fun afterEval() = android.applicationVariants.forEach { variant ->
include("**/libdaemon.so")
}
}
+ into("bin") {
+ from("${project(":dex2oat").buildDir}/intermediates/cmake/$buildTypeLowered/obj") {
+ include("**/dex2oat")
+ }
+ }
val dexOutPath = if (buildTypeLowered == "release")
"$buildDir/intermediates/dex/$variantCapped/minify${variantCapped}WithR8" else
"$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped"
diff --git a/magisk-loader/magisk_module/customize.sh b/magisk-loader/magisk_module/customize.sh
index 42a0ad11..bec3cc6b 100644
--- a/magisk-loader/magisk_module/customize.sh
+++ b/magisk-loader/magisk_module/customize.sh
@@ -79,7 +79,6 @@ fi
ui_print "- Extracting module files"
extract "$ZIPFILE" 'module.prop' "$MODPATH"
-extract "$ZIPFILE" 'system.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH"
extract "$ZIPFILE" 'uninstall.sh' "$MODPATH"
@@ -89,6 +88,31 @@ extract "$ZIPFILE" 'daemon' "$MODPATH"
rm -f /data/adb/lspd/manager.apk
extract "$ZIPFILE" 'manager.apk' '/data/adb/lspd'
+if [ "$API" -ge 29 ]; then
+ ui_print "- Extracting dex2oat binaries"
+ mkdir "$MODPATH/bin"
+
+ if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then
+ extract "$ZIPFILE" "bin/armeabi-v7a/dex2oat" "$MODPATH/bin" true
+ mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32"
+
+ if [ "$IS64BIT" = true ]; then
+ extract "$ZIPFILE" "bin/arm64-v8a/dex2oat" "$MODPATH/bin" true
+ mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64"
+ fi
+ elif [ "$ARCH" == "x86" ] || [ "$ARCH" == "x64" ]; then
+ extract "$ZIPFILE" "bin/x86/dex2oat" "$MODPATH/bin" true
+ mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32"
+
+ if [ "$IS64BIT" = true ]; then
+ extract "$ZIPFILE" "bin/x86_64/dex2oat" "$MODPATH/bin" true
+ mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64"
+ fi
+ fi
+else
+ extract "$ZIPFILE" 'system.prop' "$MODPATH"
+fi
+
ui_print "- Extracting daemon libraries"
if [ "$ARCH" = "arm" ] ; then
extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true
@@ -161,6 +185,7 @@ elif [ "$FLAVOR" == "riru" ]; then
fi
set_perm_recursive "$MODPATH" 0 0 0755 0644
+set_perm_recursive "$MODPATH/bin" 0 0 0755 0755 u:object_r:dex2oat_exec:s0
chmod 0744 "$MODPATH/daemon"
if [ "$(grep_prop ro.maple.enable)" == "1" ] && [ "$FLAVOR" == "zygisk" ]; then
diff --git a/magisk-loader/magisk_module/daemon b/magisk-loader/magisk_module/daemon
index 57f12a10..f7cf399e 100644
--- a/magisk-loader/magisk_module/daemon
+++ b/magisk-loader/magisk_module/daemon
@@ -1,6 +1,7 @@
#!/system/bin/sh
dir=${0%/*}
+magiskPath=$(magisk --path)
tmpLspdApk="/data/local/tmp/daemon.apk"
debug=@DEBUG@
flavor=@FLAVOR@
@@ -38,5 +39,16 @@ if [ ! -S "/dev/socket/zygote" ]; then
done
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 8627d068..92fce217 100644
--- a/magisk-loader/magisk_module/post-fs-data.sh
+++ b/magisk-loader/magisk_module/post-fs-data.sh
@@ -18,7 +18,23 @@
#
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
+ TMP=$($RANDOM | md5sum | head -c 16)
+ while [ -d "/dev/$TMP" ]; do
+ TMP=$($RANDOM | md5sum | head -c 16)
+ done
+ mkdir "/dev/$TMP"
+ echo "/dev/$TMP" > "/data/adb/lspd/dev_path"
+ sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$TMP/" "$MODDIR/bin/dex2oat32"
+ sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$TMP/" "$MODDIR/bin/dex2oat64"
+ 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/settings.gradle.kts b/settings.gradle.kts
index ad8ab6d4..31fab6d7 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -29,6 +29,7 @@ include(
":app",
":core",
":daemon",
+ ":dex2oat",
":hiddenapi:stubs",
":hiddenapi:bridge",
":magisk-loader",