Invoke dexopt via command line (#445)

Starting with Android 14 (API level 34), the Android Runtime (ART) Service handles on-device Ahead-Of-Time (AOT) compilation, also known as `dexopt`.
As a result, in Android 16 beta qpr2, the method `performDexOptMode` is removed.

See https://source.android.com/docs/core/runtime/configure/package-manager for details.
This commit is contained in:
JingMatrix 2025-11-08 15:58:32 +01:00 committed by GitHub
parent c622d0f1f9
commit 31e19069e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 42 additions and 3 deletions

View File

@ -51,6 +51,8 @@ import androidx.annotation.Nullable;
import org.lsposed.lspd.models.Application; import org.lsposed.lspd.models.Application;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
@ -345,6 +347,42 @@ public class PackageService {
} }
public static boolean performDexOptMode(String packageName) throws RemoteException { public static boolean performDexOptMode(String packageName) throws RemoteException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
Process process = null;
try {
// The 'speed-profile' filter is a balanced choice for performance.
String command = "cmd package compile -m speed-profile -f " + packageName;
process = Runtime.getRuntime().exec(command);
// Capture and log the output for debugging.
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}
int exitCode = process.waitFor();
Log.i(TAG, "Dexopt command finished for " + packageName + " with exit code: " + exitCode);
// A successful command returns exit code 0 and typically "Success" in its output.
return exitCode == 0 && output.toString().contains("Success");
} catch (Exception e) {
Log.e(TAG, "Failed to execute dexopt shell command for " + packageName, e);
if (e instanceof InterruptedException) {
// Preserve the interrupted status.
Thread.currentThread().interrupt();
}
return false;
} finally {
if (process != null) {
process.destroy();
}
}
} else {
// Fallback to the original reflection method for older Android versions.
IPackageManager pm = getPackageManager(); IPackageManager pm = getPackageManager();
if (pm == null) return false; if (pm == null) return false;
return pm.performDexOptMode(packageName, return pm.performDexOptMode(packageName,
@ -352,3 +390,4 @@ public class PackageService {
SystemProperties.get("pm.dexopt.install", "speed-profile"), true, true, null); SystemProperties.get("pm.dexopt.install", "speed-profile"), true, true, null);
} }
} }
}