Merge "make update-api"
diff --git a/Android.mk b/Android.mk
index 14819d9..4e036b5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -906,8 +906,7 @@
 # FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
 dirs_to_document := \
 	$(dirs_to_check_apis) \
-  $(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
-  $(addprefix ../../, $(FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS)) \
+	$(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS))
 
 patterns_to_not_document := \
 	$(call find-no-docs-pattern, $(dirs_to_document))
@@ -956,6 +955,8 @@
 	framework \
 	voip-common
 
+# Platform docs can refer to Support Library APIs, but we don't actually build
+# them as part of the docs target, so we need to include them on the classpath.
 framework_docs_LOCAL_JAVA_LIBRARIES := \
 	$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
 	$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
@@ -1002,16 +1003,11 @@
     -werror -hide 111 -hide 113 -hide 121 \
     -overview $(LOCAL_PATH)/core/java/overview.html \
 
-# Allow the support library to add its own droiddoc options.
-include $(LOCAL_PATH)/../support/droiddoc.mk
-
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
 	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
 
 framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
-	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
-	$(foreach lib,$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)) \
-	$(foreach lib,$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib)-res,,COMMON))
+	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
 
 framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
     frameworks/base/docs/knowntags.txt \
@@ -1054,6 +1050,11 @@
 		-resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
 		-resourcesoutdir reference/android/images/
 
+# Federate Support Library references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+		-federate SupportLib https://developer.android.com \
+		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
+
 # ====  the api stubs and current.xml ===========================
 include $(CLEAR_VARS)
 
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index fc697a3..d15dd6d 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -31,7 +31,7 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -116,13 +116,18 @@
         registerSecondaryDexForProfiling(dexPathsForRegistration);
     }
 
-    private void notifyPackageManager(List<BaseDexClassLoader> ignored,
+    private void notifyPackageManager(List<BaseDexClassLoader> classLoadersChain,
             List<String> classPaths) {
+        // Get the class loader names for the binder call.
+        List<String> classLoadersNames = new ArrayList<>(classPaths.size());
+        for (BaseDexClassLoader bdc : classLoadersChain) {
+            classLoadersNames.add(bdc.getClass().getName());
+        }
         String packageName = ActivityThread.currentPackageName();
         try {
             // Notify only the paths of the first class loader for now.
             ActivityThread.getPackageManager().notifyDexLoad(
-                    packageName, Arrays.asList(classPaths.get(0).split(File.pathSeparator)),
+                    packageName, classLoadersNames, classPaths,
                     VMRuntime.getRuntime().vmInstructionSet());
         } catch (RemoteException re) {
             Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64d687e..c9afd6b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -469,11 +469,19 @@
      * Notify the package manager that a list of dex files have been loaded.
      *
      * @param loadingPackageName the name of the package who performs the load
-     * @param dexPats the list of the dex files paths that have been loaded
+     * @param classLoadersNames the names of the class loaders present in the loading chain. The
+     *    list encodes the class loader chain in the natural order. The first class loader has
+     *    the second one as its parent and so on. The dex files present in the class path of the
+     *    first class loader will be recorded in the usage file.
+     * @param classPaths the class paths corresponding to the class loaders names from
+     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
+     *     and so on. A classpath is represented as a list of dex files separated by
+     *     {@code File.pathSeparator}.
+     *     The dex files found in the first class path will be recorded in the usage file.
      * @param loaderIsa the ISA of the loader process
      */
-    oneway void notifyDexLoad(String loadingPackageName, in List<String> dexPaths,
-            String loaderIsa);
+    oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
+            in List<String> classPaths, String loaderIsa);
 
     /**
      * Register an application dex module with the package manager.
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 75fea52..2efde23 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -23,7 +23,6 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.TypedProperties;
 
-import dalvik.bytecode.OpcodeInfo;
 import dalvik.system.VMDebug;
 
 import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -48,8 +47,6 @@
 import java.util.Map;
 
 
-
-
 /**
  * Provides various debugging methods for Android applications, including
  * tracing and allocation counts.
@@ -1948,13 +1945,7 @@
      */
     @Deprecated
     public static class InstructionCount {
-        private static final int NUM_INSTR =
-            OpcodeInfo.MAXIMUM_PACKED_VALUE + 1;
-
-        private int[] mCounts;
-
         public InstructionCount() {
-            mCounts = new int[NUM_INSTR];
         }
 
         /**
@@ -1964,13 +1955,7 @@
          * @return true if counting was started
          */
         public boolean resetAndStart() {
-            try {
-                VMDebug.startInstructionCounting();
-                VMDebug.resetInstructionCount();
-            } catch (UnsupportedOperationException uoe) {
-                return false;
-            }
-            return true;
+            return false;
         }
 
         /**
@@ -1978,13 +1963,7 @@
          * counting process.
          */
         public boolean collect() {
-            try {
-                VMDebug.stopInstructionCounting();
-                VMDebug.getInstructionCount(mCounts);
-            } catch (UnsupportedOperationException uoe) {
-                return false;
-            }
-            return true;
+            return false;
         }
 
         /**
@@ -1992,13 +1971,7 @@
          * all threads).
          */
         public int globalTotal() {
-            int count = 0;
-
-            for (int i = 0; i < NUM_INSTR; i++) {
-                count += mCounts[i];
-            }
-
-            return count;
+            return 0;
         }
 
         /**
@@ -2006,15 +1979,7 @@
          * executed globally.
          */
         public int globalMethodInvocations() {
-            int count = 0;
-
-            for (int i = 0; i < NUM_INSTR; i++) {
-                if (OpcodeInfo.isInvoke(i)) {
-                    count += mCounts[i];
-                }
-            }
-
-            return count;
+            return 0;
         }
     }
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 429a1d9..3cda820 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -553,7 +553,7 @@
             try {
                 dexoptNeeded = DexFile.getDexOptNeeded(
                     classPathElement, instructionSet, systemServerFilter,
-                    false /* newProfile */, false /* downgrade */);
+                    null /* classLoaderContext */, false /* newProfile */, false /* downgrade */);
             } catch (FileNotFoundException ignored) {
                 // Do not add to the classpath.
                 Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 914e81e..e5c48cc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -336,7 +336,8 @@
             int dexoptFlags =
                     DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
                     DexoptOptions.DEXOPT_BOOT_COMPLETE |
-                    (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0);
+                    (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) |
+                    DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
             if (is_for_primary_dex) {
                 int result = pm.performDexOptWithStatus(new DexoptOptions(pkg,
                         PackageManagerService.REASON_BACKGROUND_DEXOPT,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 371b3ef..210eb13 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -56,6 +56,8 @@
     public static final int DEXOPT_STORAGE_CE     = 1 << 7;
     /** Indicates that the dex file passed to dexopt in on DE storage. */
     public static final int DEXOPT_STORAGE_DE     = 1 << 8;
+    /** Indicates that dexopt is invoked from the background service. */
+    public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 241d76f..da6e26e 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -318,7 +318,7 @@
         optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */,
                 null /* CompilerStats.PackageStats */,
-                mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
+                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
                 new DexoptOptions(pkg.packageName, compilationReason,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE));
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e53a08a..dfa828d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -32,14 +32,16 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
+import com.android.server.pm.dex.PackageDexUsage;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 
 import dalvik.system.DexFile;
 
@@ -51,6 +53,7 @@
 import static com.android.server.pm.Installer.DEXOPT_FORCE;
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -123,7 +126,7 @@
      */
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
@@ -131,7 +134,7 @@
             final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets,
-                        packageStats, isUsedByOtherApps, options);
+                        packageStats, packageUseInfo, options);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -145,21 +148,13 @@
     @GuardedBy("mInstallLock")
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         final List<String> paths = pkg.getAllCodePaths();
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
 
-        final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
-                options.getCompilerFilter(), isUsedByOtherApps);
-        final boolean profileUpdated = options.isCheckForProfileUpdates() &&
-                isProfileUpdated(pkg, sharedGid, compilerFilter);
-
-        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
-        final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
-
         // Get the class loader context dependencies.
         // For each code path in the package, this array contains the class loader context that
         // needs to be passed to dexopt in order to ensure correct optimizations.
@@ -183,6 +178,17 @@
                 }
             }
 
+            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
+                    || packageUseInfo.isUsedByOtherApps(path);
+            final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+                options.getCompilerFilter(), isUsedByOtherApps);
+            final boolean profileUpdated = options.isCheckForProfileUpdates() &&
+                isProfileUpdated(pkg, sharedGid, compilerFilter);
+
+            // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
+            // flags.
+            final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
@@ -209,9 +215,10 @@
      */
     @GuardedBy("mInstallLock")
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
-            String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
+            String compilerFilter, boolean profileUpdated, String classLoaderContext,
             int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
-        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
+        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
+                profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
             return DEX_OPT_SKIPPED;
         }
@@ -224,8 +231,8 @@
         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
                 + " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
-                + " target-filter=" + compilerFilter + " oatDir=" + oatDir
-                + " sharedLibraries=" + sharedLibrariesPath);
+                + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
+                + " classLoaderContext=" + classLoaderContext);
 
         try {
             long startTime = System.currentTimeMillis();
@@ -234,7 +241,7 @@
             // installd only uses downgrade flag for secondary dex files and ignores it for
             // primary dex files.
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
-                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
+                    compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
                     false /* downgrade*/);
 
             if (packageStats != null) {
@@ -262,13 +269,12 @@
      * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
      * that seems wasteful.
      */
-    public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
+    public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
+            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
         synchronized (mInstallLock) {
             final long acquireTime = acquireWakeLockLI(info.uid);
             try {
-                return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
-                        isUsedByOtherApps, downgrade);
+                return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -309,9 +315,16 @@
     }
 
     @GuardedBy("mInstallLock")
-    private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
-        compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
+    private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
+            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+        if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
+            // We are asked to optimize only the dex files used by other apps and this is not
+            // on of them: skip it.
+            return DEX_OPT_SKIPPED;
+        }
+
+        String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
+                dexUseInfo.isUsedByOtherApps());
         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         // Secondary dex files are currently not compiled at boot.
         int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
@@ -328,20 +341,32 @@
             return DEX_OPT_FAILED;
         }
         Log.d(TAG, "Running dexopt on: " + path
-                + " pkg=" + info.packageName + " isa=" + isas
+                + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
                 + " target-filter=" + compilerFilter);
 
+        String classLoaderContext;
+        if (dexUseInfo.isUnknownClassLoaderContext() ||
+                dexUseInfo.isUnsupportedClassLoaderContext() ||
+                dexUseInfo.isVariableClassLoaderContext()) {
+            // If we have an unknown (not yet set), unsupported (custom class loaders), or a
+            // variable class loader chain, compile without a context and mark the oat file with
+            // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation.
+            // TODO(calin): We should just extract in this case.
+            classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+        } else {
+            classLoaderContext = dexUseInfo.getClassLoaderContext();
+        }
         try {
-            for (String isa : isas) {
+            for (String isa : dexUseInfo.getLoaderIsas()) {
                 // Reuse the same dexopt path as for the primary apks. We don't need all the
                 // arguments as some (dexopNeeded and oatDir) will be computed by installd because
                 // system server cannot read untrusted app content.
                 // TODO(calin): maybe add a separate call.
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
-                        compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser,
-                        downgrade);
+                        compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
+                        options.isDowngrade());
             }
 
             return DEX_OPT_PERFORMED;
