diff options
4 files changed, 333 insertions, 315 deletions
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index e625aeffc0c6..e7d0c41c0fea 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -401,7 +401,8 @@ public class PackageDexOptimizer { return DEX_OPT_FAILED; } String classLoaderContext = null; - if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) { + if (dexUseInfo.isUnsupportedClassLoaderContext() + || dexUseInfo.isVariableClassLoaderContext()) { // If we have an unknown (not yet set), or a variable class loader chain. Just extract // the dex file. compilerFilter = "extract"; 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 117cc5e8eb80..e8765ad973f3 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -45,6 +45,7 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -103,19 +104,6 @@ public class DexManager { 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(Context context, IPackageManager pms, PackageDexOptimizer pdo, Installer installer, Object installLock) { mContext = context; @@ -194,6 +182,8 @@ public class DexManager { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. + // Note this is just an optimization that makes things easier to read in the + // package-dex-use file since we don't need to pollute it with redundant info. continue; } @@ -211,7 +201,7 @@ public class DexManager { // async write to disk to make sure we don't loose the data in case of a reboot. if (mPackageDexUsage.record(searchResult.mOwningPackageName, - dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit, + dexPath, loaderUserId, loaderIsa, primaryOrSplit, loadingAppInfo.packageName, classLoaderContext)) { mPackageDexUsage.maybeWriteAsync(); } @@ -364,7 +354,9 @@ public class DexManager { try { mPackageDexUsage.read(); - mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths); + List<String> packagesToKeepDataAbout = new ArrayList<>(); + mPackageDexUsage.syncData( + packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout); } catch (Exception e) { mPackageDexUsage.clear(); Slog.w(TAG, "Exception while loading package dex usage. " @@ -391,8 +383,17 @@ public class DexManager { * to access the package use. */ public PackageUseInfo getPackageUseInfoOrDefault(String packageName) { + // 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. PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName); - return useInfo == null ? DEFAULT_USE_INFO : useInfo; + return useInfo == null ? new PackageUseInfo(packageName) : useInfo; } /** @@ -542,7 +543,7 @@ 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) { + boolean isSharedModule, int userId) { // Find the owning package record. DexSearchResult searchResult = getDexPackage(info, dexPath, userId); @@ -559,11 +560,14 @@ public class DexManager { // We found the package. Now record the usage for all declared ISAs. boolean update = false; + // If this is a shared module set the loading package to an arbitrary package name + // so that we can mark that module as usedByOthers. + String loadingPackage = isSharedModule ? ".shared.module" : searchResult.mOwningPackageName; for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, - dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false, + dexPath, userId, isa, /*primaryOrSplit*/ false, searchResult.mOwningPackageName, - PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT); + PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); update |= newUpdate; } if (update) { 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 08763e729c71..10760f52a02b 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -39,10 +39,12 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -51,25 +53,21 @@ import java.util.Set; * Stat file which store usage information about dex files. */ public class PackageDexUsage extends AbstractStatsBase<Void> { - private final static String TAG = "PackageDexUsage"; + private static final String TAG = "PackageDexUsage"; - // 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; + // We are currently at version 2. + // Version 1 was introduced in Nougat and Version 2 in Oreo. + // We dropped version 1 support in R since all devices should have updated + // already. + private static final int PACKAGE_DEX_USAGE_VERSION = 2; - private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2; - - private final static String PACKAGE_DEX_USAGE_VERSION_HEADER = + private static final 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 = "@"; + private static final String SPLIT_CHAR = ","; + private static final String CODE_PATH_LINE_CHAR = "+"; + private static final String DEX_LINE_CHAR = "#"; + private static final 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. @@ -77,10 +75,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { // skip optimizations on that dex files. /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT = "=VariableClassLoaderContext="; - // 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="; // The marker used for unsupported class loader contexts (no longer written, may occur in old // files so discarded on read). Note: this matches @@ -126,7 +120,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * has been seen before. */ /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId, - String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, + String loaderIsa, boolean primaryOrSplit, String loadingPackageName, String classLoaderContext) { if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported"); @@ -135,20 +129,22 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { throw new IllegalArgumentException("Null classLoaderContext"); } if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) { + Slog.e(TAG, "Unsupported context?"); return false; } + boolean isUsedByOtherApps = !owningPackageName.equals(loadingPackageName); + synchronized (mPackageUseInfoMap) { PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName); if (packageUseInfo == null) { // This is the first time we see the package. - packageUseInfo = new PackageUseInfo(); + packageUseInfo = new PackageUseInfo(owningPackageName); if (primaryOrSplit) { // 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.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps, - owningPackageName, loadingPackageName); + packageUseInfo.mergePrimaryCodePaths(dexPath, 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. @@ -164,9 +160,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { // We already have data on this package. Amend it. if (primaryOrSplit) { // We have a possible update on the primary apk usage. Merge - // isUsedByOtherApps information and return if there was an update. - return packageUseInfo.mergeCodePathUsedByOtherApps( - dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName); + // dex path information and return if there was an update. + return packageUseInfo.mergePrimaryCodePaths(dexPath, loadingPackageName); } else { DexUseInfo newData = new DexUseInfo( isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa); @@ -281,7 +276,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { // Write the code paths used by other apps. for (Map.Entry<String, Set<String>> codeEntry : - packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) { + packageUseInfo.mPrimaryCodePaths.entrySet()) { String codePath = codeEntry.getKey(); Set<String> loadingPackages = codeEntry.getValue(); fpw.println(CODE_PATH_LINE_CHAR + codePath); @@ -339,7 +334,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { version = Integer.parseInt( versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length())); if (!isSupportedVersion(version)) { - throw new IllegalStateException("Unexpected version: " + version); + Slog.w(TAG, "Unexpected package-dex-use version: " + version + + ". Not reading from it"); + return; } } @@ -377,9 +374,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { throw new IllegalStateException("Invalid PackageDexUsage line: " + line); } - // In version 2 we added the loading packages and class loader context. - Set<String> loadingPackages = maybeReadLoadingPackages(in, version); - String classLoaderContext = maybeReadClassLoaderContext(in, version); + Set<String> loadingPackages = readLoadingPackages(in, version); + String classLoaderContext = readClassLoaderContext(in, version); if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(classLoaderContext)) { // We used to record use of unsupported class loaders, but we no longer do. @@ -410,34 +406,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } 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); + Set<String> loadingPackages = readLoadingPackages(in, version); + currentPackageData.mPrimaryCodePaths.put(codePath, loadingPackages); } else { // This is a package line. - 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]); - } + currentPackage = line; + currentPackageData = new PackageUseInfo(currentPackage); data.put(currentPackage, currentPackageData); } } @@ -449,46 +427,31 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } /** - * Reads the class loader context encoding from the buffer {@code in} if - * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}. + * Reads the class loader context encoding from the buffer {@code in}. */ - 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."); - } + private String readClassLoaderContext(BufferedReader in, int version) throws IOException { + String 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; + return context; } /** - * Reads the list of loading packages from the buffer {@code in} if - * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}. + * Reads the list of loading packages from the buffer {@code in}. */ - private Set<String> maybeReadLoadingPackages(BufferedReader in, int version) + private Set<String> readLoadingPackages(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(); + String line = in.readLine(); + if (line == null) { + throw new IllegalStateException("Could not find the loadingPackages line."); + } + Set<String> result = new HashSet<>(); + if (line.length() != LOADING_PACKAGE_CHAR.length()) { + Collections.addAll(result, + line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR)); } + return result; } /** @@ -501,21 +464,26 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } private boolean isSupportedVersion(int version) { - return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 - || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2; + return version == PACKAGE_DEX_USAGE_VERSION; } /** * Syncs the existing data with the set of available packages by removing obsolete entries. */ /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap, - Map<String, Set<String>> packageToCodePaths) { + Map<String, Set<String>> packageToCodePaths, + List<String> packagesToKeepDataAbout) { synchronized (mPackageUseInfoMap) { Iterator<Map.Entry<String, PackageUseInfo>> pIt = mPackageUseInfoMap.entrySet().iterator(); while (pIt.hasNext()) { Map.Entry<String, PackageUseInfo> pEntry = pIt.next(); String packageName = pEntry.getKey(); + if (packagesToKeepDataAbout.contains(packageName)) { + // This is a package for which we should keep the data even if it's not + // in the list of user packages. + continue; + } PackageUseInfo packageUseInfo = pEntry.getValue(); Set<Integer> users = packageToUsersMap.get(packageName); if (users == null) { @@ -536,22 +504,31 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { // 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(); + + Iterator<Map.Entry<String, Set<String>>> recordedIt = + packageUseInfo.mPrimaryCodePaths.entrySet().iterator(); + while (recordedIt.hasNext()) { + Map.Entry<String, Set<String>> entry = recordedIt.next(); + String recordedCodePath = entry.getKey(); + if (!codePaths.contains(recordedCodePath)) { + // Clean up a non existing code path. + recordedIt.remove(); + } else { + // Clean up a non existing loading package. + Set<String> recordedLoadingPackages = entry.getValue(); + Iterator<String> recordedLoadingPackagesIt = + recordedLoadingPackages.iterator(); + while (recordedLoadingPackagesIt.hasNext()) { + String recordedLoadingPackage = recordedLoadingPackagesIt.next(); + if (!packagesToKeepDataAbout.contains(recordedLoadingPackage) + && !packageToUsersMap.containsKey(recordedLoadingPackage)) { + recordedLoadingPackagesIt.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() + 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. @@ -712,35 +689,26 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * Stores data on how a package and its dex files are used. */ public static class PackageUseInfo { + // The name of the package this info belongs to. + private final String mPackageName; // 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; + private final Map<String, Set<String>> mPrimaryCodePaths; // 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; - - /*package*/ PackageUseInfo() { - mCodePathsUsedByOtherApps = new HashMap<>(); + /*package*/ PackageUseInfo(String packageName) { + mPrimaryCodePaths = new HashMap<>(); mDexUseInfoMap = new HashMap<>(); + mPackageName = packageName; } // Creates a deep copy of the `other`. private PackageUseInfo(PackageUseInfo other) { - mCodePathsUsedByOtherApps = new HashMap<>(); - for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) { - mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue())); + mPackageName = other.mPackageName; + mPrimaryCodePaths = new HashMap<>(); + for (Map.Entry<String, Set<String>> e : other.mPrimaryCodePaths.entrySet()) { + mPrimaryCodePaths.put(e.getKey(), new HashSet<>(e.getValue())); } mDexUseInfoMap = new HashMap<>(); @@ -749,28 +717,29 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } } - 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); + private boolean mergePrimaryCodePaths(String codePath, String loadingPackage) { + Set<String> loadingPackages = mPrimaryCodePaths.get(codePath); if (loadingPackages == null) { loadingPackages = new HashSet<>(); - mCodePathsUsedByOtherApps.put(codePath, loadingPackages); - newCodePath = true; + mPrimaryCodePaths.put(codePath, loadingPackages); } - boolean newLoadingPackage = loadingPackage != null - && !loadingPackage.equals(owningPackageName) - && loadingPackages.add(loadingPackage); - return newCodePath || newLoadingPackage; + return loadingPackages.add(loadingPackage); } public boolean isUsedByOtherApps(String codePath) { - return mCodePathsUsedByOtherApps.containsKey(codePath); + if (mPrimaryCodePaths.containsKey(codePath)) { + Set<String> loadingPackages = mPrimaryCodePaths.get(codePath); + if (loadingPackages.contains(mPackageName)) { + // If the owning package is in the list then this code path + // is used by others if there are other packages in the list. + return loadingPackages.size() > 1; + } else { + // The owning package is not in the loading packages. So if + // the list is non-empty then the code path is used by others. + return !loadingPackages.isEmpty(); + } + } + return false; } public Map<String, DexUseInfo> getDexUseInfoMap() { @@ -778,11 +747,11 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } public Set<String> getLoadingPackages(String codePath) { - return mCodePathsUsedByOtherApps.getOrDefault(codePath, null); + return mPrimaryCodePaths.getOrDefault(codePath, null); } public boolean isAnyCodePathUsedByOtherApps() { - return !mCodePathsUsedByOtherApps.isEmpty(); + return !mPrimaryCodePaths.isEmpty(); } /** @@ -790,16 +759,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * 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; + boolean updated = false; + List<String> retainOnlyOwningPackage = new ArrayList<>(1); + retainOnlyOwningPackage.add(mPackageName); + for (Map.Entry<String, Set<String>> entry : mPrimaryCodePaths.entrySet()) { + // Remove or loading packages but the owning one. + if (entry.getValue().retainAll(retainOnlyOwningPackage)) { + updated = true; + } } + return updated; } } @@ -847,11 +816,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages); String oldClassLoaderContext = mClassLoaderContext; - if (isUnknownOrUnsupportedContext(mClassLoaderContext)) { - // Can happen if we read a previous version. + if (isUnsupportedContext(mClassLoaderContext)) { mClassLoaderContext = dexUseInfo.mClassLoaderContext; - } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext) - && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { + } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { // We detected a context change. mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT; } @@ -862,11 +829,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { || !Objects.equals(oldClassLoaderContext, mClassLoaderContext); } - private static boolean isUnknownOrUnsupportedContext(String context) { - // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases - // into UNSUPPORTED_CLASS_LOADER_CONTEXT. - return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context) - || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context); + private static boolean isUnsupportedContext(String context) { + return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context); } public boolean isUsedByOtherApps() { @@ -887,10 +851,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { public String getClassLoaderContext() { return 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 isUnknownOrUnsupportedContext(mClassLoaderContext); + public boolean isUnsupportedClassLoaderContext() { + return isUnsupportedContext(mClassLoaderContext); } public boolean isVariableClassLoaderContext() { 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 5df4509af885..adf4551e79a8 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 @@ -77,25 +77,25 @@ public class PackageDexUsageTests { String fooDataDir = "/data/user/0/com.google.foo/"; mFooBaseUser0 = new TestData(fooPackageName, - fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName); + fooCodeDir + "base.apk", 0, ISA, true, fooPackageName); mFooSplit1User0 = new TestData(fooPackageName, - fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName); + fooCodeDir + "split-1.apk", 0, ISA, true, fooPackageName); mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName, - fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com"); + fooCodeDir + "split-2.apk", 0, ISA, true, "used.by.other.com"); mFooSecondary1User0 = new TestData(fooPackageName, - fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName); + fooDataDir + "sec-1.dex", 0, ISA, false, fooPackageName); mFooSecondary1User1 = new TestData(fooPackageName, - fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName); + fooDataDir + "sec-1.dex", 1, ISA, false, fooPackageName); mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName, - fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com"); + fooDataDir + "sec-2.dex", 0, ISA, false, "used.by.other.com"); mInvalidIsa = new TestData(fooPackageName, - fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER"); + fooCodeDir + "base.apk", 0, "INVALID_ISA", true, "INALID_USER"); String barPackageName = "com.google.bar"; String barCodeDir = "/data/app/com.google.bar/"; @@ -103,11 +103,11 @@ public class PackageDexUsageTests { String barDataDir1 = "/data/user/1/com.google.bar/"; mBarBaseUser0 = new TestData(barPackageName, - barCodeDir + "base.apk", 0, ISA, false, true, barPackageName); + barCodeDir + "base.apk", 0, ISA, true, barPackageName); mBarSecondary1User0 = new TestData(barPackageName, - barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName); + barDataDir + "sec-1.dex", 0, ISA, false, barPackageName); mBarSecondary2User1 = new TestData(barPackageName, - barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName); + barDataDir1 + "sec-2.dex", 1, ISA, false, barPackageName); } @Test @@ -134,7 +134,9 @@ public class PackageDexUsageTests { public void testRecordSplitPrimarySequence() { // Assert new information. assertTrue(record(mFooBaseUser0)); - // Assert no new information. + assertTrue(record(mFooSplit1User0)); + // Assert no new information if we add again + assertFalse(record(mFooBaseUser0)); assertFalse(record(mFooSplit1User0)); assertPackageDexUsage(mFooBaseUser0); @@ -192,7 +194,7 @@ public class PackageDexUsageTests { for (int i = 1; i <= tooManyFiles; i++) { String fooPackageName = "com.google.foo"; TestData testData = new TestData(fooPackageName, - "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false, + "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, fooPackageName); if (i < tooManyFiles) { assertTrue("Adding " + testData.mDexFile, record(testData)); @@ -200,7 +202,11 @@ public class PackageDexUsageTests { } else { assertFalse("Adding " + testData.mDexFile, record(testData)); } - assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries); + assertPackageDexUsage( + mPackageDexUsage, + /* usdeBy=*/ (Set<String>) null, + /* primaryDex= */ null, + expectedSecondaries); } } @@ -276,7 +282,7 @@ public class PackageDexUsageTests { Map<String, Set<String>> packageToCodePaths = new HashMap<>(); packageToCodePaths.put(mBarBaseUser0.mPackageName, new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile))); - mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths); + mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<String>()); // Assert that only user 1 files are there. assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1); @@ -284,6 +290,41 @@ public class PackageDexUsageTests { } @Test + public void testSyncDataKeepPackages() { + PackageDexUsage packageDexUsage = new PackageDexUsage(); + // Write the record we want to keep and which won't be keep by default. + Set<String> fooUsers = new HashSet<>(Arrays.asList( + new String[] {mFooBaseUser0.mPackageName})); + assertTrue(record(packageDexUsage, mFooBaseUser0, fooUsers)); + // Write a record that would be kept by default. + Set<String> barUsers = new HashSet<>(Arrays.asList( + new String[] {"another.package", mFooBaseUser0.mPackageName})); + assertTrue(record(packageDexUsage, mBarBaseUser0, barUsers)); + + // Construct the user packages and their code paths (things that will be + // kept by default during sync). + Map<String, Set<Integer>> packageToUsersMap = new HashMap<>(); + packageToUsersMap.put(mBarBaseUser0.mPackageName, + new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId))); + Map<String, Set<String>> packageToCodePaths = new HashMap<>(); + packageToCodePaths.put(mBarBaseUser0.mPackageName, + new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile))); + + // Sync data. + List<String> keepData = new ArrayList<String>(); + keepData.add(mFooBaseUser0.mPackageName); + packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, keepData); + + // Assert that both packages are kept + assertPackageDexUsage(packageDexUsage, fooUsers, mFooBaseUser0); + // "another.package" should not be in the loading packages after sync. + Set<String> expectedBarUsers = new HashSet<>(Arrays.asList( + new String[] {mFooBaseUser0.mPackageName})); + assertPackageDexUsage(packageDexUsage, expectedBarUsers, + mBarBaseUser0.updateUsedBy(mFooBaseUser0.mPackageName)); + } + + @Test public void testRemovePackage() { // Record Bar secondaries for two different users. assertTrue(record(mBarSecondary1User0)); @@ -345,9 +386,8 @@ public class PackageDexUsageTests { mFooSplit2UsedByOtherApps0.mDexFile, mFooSplit2UsedByOtherApps0.mOwnerUserId, mFooSplit2UsedByOtherApps0.mLoaderIsa, - /*mIsUsedByOtherApps*/false, mFooSplit2UsedByOtherApps0.mPrimaryOrSplit, - mFooSplit2UsedByOtherApps0.mUsedBy); + /*usedBy=*/ null); assertPackageDexUsage(noLongerUsedByOtherApps); } @@ -371,19 +411,19 @@ public class PackageDexUsageTests { assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users)); assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra)); - assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users)); - assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra)); + assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, users)); + assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, usersExtra)); packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers); // Verify that the users were recorded. Set<String> userAll = new HashSet<>(users); userAll.addAll(usersExtra); assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0, - mFooSecondary1User0); + mFooSecondary2UsedByOtherApps0); } @Test - public void testRecordDexFileUsersNotTheOwningPackage() { + public void testRecordDexFileUsersAndTheOwningPackage() { PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage(); Set<String> users = new HashSet<>(Arrays.asList( new String[] {mFooSplit2UsedByOtherApps0.mPackageName})); @@ -393,13 +433,13 @@ public class PackageDexUsageTests { 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); + + Set<String> expectedUsers = new HashSet<>(users); + expectedUsers.addAll(usersExtra); + // Verify that all loading packages were recorded. + assertPackageDexUsage( + packageDexUsageRecordUsers, expectedUsers, mFooSplit2UsedByOtherApps0); } @Test @@ -421,44 +461,97 @@ public class PackageDexUsageTests { } @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 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.isVariableClassLoaderContext()); + @Test + public void testRead() { + 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__2\n" + + "com.google.foo\n" + + "+/data/app/com.google.foo/split-2.apk\n" + + "@used.by.other.com\n" + + "#/data/user/0/com.google.foo/sec-2.dex\n" + + "0,1," + ISA + "\n" + + "@used.by.other.com\n" + + "PCL[/data/user/0/com.google.foo/sec-2.dex]\n" + + "#/data/user/0/com.google.foo/sec-1.dex\n" + + "0,0," + ISA + "\n" + + "@\n" + + "PCL[/data/user/0/com.google.foo/sec-1.dex]\n" + + "com.google.bar\n" + + "+/data/app/com.google.bar/base.apk\n" + + "@com.google.bar\n" + + "#/data/user/0/com.google.bar/sec-1.dex\n" + + "0,0," + ISA + "\n" + + "@\n" + + "PCL[/data/user/0/com.google.bar/sec-1.dex]"; + + 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))); + // Handle the loading package. + packageToUsersMap.put( + mFooSplit2UsedByOtherApps0.mUsedBy, + new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId))); + + // Sync the data. + packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<>()); + + // Assert foo code paths. + assertPackageDexUsage( + packageDexUsage, + /*nonDefaultUsers=*/ null, + mFooSplit2UsedByOtherApps0, + mFooSecondary2UsedByOtherApps0, + mFooSecondary1User0); + + // Assert bar code paths. + assertPackageDexUsage( + packageDexUsage, + /*nonDefaultUsers=*/ null, + mBarBaseUser0, + mBarSecondary1User0); } @Test @@ -483,77 +576,19 @@ public class PackageDexUsageTests { } @Test - public void testReadVersion1() { + public void testEnsureLoadingPackagesCanBeExtended() { 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"; - + String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n" + + "com.google.foo\n" + + "+/data/app/com.google.foo/split-2.apk\n" + + "@\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); + record(packageDexUsage, mFooSplit2UsedByOtherApps0, mFooSplit2UsedByOtherApps0.getUsedBy()); } private void assertPackageDexUsage(TestData primary, TestData... secondaries) { @@ -570,16 +605,18 @@ public class PackageDexUsageTests { String packageName = primary == null ? secondaries.get(0).mPackageName : primary.mPackageName; - boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps; + boolean primaryUsedByOtherApps = primary != null && primary.isUsedByOtherApps(); PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName); // Check package use info assertNotNull(pInfo); if (primary != null) { - assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile)); if (users != null) { assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users); + } else if (pInfo.getLoadingPackages(primary.mDexFile) != null) { + assertEquals(pInfo.getLoadingPackages(primary.mDexFile), primary.getUsedBy()); } + assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile)); } Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap(); @@ -589,13 +626,15 @@ public class PackageDexUsageTests { for (TestData testData : secondaries) { DexUseInfo dInfo = dexUseInfoMap.get(testData.mDexFile); assertNotNull(dInfo); - assertEquals(testData.mUsedByOtherApps, dInfo.isUsedByOtherApps()); + if (users != null) { + assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), users); + } else { + assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), testData.getUsedBy()); + } + assertEquals(testData.isUsedByOtherApps(), dInfo.isUsedByOtherApps()); 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()); } @@ -603,7 +642,7 @@ public class PackageDexUsageTests { private boolean record(TestData testData) { return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile, - testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps, + testData.mOwnerUserId, testData.mLoaderIsa, testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext); } @@ -611,7 +650,7 @@ public class PackageDexUsageTests { boolean result = true; for (String user : users) { result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile, - testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps, + testData.mOwnerUserId, testData.mLoaderIsa, testData.mPrimaryOrSplit, user, testData.mClassLoaderContext); } return result; @@ -640,37 +679,49 @@ public class PackageDexUsageTests { private final String mDexFile; private final int mOwnerUserId; 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 usedBy) { - this(packageName, dexFile, ownerUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit, - usedBy, "DefaultClassLoaderContextFor_" + dexFile); + String loaderIsa, boolean primaryOrSplit, String usedBy) { + this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit, + usedBy, "PCL[" + dexFile + "]"); } private TestData(String packageName, String dexFile, int ownerUserId, - String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy, + String loaderIsa, 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, + return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mPrimaryOrSplit, mUsedBy, newContext); } - private TestData updateUseByOthers(boolean newUsedByOthers) { - return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers, - mPrimaryOrSplit, mUsedBy, mClassLoaderContext); + private TestData updateUsedBy(String newUsedBy) { + return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, + mPrimaryOrSplit, newUsedBy, mClassLoaderContext); + } + + private boolean isUsedByOtherApps() { + return mUsedBy != null && !mPackageName.equals(mUsedBy); + } + + private Set<String> getUsedBy() { + Set<String> users = new HashSet<>(); + if ((mUsedBy != null) && (mPrimaryOrSplit || isUsedByOtherApps())) { + // We do not store the loading package for secondary dex files + // which are not used by others. + users.add(mUsedBy); + } + return users; } } } |