Merge "Enable profile guided compilation on the framework side"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2f2fbbe..a7d3669 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4723,6 +4723,9 @@
     }
 
     private static void setupJitProfileSupport(LoadedApk loadedApk, File cacheDir) {
+        if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
+            return;
+        }
         final ApplicationInfo appInfo = loadedApk.getApplicationInfo();
         if (isSharingRuntime(appInfo)) {
             // If sharing is enabled we do not have a unique application
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index b3222f0..ed4722d 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -21,6 +21,7 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
@@ -139,14 +140,24 @@
     }
 
     public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
-            int dexFlags) throws InstallerException {
-        dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags);
+            int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
+        dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
+                null /*outputPath*/, dexFlags, volumeUuid, useProfiles);
     }
 
     public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException {
-        execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
-                dexFlags);
+            int dexoptNeeded, String outputPath, int dexFlags, String volumeUuid,
+            boolean useProfiles) throws InstallerException {
+        execute("dexopt",
+                apkPath,
+                uid,
+                pkgName,
+                instructionSet,
+                dexoptNeeded,
+                outputPath,
+                dexFlags,
+                volumeUuid,
+                useProfiles ? '1' : '0');
     }
 
     private boolean connect() {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index eecc0ee..6ad9e20 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -500,8 +500,11 @@
                 final int dexoptNeeded = DexFile.getDexOptNeeded(
                         classPathElement, "*", instructionSet, false /* defer */);
                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                    // System server is fully AOTed and never profiled
+                    // for profile guided compilation.
                     installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
-                            dexoptNeeded, 0 /*dexFlags*/);
+                            dexoptNeeded, 0 /*dexFlags*/, null /*volumeUuid*/,
+                            false /*useProfiles*/);
                 }
             }
         } catch (IOException | InstallerException e) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 40af22a..8e11e06 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -586,6 +586,7 @@
     char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
     char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
     char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
+    char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -691,6 +692,10 @@
     parseRuntimeOption("dalvik.vm.jitmaxsize", jitmaxsizeOptsBuf, "-Xjitmaxsize:");
     parseRuntimeOption("dalvik.vm.jitinitialsize", jitinitialsizeOptsBuf, "-Xjitinitialsize:");
     parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");
+    property_get("dalvik.vm.usejitprofiles", useJitProfilesOptsBuf, "");
+    if (strcmp(useJitProfilesOptsBuf, "true") == 0) {
+        addOption("-Xjitsaveprofilinginfo");
+    }
 
     property_get("ro.config.low_ram", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index af20679..06f828e 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -24,6 +24,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -50,12 +51,13 @@
 
     final AtomicBoolean mIdleTime = new AtomicBoolean(false);
 
-    public static void schedule(Context context, long minLatency) {
+    private boolean useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+
+    public static void schedule(Context context) {
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
         JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
                 .setRequiresDeviceIdle(true)
                 .setRequiresCharging(true)
-                .setMinimumLatency(minLatency)
                 .setPeriodic(TimeUnit.DAYS.toMillis(1))
                 .build();
         js.schedule(job);
@@ -63,16 +65,17 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        Log.i(TAG, "onIdleStart");
+        Log.i(TAG, "onStartJob");
         final PackageManagerService pm =
                 (PackageManagerService)ServiceManager.getService("package");
 
         if (pm.isStorageLow()) {
-            schedule(BackgroundDexOptService.this, RETRY_LATENCY);
+            Log.i(TAG, "Low storage, skipping this run");
             return false;
         }
-        final ArraySet<String> pkgs = pm.getPackagesThatNeedDexOpt();
-        if (pkgs == null) {
+        final ArraySet<String> pkgs = pm.getOptimizablePackages();
+        if (pkgs == null || pkgs.isEmpty()) {
+            Log.i(TAG, "No packages to optimize");
             return false;
         }
 
@@ -83,15 +86,14 @@
             public void run() {
                 for (String pkg : pkgs) {
                     if (!mIdleTime.get()) {
-                        // stopped while still working, so we need to reschedule
-                        schedule(BackgroundDexOptService.this, 0);
+                        // Out of the idle state. Stop the compilation.
                         return;
                     }
                     if (sFailedPackageNames.contains(pkg)) {
                         // skip previously failing package
                         continue;
                     }
-                    if (!pm.performDexOpt(pkg, null /* instruction set */)) {
+                    if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles)) {
                         // there was a problem running dexopt,
                         // remember this so we do not keep retrying.
                         sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a730de4..366edf9 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -143,17 +143,19 @@
     }
 
     public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
-            int dexFlags) throws InstallerException {
+            int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
         assertValidInstructionSet(instructionSet);
-        mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
+        mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags,
+                volumeUuid, useProfiles);
     }
 
     public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, @Nullable String outputPath, int dexFlags)
+            int dexoptNeeded, @Nullable String outputPath, int dexFlags,
+            String volumeUuid, boolean useProfiles)
                     throws InstallerException {
         assertValidInstructionSet(instructionSet);
         mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
-                outputPath, dexFlags);
+                outputPath, dexFlags, volumeUuid, useProfiles);
     }
 
     public void idmap(String targetApkPath, String overlayApkPath, int uid)
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b45a922..44b51d4 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -23,6 +23,7 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.os.storage.StorageManager;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
@@ -68,6 +69,11 @@
         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*dexopt*");
     }
 