@@ -369,26 +394,60 @@
     /**
      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
      */
-    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg) {
+    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
+            PackageDexUsage.PackageUseInfo useInfo) {
         final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
 
         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
 
-        for (String instructionSet : dexCodeInstructionSets) {
-             pw.println("Instruction Set: " + instructionSet);
-             pw.increaseIndent();
-             for (String path : paths) {
-                  String status = null;
-                  try {
-                      status = DexFile.getDexFileStatus(path, instructionSet);
-                  } catch (IOException ioe) {
-                      status = "[Exception]: " + ioe.getMessage();
-                  }
-                  pw.println("path: " + path);
-                  pw.println("status: " + status);
-             }
-             pw.decreaseIndent();
+        for (String path : paths) {
+            pw.println("path: " + path);
+            pw.increaseIndent();
+
+            for (String isa : dexCodeInstructionSets) {
+                String status = null;
+                try {
+                    status = DexFile.getDexFileStatus(path, isa);
+                } catch (IOException ioe) {
+                     status = "[Exception]: " + ioe.getMessage();
+                }
+                pw.println(isa + ": " + status);
+            }
+
+            if (useInfo.isUsedByOtherApps(path)) {
+                pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+            }
+
+            Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
+
+            if (!dexUseInfoMap.isEmpty()) {
+                pw.println("known secondary dex files:");
+                pw.increaseIndent();
+                for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
+                    String dex = e.getKey();
+                    PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
+                    pw.println(dex);
+                    pw.increaseIndent();
+                    for (String isa : dexUseInfo.getLoaderIsas()) {
+                        String status = null;
+                        try {
+                            status = DexFile.getDexFileStatus(path, isa);
+                        } catch (IOException ioe) {
+                             status = "[Exception]: " + ioe.getMessage();
+                        }
+                        pw.println(isa + ": " + status);
+                    }
+
+                    pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
+                    if (dexUseInfo.isUsedByOtherApps()) {
+                        pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+                    }
+                    pw.decreaseIndent();
+                }
+                pw.decreaseIndent();
+            }
+            pw.decreaseIndent();
         }
     }
 
@@ -442,11 +501,11 @@
      * configuration (isa, compiler filter, profile).
      */
     private int getDexoptNeeded(String path, String isa, String compilerFilter,
-            boolean newProfile, boolean downgrade) {
+            String classLoaderContext, boolean newProfile, boolean downgrade) {
         int dexoptNeeded;
         try {
-            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
-                    downgrade);
+            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext,
+                    newProfile, downgrade);
         } catch (IOException ioe) {
             Slog.w(TAG, "IOException reading apk: " + path, ioe);
             return DEX_OPT_FAILED;
@@ -545,6 +604,9 @@
         if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
             flagsList.add("storage_de");
         }
+        if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
+            flagsList.add("idle_background_job");
+        }
 
         return String.join(",", flagsList);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6b8a415..a8ea4ea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9461,7 +9461,8 @@
     }
 
     @Override
-    public void notifyDexLoad(String loadingPackageName, List<String> dexPaths, String loaderIsa) {
+    public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
+            List<String> classPaths, String loaderIsa) {
         int userId = UserHandle.getCallingUserId();
         ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
         if (ai == null) {
@@ -9469,7 +9470,7 @@
                 + loadingPackageName + ", user=" + userId);
             return;
         }
-        mDexManager.notifyDexLoad(ai, dexPaths, loaderIsa, userId);
+        mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
     }
 
     @Override
@@ -9619,17 +9620,19 @@
         Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
         final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
         if (!deps.isEmpty()) {
+            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+                    options.getCompilerFilter(), options.getSplitName(),
+                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
             for (PackageParser.Package depPackage : deps) {
                 // TODO: Analyze and investigate if we (should) profile libraries.
                 pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
                         getOrCreateCompilerPackageStats(depPackage),
-                        true /* isUsedByOtherApps */,
-                        options);
+                    mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
                 getOrCreateCompilerPackageStats(p),
-                mDexManager.isUsedByOtherApps(p.packageName), options);
+                mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
     }
 
     /**
@@ -9755,6 +9758,7 @@
     public void shutdown() {
         mPackageUsage.writeNow(mPackages);
         mCompilerStats.writeNow();
+        mDexManager.writePackageDexUsageNow();
     }
 
     @Override
@@ -18279,7 +18283,7 @@
                 mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                         null /* instructionSets */,
                         getOrCreateCompilerPackageStats(pkg),
-                        mDexManager.isUsedByOtherApps(pkg.packageName),
+                        mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
                         dexoptOptions);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
@@ -22551,7 +22555,8 @@
         for (PackageParser.Package pkg : packages) {
             ipw.println("[" + pkg.packageName + "]");
             ipw.increaseIndent();
-            mPackageDexOptimizer.dumpDexoptState(ipw, pkg);
+            mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
+                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
             ipw.decreaseIndent();
         }
     }
@@ -25064,8 +25069,8 @@
                 if (ps == null) {
                     continue;
                 }
-                PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
-                        pkg.packageName);
+                PackageDexUsage.PackageUseInfo packageUseInfo =
+                      getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
                 if (PackageManagerServiceUtils
                         .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
                                 downgradeTimeThresholdMillis, packageUseInfo,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 1a97a72..0a9480b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -47,7 +47,16 @@
     // Load the property for the given reason and check for validity. This will throw an
     // exception in case the reason or value are invalid.
     private static String getAndCheckValidity(int reason) {
-        String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
+        String sysPropName = getSystemPropertyName(reason);
+        String sysPropValue;
+        // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
+        // figure out how to deal with these system properties that currently appear on
+        // vendor.
+        if ("pm.dexopt.inactive".equals(sysPropName)) {
+            sysPropValue = "verify";
+        } else {
+            sysPropValue = SystemProperties.get(sysPropName);
+        }
         if (sysPropValue == null || sysPropValue.isEmpty() ||
                 !DexFile.isValidCompilerFilter(sysPropValue)) {
             throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 820b2cb..1e0ce7a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -27,7 +27,6 @@
 import android.annotation.NonNull;
 import android.app.AppGlobals;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -144,9 +143,11 @@
                 sortTemp, packageManagerService);
 
         // Give priority to apps used by other apps.
+        DexManager dexManager = packageManagerService.getDexManager();
         applyPackageFilter((pkg) ->
-                packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
-                remainingPkgs, sortTemp, packageManagerService);
+                dexManager.getPackageUseInfoOrDefault(pkg.packageName)
+                        .isAnyCodePathUsedByOtherApps(),
+                result, remainingPkgs, sortTemp, packageManagerService);
 
         // Filter out packages that aren't recently used, add all remaining apps.
         // TODO: add a property to control this?
@@ -219,7 +220,7 @@
         boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
                 - latestPackageUseTimeInMillis)
                 < thresholdTimeinMillis)
-                && packageUseInfo.isUsedByOtherApps();
+                && packageUseInfo.isAnyCodePathUsedByOtherApps();
 
         return !isActiveInBackgroundAndUsedByOtherPackages;
     }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 0d4df4d..6274754 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -36,6 +36,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -81,6 +83,19 @@
     private static int DEX_SEARCH_FOUND_SPLIT = 2;  // dex file is a split apk
     private static int DEX_SEARCH_FOUND_SECONDARY = 3;  // dex file is a secondary dex
 
+    /**
+     * We do not record packages that have no secondary dex files or that are not used by other
+     * apps. This is an optimization to reduce the amount of data that needs to be written to
+     * disk (apps will not usually be shared so this trims quite a bit the number we record).
+     *
+     * To make this behaviour transparent to the callers which need use information on packages,
+     * DexManager will return this DEFAULT instance from
+     * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
+     * is marked as not being used by other apps. This reflects the intended behaviour when we don't
+     * find the package in the underlying data file.
+     */
+    private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+
     public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
             Installer installer, Object installLock) {
       mPackageCodeLocationsCache = new HashMap<>();
@@ -97,29 +112,55 @@
      * return as fast as possible.
      *
      * @param loadingAppInfo the package performing the load
-     * @param dexPaths the list of dex files being loaded
+     * @param classLoadersNames the names of the class loaders present in the loading chain. The
+     *    list encodes the class loader chain in the natural order. The first class loader has
+     *    the second one as its parent and so on. The dex files present in the class path of the
+     *    first class loader will be recorded in the usage file.
+     * @param classPaths the class paths corresponding to the class loaders names from
+     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
+     *     and so on. A classpath is represented as a list of dex files separated by
+     *     {@code File.pathSeparator}.
+     *     The dex files found in the first class path will be recorded in the usage file.
      * @param loaderIsa the ISA of the app loading the dex files
      * @param loaderUserId the user id which runs the code loading the dex files
      */
-    public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> dexPaths,
-            String loaderIsa, int loaderUserId) {
+    public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
+            List<String> classPaths, String loaderIsa, int loaderUserId) {
         try {
-            notifyDexLoadInternal(loadingAppInfo, dexPaths, loaderIsa, loaderUserId);
+            notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+                    loaderUserId);
         } catch (Exception e) {
             Slog.w(TAG, "Exception while notifying dex load for package " +
                     loadingAppInfo.packageName, e);
         }
     }
 
-    private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, List<String> dexPaths,
-            String loaderIsa, int loaderUserId) {
+    private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
+            List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+            int loaderUserId) {
+        if (classLoaderNames.size() != classPaths.size()) {
+            Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+            return;
+        }
+        if (classLoaderNames.isEmpty()) {
+            Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
+            return;
+        }
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
-            Slog.w(TAG, "Loading dex files " + dexPaths + " in unsupported ISA: " +
+            Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
                     loaderIsa + "?");
             return;
         }
 
-        for (String dexPath : dexPaths) {
+        // The classpath is represented as a list of dex files separated by File.pathSeparator.
+        String[] dexPathsToRegister = classPaths.get(0).split(File.pathSeparator);
+
+        // Encode the class loader contexts for the dexPathsToRegister.
+        String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
+                classLoaderNames, classPaths);
+
+        int dexPathIndex = 0;
+        for (String dexPath : dexPathsToRegister) {
             // Find the owning package name.
             DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
 
@@ -147,23 +188,25 @@
                 // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
                 // or UsedBytOtherApps), record will return true and we trigger an async write
                 // to disk to make sure we don't loose the data in case of a reboot.
+
+                // A null classLoaderContexts means that there are unsupported class loaders in the
+                // chain.
+                String classLoaderContext = classLoaderContexts == null
+                        ? PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT
+                        : classLoaderContexts[dexPathIndex];
                 if (mPackageDexUsage.record(searchResult.mOwningPackageName,
-                        dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit)) {
+                        dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+                        loadingAppInfo.packageName, classLoaderContext)) {
                     mPackageDexUsage.maybeWriteAsync();
                 }
             } else {
-                // This can happen in a few situations:
-                // - bogus dex loads
-                // - recent installs/uninstalls that we didn't detect.
-                // - new installed splits
                 // If we can't find the owner of the dex we simply do not track it. The impact is
                 // that the dex file will not be considered for offline optimizations.
-                // TODO(calin): add hooks for move/uninstall notifications to
-                // capture package moves or obsolete packages.
                 if (DEBUG) {
                     Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
                 }
             }
