diff options
| author | 2018-12-02 20:39:38 +0000 | |
|---|---|---|
| committer | 2018-12-02 20:39:38 +0000 | |
| commit | e7753e084fc668642aae74ec05af62f03a2688e7 (patch) | |
| tree | c72c76e74136656b3a1cb56acfa8b5709af1faf9 | |
| parent | a920b9c85e97afb1cbadb03c0ad86c655b2784c5 (diff) | |
| parent | 972b39e4e443754ad83eef260d121777cd3e3592 (diff) | |
Merge "Start using shared libraries class loader."
13 files changed, 323 insertions, 89 deletions
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 30d6beed80dd..9ef24c6c2aeb 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -27,6 +27,7 @@ import com.android.internal.os.ClassLoaderFactory; import dalvik.system.PathClassLoader; import java.util.Collection; +import java.util.List; /** @hide */ public class ApplicationLoaders { @@ -38,15 +39,25 @@ public class ApplicationLoaders { ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String classLoaderName) { + return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, + librarySearchPath, libraryPermittedPath, parent, classLoaderName, + null); + } + + ClassLoader getClassLoaderWithSharedLibraries( + String zip, int targetSdkVersion, boolean isBundled, + String librarySearchPath, String libraryPermittedPath, + ClassLoader parent, String classLoaderName, + List<ClassLoader> sharedLibraries) { // For normal usage the cache key used is the same as the zip path. return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath, - libraryPermittedPath, parent, zip, classLoaderName); + libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries); } private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String cacheKey, - String classLoaderName) { + String classLoaderName, List<ClassLoader> sharedLibraries) { /* * This is the parent we use if they pass "null" in. In theory * this should be the "system" class loader; in practice we @@ -75,7 +86,7 @@ public class ApplicationLoaders { ClassLoader classloader = ClassLoaderFactory.createClassLoader( zip, librarySearchPath, libraryPermittedPath, parent, - targetSdkVersion, isBundled, classLoaderName); + targetSdkVersion, isBundled, classLoaderName, sharedLibraries); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); @@ -90,7 +101,7 @@ public class ApplicationLoaders { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); ClassLoader loader = ClassLoaderFactory.createClassLoader( - zip, null, parent, classLoaderName); + zip, null, parent, classLoaderName, sharedLibraries); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return loader; } @@ -110,7 +121,7 @@ public class ApplicationLoaders { // The cache key is passed separately to enable the stub WebView to be cached under the // stub's APK path, when the actual package path is the donor APK. return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null, - cacheKey, null /* classLoaderName */); + cacheKey, null /* classLoaderName */, null /* sharedLibraries */); } /** diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index da4f77baac41..3f1075448c84 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -29,6 +29,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.SharedLibraryInfo; import android.content.pm.dex.ArtManager; import android.content.pm.split.SplitDependencyLoader; import android.content.res.AssetManager; @@ -70,8 +71,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Set; final class IntentReceiverLeaked extends AndroidRuntimeException { @UnsupportedAppUsage @@ -397,6 +400,24 @@ public final class LoadedApk { makePaths(activityThread, false, aInfo, outZipPaths, null); } + private static void appendSharedLibrariesLibPathsIfNeeded( + List<SharedLibraryInfo> sharedLibraries, ApplicationInfo aInfo, + Set<String> outSeenPaths, + List<String> outLibPaths) { + if (sharedLibraries == null) { + return; + } + for (SharedLibraryInfo lib : sharedLibraries) { + List<String> paths = lib.getAllCodePaths(); + outSeenPaths.addAll(paths); + for (String path : paths) { + appendApkLibPathIfNeeded(path, aInfo, outLibPaths); + } + appendSharedLibrariesLibPathsIfNeeded( + lib.getDependencies(), aInfo, outSeenPaths, outLibPaths); + } + } + public static void makePaths(ActivityThread activityThread, boolean isBundledApp, ApplicationInfo aInfo, @@ -404,7 +425,6 @@ public final class LoadedApk { List<String> outLibPaths) { final String appDir = aInfo.sourceDir; final String libDir = aInfo.nativeLibraryDir; - final String[] sharedLibraries = aInfo.sharedLibraryFiles; outZipPaths.clear(); outZipPaths.add(appDir); @@ -499,11 +519,19 @@ public final class LoadedApk { } } - // Prepend the shared libraries, maintaining their original order where possible. - if (sharedLibraries != null) { + // Add the shared libraries native paths. The dex files in shared libraries will + // be resolved through shared library loaders, which are setup later. + Set<String> outSeenPaths = new LinkedHashSet<>(); + appendSharedLibrariesLibPathsIfNeeded( + aInfo.sharedLibraryInfos, aInfo, outSeenPaths, outLibPaths); + + // ApplicationInfo.sharedLibraryFiles is a public API, so anyone can change it. + // We prepend shared libraries that the package manager hasn't seen, maintaining their + // original order where possible. + if (aInfo.sharedLibraryFiles != null) { int index = 0; - for (String lib : sharedLibraries) { - if (!outZipPaths.contains(lib)) { + for (String lib : aInfo.sharedLibraryFiles) { + if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) { outZipPaths.add(index, lib); index++; appendApkLibPathIfNeeded(lib, aInfo, outLibPaths); @@ -631,6 +659,43 @@ public final class LoadedApk { return mSplitLoader.getSplitPathsForSplit(splitName); } + /** + * Create a class loader for the {@code sharedLibrary}. Shared libraries are canonicalized, + * so if we already created a class loader with that shared library, we return it. + * + * Implementation notes: the canonicalization of shared libraries is something dex2oat + * also does. + */ + ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary, + boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) { + List<String> paths = sharedLibrary.getAllCodePaths(); + List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders( + sharedLibrary.getDependencies(), isBundledApp, librarySearchPath, + libraryPermittedPath); + final String jars = (paths.size() == 1) ? paths.get(0) : + TextUtils.join(File.pathSeparator, paths); + + // Shared libraries get a null parent: this has the side effect of having canonicalized + // shared libraries using ApplicationLoaders cache, which is the behavior we want. + return ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(jars, + mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, + libraryPermittedPath, /* parent */ null, + /* classLoaderName */ null, sharedLibraries); + } + + private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries, + boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) { + if (sharedLibraries == null) { + return null; + } + List<ClassLoader> loaders = new ArrayList<>(); + for (SharedLibraryInfo info : sharedLibraries) { + loaders.add(createSharedLibraryLoader( + info, isBundledApp, librarySearchPath, libraryPermittedPath)); + } + return loaders; + } + private void createOrUpdateClassLoaderLocked(List<String> addedPaths) { if (mPackageName.equals("android")) { // Note: This branch is taken for system server and we don't need to setup @@ -759,10 +824,14 @@ public final class LoadedApk { // as this is early and necessary. StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, - mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, + List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders( + mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath, + libraryPermittedPath); + + mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries( + zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, mBaseClassLoader, - mApplicationInfo.classLoaderName); + mApplicationInfo.classLoaderName, sharedLibraries); mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader); StrictMode.setThreadPolicy(oldPolicy); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 0ec509e1164e..a74e4d75a33c 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -45,6 +45,7 @@ import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.Objects; import java.util.UUID; @@ -814,7 +815,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * the structure. */ public String[] sharedLibraryFiles; - + + /** + * List of all shared libraries this application is linked against. This + * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving + * the structure. + * + * {@hide} + */ + public List<SharedLibraryInfo> sharedLibraryInfos; + /** * Full path to the default directory assigned to the package for its * persistent data. @@ -1458,6 +1469,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfo = orig.seInfo; seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; + sharedLibraryInfos = orig.sharedLibraryInfos; dataDir = orig.dataDir; deviceProtectedDataDir = orig.deviceProtectedDataDir; credentialProtectedDataDir = orig.credentialProtectedDataDir; @@ -1533,6 +1545,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(seInfo); dest.writeString(seInfoUser); dest.writeStringArray(sharedLibraryFiles); + dest.writeTypedList(sharedLibraryInfos); dest.writeString(dataDir); dest.writeString(deviceProtectedDataDir); dest.writeString(credentialProtectedDataDir); @@ -1605,6 +1618,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfo = source.readString(); seInfoUser = source.readString(); sharedLibraryFiles = source.readStringArray(); + sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); dataDir = source.readString(); deviceProtectedDataDir = source.readString(); credentialProtectedDataDir = source.readString(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 7aea261a48f1..ecb9dbf38614 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -7555,6 +7555,7 @@ public class PackageParser { } if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) { ai.sharedLibraryFiles = p.usesLibraryFiles; + ai.sharedLibraryInfos = p.usesLibraryInfos; } if (state.stopped) { ai.flags |= ApplicationInfo.FLAG_STOPPED; diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 096301c92faa..ad82626dd6ad 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -74,6 +74,7 @@ public final class SharedLibraryInfo implements Parcelable { private final String mPath; private final String mPackageName; private final String mName; + private final List<String> mCodePaths; private final long mVersion; private final @Type int mType; @@ -84,6 +85,8 @@ public final class SharedLibraryInfo implements Parcelable { /** * Creates a new instance. * + * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of + * this shared library. Null for builtin library. * @param name The lib name. * @param version The lib version if not builtin. * @param type The lib type. @@ -92,11 +95,13 @@ public final class SharedLibraryInfo implements Parcelable { * * @hide */ - public SharedLibraryInfo(String path, String packageName, String name, long version, int type, + public SharedLibraryInfo(String path, String packageName, List<String> codePaths, + String name, long version, int type, VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, List<SharedLibraryInfo> dependencies) { mPath = path; mPackageName = packageName; + mCodePaths = codePaths; mName = name; mVersion = version; mType = type; @@ -106,7 +111,8 @@ public final class SharedLibraryInfo implements Parcelable { } private SharedLibraryInfo(Parcel parcel) { - this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readLong(), + this(parcel.readString(), parcel.readString(), parcel.readArrayList(null), + parcel.readString(), parcel.readLong(), parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null), parcel.createTypedArrayList(SharedLibraryInfo.CREATOR)); } @@ -155,6 +161,25 @@ public final class SharedLibraryInfo implements Parcelable { } /** + * Get all code paths for that library. + * + * @return All code paths. + * + * @hide + */ + public List<String> getAllCodePaths() { + if (getPath() != null) { + // Builtin library. + ArrayList<String> list = new ArrayList<>(); + list.add(getPath()); + return list; + } else { + // Static or dynamic library. + return mCodePaths; + } + } + + /** * Add a library dependency to that library. Note that this * should be called under the package manager lock. * @@ -273,6 +298,7 @@ public final class SharedLibraryInfo implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mPath); parcel.writeString(mPackageName); + parcel.writeList(mCodePaths); parcel.writeString(mName); parcel.writeLong(mVersion); parcel.writeInt(mType); diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java index 387857f0c997..c5bc45afee95 100644 --- a/core/java/com/android/internal/os/ClassLoaderFactory.java +++ b/core/java/com/android/internal/os/ClassLoaderFactory.java @@ -22,6 +22,8 @@ import dalvik.system.DelegateLastClassLoader; import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader; +import java.util.List; + /** * Creates class loaders. * @@ -37,6 +39,13 @@ public class ClassLoaderFactory { DelegateLastClassLoader.class.getName(); /** + * Returns the name of the class for PathClassLoader. + */ + public static String getPathClassLoaderName() { + return PATH_CLASS_LOADER_NAME; + } + + /** * Returns true if {@code name} is a supported classloader. {@code name} must be a * binary name of a class, as defined by {@code Class.getName}. */ @@ -68,25 +77,43 @@ public class ClassLoaderFactory { * is created. */ public static ClassLoader createClassLoader(String dexPath, - String librarySearchPath, ClassLoader parent, String classloaderName) { + String librarySearchPath, ClassLoader parent, String classloaderName, + List<ClassLoader> sharedLibraries) { + ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null) + ? null + : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]); if (isPathClassLoaderName(classloaderName)) { - return new PathClassLoader(dexPath, librarySearchPath, parent); + return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries); } else if (isDelegateLastClassLoaderName(classloaderName)) { - return new DelegateLastClassLoader(dexPath, librarySearchPath, parent); + return new DelegateLastClassLoader(dexPath, librarySearchPath, parent, + arrayOfSharedLibraries); } throw new AssertionError("Invalid classLoaderName: " + classloaderName); } /** + * Same as {@code createClassLoader} below, but passes a null list of shared + * libraries. + */ + public static ClassLoader createClassLoader(String dexPath, + String librarySearchPath, String libraryPermittedPath, ClassLoader parent, + int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) { + return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath, + parent, targetSdkVersion, isNamespaceShared, classLoaderName, null); + } + + + /** * Create a ClassLoader and initialize a linker-namespace for it. */ public static ClassLoader createClassLoader(String dexPath, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, - int targetSdkVersion, boolean isNamespaceShared, String classloaderName) { + int targetSdkVersion, boolean isNamespaceShared, String classLoaderName, + List<ClassLoader> sharedLibraries) { final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent, - classloaderName); + classLoaderName, sharedLibraries); boolean isForVendor = false; for (String path : dexPath.split(":")) { diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index bd51af270dd8..af3bd6d99747 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1418,6 +1418,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { app = mPackageManager.getApplicationInfo(pkg.packageName, PackageManager.GET_SHARED_LIBRARY_FILES); pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; + pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos; } } catch (NameNotFoundException e) { packages.remove(a); diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index dea78630e4fe..41eaa24d898e 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { 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 dc5213fa0cad..c197d2302ebf 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.annotation.Nullable; 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; @@ -128,7 +129,7 @@ public class PackageDexOptimizer { * <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) { @@ -154,7 +155,8 @@ public class PackageDexOptimizer { * 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 2a605f555bdf..47723daf95b0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2521,7 +2521,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ -5086,7 +5086,8 @@ public class PackageManagerService extends IPackageManager.Stub } 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 @@ -9386,7 +9387,7 @@ public class PackageManagerService extends IPackageManager.Stub 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); } @@ -9756,7 +9757,6 @@ public class PackageManagerService extends IPackageManager.Stub @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; @@ -11324,8 +11324,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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<>(); @@ -11336,7 +11337,7 @@ public class PackageManagerService extends IPackageManager.Stub } 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); @@ -11423,10 +11424,17 @@ public class PackageManagerService extends IPackageManager.Stub 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"); @@ -11468,13 +11476,19 @@ public class PackageManagerService extends IPackageManager.Stub 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"); @@ -17711,7 +17725,7 @@ public class PackageManagerService extends IPackageManager.Stub 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(pkg.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 9a12a2f140bf..93ee44cdf0ce 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 @@ import java.util.List; 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 @@ public final class DexoptUtils { * 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 @@ public final class DexoptUtils { // 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 @@ public final class DexoptUtils { // 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 @@ public final class DexoptUtils { 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 @@ public final class DexoptUtils { // 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 @@ public final class DexoptUtils { 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 @@ public final class DexoptUtils { } /** + * 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 diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index cdbc665dc0c2..69c0c0f74b8a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -489,7 +489,8 @@ public class PackageParserTest { pkg.usesLibraryFiles = new String[] { "foo13"}; pkg.usesLibraryInfos = new ArrayList<>(); - pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null, null)); + pkg.usesLibraryInfos.add( + new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null)); pkg.mOriginalPackages = new ArrayList<>(); pkg.mOriginalPackages.add("foo14"); 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 6e0f56c8e7c8..154212053795 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 @@ -28,6 +28,7 @@ import static org.junit.Assert.fail; import android.content.pm.ApplicationInfo; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.content.pm.SharedLibraryInfo; import android.util.SparseArray; import dalvik.system.DelegateLastClassLoader; @@ -39,6 +40,7 @@ import org.junit.runner.RunWith; import java.io.File; import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -108,22 +110,31 @@ public class DexoptUtilsTest { return data; } + private List<SharedLibraryInfo> createMockSharedLibrary(String [] sharedLibrary) { + SharedLibraryInfo info = new SharedLibraryInfo(null, null, Arrays.asList(sharedLibrary), + null, 0L, SharedLibraryInfo.TYPE_STATIC, null, null, null); + ArrayList<SharedLibraryInfo> libraries = new ArrayList<>(); + libraries.add(info); + return libraries; + } + @Test public void testSplitChain() { TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); - assertEquals("PCL[a.dex:b.dex]", contexts[0]); - assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", + assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]); + assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[1]); - assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); - assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); - assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); + assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]); + assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]); + assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @@ -131,25 +142,28 @@ public class DexoptUtilsTest { @Test public void testSplitChainNoSplitDependencies() { TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); - assertEquals("PCL[a.dex:b.dex]", contexts[0]); - assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]); - assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]); - assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]); - assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]); + assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]); + assertEquals("PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[1]); + assertEquals("PCL[base.dex:base-1.dex]{PCL[a.dex:b.dex]}", contexts[2]); + assertEquals("PCL[base.dex:base-1.dex:base-2.dex]{PCL[a.dex:b.dex]}", contexts[3]); assertEquals( - "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]", + "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]{PCL[a.dex:b.dex]}", + contexts[4]); + assertEquals( + "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]{PCL[a.dex:b.dex]}", contexts[5]); assertEquals( - "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]", + "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]{PCL[a.dex:b.dex]}", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals( - "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]", + "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]{PCL[a.dex:b.dex]}", contexts[8]); // feature split with no dependency } @@ -200,18 +214,21 @@ public class DexoptUtilsTest { public void testSplitChainWithNullPrimaryClassLoader() { // A null classLoaderName should mean PathClassLoader. TestData data = createMockApplicationInfo(null, true, true); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); - assertEquals("PCL[a.dex:b.dex]", contexts[0]); - assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); - assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); - assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); - assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); + assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]); + assertEquals( + "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", + contexts[1]); + assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]); + assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]); + assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @@ -219,35 +236,38 @@ public class DexoptUtilsTest { @Test public void tesNoSplits() { TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); - assertEquals("PCL[a.dex:b.dex]", contexts[0]); + assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]); } @Test public void tesNoSplitsNullClassLoaderName() { TestData data = createMockApplicationInfo(null, false, false); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); - assertEquals("PCL[a.dex:b.dex]", contexts[0]); + assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]); } @Test public void tesNoSplitDelegateLast() { TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, false, false); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); - assertEquals("DLC[a.dex:b.dex]", contexts[0]); + assertEquals("DLC[]{PCL[a.dex:b.dex]}", contexts[0]); } @Test @@ -276,7 +296,8 @@ public class DexoptUtilsTest { TestData data = createMockApplicationInfo(null, true, false); Arrays.fill(data.pathsWithCode, false); - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); @@ -295,18 +316,21 @@ public class DexoptUtilsTest { public void testContextBaseNoCode() { TestData data = createMockApplicationInfo(null, true, true); data.pathsWithCode[0] = false; - String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; + List<SharedLibraryInfo> sharedLibrary = + createMockSharedLibrary(new String[] {"a.dex", "b.dex"}); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); assertEquals(null, contexts[0]); - assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); - assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); - assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); - assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); + assertEquals( + "DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", + contexts[1]); + assertEquals("DLC[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[2]); + assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[3]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[4]); + assertEquals("PCL[];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[5]); + assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]{PCL[a.dex:b.dex]}", contexts[6]); assertEquals(null, contexts[7]); } |