Loading core/java/android/app/DexLoadReporter.java +31 −9 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; Loading Loading @@ -86,29 +87,50 @@ import java.util.Set; } @Override public void report(List<String> dexPaths) { if (dexPaths.isEmpty()) { public void report(List<BaseDexClassLoader> classLoadersChain, List<String> classPaths) { if (classLoadersChain.size() != classPaths.size()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch"); return; } if (classPaths.isEmpty()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths"); return; } // The first element of classPaths is the list of dex files that should be registered. // The classpath is represented as a list of dex files separated by File.pathSeparator. String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator); if (dexPathsForRegistration.length == 0) { // No dex files to register. return; } // Notify the package manager about the dex loads unconditionally. // The load might be for either a primary or secondary dex file. notifyPackageManager(dexPaths); // Check for secondary dex files and register them for profiling if // possible. registerSecondaryDexForProfiling(dexPaths); notifyPackageManager(classLoadersChain, classPaths); // Check for secondary dex files and register them for profiling if possible. // Note that we only register the dex paths belonging to the first class loader. registerSecondaryDexForProfiling(dexPathsForRegistration); } private void notifyPackageManager(List<String> dexPaths) { 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 { ActivityThread.getPackageManager().notifyDexLoad( packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet()); packageName, classLoadersNames, classPaths, VMRuntime.getRuntime().vmInstructionSet()); } catch (RemoteException re) { Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re); } } private void registerSecondaryDexForProfiling(List<String> dexPaths) { private void registerSecondaryDexForProfiling(String[] dexPaths) { if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) { return; } Loading core/java/android/content/pm/IPackageManager.aidl +11 −3 Original line number Diff line number Diff line Loading @@ -469,11 +469,19 @@ interface IPackageManager { * 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. Loading services/core/java/com/android/server/pm/PackageDexOptimizer.java +30 −12 Original line number Diff line number Diff line Loading @@ -33,12 +33,12 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; 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 dalvik.system.DexFile; Loading Loading @@ -251,13 +251,12 @@ public class PackageDexOptimizer { * 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); } Loading Loading @@ -298,9 +297,16 @@ public class PackageDexOptimizer { } @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) Loading @@ -317,20 +323,32 @@ public class PackageDexOptimizer { 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; Loading services/core/java/com/android/server/pm/PackageManagerService.java +3 −2 Original line number Diff line number Diff line Loading @@ -9435,7 +9435,8 @@ public class PackageManagerService extends IPackageManager.Stub } @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) { Loading @@ -9443,7 +9444,7 @@ public class PackageManagerService extends IPackageManager.Stub + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, dexPaths, loaderIsa, userId); mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override services/core/java/com/android/server/pm/dex/DexManager.java +56 −26 Original line number Diff line number Diff line Loading @@ -97,29 +97,55 @@ public class DexManager { * 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); Loading Loading @@ -147,24 +173,25 @@ public class DexManager { // 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, loadingAppInfo.packageName)) { 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++; } } Loading Loading @@ -328,10 +355,8 @@ public class DexManager { 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()); Loading @@ -350,8 +375,7 @@ public class DexManager { } 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; Loading Loading @@ -434,6 +458,8 @@ public class DexManager { } } // 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. Loading @@ -452,12 +478,11 @@ public class DexManager { // 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, searchResult.mOwningPackageName); searchResult.mOwningPackageName, PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT); update |= newUpdate; } if (update) { Loading @@ -467,8 +492,13 @@ public class DexManager { // 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 Loading Loading
core/java/android/app/DexLoadReporter.java +31 −9 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; Loading Loading @@ -86,29 +87,50 @@ import java.util.Set; } @Override public void report(List<String> dexPaths) { if (dexPaths.isEmpty()) { public void report(List<BaseDexClassLoader> classLoadersChain, List<String> classPaths) { if (classLoadersChain.size() != classPaths.size()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch"); return; } if (classPaths.isEmpty()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths"); return; } // The first element of classPaths is the list of dex files that should be registered. // The classpath is represented as a list of dex files separated by File.pathSeparator. String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator); if (dexPathsForRegistration.length == 0) { // No dex files to register. return; } // Notify the package manager about the dex loads unconditionally. // The load might be for either a primary or secondary dex file. notifyPackageManager(dexPaths); // Check for secondary dex files and register them for profiling if // possible. registerSecondaryDexForProfiling(dexPaths); notifyPackageManager(classLoadersChain, classPaths); // Check for secondary dex files and register them for profiling if possible. // Note that we only register the dex paths belonging to the first class loader. registerSecondaryDexForProfiling(dexPathsForRegistration); } private void notifyPackageManager(List<String> dexPaths) { 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 { ActivityThread.getPackageManager().notifyDexLoad( packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet()); packageName, classLoadersNames, classPaths, VMRuntime.getRuntime().vmInstructionSet()); } catch (RemoteException re) { Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re); } } private void registerSecondaryDexForProfiling(List<String> dexPaths) { private void registerSecondaryDexForProfiling(String[] dexPaths) { if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) { return; } Loading
core/java/android/content/pm/IPackageManager.aidl +11 −3 Original line number Diff line number Diff line Loading @@ -469,11 +469,19 @@ interface IPackageManager { * 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. Loading
services/core/java/com/android/server/pm/PackageDexOptimizer.java +30 −12 Original line number Diff line number Diff line Loading @@ -33,12 +33,12 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; 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 dalvik.system.DexFile; Loading Loading @@ -251,13 +251,12 @@ public class PackageDexOptimizer { * 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); } Loading Loading @@ -298,9 +297,16 @@ public class PackageDexOptimizer { } @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) Loading @@ -317,20 +323,32 @@ public class PackageDexOptimizer { 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; Loading
services/core/java/com/android/server/pm/PackageManagerService.java +3 −2 Original line number Diff line number Diff line Loading @@ -9435,7 +9435,8 @@ public class PackageManagerService extends IPackageManager.Stub } @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) { Loading @@ -9443,7 +9444,7 @@ public class PackageManagerService extends IPackageManager.Stub + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, dexPaths, loaderIsa, userId); mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override
services/core/java/com/android/server/pm/dex/DexManager.java +56 −26 Original line number Diff line number Diff line Loading @@ -97,29 +97,55 @@ public class DexManager { * 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); Loading Loading @@ -147,24 +173,25 @@ public class DexManager { // 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, loadingAppInfo.packageName)) { 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++; } } Loading Loading @@ -328,10 +355,8 @@ public class DexManager { 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()); Loading @@ -350,8 +375,7 @@ public class DexManager { } 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; Loading Loading @@ -434,6 +458,8 @@ public class DexManager { } } // 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. Loading @@ -452,12 +478,11 @@ public class DexManager { // 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, searchResult.mOwningPackageName); searchResult.mOwningPackageName, PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT); update |= newUpdate; } if (update) { Loading @@ -467,8 +492,13 @@ public class DexManager { // 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 Loading