+            dexPathIndex++;
         }
     }
 
@@ -269,6 +312,8 @@
 
     private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
         // Cache the code locations for the installed packages. This allows for
         // faster lookups (no locks) when finding what package owns the dex file.
         for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) {
@@ -278,25 +323,53 @@
                 // Cache the code locations.
                 cachePackageInfo(pi, userId);
 
-                // Cache a map from package name to the set of user ids who installed the package.
+                // Cache two maps:
+                //   - from package name to the set of user ids who installed the package.
+                //   - from package name to the set of code paths.
                 // We will use it to sync the data and remove obsolete entries from
                 // mPackageDexUsage.
                 Set<Integer> users = putIfAbsent(
                         packageToUsersMap, pi.packageName, new HashSet<>());
                 users.add(userId);
+
+                Set<String> codePaths = putIfAbsent(
+                    packageToCodePaths, pi.packageName, new HashSet<>());
+                codePaths.add(pi.applicationInfo.sourceDir);
+                if (pi.applicationInfo.splitSourceDirs != null) {
+                    Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs);
+                }
             }
         }
 
         mPackageDexUsage.read();
-        mPackageDexUsage.syncData(packageToUsersMap);
+        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
     }
 
     /**
      * Get the package dex usage for the given package name.
-     * @return the package data or null if there is no data available for this package.
+     * If there is no usage info the method will return a default {@code PackageUseInfo} with
+     * no data about secondary dex files and marked as not being used by other apps.
+     *
+     * Note that no use info means the package was not used or it was used but not by other apps.
+     * Also, note that right now we might prune packages which are not used by other apps.
+     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+     * to access the package use.
      */
-    public PackageUseInfo getPackageUseInfo(String packageName) {
-        return mPackageDexUsage.getPackageUseInfo(packageName);
+    public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
+        PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+        return useInfo == null ? DEFAULT_USE_INFO : useInfo;
+    }
+
+    /**
+     * Return whether or not the manager has usage information on the give package.
+     *
+     * Note that no use info means the package was not used or it was used but not by other apps.
+     * Also, note that right now we might prune packages which are not used by other apps.
+     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+     * to access the package use.
+     */
+    /*package*/ boolean hasInfoOnPackage(String packageName) {
+        return mPackageDexUsage.getPackageUseInfo(packageName) != null;
     }
 
     /**
@@ -315,7 +388,7 @@
                 ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
                 : mPackageDexOptimizer;
         String packageName = options.getPackageName();
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
+        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
         if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -327,10 +400,8 @@
         for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
             String dexPath = entry.getKey();
             DexUseInfo dexUseInfo = entry.getValue();
-            if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
-                continue;
-            }
-            PackageInfo pkg = null;
+
+            PackageInfo pkg;
             try {
                 pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
                     dexUseInfo.getOwnerUserId());
@@ -349,8 +420,7 @@
             }
 
             int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
-                    dexUseInfo.getLoaderIsas(), options.getCompilerFilter(),
-                    dexUseInfo.isUsedByOtherApps(), options.isDowngrade());
+                    dexUseInfo, options);
             success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
         }
         return success;
@@ -362,7 +432,7 @@
      * deleted, update the internal records and delete any generated oat files.
      */
     public void reconcileSecondaryDexFiles(String packageName) {
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
+        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
         if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -433,6 +503,8 @@
         }
     }
 
+    // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
+    // compilation happening here will use a pessimistic context.
     public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
             boolean isUsedByOtherApps, int userId) {
         // Find the owning package record.
@@ -451,11 +523,11 @@
 
         // We found the package. Now record the usage for all declared ISAs.
         boolean update = false;
-        Set<String> isas = new HashSet<>();
         for (String isa : getAppDexInstructionSets(info)) {
-            isas.add(isa);
             boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
-                dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false);
+                    dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
+                    searchResult.mOwningPackageName,
+                    PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
             update |= newUpdate;
         }
         if (update) {
@@ -465,8 +537,13 @@
         // Try to optimize the package according to the install reason.
         String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                 PackageManagerService.REASON_INSTALL);
-        int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, isas,
-                compilerFilter, isUsedByOtherApps, /* downgrade */ false);
+        DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName)
+                .getDexUseInfoMap().get(dexPath);
+
+        DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0);
+
+        int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo,
+                options);
 
         // If we fail to optimize the package log an error but don't propagate the error
         // back to the app. The app cannot do much about it and the background job
@@ -487,23 +564,6 @@
     }
 
     /**
-     * Return true if the profiling data collected for the given app indicate
-     * that the apps's APK has been loaded by another app.
-     * Note that this returns false for all apps without any collected profiling data.
-    */
-    public boolean isUsedByOtherApps(String packageName) {
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
-        if (useInfo == null) {
-            // No use info, means the package was not used or it was used but not by other apps.
-            // Note that right now we might prune packages which are not used by other apps.
-            // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
-            // to access the package use.
-            return false;
-        }
-        return useInfo.isUsedByOtherApps();
-    }
-
-    /**
      * Retrieves the package which owns the given dexPath.
      */
     private DexSearchResult getDexPackage(
@@ -560,6 +620,13 @@
         return existingValue == null ? newValue : existingValue;
     }
 