+    static boolean canOptimizePackage(PackageParser.Package pkg) {
+        return pkg.canHaveOatDir() &&
+                ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
+    }
+
     /**
      * Performs dexopt on all code paths and libraries of the specified package for specified
      * instruction sets.
@@ -76,7 +82,7 @@
      * {@link PackageManagerService#mInstallLock}.
      */
     int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
-            boolean inclDependencies) {
+            boolean inclDependencies, String volumeUuid, boolean useProfiles) {
         ArraySet<String> done;
         if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
             done = new ArraySet<String>();
@@ -91,7 +97,7 @@
                 mDexoptWakeLock.acquire();
             }
             try {
-                return performDexOptLI(pkg, instructionSets, done);
+                return performDexOptLI(pkg, instructionSets, done, volumeUuid, useProfiles);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -101,7 +107,7 @@
     }
 
     private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
-            ArraySet<String> done) {
+            ArraySet<String> done, String volumeUuid, boolean useProfiles) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
 
@@ -115,7 +121,7 @@
             }
         }
 
-        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
+        if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
 
@@ -124,18 +130,15 @@
 
         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
         boolean performedDexOpt = false;
-        // There are three basic cases here:
-        // 1.) we need to dexopt, either because we are forced or it is needed
-        // 2.) we are deferring a needed dexopt
-        // 3.) we are skipping an unneeded dexopt
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-            if (pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
+            if (!useProfiles && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
+                // Skip only if we do not use profiles since they might trigger a recompilation.
                 continue;
             }
 
             for (String path : paths) {
-                final int dexoptNeeded;
+                int dexoptNeeded;
                 try {
                     dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
                             dexCodeInstructionSet, /* defer */false);
@@ -144,38 +147,46 @@
                     return DEX_OPT_FAILED;
                 }
 
-                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                    final String dexoptType;
-                    String oatDir = null;
-                    if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
-                        dexoptType = "dex2oat";
-                        oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
-                    } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
-                        dexoptType = "patchoat";
-                    } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
-                        dexoptType = "self patchoat";
+                if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+                    if (useProfiles) {
+                        // If we do a profile guided compilation then we might recompile the same
+                        // package if more profile information is available.
+                        dexoptNeeded = DexFile.DEX2OAT_NEEDED;
                     } else {
-                        throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
+                        // No dexopt needed and we don't use profiles. Nothing to do.
+                        continue;
                     }
+                }
+                final String dexoptType;
+                String oatDir = null;
+                if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
+                    dexoptType = "dex2oat";
+                    oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
+                } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
+                    dexoptType = "patchoat";
+                } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
+                    dexoptType = "self patchoat";
+                } else {
+                    throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
+                }
 
