OSDN Git Service

Extract APKs on install or OTA
authorDavid Brazdil <dbrazdil@google.com>
Wed, 20 Jan 2016 14:04:40 +0000 (14:04 +0000)
committerDavid Brazdil <dbrazdil@google.com>
Thu, 21 Jan 2016 09:36:12 +0000 (09:36 +0000)
ART will unzip APKs in memory during launch if an OAT file is not
present. To save the time and memory, this patch will invoke dex2oat
with '--compiler-filter=verify-at-runtime' to unzip the APK during
install or after an OTA.

Change-Id: I16583f9450ad60356123a29f7a6a649b2ab9999f

core/java/android/content/pm/IPackageManager.aidl
services/core/java/com/android/server/pm/BackgroundDexOptService.java
services/core/java/com/android/server/pm/Installer.java
services/core/java/com/android/server/pm/PackageDexOptimizer.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/java/com/android/server/SystemServer.java

index 90a1198..2c6b604 100644 (file)
@@ -429,6 +429,12 @@ interface IPackageManager {
     void performFstrimIfNeeded();
 
     /**
+     * Ask the package manager to extract packages if needed, to save
+     * the VM unzipping the APK in memory during launch.
+     */
+    void extractPackagesIfNeeded();
+
+    /**
      * Notify the package manager that a package is going to be used.
      */
     void notifyPackageUse(String packageName);
index 06f828e..89e89b0 100644 (file)
@@ -93,7 +93,8 @@ public class BackgroundDexOptService extends JobService {
                         // skip previously failing package
                         continue;
                     }
-                    if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles)) {
+                    if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles,
+                            /* extractOnly */ false)) {
                         // there was a problem running dexopt,
                         // remember this so we do not keep retrying.
                         sFailedPackageNames.add(pkg);
index 366edf9..cf876ee 100644 (file)
@@ -47,8 +47,8 @@ public final class Installer extends SystemService {
     public static final int DEXOPT_DEBUGGABLE   = 1 << 3;
     /** The system boot has finished */
     public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
-    /** Run the application with the JIT compiler */
-    public static final int DEXOPT_USEJIT       = 1 << 5;
+    /** Do not compile, only extract bytecode into an OAT file */
+    public static final int DEXOPT_EXTRACTONLY  = 1 << 5;
 
     /** @hide */
     @IntDef(flag = true, value = {
index 44b51d4..482217c 100644 (file)
@@ -41,7 +41,7 @@ import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
 import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
 import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
-import static com.android.server.pm.Installer.DEXOPT_USEJIT;
+import static com.android.server.pm.Installer.DEXOPT_EXTRACTONLY;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -82,7 +82,7 @@ final class PackageDexOptimizer {
      * {@link PackageManagerService#mInstallLock}.
      */
     int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
-            boolean inclDependencies, String volumeUuid, boolean useProfiles) {
+            boolean inclDependencies, String volumeUuid, boolean useProfiles, boolean extractOnly) {
         ArraySet<String> done;
         if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
             done = new ArraySet<String>();
@@ -97,7 +97,8 @@ final class PackageDexOptimizer {
                 mDexoptWakeLock.acquire();
             }
             try {
-                return performDexOptLI(pkg, instructionSets, done, volumeUuid, useProfiles);
+                return performDexOptLI(pkg, instructionSets, done, volumeUuid, useProfiles,
+                        extractOnly);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -107,7 +108,7 @@ final class PackageDexOptimizer {
     }
 
     private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
-            ArraySet<String> done, String volumeUuid, boolean useProfiles) {
+            ArraySet<String> done, String volumeUuid, boolean useProfiles, boolean extractOnly) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
 
@@ -173,12 +174,13 @@ final class PackageDexOptimizer {
                 Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                         + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                         + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
-                        + " oatDir = " + oatDir);
+                        + " extractOnly=" + extractOnly + " oatDir = " + oatDir);
                 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
                 final int dexFlags =
                         (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
                         | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
                         | (debuggable ? DEXOPT_DEBUGGABLE : 0)
+                        | (extractOnly ? DEXOPT_EXTRACTONLY : 0)
                         | DEXOPT_BOOTCOMPLETE;
                 try {
                     mPackageManagerService.mInstaller.dexopt(path, sharedGid,
@@ -190,12 +192,14 @@ final class PackageDexOptimizer {
                 }
             }
 
-            // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
-            // either have either succeeded dexopt, or have had getDexOptNeeded tell us
-            // it isn't required. We therefore mark that this package doesn't need dexopt unless
-            // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
-            // it.
-            pkg.mDexOptPerformed.add(dexCodeInstructionSet);
+            if (!extractOnly) {
+                // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
+                // either have either succeeded dexopt, or have had getDexOptNeeded tell us
+                // it isn't required. We therefore mark that this package doesn't need dexopt unless
+                // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
+                // it.
+                pkg.mDexOptPerformed.add(dexCodeInstructionSet);
+            }
         }
 
         // If we've gotten here, we're sure that no error occurred and that we haven't
@@ -249,7 +253,8 @@ final class PackageDexOptimizer {
                 // TODO: Analyze and investigate if we (should) profile libraries.
                 // Currently this will do a full compilation of the library.
                 performDexOptLI(libPkg, instructionSets, done,
-                        StorageManager.UUID_PRIVATE_INTERNAL, /*useProfiles*/ false);
+                        StorageManager.UUID_PRIVATE_INTERNAL, /*useProfiles*/ false,
+                        /* extractOnly */ false);
             }
         }
     }
index 26660df..b86e1dd 100644 (file)
@@ -34,6 +34,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DEXOPT;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID;
@@ -627,6 +628,9 @@ public class PackageManagerService extends IPackageManager.Stub {
     // List of packages names to keep cached, even if they are uninstalled for all users
     private List<String> mKeepUninstalledPackages;
 
+    private boolean mUseJitProfiles =
+            SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+
     private static class IFVerificationParams {
         PackageParser.Package pkg;
         boolean replacing;
@@ -6624,6 +6628,23 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
+    @Override
+    public void extractPackagesIfNeeded() {
+        enforceSystemOrRoot("Only the system can request package extraction");
+
+        // Extract pacakges only if profile-guided compilation is enabled because
+        // otherwise BackgroundDexOptService will not dexopt them later.
+        if (mUseJitProfiles) {
+            ArraySet<String> pkgs = getOptimizablePackages();
+            if (pkgs != null) {
+                for (String pkg : pkgs) {
+                    performDexOpt(pkg, null /* instructionSet */, false /* useProfiles */,
+                            true /* extractOnly */);
+                }
+            }
+        }
+    }
+
     private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
         List<ResolveInfo> ris = null;
         try {
@@ -6654,25 +6675,27 @@ public class PackageManagerService extends IPackageManager.Stub {
     // TODO: this is not used nor needed. Delete it.
     @Override
     public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
-        return performDexOptTraced(packageName, instructionSet, false);
+        return performDexOptTraced(packageName, instructionSet, false /* useProfiles */,
+                false /* extractOnly */);
     }
 
-    public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles) {
-        return performDexOptTraced(packageName, instructionSet, useProfiles);
+    public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
+            boolean extractOnly) {
+        return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly);
     }
 
     private boolean performDexOptTraced(String packageName, String instructionSet,
-                boolean useProfiles) {
+                boolean useProfiles, boolean extractOnly) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
         try {
-            return performDexOptInternal(packageName, instructionSet, useProfiles);
+            return performDexOptInternal(packageName, instructionSet, useProfiles, extractOnly);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
     private boolean performDexOptInternal(String packageName, String instructionSet,
-                boolean useProfiles) {
+                boolean useProfiles, boolean extractOnly) {
         PackageParser.Package p;
         final String targetInstructionSet;
         synchronized (mPackages) {
@@ -6694,7 +6717,7 @@ public class PackageManagerService extends IPackageManager.Stub {
             synchronized (mInstallLock) {
                 final String[] instructionSets = new String[] { targetInstructionSet };
                 int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
-                        true /* inclDependencies */, p.volumeUuid, useProfiles);
+                        true /* inclDependencies */, p.volumeUuid, useProfiles, extractOnly);
                 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
             }
         } finally {
@@ -6739,7 +6762,8 @@ public class PackageManagerService extends IPackageManager.Stub {
             // Whoever is calling forceDexOpt wants a fully compiled package.
             // Don't use profiles since that may cause compilation to be skipped.
             final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
-                    true /* inclDependencies */, pkg.volumeUuid, false /* useProfiles */);
+                    true /* inclDependencies */, pkg.volumeUuid, false /* useProfiles */,
+                    false /* extractOnly */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -12981,6 +13005,24 @@ public class PackageManagerService extends IPackageManager.Stub {
                 res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
                 return;
             }
+
+            // Extract package to save the VM unzipping the APK in memory during
+            // launch. Only do this if profile-guided compilation is enabled because
+            // otherwise BackgroundDexOptService will not dexopt the package later.
+            if (mUseJitProfiles) {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                // Do not run PackageDexOptimizer through the local performDexOpt
+                // method because `pkg` is not in `mPackages` yet.
+                int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instructionSets */,
+                        false /* inclDependencies */, volumeUuid, false /* useProfiles */,
+                        true /* extractOnly */);
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
+                    String msg = "Extracking package failed for " + pkgName;
+                    res.setError(INSTALL_FAILED_DEXOPT, msg);
+                    return;
+                }
+            }
         }
 
         if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
index b5330e4..8114031 100644 (file)
@@ -667,6 +667,14 @@ public final class SystemServer {
         }
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
+        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "ExtractPackagesIfNeeded");
+        try {
+            mPackageManagerService.extractPackagesIfNeeded();
+        } catch (Throwable e) {
+            reportWtf("extract packages", e);
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+
         try {
             ActivityManagerNative.getDefault().showBootMessage(
                     context.getResources().getText(