+    /**
+     * Writes the in-memory package dex usage to disk right away.
+     */
+    public void writePackageDexUsageNow() {
+        mPackageDexUsage.writeNow();
+    }
+
     public static class RegisterDexModuleResult {
         public RegisterDexModuleResult() {
             this(false, null);
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index f57cf5e..0966770 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -50,6 +50,15 @@
     // save disk space.
     public static final int DEXOPT_DOWNGRADE = 1 << 5;
 
+    // When set, dexopt will compile the dex file as a shared library even if it is not actually
+    // used by other apps. This is used to force the compilation or shared libraries declared
+    // with in the manifest with ''uses-library' before we have a chance to detect they are
+    // actually shared at runtime.
+    public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6;
+
+    // When set, indicates that dexopt is invoked from the background service.
+    public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+
     // The name of package to optimize.
     private final String mPackageName;
 
@@ -79,7 +88,9 @@
                 DEXOPT_BOOT_COMPLETE |
                 DEXOPT_ONLY_SECONDARY_DEX |
                 DEXOPT_ONLY_SHARED_DEX |
-                DEXOPT_DOWNGRADE;
+                DEXOPT_DOWNGRADE |
+                DEXOPT_AS_SHARED_LIBRARY |
+                DEXOPT_IDLE_BACKGROUND_JOB;
         if ((flags & (~validityMask)) != 0) {
             throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
         }
@@ -122,7 +133,19 @@
         return (mFlags & DEXOPT_DOWNGRADE) != 0;
     }
 
+    public boolean isDexoptAsSharedLibrary() {
+        return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
+    }
+
+    public boolean isDexoptIdleBackgroundJob() {
+        return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
+    }
+
     public String getSplitName() {
         return mSplitName;
     }
+
+    public int getFlags() {
+        return mFlags;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index abac52f..bc8bf5e 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -21,6 +21,8 @@
 import android.util.SparseArray;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 public final class DexoptUtils {
@@ -225,7 +227,81 @@
      * dependencies {@see encodeClassLoader} separated by ';'.
      */
     private static String encodeClassLoaderChain(String cl1, String cl2) {
-        return cl1.isEmpty() ? cl2 : (cl1 + ";" + cl2);
+        if (cl1.isEmpty()) return cl2;
+        if (cl2.isEmpty()) return cl1;
+        return cl1 + ";" + cl2;
+    }
+
+    /**
+     * Compute the class loader context for the dex files present in the classpath of the first
+     * class loader from the given list (referred in the code as the {@code loadingClassLoader}).
+     * Each dex files gets its own class loader context in the returned array.
+     *
+     * Example:
+     *    If classLoadersNames = {"dalvik.system.DelegateLastClassLoader",
+     *    "dalvik.system.PathClassLoader"} and classPaths = {"foo.dex:bar.dex", "other.dex"}
+     *    The output will be
+     *    {"DLC[];PCL[other.dex]", "DLC[foo.dex];PCL[other.dex]"}
+     *    with "DLC[];PCL[other.dex]" being the context for "foo.dex"
+     *    and "DLC[foo.dex];PCL[other.dex]" the context for "bar.dex".
+     *
+     * If any of the class loaders names is unsupported the method will return null.
+     *
+     * The argument lists must be non empty and of the same size.
+     *
+     * @param classLoadersNames the names of the class loaders present in the loading chain. The
+     *    list encodes the class loader chain in the natural order. The first class loader has
+     *    the second one as its parent and so on.
+     * @param classPaths the class paths for the elements of {@param classLoadersNames}. The
+     *     the first element corresponds to the first class loader and so on. A classpath is
+     *     represented as a list of dex files separated by {@code File.pathSeparator}.
+     *     The return context will be for the dex files found in the first class path.
+     */
+    /*package*/ static String[] processContextForDexLoad(List<String> classLoadersNames,
+            List<String> classPaths) {
+        if (classLoadersNames.size() != classPaths.size()) {
+            throw new IllegalArgumentException(
+                    "The size of the class loader names and the dex paths do not match.");
+        }
+        if (classLoadersNames.isEmpty()) {
+            throw new IllegalArgumentException("Empty classLoadersNames");
+        }
+
+        // Compute the context for the parent class loaders.
+        String parentContext = "";
+        // We know that these lists are actually ArrayLists so getting the elements by index
+        // is fine (they come over binder). Even if something changes we expect the sizes to be
+        // very small and it shouldn't matter much.
+        for (int i = 1; i < classLoadersNames.size(); i++) {
+            if (!isValidClassLoaderName(classLoadersNames.get(i))) {
+                return null;
+            }
+            String classpath = encodeClasspath(classPaths.get(i).split(File.pathSeparator));
+            parentContext = encodeClassLoaderChain(parentContext,
+                    encodeClassLoader(classpath, classLoadersNames.get(i)));
+        }
+
+        // Now compute the class loader context for each dex file from the first classpath.
+        String loadingClassLoader = classLoadersNames.get(0);
+        if (!isValidClassLoaderName(loadingClassLoader)) {
+            return null;
+        }
+        String[] loadedDexPaths = classPaths.get(0).split(File.pathSeparator);
+        String[] loadedDexPathsContext = new String[loadedDexPaths.length];
+        String currentLoadedDexPathClasspath = "";
+        for (int i = 0; i < loadedDexPaths.length; i++) {
+            String dexPath = loadedDexPaths[i];
+            String currentContext = encodeClassLoader(
+                    currentLoadedDexPathClasspath, loadingClassLoader);
+            loadedDexPathsContext[i] = encodeClassLoaderChain(currentContext, parentContext);
+            currentLoadedDexPathClasspath = encodeClasspath(currentLoadedDexPathClasspath, dexPath);
+        }
+        return loadedDexPathsContext;
+    }
+
+    // AOSP-only hack.
+    private static boolean isValidClassLoaderName(String name) {
+        return "dalvik.system.PathClassLoader".equals(name) || "dalvik.system.DexClassLoader".equals(name);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 8a66f12..a4a0a54 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -26,7 +26,6 @@
 import com.android.server.pm.PackageManagerServiceUtils;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.InputStreamReader;
@@ -35,14 +34,19 @@
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import dalvik.system.VMRuntime;
 import libcore.io.IoUtils;
+import libcore.util.Objects;
 
 /**
  * Stat file which store usage information about dex files.
@@ -50,19 +54,44 @@
 public class PackageDexUsage extends AbstractStatsBase<Void> {
     private final static String TAG = "PackageDexUsage";
 
-    private final static int PACKAGE_DEX_USAGE_VERSION = 1;
+    // We support previous version to ensure that the usage list remains valid cross OTAs.
+    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
+    // Version 2 added:
+    //  - the list of packages that load the dex files
+    //  - class loader contexts for secondary dex files
+    //  - usage for all code paths (including splits)
+    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
+
+    private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+
     private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
             "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
 
     private final static String SPLIT_CHAR = ",";
+    private final static String CODE_PATH_LINE_CHAR = "+";
     private final static String DEX_LINE_CHAR = "#";
+    private final static String LOADING_PACKAGE_CHAR = "@";
+
+    // One of the things we record about dex files is the class loader context that was used to
+    // load them. That should be stable but if it changes we don't keep track of variable contexts.
+    // Instead we put a special marker in the dex usage file in order to recognize the case and
+    // skip optimizations on that dex files.
+    /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
+            "=VariableClassLoaderContext=";
+    // The marker used for unsupported class loader contexts.
+    /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+            "=UnsupportedClassLoaderContext=";
+    // The markers used for unknown class loader contexts. This can happen if the dex file was
+    // recorded in a previous version and we didn't have a chance to update its usage.
+    /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
+            "=UnknownClassLoaderContext=";
 
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
     // Access to this map needs synchronized.
     @GuardedBy("mPackageUseInfoMap")
-    private Map<String, PackageUseInfo> mPackageUseInfoMap;
+    private final Map<String, PackageUseInfo> mPackageUseInfoMap;
 
     public PackageDexUsage() {
         super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false);
@@ -75,21 +104,28 @@
      * Note this is called when apps load dex files and as such it should return
      * as fast as possible.
      *
-     * @param loadingPackage the package performing the load
+     * @param owningPackageName the package owning the dex path
      * @param dexPath the path of the dex files being loaded
      * @param ownerUserId the user id which runs the code loading the dex files
      * @param loaderIsa the ISA of the app loading the dex files
      * @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package
      * @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates
      *        the file is either primary or a split. False indicates the file is secondary dex.
+     * @param loadingPackageName the package performing the load. Recorded only if it is different
+     *        than {@param owningPackageName}.
      * @return true if the dex load constitutes new information, or false if this information
      *         has been seen before.
      */
     public boolean record(String owningPackageName, String dexPath, int ownerUserId,
-            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
+            String loadingPackageName, String classLoaderContext) {
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
             throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
         }
+        if (classLoaderContext == null) {
+            throw new IllegalArgumentException("Null classLoaderContext");
+        }
+
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
             if (packageUseInfo == null) {
@@ -99,12 +135,16 @@
                     // If we have a primary or a split apk, set isUsedByOtherApps.
                     // We do not need to record the loaderIsa or the owner because we compile
                     // primaries for all users and all ISAs.
-                    packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
+                    packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
+                            owningPackageName, loadingPackageName);
                 } else {
                     // For secondary dex files record the loaderISA and the owner. We'll need
                     // to know under which user to compile and for what ISA.
-                    packageUseInfo.mDexUseInfoMap.put(
-                            dexPath, new DexUseInfo(isUsedByOtherApps, ownerUserId, loaderIsa));
+                    DexUseInfo newData = new DexUseInfo(isUsedByOtherApps, ownerUserId,
+                            classLoaderContext, loaderIsa);
+                    packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+                    maybeAddLoadingPackage(owningPackageName, loadingPackageName,
+                            newData.mLoadingPackages);
                 }
                 mPackageUseInfoMap.put(owningPackageName, packageUseInfo);
                 return true;
@@ -113,10 +153,14 @@
                 if (primaryOrSplit) {
                     // We have a possible update on the primary apk usage. Merge
                     // isUsedByOtherApps information and return if there was an update.
-                    return packageUseInfo.merge(isUsedByOtherApps);
+                    return packageUseInfo.mergeCodePathUsedByOtherApps(
+                            dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
                 } else {
                     DexUseInfo newData = new DexUseInfo(
-                            isUsedByOtherApps, ownerUserId, loaderIsa);
+                            isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
+                    boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
+                            loadingPackageName, newData.mLoadingPackages);
+
                     DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
                     if (existingData == null) {
                         // It's the first time we see this dex file.
@@ -138,7 +182,7 @@
                         }
                         // Merge the information into the existing data.
                         // Returns true if there was an update.
-                        return existingData.merge(newData);
+                        return existingData.merge(newData) || updateLoadingPackages;
                     }
                 }
             }
@@ -157,8 +201,12 @@
      * Convenience method for async writes which does not force the user to pass a useless
      * (Void) null.
      */
-    public void maybeWriteAsync() {
-      maybeWriteAsync((Void) null);
+    /*package*/ void maybeWriteAsync() {
+      maybeWriteAsync(null);
+    }
+
+    /*package*/ void writeNow() {
+        writeInternal(null);
     }
 
     @Override
@@ -185,16 +233,18 @@
      *
      * file_magic_version
      * package_name_1
+     * +code_path1
+     * @ loading_package_1_1, loading_package_1_2...
+     * +code_path2
+     * @ loading_package_2_1, loading_package_2_2...
      * #dex_file_path_1_1
      * user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
+     * @ loading_package_1_1_1, loading_package_1_1_2...
+     * class_loader_context_1_1
      * #dex_file_path_1_2
      * user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
-     * ...
-     * package_name_2
-     * #dex_file_path_2_1
-     * user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
-     * #dex_file_path_2_2,
-     * user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
+     * @ loading_package_1_2_1, loading_package_1_2_2...
+     * class_loader_context_1_2
      * ...
     */
     /* package */ void write(Writer out) {
@@ -211,9 +261,16 @@
             // Write the package line.
             String packageName = pEntry.getKey();
             PackageUseInfo packageUseInfo = pEntry.getValue();
+            fpw.println(packageName);
 
-            fpw.println(String.join(SPLIT_CHAR, packageName,
-                    writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
+            // Write the code paths used by other apps.
+            for (Map.Entry<String, Set<String>> codeEntry :
+                    packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
+                String codePath = codeEntry.getKey();
+                Set<String> loadingPackages = codeEntry.getValue();
+                fpw.println(CODE_PATH_LINE_CHAR + codePath);
+                fpw.println(LOADING_PACKAGE_CHAR + String.join(SPLIT_CHAR, loadingPackages));
+            }
 
             // Write dex file lines.
             for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
@@ -221,11 +278,14 @@
                 DexUseInfo dexUseInfo = dEntry.getValue();
                 fpw.println(DEX_LINE_CHAR + dexPath);
                 fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
-                        writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
+                    writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
                 for (String isa : dexUseInfo.mLoaderIsas) {
                     fpw.print(SPLIT_CHAR + isa);
                 }
                 fpw.println();
+                fpw.println(LOADING_PACKAGE_CHAR
+                        + String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
+                fpw.println(dexUseInfo.getClassLoaderContext());
             }
         }
         fpw.flush();
@@ -252,6 +312,7 @@
         BufferedReader in = new BufferedReader(reader);
         // Read header, do version check.
         String versionLine = in.readLine();
+        int version;
         if (versionLine == null) {
             throw new IllegalStateException("No version line found.");
         } else {
@@ -259,48 +320,56 @@
                 // TODO(calin): the caller is responsible to clear the file.
                 throw new IllegalStateException("Invalid version line: " + versionLine);
             }
-            int version = Integer.parseInt(
+            version = Integer.parseInt(
                     versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length()));
-            if (version != PACKAGE_DEX_USAGE_VERSION) {
+            if (!isSupportedVersion(version)) {
                 throw new IllegalStateException("Unexpected version: " + version);
             }
         }
 
-        String s = null;
-        String currentPakage = null;
-        PackageUseInfo currentPakageData = null;
+        String line;
+        String currentPackage = null;
+        PackageUseInfo currentPackageData = null;
 
         Set<String> supportedIsas = new HashSet<>();
         for (String abi : Build.SUPPORTED_ABIS) {
             supportedIsas.add(VMRuntime.getInstructionSet(abi));
         }
-        while ((s = in.readLine()) != null) {
-            if (s.startsWith(DEX_LINE_CHAR)) {
+        while ((line = in.readLine()) != null) {
+            if (line.startsWith(DEX_LINE_CHAR)) {
                 // This is the start of the the dex lines.
-                // We expect two lines for each dex entry:
+                // We expect 4 lines for each dex entry:
                 // #dexPaths
+                // @loading_package_1,loading_package_2,...
+                // class_loader_context
                 // onwerUserId,isUsedByOtherApps,isa1,isa2
-                if (currentPakage == null) {
+                if (currentPackage == null) {
                     throw new IllegalStateException(
                         "Malformed PackageDexUsage file. Expected package line before dex line.");
                 }
 
-                // First line is the dex path.
-                String dexPath = s.substring(DEX_LINE_CHAR.length());
-                // Next line is the dex data.
-                s = in.readLine();
-                if (s == null) {
-                    throw new IllegalStateException("Could not fine dexUseInfo for line: " + s);
+                // Line 1 is the dex path.
+                String dexPath = line.substring(DEX_LINE_CHAR.length());
+
+                // Line 2 is the dex data: (userId, isUsedByOtherApps, isa).
+                line = in.readLine();
+                if (line == null) {
+                    throw new IllegalStateException("Could not find dexUseInfo line");
+                }
+                String[] elems = line.split(SPLIT_CHAR);
+                if (elems.length < 3) {
+                    throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
                 }
 
-                // We expect at least 3 elements (isUsedByOtherApps, userId, isa).
-                String[] elems = s.split(SPLIT_CHAR);
-                if (elems.length < 3) {
-                    throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
-                }
+                // In version 2 we added the loading packages and class loader context.
+                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+                String classLoaderContext = maybeReadClassLoaderContext(in, version);
+
                 int ownerUserId = Integer.parseInt(elems[0]);
                 boolean isUsedByOtherApps = readBoolean(elems[1]);
-                DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId);
+                DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
+                        classLoaderContext, /*isa*/ null);
+                dexUseInfo.mLoadingPackages.addAll(loadingPackages);
                 for (int i = 2; i < elems.length; i++) {
                     String isa = elems[i];
                     if (supportedIsas.contains(isa)) {
@@ -317,18 +386,37 @@
                             "unsupported isas. dexPath=" + dexPath);
                     continue;
                 }
-                currentPakageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+                currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+            } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
+                // This is a code path used by other apps line.
+                if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+                    throw new IllegalArgumentException("Unexpected code path line when parsing " +
+                            "PackageDexUseData: " + line);
+                }
+
+                // Expects 2 lines:
+                //    +code_paths
+                //    @loading_packages
+                String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
+                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+                currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
             } else {
                 // This is a package line.
-                // We expect it to be: `packageName,isUsedByOtherApps`.
-                String[] elems = s.split(SPLIT_CHAR);
-                if (elems.length != 2) {
-                    throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+                if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+                    currentPackage = line;
+                    currentPackageData = new PackageUseInfo();
+                } else {
+                    // Old version (<2)
+                    // We expect it to be: `packageName,isUsedByOtherApps`.
+                    String[] elems = line.split(SPLIT_CHAR);
+                    if (elems.length != 2) {
+                        throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
+                    }
+                    currentPackage = elems[0];
+                    currentPackageData = new PackageUseInfo();
+                    currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
                 }
-                currentPakage = elems[0];
-                currentPakageData = new PackageUseInfo();
-                currentPakageData.mIsUsedByOtherApps = readBoolean(elems[1]);
-                data.put(currentPakage, currentPakageData);
+                data.put(currentPackage, currentPackageData);
             }
         }
 
@@ -339,9 +427,67 @@
     }
 
     /**
+     * Reads the class loader context encoding from the buffer {@code in} if
+     * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}.
+     */
+    private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
+        String context = null;
+        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+            context = in.readLine();
+            if (context == null) {
+                throw new IllegalStateException("Could not find the classLoaderContext line.");
+            }
+        }
+        // The context might be empty if we didn't have the chance to update it after a version
+        // upgrade. In this case return the special marker so that we recognize this is an unknown
+        // context.
+        return context == null ? UNKNOWN_CLASS_LOADER_CONTEXT : context;
+    }
+
+    /**
+     * Reads the list of loading packages from the buffer {@code in} if
+     * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
+     */
+    private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
+            throws IOException {
+        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+            String line = in.readLine();
+            if (line == null) {
+                throw new IllegalStateException("Could not find the loadingPackages line.");
+            }
+            // We expect that most of the times the list of loading packages will be empty.
+            if (line.length() == LOADING_PACKAGE_CHAR.length()) {
+                return Collections.emptySet();
+            } else {
+                Set<String> result = new HashSet<>();
+                Collections.addAll(result,
+                        line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+                return result;
+            }
+        } else {
+            return Collections.emptySet();
+        }
+    }
+
+    /**
+     * Utility method which adds {@param loadingPackage} to {@param loadingPackages} only if it's
+     * not equal to {@param owningPackage}
+     */
+    private boolean maybeAddLoadingPackage(String owningPackage, String loadingPackage,
+            Set<String> loadingPackages) {
+        return !owningPackage.equals(loadingPackage) && loadingPackages.add(loadingPackage);
+    }
+
+    private boolean isSupportedVersion(int version) {
+        return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
+                || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+    }
+
+    /**
      * Syncs the existing data with the set of available packages by removing obsolete entries.
      */
-    public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+    /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
+            Map<String, Set<String>> packageToCodePaths) {
         synchronized (mPackageUseInfoMap) {
             Iterator<Map.Entry<String, PackageUseInfo>> pIt =
                     mPackageUseInfoMap.entrySet().iterator();
@@ -365,8 +511,26 @@
                             dIt.remove();
                         }
                     }