-                    Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
-                            + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
-                            + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
-                            + " 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)
-                            | DEXOPT_BOOTCOMPLETE;
-                    try {
-                        mPackageManagerService.mInstaller.dexopt(path, sharedGid,
-                                pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir,
-                                dexFlags);
-                        performedDexOpt = true;
-                    } catch (InstallerException e) {
-                        Slog.w(TAG, "Failed to dexopt", e);
-                    }
+                Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+                        + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+                        + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
+                        + " 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)
+                        | DEXOPT_BOOTCOMPLETE;
+                try {
+                    mPackageManagerService.mInstaller.dexopt(path, sharedGid,
+                            pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir,
+                            dexFlags, volumeUuid, useProfiles);
+                    performedDexOpt = true;
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to dexopt", e);
                 }
             }
 
@@ -235,7 +246,10 @@
             PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
                     libName);
             if (libPkg != null && !done.contains(libName)) {
-                performDexOptLI(libPkg, instructionSets, done);
+                // 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);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8a243a8..88198d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2093,8 +2093,12 @@
                         try {
                             int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
                             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                                // Shared libraries do not have profiles so we perform a full
+                                // AOT compilation.
                                 mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
-                                        dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
+                                        dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
+                                        StorageManager.UUID_PRIVATE_INTERNAL,
+                                        false /*useProfiles*/);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
@@ -6647,25 +6651,28 @@
         }
     }
 
+    // TODO: this is not used nor needed. Delete it.
     @Override
     public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
-        return performDexOptTraced(packageName, instructionSet);
+        return performDexOptTraced(packageName, instructionSet, false);
     }
 
-    public boolean performDexOpt(String packageName, String instructionSet) {
-        return performDexOptTraced(packageName, instructionSet);
+    public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles) {
+        return performDexOptTraced(packageName, instructionSet, useProfiles);
     }
 
-    private boolean performDexOptTraced(String packageName, String instructionSet) {
+    private boolean performDexOptTraced(String packageName, String instructionSet,
+                boolean useProfiles) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
         try {
-            return performDexOptInternal(packageName, instructionSet);
+            return performDexOptInternal(packageName, instructionSet, useProfiles);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
-    private boolean performDexOptInternal(String packageName, String instructionSet) {
+    private boolean performDexOptInternal(String packageName, String instructionSet,
+                boolean useProfiles) {
         PackageParser.Package p;
         final String targetInstructionSet;
         synchronized (mPackages) {
@@ -6677,7 +6684,8 @@
 
             targetInstructionSet = instructionSet != null ? instructionSet :
                     getPrimaryInstructionSet(p.applicationInfo);
-            if (p.mDexOptPerformed.contains(targetInstructionSet)) {
+            if (!useProfiles && p.mDexOptPerformed.contains(targetInstructionSet)) {
+                // Skip only if we do not use profiles since they might trigger a recompilation.
                 return false;
             }
         }
@@ -6686,7 +6694,7 @@
             synchronized (mInstallLock) {
                 final String[] instructionSets = new String[] { targetInstructionSet };
                 int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
-                        true /* inclDependencies */);
+                        true /* inclDependencies */, p.volumeUuid, useProfiles);
                 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
             }
         } finally {
@@ -6694,20 +6702,13 @@
         }
     }
 
-    public ArraySet<String> getPackagesThatNeedDexOpt() {
-        ArraySet<String> pkgs = null;
+    public ArraySet<String> getOptimizablePackages() {
+        ArraySet<String> pkgs = new ArraySet<String>();
         synchronized (mPackages) {
             for (PackageParser.Package p : mPackages.values()) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, p.packageName + " mDexOptPerformed=" + p.mDexOptPerformed.toArray());
+                if (PackageDexOptimizer.canOptimizePackage(p)) {
+                    pkgs.add(p.packageName);
                 }
-                if (!p.mDexOptPerformed.isEmpty()) {
-                    continue;
-                }
-                if (pkgs == null) {
-                    pkgs = new ArraySet<String>();
-                }
-                pkgs.add(p.packageName);
             }
         }
         return pkgs;
@@ -6735,8 +6736,10 @@
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
+            // 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 */);
+                    true /* inclDependencies */, pkg.volumeUuid, false /* useProfiles */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ee5f23f..7ce945bf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1051,7 +1051,7 @@
 
                 traceBeginAndSlog("StartBackgroundDexOptService");
                 try {
-                    BackgroundDexOptService.schedule(context, 0);
+                    BackgroundDexOptService.schedule(context);
                 } catch (Throwable e) {
                     reportWtf("starting BackgroundDexOptService", e);
                 }