Start using shared libraries class loader.

Change 1/2. Change 2/2 will setup the class loader namespace for
shared libraries.

This change sets up shared libraries class loaders for applications
and for dexopt.

bug: 111174995
Test: DexoptUtilsTest, device boots

Change-Id: Ie9a2b4eaa85cda59951703433f7a2d03bc12095d
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5810e30..b52c021 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -333,9 +333,7 @@
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
-        String[] libraryDependencies = pkg.usesLibraryFiles;
-
-        optimizer.performDexOpt(pkg, libraryDependencies,
+        optimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
                 null /* ISAs */,
                 null /* CompilerStats.PackageStats */,
                 mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 95d2154..cc640f0 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.os.FileUtils;
@@ -129,7 +130,7 @@
      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
      * synchronized on {@link #mInstallLock}.
      */
-    int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
+    int performDexOpt(PackageParser.Package pkg, List<SharedLibraryInfo> sharedLibraries,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         if (pkg.applicationInfo.uid == -1) {
@@ -155,7 +156,8 @@
      * It assumes the install lock is held.
      */
     @GuardedBy("mInstallLock")
-    private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
+    private int performDexOptLI(PackageParser.Package pkg,
+            List<SharedLibraryInfo> sharedLibraries,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         final String[] instructionSets = targetInstructionSets != null ?
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9856a2b..acbd81d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2233,7 +2233,7 @@
             for (int i = 0; i < builtInLibCount; i++) {
                 String name = libConfig.keyAt(i);
                 String path = libConfig.valueAt(i);
-                addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
+                addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                         SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
             // Builtin libraries cannot encode their dependency where they are
@@ -4844,7 +4844,8 @@
                     }
 
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
-                            libInfo.getPackageName(), libInfo.getName(), libInfo.getLongVersion(),
+                            libInfo.getPackageName(), libInfo.getAllCodePaths(),
+                            libInfo.getName(), libInfo.getLongVersion(),
                             libInfo.getType(), libInfo.getDeclaringPackage(),
                             getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
                             (libInfo.getDependencies() == null
@@ -9225,7 +9226,7 @@
                     mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
             }
         }
-        return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
+        return pdo.performDexOpt(p, p.usesLibraryInfos, instructionSets,
                 getOrCreateCompilerPackageStats(p),
                 mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
     }
@@ -9628,7 +9629,6 @@
     @GuardedBy("mPackages")
     private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
             SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
-
         if (libInfo.getPath() != null) {
             usesLibraryFiles.add(libInfo.getPath());
             return;
@@ -11224,8 +11224,9 @@
         }
     }
 
-    private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
-            int type, String declaringPackageName, long declaringVersionCode) {
+    private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths,
+            String name, long version, int type, String declaringPackageName,
+            long declaringVersionCode) {
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             versionedLib = new LongSparseArray<>();
@@ -11236,7 +11237,7 @@
         } else if (versionedLib.indexOfKey(version) >= 0) {
             return false;
         }
-        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, name,
+        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name,
                 version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
                 null, null);
         versionedLib.put(version, libraryInfo);
@@ -11323,10 +11324,17 @@
             if (pkg.staticSharedLibName != null) {
                 // Static shared libs don't allow renaming as they have synthetic package
                 // names to allow install of multiple versions, so use name from manifest.
-                if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
+                if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+                        pkg.staticSharedLibName,
                         pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
                         pkg.manifestPackageName, pkg.getLongVersionCode())) {
                     hasStaticSharedLibs = true;
+                    // Shared libraries for the package need to be updated.
+                    try {
+                        updateSharedLibrariesLPr(pkg, null);
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                    }
                 } else {
                     Slog.w(TAG, "Package " + pkg.packageName + " library "
                                 + pkg.staticSharedLibName + " already exists; skipping");
@@ -11368,13 +11376,19 @@
                             allowed = true;
                         }
                         if (allowed) {
-                            if (!addSharedLibraryLPw(null, pkg.packageName, name,
-                                    SharedLibraryInfo.VERSION_UNDEFINED,
+                            if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
+                                    name, SharedLibraryInfo.VERSION_UNDEFINED,
                                     SharedLibraryInfo.TYPE_DYNAMIC,
                                     pkg.packageName, pkg.getLongVersionCode())) {
                                 Slog.w(TAG, "Package " + pkg.packageName + " library "
                                         + name + " already exists; skipping");
                             }
+                            // Shared libraries for the package need to be updated.
+                            try {
+                                updateSharedLibrariesLPr(pkg, null);
+                            } catch (PackageManagerException e) {
+                                Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                            }
                         } else {
                             Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
                                     + name + " that is not declared on system image; skipping");
@@ -15617,7 +15631,7 @@
                         REASON_INSTALL,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE
                                 | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