-                    if (!packageUseInfo.mIsUsedByOtherApps
-                            && packageUseInfo.mDexUseInfoMap.isEmpty()) {
+
+                    // Sync the code paths.
+                    Set<String> codePaths = packageToCodePaths.get(packageName);
+                    Iterator<Map.Entry<String, Set<String>>> codeIt =
+                        packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
+                    while (codeIt.hasNext()) {
+                        if (!codePaths.contains(codeIt.next().getKey())) {
+                            codeIt.remove();
+                        }
+                    }
+
+                    // In case the package was marked as used by other apps in a previous version
+                    // propagate the flag to all the code paths.
+                    // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
+                    if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
+                        for (String codePath : codePaths) {
+                            packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
+                        }
+                    } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
+                        && packageUseInfo.mDexUseInfoMap.isEmpty()) {
                         // The package is not used by other apps and we removed all its dex files
                         // records. Remove the entire package record as well.
                         pIt.remove();
@@ -380,14 +544,13 @@
      * Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
      * @return true if the package usage info was updated.
      */
-    public boolean clearUsedByOtherApps(String packageName) {
+    /*package*/ boolean clearUsedByOtherApps(String packageName) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
-            if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+            if (packageUseInfo == null) {
                 return false;
             }
-            packageUseInfo.mIsUsedByOtherApps = false;
-            return true;
+            return packageUseInfo.clearCodePathUsedByOtherApps();
         }
     }
 
@@ -408,7 +571,7 @@
      * @return true if the record was found and actually deleted,
      *         false if the record doesn't exist
      */
