Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1256)

Unified Diff: chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java

Issue 1981473002: Upstream: DexOptimizer and DexLoader classes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/android/webapk/libs/client/DEPS ('k') | chrome/android/webapk/shell_apk/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dbfdbbe9ff46d40a0e6d8e5e394837a128164df
--- /dev/null
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
@@ -0,0 +1,194 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.webapk.lib.client;
+
+import android.os.Build;
+import android.util.Log;
+
+import dalvik.system.DexClassLoader;
+import dalvik.system.DexFile;
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class provides a method to optimize .dex files.
+ * Note: This class is copied (mostly) verbatim from DexOptUtils in GMSCore.
+ */
+public class DexOptimizer {
+ private static final String TAG = "cr_DexOptimzer";
+
+ private static final String DEX_SUFFIX = ".dex";
+ private static final String ODEX_SUFFIX = ".odex";
+
+ /**
+ * Creates optimized odex file for the specified dex file.
+ * @param dexFile Path to a dex file.
+ * @return True if the dex file was successfully optimized.
+ */
+ @SuppressFBWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
+ public static boolean optimize(File dexFile) {
+ if (!dexFile.exists()) {
+ Log.e(TAG, "Dex file does not exist! " + dexFile.getAbsolutePath());
+ return false;
+ }
+
+ try {
+ if (!DexFile.isDexOptNeeded(dexFile.getAbsolutePath())) {
+ return true;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to check optimization status: " + e.toString() + " : "
+ + e.getMessage());
+ }
+
+ File odexDir = null;
+ try {
+ odexDir = ensureOdexDirectory(dexFile);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to create odex directory! " + e.getMessage());
+ return false;
+ }
+
+ File generatedDexDir = odexDir;
+ if (generatedDexDir.equals(dexFile.getParentFile())) {
+ generatedDexDir = new File(odexDir, "optimized");
+ if (!generatedDexDir.exists() && !generatedDexDir.mkdirs()) {
+ return false;
+ }
+ }
+
+ new DexClassLoader(
+ dexFile.getAbsolutePath(),
+ generatedDexDir.getAbsolutePath(),
+ null,
+ ClassLoader.getSystemClassLoader());
+ File optimizedFile = new File(generatedDexDir, dexFile.getName());
+ if (!optimizedFile.exists()) {
+ Log.e(TAG, "Failed to create dex.");
+ return false;
+ }
+
+ File destOdexFile = new File(odexDir, replaceExtension(optimizedFile.getName(), "odex"));
+ if (!optimizedFile.renameTo(destOdexFile)) {
+ Log.e(TAG, "Failed to rename optimized file.");
+ return false;
+ }
+
+ if (!destOdexFile.setReadable(true, false)) {
+ Log.e(TAG, "Failed to make odex world readable.");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Guesses the directory that DexClassLoader looks in for the odex file based on the
+ * Android OS version and the dex path.
+ * @param dexPath
+ * @return Guess for the default odex directory.
+ */
+ private static File odexDirectory(File dexPath) {
+ int currentApiVersion = Build.VERSION.SDK_INT;
+ try {
+ if (currentApiVersion >= Build.VERSION_CODES.M) {
+ return new File(
+ dexPath.getParentFile(), "oat/" + VMRuntime.getCurrentInstructionSet());
+ } else if (currentApiVersion >= Build.VERSION_CODES.LOLLIPOP) {
+ return new File(dexPath.getParentFile(), VMRuntime.getCurrentInstructionSet());
+ } else {
+ return dexPath.getParentFile();
+ }
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Guesses the directory that DexClassLoader looks in for the odex file based on the
+ * Android OS version and the dex path. Creates the directory if it does not exist.
+ * @param dexPath
+ * @return Guess for the default odex directory.
+ */
+ private static File ensureOdexDirectory(File dexPath) throws IOException {
+ File odexDir = odexDirectory(dexPath);
+ if (odexDir == null) {
+ throw new IOException("Failed to create odex cache directory. "
+ + "Could not determine odex directory.");
+ }
+ if (!odexDir.exists()) {
+ boolean success = odexDir.mkdirs();
+ if (!success) {
+ throw new IOException(
+ "Failed to create odex cache directory in data directory.");
+ }
+ // The full path to the odex must be traversable.
+ File root = dexPath.getParentFile();
+ File dir = odexDir;
+ while (dir != null && !root.equals(dir)) {
+ if (!dir.setExecutable(true, false)) {
+ throw new IOException("Failed to make odex directory world traversable: "
+ + dir.getAbsolutePath());
+ }
+ dir = dir.getParentFile();
+ }
+ }
+ return odexDir;
+ }
+
+ /**
+ * Replaces a file name's extension.
+ *
+ * @param name File name to modify.
+ * @param extension New extension.
+ * @return File name with new extension.
+ */
+ private static String replaceExtension(String name, String extension) {
+ int lastDot = name.lastIndexOf(".");
+ StringBuilder sb = new StringBuilder(lastDot + extension.length());
+ sb.append(name, 0, lastDot + 1);
+ sb.append(extension);
+ return sb.toString();
+ }
+
+ /**
+ * Makes use of a hidden API to retrieve the instruction set name for the currently
+ * executing process. This string is used to form the directory name for the generated
+ * odex.
+ *
+ * - This API is not available on pre-L devices, but as the pre-L runtime did not scope odex
+ * files by <isa> on pre-L, this is not a problem.
+ *
+ * - For devices L+, it's still possible for this API to be missing. In that case
+ * we will fallback to A) interpretation, and failing that B) generate an odex in the
+ * client's file space.
+ */
+ private static class VMRuntime {
+ @SuppressWarnings("unchecked")
+ public static String getCurrentInstructionSet() throws NoSuchMethodException {
+ Method getCurrentInstructionSetMethod;
+ try {
+ Class c = Class.forName("dalvik.system.VMRuntime");
+ getCurrentInstructionSetMethod = c.getDeclaredMethod("getCurrentInstructionSet");
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ Log.w(TAG, "dalvik.system.VMRuntime#getCurrentInstructionSet is unsupported.", e);
+ throw new NoSuchMethodException(
+ "dalvik.system.VMRuntime#getCurrentInstructionSet could not be found.");
+ }
+ try {
+ return (String) getCurrentInstructionSetMethod.invoke(null);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ Log.w(TAG, "Failed to call dalvik.system.VMRuntime#getCurrentInstructionSet", e);
+ throw new NoSuchMethodException(
+ "dalvik.system.VMRuntime#getCurrentInstructionSet could not be found.");
+ }
+ }
+ }
+}
« no previous file with comments | « chrome/android/webapk/libs/client/DEPS ('k') | chrome/android/webapk/shell_apk/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698