-                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryInfos,
                         null /* instructionSets */,
                         getOrCreateCompilerPackageStats(pkg),
                         mDexManager.getPackageUseInfoOrDefault(packageName),
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 9a12a2f..93ee44c 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.dex;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -29,6 +30,11 @@
 public final class DexoptUtils {
     private static final String TAG = "DexoptUtils";
 
+    // Shared libraries have more or less followed PCL behavior due to the way
+    // they were added to the classpath pre Q.
+    private static final String SHARED_LIBRARY_LOADER_TYPE =
+            ClassLoaderFactory.getPathClassLoaderName();
+
     private DexoptUtils() {}
 
     /**
@@ -62,12 +68,15 @@
      * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
      */
     public static String[] getClassLoaderContexts(ApplicationInfo info,
-            String[] sharedLibraries, boolean[] pathsWithCode) {
+            List<SharedLibraryInfo> sharedLibraries, boolean[] pathsWithCode) {
         // The base class loader context contains only the shared library.
-        String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
-        String baseApkContextClassLoader = encodeClassLoader(
-                sharedLibrariesClassPath, info.classLoaderName);
+        String sharedLibrariesContext = "";
+        if (sharedLibraries != null) {
+            sharedLibrariesContext = encodeSharedLibraries(sharedLibraries);
+        }
 
+        String baseApkContextClassLoader = encodeClassLoader(
+                "", info.classLoaderName, sharedLibrariesContext);
         if (info.getSplitCodePaths() == null) {
             // The application has no splits.
             return new String[] {baseApkContextClassLoader};
@@ -81,11 +90,10 @@
         // The splits have an implicit dependency on the base apk.
         // This means that we have to add the base apk file in addition to the shared libraries.
         String baseApkName = new File(info.getBaseCodePath()).getName();
-        String sharedLibrariesAndBaseClassPath =
-                encodeClasspath(sharedLibrariesClassPath, baseApkName);
+        String baseClassPath = baseApkName;
 
         // The result is stored in classLoaderContexts.
-        // Index 0 is the class loaded context for the base apk.
+        // Index 0 is the class loader context for the base apk.
         // Index `i` is the class loader context encoding for split `i`.
         String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
         classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
@@ -94,10 +102,14 @@
             // If the app didn't request for the splits to be loaded in isolation or if it does not
             // declare inter-split dependencies, then all the splits will be loaded in the base
             // apk class loader (in the order of their definition).
-            String classpath = sharedLibrariesAndBaseClassPath;
+            String classpath = baseClassPath;
             for (int i = 1; i < classLoaderContexts.length; i++) {
-                classLoaderContexts[i] = pathsWithCode[i]
-                        ? encodeClassLoader(classpath, info.classLoaderName) : null;
+                if (pathsWithCode[i]) {
+                    classLoaderContexts[i] = encodeClassLoader(
+                            classpath, info.classLoaderName, sharedLibrariesContext);
+                } else {
+                    classLoaderContexts[i] = null;
+                }
                 // Note that the splits with no code are not removed from the classpath computation.
                 // i.e. split_n might get the split_n-1 in its classpath dependency even
                 // if split_n-1 has no code.
@@ -124,7 +136,7 @@
                         info.splitClassLoaderNames[i]);
             }
             String splitDependencyOnBase = encodeClassLoader(
-                    sharedLibrariesAndBaseClassPath, info.classLoaderName);
+                    baseClassPath, info.classLoaderName);
             SparseArray<int[]> splitDependencies = info.splitDependencies;
 
             // Note that not all splits have dependencies (e.g. configuration splits)
@@ -149,7 +161,8 @@
                     // any dependency. In this case its context equals its declared class loader.
                     classLoaderContexts[i] = classLoaderContexts[i] == null
                             ? splitClassLoader
-                            : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+                            : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i])
+                                    + sharedLibrariesContext;
                 } else {
                     // This is a split without code, it has no dependency and it is not compiled.
                     // Its context will be null.
@@ -207,6 +220,31 @@
         return splitContext;
     }
 
+    private static String encodeSharedLibrary(SharedLibraryInfo sharedLibrary) {
+        List<String> paths = sharedLibrary.getAllCodePaths();
+        String classLoaderSpec = encodeClassLoader(
+                encodeClasspath(paths.toArray(new String[paths.size()])),
+                SHARED_LIBRARY_LOADER_TYPE);
+        if (sharedLibrary.getDependencies() != null) {
+            classLoaderSpec += encodeSharedLibraries(sharedLibrary.getDependencies());
+        }
+        return classLoaderSpec;
+    }
+
+    private static String encodeSharedLibraries(List<SharedLibraryInfo> sharedLibraries) {
+        String sharedLibrariesContext = "{";
+        boolean first = true;
+        for (SharedLibraryInfo info : sharedLibraries) {
+            if (!first) {
+                sharedLibrariesContext += "#";
+            }
+            first = false;
+            sharedLibrariesContext += encodeSharedLibrary(info);
+        }
+        sharedLibrariesContext += "}";
+        return sharedLibrariesContext;
+    }
+
     /**
      * Encodes the shared libraries classpathElements in a format accepted by dexopt.
      * NOTE: Keep this in sync with the dexopt expectations! Right now that is
@@ -258,6 +296,14 @@
     }
 
     /**
+     * Same as above, but appends {@param sharedLibraries} to the result.
+     */
+    private static String encodeClassLoader(String classpath, String classLoaderName,
+            String sharedLibraries) {
+        return encodeClassLoader(classpath, classLoaderName) + sharedLibraries;
+    }
+
+    /**
      * Links to dependencies together in a format accepted by dexopt.
      * For the special case when either of cl1 or cl2 equals
      * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This