-    public boolean removeUserPackage(String packageName, int userId) {
+    /*package*/ boolean removeUserPackage(String packageName, int userId) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
             if (packageUseInfo == null) {
@@ -426,7 +589,8 @@
             }
             // If no secondary dex info is left and the package is not used by other apps
             // remove the data since it is now useless.
-            if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+            if (packageUseInfo.mDexUseInfoMap.isEmpty()
+                    && !packageUseInfo.isAnyCodePathUsedByOtherApps()) {
                 mPackageUseInfoMap.remove(packageName);
                 updated = true;
             }
@@ -440,7 +604,7 @@
      * @return true if the record was found and actually deleted,
      *         false if the record doesn't exist
      */
-    public boolean removeDexFile(String packageName, String dexFile, int userId) {
+    /*package*/ boolean removeDexFile(String packageName, String dexFile, int userId) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
             if (packageUseInfo == null) {
@@ -462,7 +626,7 @@
         return false;
     }
 
-    public PackageUseInfo getPackageUseInfo(String packageName) {
+    /*package*/ PackageUseInfo getPackageUseInfo(String packageName) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
             // The useInfo contains a map for secondary dex files which could be modified
@@ -477,7 +641,7 @@
     /**
      * Return all packages that contain records of secondary dex files.
      */
-    public Set<String> getAllPackagesWithSecondaryDexFiles() {
+    /*package*/ Set<String> getAllPackagesWithSecondaryDexFiles() {
         Set<String> packages = new HashSet<>();
         synchronized (mPackageUseInfoMap) {
             for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
@@ -515,15 +679,6 @@
         throw new IllegalArgumentException("Unknown bool encoding: " + bool);
     }
 
-    private boolean contains(int[] array, int elem) {
-        for (int i = 0; i < array.length; i++) {
-            if (elem == array[i]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     public String dump() {
         StringWriter sw = new StringWriter();
         write(sw);
@@ -534,39 +689,95 @@
      * Stores data on how a package and its dex files are used.
      */
     public static class PackageUseInfo {
-        // This flag is for the primary and split apks. It is set to true whenever one of them
-        // is loaded by another app.
-        private boolean mIsUsedByOtherApps;
+        // The app's code paths that are used by other apps.
+        // The key is the code path and the value is the set of loading packages.
+        private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
         // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
         private final Map<String, DexUseInfo> mDexUseInfoMap;
 
+        // Keeps track of whether or not this package was used by other apps before
+        // we upgraded to VERSION 4 which records the info for each code path separately.
+        // This is unwanted complexity but without it we risk to profile guide compile
+        // something that supposed to be shared. For example:
+        //   1) we determine that chrome is used by another app
+        //   2) we take an OTA which upgrades the way we keep track of usage data
+        //   3) chrome doesn't get used until the background job executes
+        //   4) as part of the backgound job we now think that chrome is not used by others
+        //      and we speed-profile.
+        //   5) as a result the next time someone uses chrome it will extract from apk since
+        //      the compiled code will be private.
+        private boolean mUsedByOtherAppsBeforeUpgrade;
+
         public PackageUseInfo() {
-            mIsUsedByOtherApps = false;
+            mCodePathsUsedByOtherApps = new HashMap<>();
             mDexUseInfoMap = new HashMap<>();
         }
 
         // Creates a deep copy of the `other`.
         public PackageUseInfo(PackageUseInfo other) {
-            mIsUsedByOtherApps = other.mIsUsedByOtherApps;
+            mCodePathsUsedByOtherApps = new HashMap<>();
+            for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
+                mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
+            }
+
             mDexUseInfoMap = new HashMap<>();
             for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
                 mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
             }
         }
 
-        private boolean merge(boolean isUsedByOtherApps) {
-            boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
-            mIsUsedByOtherApps = mIsUsedByOtherApps || isUsedByOtherApps;
-            return oldIsUsedByOtherApps != this.mIsUsedByOtherApps;
+        private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
+                String owningPackageName, String loadingPackage) {
+            if (!isUsedByOtherApps) {
+                // Nothing to update if the the code path is not used by other apps.
+                return false;
+            }
+
+            boolean newCodePath = false;
+            Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
+            if (loadingPackages == null) {
+                loadingPackages = new HashSet<>();
+                mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
+                newCodePath = true;
+            }
+            boolean newLoadingPackage = loadingPackage != null
+                    && !loadingPackage.equals(owningPackageName)
+                    && loadingPackages.add(loadingPackage);
+            return newCodePath || newLoadingPackage;
         }
 
-        public boolean isUsedByOtherApps() {
-            return mIsUsedByOtherApps;
+        public boolean isUsedByOtherApps(String codePath) {
+            return mCodePathsUsedByOtherApps.containsKey(codePath);
         }
 
         public Map<String, DexUseInfo> getDexUseInfoMap() {
             return mDexUseInfoMap;
         }
+
+        public Set<String> getLoadingPackages(String codePath) {
+            return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
+        }
+
+        public boolean isAnyCodePathUsedByOtherApps() {
+            return !mCodePathsUsedByOtherApps.isEmpty();
+        }
+
+        /**
+         * Clears the usedByOtherApps markers from all code paths.
+         * Returns whether or not there was an update.
+         */
+        /*package*/ boolean clearCodePathUsedByOtherApps() {
+            // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
+            // the new data. This is not saved to disk so we don't need to return it.
+            mUsedByOtherAppsBeforeUpgrade = true;
+
+            if (mCodePathsUsedByOtherApps.isEmpty()) {
+                return false;
+            } else {
+                mCodePathsUsedByOtherApps.clear();
+                return true;
+            }
+        }
     }
 
     /**
@@ -575,33 +786,59 @@
     public static class DexUseInfo {
         private boolean mIsUsedByOtherApps;
         private final int mOwnerUserId;
+        // The class loader context for the dex file. This encodes the class loader chain
+        // (class loader type + class path) in a format compatible to dex2oat.
+        // See {@code DexoptUtils.processContextForDexLoad}.
+        private String mClassLoaderContext;
+        // The instructions sets of the applications loading the dex file.
         private final Set<String> mLoaderIsas;
+        // Packages who load this dex file.
+        private final Set<String> mLoadingPackages;
 
-        public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId) {
-            this(isUsedByOtherApps, ownerUserId, null);
-        }
-
-        public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String loaderIsa) {
+        public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String classLoaderContext,
+                String loaderIsa) {
             mIsUsedByOtherApps = isUsedByOtherApps;
             mOwnerUserId = ownerUserId;
+            mClassLoaderContext = classLoaderContext;
             mLoaderIsas = new HashSet<>();
             if (loaderIsa != null) {
                 mLoaderIsas.add(loaderIsa);
             }
+            mLoadingPackages = new HashSet<>();
         }
 
         // Creates a deep copy of the `other`.
         public DexUseInfo(DexUseInfo other) {
             mIsUsedByOtherApps = other.mIsUsedByOtherApps;
             mOwnerUserId = other.mOwnerUserId;
+            mClassLoaderContext = other.mClassLoaderContext;
             mLoaderIsas = new HashSet<>(other.mLoaderIsas);
+            mLoadingPackages = new HashSet<>(other.mLoadingPackages);
         }
 
         private boolean merge(DexUseInfo dexUseInfo) {
             boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
             mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps;
             boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas);
-            return updateIsas || (oldIsUsedByOtherApps != mIsUsedByOtherApps);
+            boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
+
+            String oldClassLoaderContext = mClassLoaderContext;
+            if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+                // Can happen if we read a previous version.
+                mClassLoaderContext = dexUseInfo.mClassLoaderContext;
+            } else if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(dexUseInfo.mClassLoaderContext)) {
+                // We detected an unsupported context.
+                mClassLoaderContext = UNSUPPORTED_CLASS_LOADER_CONTEXT;
+            } else if (!UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext) &&
+                    !Objects.equal(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+                // We detected a context change.
+                mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
+            }
+
+            return updateIsas ||
+                    (oldIsUsedByOtherApps != mIsUsedByOtherApps) ||
+                    updateLoadingPackages
+                    || !Objects.equal(oldClassLoaderContext, mClassLoaderContext);
         }
 
         public boolean isUsedByOtherApps() {
@@ -615,5 +852,25 @@
         public Set<String> getLoaderIsas() {
             return mLoaderIsas;
         }
+
+        public Set<String> getLoadingPackages() {
+            return mLoadingPackages;
+        }
+
+        public String getClassLoaderContext() { return mClassLoaderContext; }
+
+        public boolean isUnsupportedClassLoaderContext() {
+            return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+        }
+
+        public boolean isUnknownClassLoaderContext() {
+            // The class loader context may be unknown if we loaded the data from a previous version
+            // which didn't save the context.
+            return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+        }
+
+        public boolean isVariableClassLoaderContext() {
+            return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index afc0f67..4db9a30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -23,10 +23,14 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -48,6 +52,10 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DexManagerTests {
+    private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
+    private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
+            DelegateLastClassLoader.class.getName();
+
     private DexManager mDexManager;
 
     private TestData mFooUser0;
@@ -56,6 +64,9 @@
     private TestData mInvalidIsa;
     private TestData mDoesNotExist;
 
+    private TestData mBarUser0UnsupportedClassLoader;
+    private TestData mBarUser0DelegateLastClassLoader;
+
     private int mUser0;
     private int mUser1;
 
@@ -68,12 +79,17 @@
         String foo = "foo";
         String bar = "bar";
 
-        mFooUser0 = new TestData(foo, isa, mUser0);
-        mBarUser0 = new TestData(bar, isa, mUser0);
-        mBarUser1 = new TestData(bar, isa, mUser1);
+        mFooUser0 = new TestData(foo, isa, mUser0, PATH_CLASS_LOADER_NAME);
+        mBarUser0 = new TestData(bar, isa, mUser0, PATH_CLASS_LOADER_NAME);
+        mBarUser1 = new TestData(bar, isa, mUser1, PATH_CLASS_LOADER_NAME);
         mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
         mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
 
+        mBarUser0UnsupportedClassLoader = new TestData(bar, isa, mUser0,
+                "unsupported.class_loader");
+        mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
+                DELEGATE_LAST_CLASS_LOADER_NAME);
+
         mDexManager = new DexManager(null, null, null, null);
 
         // Foo and Bar are available to user0.
@@ -90,7 +106,7 @@
         notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
 
         // Package is not used by others, so we should get nothing back.
-        assertNull(getPackageUseInfo(mFooUser0));
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
@@ -100,8 +116,7 @@
 
         // Bar is used by others now and should be in our records
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
     }
 
@@ -112,8 +127,7 @@
         notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -125,8 +139,7 @@
         notifyDexLoad(mFooUser0, barSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
         assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
     }
@@ -149,8 +162,7 @@
 
         // Check bar usage. Should be used by other app (for primary and barSecondaries).
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
                 pui.getDexUseInfoMap().size());
 
@@ -160,8 +172,7 @@
 
         // Check foo usage. Should not be used by other app.
         pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -169,22 +180,22 @@
     @Test
     public void testPackageUseInfoNotFound() {
         // Assert we don't get back data we did not previously record.
-        assertNull(getPackageUseInfo(mFooUser0));
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
     public void testInvalidIsa() {
         // Notifying with an invalid ISA should be ignored.
         notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
-        assertNull(getPackageUseInfo(mInvalidIsa));
+        assertNoUseInfo(mInvalidIsa);
     }
 
     @Test
-    public void testNotExistingPackate() {
+    public void testNotExistingPackage() {
         // Notifying about the load of a package which was previously not
         // register in DexManager#load should be ignored.
         notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
-        assertNull(getPackageUseInfo(mDoesNotExist));
+        assertNoUseInfo(mDoesNotExist);
     }
 
     @Test
@@ -192,7 +203,7 @@
         // Bar from User1 tries to load secondary dex files from User0 Bar.
         // Request should be ignored.
         notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
-        assertNull(getPackageUseInfo(mBarUser1));
+        assertNoUseInfo(mBarUser1);
     }
 
     @Test
@@ -201,7 +212,7 @@
         // Note that the PackageManagerService already filters this out but we
         // still check that nothing goes unexpected in DexManager.
         notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
-        assertNull(getPackageUseInfo(mBarUser1));
+        assertNoUseInfo(mBarUser1);
     }
 
     @Test
@@ -213,7 +224,7 @@
         // Before we notify about the installation of the newPackage if mFoo
         // is trying to load something from it we should not find it.
         notifyDexLoad(mFooUser0, newSecondaries, mUser0);
-        assertNull(getPackageUseInfo(newPackage));
+        assertNoUseInfo(newPackage);
 
         // Notify about newPackage install and let mFoo load its dexes.
         mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -221,8 +232,7 @@
 
         // We should get back the right info.
         PackageUseInfo pui = getPackageUseInfo(newPackage);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
     }
@@ -238,8 +248,7 @@
         notifyDexLoad(newPackage, newSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(newPackage);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -251,8 +260,7 @@
 
         // Bar is used by others now and should be in our records.
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
 
         // Notify that bar is updated.
@@ -262,8 +270,7 @@
 
         // The usedByOtherApps flag should be clear now.
         pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
     }
 
     @Test
@@ -275,8 +282,7 @@
 
         // We shouldn't find yet the new split as we didn't notify the package update.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNull(pui);
+        assertNoUseInfo(mBarUser0);
 
         // Notify that bar is updated. splitSourceDirs will contain the updated path.
         mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -285,9 +291,9 @@
 
         // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
-        pui = getPackageUseInfo(mBarUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
         assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newSplits, pui, true);
     }
 
     @Test
@@ -319,8 +325,7 @@
         // Foo should still be around since it's used by other apps but with no
         // secondary dex info.
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
     }
 
@@ -334,8 +339,7 @@
 
         // Foo should not be around since all its secondary dex info were deleted
         // and it is not used by other apps.
-        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNull(pui);
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
@@ -347,8 +351,7 @@
         mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
 
         // Bar should not be around since it was removed for all users.
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNull(pui);
+        assertNoUseInfo(mBarUser0);
     }
 
     @Test
@@ -357,7 +360,7 @@
         // Load a dex file from framework.
         notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
         // The dex file should not be recognized as a package.
-        assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+        assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
     }
 
     @Test
@@ -367,14 +370,83 @@
         notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
 
+    @Test
+    public void testNotifyUnsupportedClassLoader() {
+        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+        notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
+        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+        // We expect that all the contexts are unsupported.
+        String[] expectedContexts =
+                Collections.nCopies(secondaries.size(),
+                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+    }
+
+    @Test
+    public void testNotifyVariableClassLoader() {
+        // Record bar secondaries with the default PathClassLoader.
+        List<String> secondaries = mBarUser0.getSecondaryDexPaths();
+
+        notifyDexLoad(mBarUser0, secondaries, mUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
+        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+        assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+
+        // Record bar secondaries again with a different class loader. This will change the context.
+        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
+
+        pui = getPackageUseInfo(mBarUser0);
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
+        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+        // We expect that all the contexts to be changed to variable now.
+        String[] expectedContexts =
+                Collections.nCopies(secondaries.size(),
+                        PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+        assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0,
+                expectedContexts);
+    }
+
+    @Test
+    public void testNotifyUnsupportedClassLoaderDoesNotChange() {
+        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+        notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
+        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+        // We expect that all the contexts are unsupported.
+        String[] expectedContexts =
+                Collections.nCopies(secondaries.size(),
+                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+
+        // Record bar secondaries again with a different class loader. This will change the context.
+        // However, because the context was already marked as unsupported we should not chage it.
+        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
+        pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+
+    }
+
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
-            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
+            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
+            String[] expectedContexts) {
+        assertNotNull(expectedContexts);
+        assertEquals(expectedContexts.length, secondaries.size());
+        int index = 0;
         for (String dex : secondaries) {
             DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
             assertNotNull(dui);
@@ -382,16 +454,50 @@
             assertEquals(ownerUserId, dui.getOwnerUserId());
             assertEquals(1, dui.getLoaderIsas().size());
             assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
+            assertEquals(expectedContexts[index++], dui.getClassLoaderContext());
         }
     }
+    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
+            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
+        String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+                Arrays.asList(testData.mClassLoader),
+                Arrays.asList(String.join(File.pathSeparator, secondaries)));
+        assertSecondaryUse(testData, pui, secondaries, isUsedByOtherApps, ownerUserId,
+                expectedContexts);
+    }
 
+    private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
+            boolean isUsedByOtherApps) {
+        assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
+    }
+
+    private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
+            boolean isUsedByOtherApps) {
+        for (String codePath : codePaths) {
+            assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
+        }
+    }
     private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
-        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
+        // By default, assume a single class loader in the chain.
+        // This makes writing tests much easier.
+        List<String> classLoaders = Arrays.asList(testData.mClassLoader);
+        List<String> classPaths = Arrays.asList(String.join(File.pathSeparator, dexPaths));
+        notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
+    }
+
+    private void notifyDexLoad(TestData testData, List<String> classLoader, List<String> classPaths,
+            int loaderUserId) {
+        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, classLoader, classPaths,
                 testData.mLoaderIsa, loaderUserId);
     }
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
-        return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
+        assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+        return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+    }
+
+    private void assertNoUseInfo(TestData testData) {
+        assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
     }
 
     private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -416,10 +522,16 @@
     private static class TestData {
         private final PackageInfo mPackageInfo;
         private final String mLoaderIsa;
+        private final String mClassLoader;
 
-        private TestData(String  packageName, String loaderIsa, int userId) {
+        private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
             mPackageInfo = getMockPackageInfo(packageName, userId);
             mLoaderIsa = loaderIsa;
+            mClassLoader = classLoader;
+        }
+
+        private TestData(String packageName, String loaderIsa, int userId) {
+            this(packageName, loaderIsa, userId, PATH_CLASS_LOADER_NAME);
         }
 
         private String getPackageName() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 1eb5552..c2072df 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -52,6 +52,7 @@
         assertFalse(opt.isDexoptOnlySharedDex());
         assertFalse(opt.isDowngrade());
         assertFalse(opt.isForce());
+        assertFalse(opt.isDexoptIdleBackgroundJob());
     }
 
     @Test
@@ -62,7 +63,9 @@
                 DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
                 DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
                 DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
-                DexoptOptions.DEXOPT_DOWNGRADE;
+                DexoptOptions.DEXOPT_DOWNGRADE  |
+                DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
+                DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
 
         DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
         assertEquals(mPackageName, opt.getPackageName());
@@ -74,6 +77,8 @@
         assertTrue(opt.isDexoptOnlySharedDex());
         assertTrue(opt.isDowngrade());
         assertTrue(opt.isForce());
+        assertTrue(opt.isDexoptAsSharedLibrary());
+        assertTrue(opt.isDexoptIdleBackgroundJob());
     }
 
     @Test
@@ -89,7 +94,7 @@
                 PackageManagerService.REASON_INSTALL,
                 PackageManagerService.REASON_BACKGROUND_DEXOPT,
                 PackageManagerService.REASON_AB_OTA,
-                PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE};
+                PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,};
 
         for (int reason : reasons) {
             DexoptOptions opt = new DexoptOptions(mPackageName, reason, flags);
@@ -102,6 +107,7 @@
             assertFalse(opt.isDexoptOnlySharedDex());
             assertFalse(opt.isDowngrade());
             assertTrue(opt.isForce());
+            assertFalse(opt.isDexoptAsSharedLibrary());
         }
     }
 
@@ -119,6 +125,7 @@
         assertFalse(opt.isDexoptOnlySharedDex());
         assertFalse(opt.isDowngrade());
         assertTrue(opt.isForce());
+        assertFalse(opt.isDexoptAsSharedLibrary());
     }
 
     @Test
@@ -133,4 +140,4 @@
 
         assertTrue(gotException);
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 21b286e..ff7bd72 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -17,6 +17,9 @@
 package com.android.server.pm.dex;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.content.pm.ApplicationInfo;
 import android.support.test.filters.SmallTest;
@@ -24,14 +27,21 @@
 import android.util.SparseArray;
 
 import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.DexClassLoader;
 import dalvik.system.PathClassLoader;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DexoptUtilsTest {
+    private static final String DEX_CLASS_LOADER_NAME = DexClassLoader.class.getName();
     private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
             DelegateLastClassLoader.class.getName();
@@ -202,4 +212,68 @@
         assertEquals(1, contexts.length);
         assertEquals("PCL[]", contexts[0]);
     }
+
+    @Test
+    public void testProcessContextForDexLoad() {
+        List<String> classLoaders = Arrays.asList(
+                DELEGATE_LAST_CLASS_LOADER_NAME,
+                PATH_CLASS_LOADER_NAME,
+                PATH_CLASS_LOADER_NAME);
+        List<String> classPaths = Arrays.asList(
+                String.join(File.pathSeparator, "foo.dex", "bar.dex"),
+                String.join(File.pathSeparator, "parent1.dex"),
+                String.join(File.pathSeparator, "parent2.dex", "parent3.dex"));
+        String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+        assertNotNull(context);
+        assertEquals(2, context.length);
+        assertEquals("DLC[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]);
+        assertEquals("DLC[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]);
+    }
+
+    @Test
+    public void testProcessContextForDexLoadSingleElement() {
+        List<String> classLoaders = Arrays.asList(PATH_CLASS_LOADER_NAME);
+        List<String> classPaths = Arrays.asList(
+                String.join(File.pathSeparator, "foo.dex", "bar.dex", "zoo.dex"));
+        String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+        assertNotNull(context);
+        assertEquals(3, context.length);
+        assertEquals("PCL[]", context[0]);
+        assertEquals("PCL[foo.dex]", context[1]);
+        assertEquals("PCL[foo.dex:bar.dex]", context[2]);
+    }
+
+    @Test
+    public void testProcessContextForDexLoadUnsupported() {
+        List<String> classLoaders = Arrays.asList(
+                DELEGATE_LAST_CLASS_LOADER_NAME,
+                "unsupported.class.loader");
+        List<String> classPaths = Arrays.asList(
+                String.join(File.pathSeparator, "foo.dex", "bar.dex"),
+                String.join(File.pathSeparator, "parent1.dex"));
+        String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+        assertNull(context);
+    }
+
+    @Test
+    public void testProcessContextForDexLoadIllegalCallEmptyList() {
+        boolean gotException = false;
+        try {
+            DexoptUtils.processContextForDexLoad(Collections.emptyList(), Collections.emptyList());
+        } catch (IllegalArgumentException ignore) {
+            gotException = true;
+        }
+        assertTrue(gotException);
+    }
+
+    @Test
+    public void testProcessContextForDexLoadIllegalCallDifferentSize() {
+        boolean gotException = false;
+        try {
+            DexoptUtils.processContextForDexLoad(Collections.emptyList(), Arrays.asList("a"));
+        } catch (IllegalArgumentException ignore) {
+            gotException = true;
+        }
+        assertTrue(gotException);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 2e99433..69a148d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -21,6 +21,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import dalvik.system.VMRuntime;
 
+import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -72,25 +73,25 @@
         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
 
         mFooBaseUser0 = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, isa, false, true);
+                fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
 
         mFooSplit1User0 = new TestData(fooPackageName,
-                fooCodeDir + "split-1.apk", 0, isa, false, true);
+                fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
 
         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooCodeDir + "split-2.apk", 0, isa, true, true);
+                fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
 
         mFooSecondary1User0 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 0, isa, false, false);
+                fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
 
         mFooSecondary1User1 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 1, isa, false, false);
+                fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
 
         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooDataDir + "sec-2.dex", 0, isa, true, false);
+                fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
 
         mInvalidIsa = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true);
+                fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
 
         String barPackageName = "com.google.bar";
         String barCodeDir = "/data/app/com.google.bar/";
@@ -98,11 +99,11 @@
         String barDataDir1 = "/data/user/1/com.google.bar/";
 
         mBarBaseUser0 = new TestData(barPackageName,
-                barCodeDir + "base.apk", 0, isa, false, true);
+                barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
         mBarSecondary1User0 = new TestData(barPackageName,
-                barDataDir + "sec-1.dex", 0, isa, false, false);
+                barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
         mBarSecondary2User1 = new TestData(barPackageName,
-                barDataDir1 + "sec-2.dex", 1, isa, false, false);
+                barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
     }
 
     @Test
@@ -249,7 +250,10 @@
         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
         packageToUsersMap.put(mBarSecondary2User1.mPackageName,
                 new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
-        mPackageDexUsage.syncData(packageToUsersMap);
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+        packageToCodePaths.put(mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
 
         // Assert that only user 1 files are there.
         assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
@@ -319,7 +323,8 @@
             mFooSplit2UsedByOtherApps0.mOwnerUserId,
             mFooSplit2UsedByOtherApps0.mLoaderIsa,
             /*mIsUsedByOtherApps*/false,
-            mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
+            mFooSplit2UsedByOtherApps0.mPrimaryOrSplit,
+            mFooSplit2UsedByOtherApps0.mUsedBy);
         assertPackageDexUsage(noLongerUsedByOtherApps);
     }
 
@@ -332,14 +337,223 @@
         assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
     }
 
+    @Test
+    public void testRecordDexFileUsers() {
+        PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+        Set<String> users = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.1"}));
+        Set<String> usersExtra = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.2", "another.package.3"}));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+        packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+        // Verify that the users were recorded.
+        Set<String> userAll = new HashSet<>(users);
+        userAll.addAll(usersExtra);
+        assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
+                mFooSecondary1User0);
+    }
+
+    @Test
+    public void testRecordDexFileUsersNotTheOwningPackage() {
+        PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+        Set<String> users = new HashSet<>(Arrays.asList(
+                new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
+        Set<String> usersExtra = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.2", "another.package.3"}));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+        packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+        // Verify that only the non owning packages were recorded.
+        assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
+                mFooSecondary1User0);
+    }
+
+    @Test
+    public void testRecordClassLoaderContextVariableContext() {
+        // Record a secondary dex file.
+        assertTrue(record(mFooSecondary1User0));
+        // Now update its context.
+        TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext(
+                "PCL[new_context.dex]");
+        assertTrue(record(fooSecondary1User0NewContext));
+
+        // Not check that the context was switch to variable.
+        TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
+                PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
+
+        assertPackageDexUsage(null, expectedContext);
+        writeAndReadBack();
+        assertPackageDexUsage(null, expectedContext);
+    }
+
+    @Test
+    public void testRecordClassLoaderContextUnsupportedContext() {
+        // Record a secondary dex file.
+        assertTrue(record(mFooSecondary1User0));
+        // Now update its context.
+        TestData unsupportedContext = mFooSecondary1User0.updateClassLoaderContext(
+                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+        assertTrue(record(unsupportedContext));
+
+        assertPackageDexUsage(null, unsupportedContext);
+        writeAndReadBack();
+        assertPackageDexUsage(null, unsupportedContext);
+    }
+
+    @Test
+    public void testRecordClassLoaderContextTransitionFromUnknown() {
+        // Record a secondary dex file.
+        TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
+                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
+        assertTrue(record(unknownContext));
+
+        assertPackageDexUsage(null, unknownContext);
+        writeAndReadBack();
+        assertPackageDexUsage(null, unknownContext);
+
+        // Now update the secondary dex record with a class loader context. This simulates the
+        // version 2 to version 3 upgrade.
+
+        assertTrue(record(mFooSecondary1User0));
+
+        assertPackageDexUsage(null, mFooSecondary1User0);
+        writeAndReadBack();
+        assertPackageDexUsage(null, mFooSecondary1User0);
+    }
+
+    @Test
+    public void testDexUsageClassLoaderContext() {
+        final boolean isUsedByOtherApps = false;
+        final int userId = 0;
+        PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
+                "valid_context", "arm");
+        assertFalse(validContext.isUnknownClassLoaderContext());
+        assertFalse(validContext.isUnsupportedClassLoaderContext());
+        assertFalse(validContext.isVariableClassLoaderContext());
+
+        PackageDexUsage.DexUseInfo unsupportedContext = new DexUseInfo(isUsedByOtherApps, userId,
+                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT, "arm");
+        assertFalse(unsupportedContext.isUnknownClassLoaderContext());
+        assertTrue(unsupportedContext.isUnsupportedClassLoaderContext());
+        assertFalse(unsupportedContext.isVariableClassLoaderContext());
+
+        PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
+                PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
+        assertFalse(variableContext.isUnknownClassLoaderContext());
+        assertFalse(variableContext.isUnsupportedClassLoaderContext());
+        assertTrue(variableContext.isVariableClassLoaderContext());
+
+        PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
+                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
+        assertTrue(unknownContext.isUnknownClassLoaderContext());
+        assertFalse(unknownContext.isUnsupportedClassLoaderContext());
+        assertFalse(unknownContext.isVariableClassLoaderContext());
+    }
+
+    @Test
+    public void testReadVersion1() {
+        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+        // Equivalent to
+        //   record(mFooSplit2UsedByOtherApps0);
+        //   record(mFooSecondary1User0);
+        //   record(mFooSecondary2UsedByOtherApps0);
+        //   record(mBarBaseUser0);
+        //   record(mBarSecondary1User0);
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
+                + "com.google.foo,1\n"
+                + "#/data/user/0/com.google.foo/sec-1.dex\n"
+                + "0,0," + isa + "\n"
+                + "#/data/user/0/com.google.foo/sec-2.dex\n"
+                + "0,1," + isa + "\n"
+                + "com.google.bar,0\n"
+                + "#/data/user/0/com.google.bar/sec-1.dex\n"
+                + "0,0," + isa + "\n";
+
+        PackageDexUsage packageDexUsage = new PackageDexUsage();
+        try {
+            packageDexUsage.read(new StringReader(content));
+        } catch (IOException e) {
+            fail();
+        }
+
+        // After the read we must sync the data to fill the missing information on the code paths.
+        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
+        // Handle foo package.
+        packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
+            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+        packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
+            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
+                mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
+        // Handle bar package.
+        packageToUsersMap.put(mBarBaseUser0.mPackageName,
+            new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+        packageToCodePaths.put(mBarBaseUser0.mPackageName,
+            new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+
+        // Sync the data.
+        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+
+        // Update the class loaders to unknown before asserting if needed. Before version 2 we
+        // didn't have any.
+        String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
+        TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
+        TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
+        TestData fooSplit2UsedByOtherApps0 =
+            mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
+        TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
+        TestData fooSecondary2UsedByOtherApps0 =
+            mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
+        TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
+        TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
+
+        // Assert foo code paths. Note that we ignore the users during upgrade.
+        final Set<String> ignoredUsers = null;
+        assertPackageDexUsage(packageDexUsage, ignoredUsers,
+            fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+        // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
+        // share the same data.
+        assertPackageDexUsage(packageDexUsage, ignoredUsers,
+            fooSplit1User0.updateUseByOthers(true),
+            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+        assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
+            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+
+        // Assert bar code paths. Note that we ignore the users during upgrade.
+        assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
+    }
+
     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
+        assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
+    }
+
+    private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+            TestData primary, TestData... secondaries) {
         String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
-        boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
-        PackageUseInfo pInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+        boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
+        PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
 
         // Check package use info
         assertNotNull(pInfo);
-        assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
+        if (primary != null) {
+            assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
+            if (users != null) {
+                assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
+            }
+        }
+
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
         assertEquals(secondaries.length, dexUseInfoMap.size());
 
@@ -351,24 +565,45 @@
             assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId());
             assertEquals(1, dInfo.getLoaderIsas().size());
             assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa));
+            if (users != null) {
+                 assertEquals(dInfo.getLoadingPackages(), users);
+            }
+
+            assertEquals(testData.mClassLoaderContext, dInfo.getClassLoaderContext());
         }
     }
 
     private boolean record(TestData testData) {
         return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
-                testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
-                testData.mPrimaryOrSplit);
+               testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+               testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext);
+    }
+
+    private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) {
+        boolean result = true;
+        for (String user : users) {
+            result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
+                    testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+                    testData.mPrimaryOrSplit, user, testData.mClassLoaderContext);
+        }
+        return result;
     }
 
     private void writeAndReadBack() {
+        mPackageDexUsage = writeAndReadBack(mPackageDexUsage);
+    }
+
+    private PackageDexUsage writeAndReadBack(PackageDexUsage packageDexUsage) {
         try {
             StringWriter writer = new StringWriter();
-            mPackageDexUsage.write(writer);
+            packageDexUsage.write(writer);
 
-            mPackageDexUsage = new PackageDexUsage();
-            mPackageDexUsage.read(new StringReader(writer.toString()));
+            PackageDexUsage newPackageDexUsage = new PackageDexUsage();
+            newPackageDexUsage.read(new StringReader(writer.toString()));
+            return newPackageDexUsage;
         } catch (IOException e) {
             fail("Unexpected IOException: " + e.getMessage());
+            return null;
         }
     }
 
@@ -379,16 +614,35 @@
         private final String mLoaderIsa;
         private final boolean mUsedByOtherApps;
         private final boolean mPrimaryOrSplit;
+        private final String mUsedBy;
+        private final String mClassLoaderContext;
 
         private TestData(String packageName, String dexFile, int ownerUserId,
-                 String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+                String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy) {
+            this(packageName, dexFile, ownerUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+                    usedBy, "DefaultClassLoaderContextFor_" + dexFile);
+        }
+        private TestData(String packageName, String dexFile, int ownerUserId,
+                String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy,
+                String classLoaderContext) {
             mPackageName = packageName;
             mDexFile = dexFile;
             mOwnerUserId = ownerUserId;
             mLoaderIsa = loaderIsa;
             mUsedByOtherApps = isUsedByOtherApps;
             mPrimaryOrSplit = primaryOrSplit;
+            mUsedBy = usedBy;
+            mClassLoaderContext = classLoaderContext;
         }
 
+        private TestData updateClassLoaderContext(String newContext) {
+            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
+                    mPrimaryOrSplit, mUsedBy, newContext);
+        }
+
+        private TestData updateUseByOthers(boolean newUsedByOthers) {
+            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
+                mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91b2d52..2e621f1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1508,6 +1508,13 @@
             "boosted_lte_earfcns_string_array";
 
     /**
+     * Determine whether to use only RSRP for the number of LTE signal bars.
+     * @hide
+     */
+    public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
+            "use_only_rsrp_for_lte_signal_bar_bool";
+
+    /**
      * Key identifying if voice call barring notification is required to be shown to the user.
      * @hide
      */
@@ -1862,6 +1869,7 @@
                 null);
         sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
         sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
         sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED);
         sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c8b4776..de02de7 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -68,6 +68,7 @@
     private int mTdScdmaRscp;
 
     private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+    private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
     /**
      * Create a new SignalStrength from a intent notifier Bundle
@@ -108,6 +109,7 @@
         mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
         isGsm = true;
+        mUseOnlyRsrpForLteLevel = false;
     }
 
     /**
@@ -134,6 +136,7 @@
         mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
         isGsm = gsmFlag;
+        mUseOnlyRsrpForLteLevel = false;
     }
 
     /**
@@ -145,10 +148,10 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+            int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+                lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
         mTdScdmaRscp = tdScdmaRscp;
     }
 
@@ -164,7 +167,7 @@
             int tdScdmaRscp, boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
         mTdScdmaRscp = tdScdmaRscp;
     }
 
@@ -180,7 +183,7 @@
             boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
     }
 
     /**
@@ -194,7 +197,7 @@
             boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsmFlag);
+                INVALID, INVALID, INVALID, 0, gsmFlag, false);
     }
 
     /**
@@ -228,7 +231,7 @@
             boolean gsm) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsm);
+                INVALID, INVALID, INVALID, 0, gsm, false);
     }
 
     /**
@@ -248,6 +251,7 @@
      * @param lteCqi
      * @param lteRsrpBoost
      * @param gsm
+     * @param useOnlyRsrpForLteLevel
      *
      * @hide
      */
@@ -255,7 +259,7 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, boolean gsm) {
+            int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -271,6 +275,7 @@
         mLteRsrpBoost = lteRsrpBoost;
         mTdScdmaRscp = INVALID;
         isGsm = gsm;
+        mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
         if (DBG) log("initialize: " + toString());
     }
 
@@ -293,6 +298,7 @@
         mLteRsrpBoost = s.mLteRsrpBoost;
         mTdScdmaRscp = s.mTdScdmaRscp;
         isGsm = s.isGsm;
+        mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
     }
 
     /**
@@ -318,6 +324,7 @@
         mLteRsrpBoost = in.readInt();
         mTdScdmaRscp = in.readInt();
         isGsm = (in.readInt() != 0);
+        mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
     }
 
     /**
@@ -366,6 +373,7 @@
         out.writeInt(mLteRsrpBoost);
         out.writeInt(mTdScdmaRscp);
         out.writeInt(isGsm ? 1 : 0);
+        out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
     }
 
     /**
@@ -449,6 +457,17 @@
     }
 
     /**
+     * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
+     * otherwise false.
+     *
+     * Used by phone to use only RSRP or not for the number of LTE signal bar.
+     * @hide
+     */
+    public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
+        mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+    }
+
+    /**
      * @param lteRsrpBoost - signal strength offset
      *
      * Used by phone to set the lte signal strength offset which will be
@@ -835,6 +854,13 @@
             }
         }
 
+        if (useOnlyRsrpForLteLevel()) {
+            log("getLTELevel - rsrp = " + rsrpIconLevel);
+            if (rsrpIconLevel != -1) {
+                return rsrpIconLevel;
+            }
+        }
+
         /*
          * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
          * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
@@ -915,6 +941,15 @@
     }
 
     /**
+     * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
+     *
+     * @hide
+     */
+    public boolean useOnlyRsrpForLteLevel() {
+        return this.mUseOnlyRsrpForLteLevel;
+    }
+
+    /**
      * @return get TD_SCDMA dbm
      *
      * @hide
@@ -974,7 +1009,8 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
+                + (mUseOnlyRsrpForLteLevel ? 1 : 0));
     }
 
     /**
@@ -1008,7 +1044,8 @@
                 && mLteCqi == s.mLteCqi
                 && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
-                && isGsm == s.isGsm);
+                && isGsm == s.isGsm
+                && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
     }
 
     /**
@@ -1031,7 +1068,9 @@
                 + " " + mLteCqi
                 + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
-                + " " + (isGsm ? "gsm|lte" : "cdma"));
+                + " " + (isGsm ? "gsm|lte" : "cdma")
+                + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
+                         "use_rsrp_and_rssnr_for_lte_level"));
     }
 
     /** Returns the signal strength related to GSM. */
@@ -1086,6 +1125,7 @@
         mLteRsrpBoost = m.getInt("lteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
         isGsm = m.getBoolean("isGsm");
+        mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
     }
 
     /**
@@ -1110,6 +1150,7 @@
         m.putInt("lteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
         m.putBoolean("isGsm", isGsm);
+        m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
     }
 
     /**