Merge changes from topic 'info-per-split' into oc-mr1-dev am: df421a8b47
am: fe8ff8215f
Change-Id: I2ae4dd9455f34a10ddfdb368edd62f048b230d99
This commit is contained in:
@@ -318,7 +318,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
|
||||
optimizer.performDexOpt(pkg, libraryDependencies,
|
||||
null /* ISAs */,
|
||||
null /* CompilerStats.PackageStats */,
|
||||
mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
|
||||
mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
|
||||
new DexoptOptions(pkg.packageName, compilationReason,
|
||||
DexoptOptions.DEXOPT_BOOT_COMPLETE));
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.util.Slog;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.pm.Installer.InstallerException;
|
||||
import com.android.server.pm.dex.DexManager;
|
||||
import com.android.server.pm.dex.DexoptOptions;
|
||||
import com.android.server.pm.dex.DexoptUtils;
|
||||
import com.android.server.pm.dex.PackageDexUsage;
|
||||
@@ -112,7 +113,7 @@ public class PackageDexOptimizer {
|
||||
*/
|
||||
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
|
||||
String[] instructionSets, CompilerStats.PackageStats packageStats,
|
||||
boolean isUsedByOtherApps, DexoptOptions options) {
|
||||
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
|
||||
if (!canOptimizePackage(pkg)) {
|
||||
return DEX_OPT_SKIPPED;
|
||||
}
|
||||
@@ -120,7 +121,7 @@ public class PackageDexOptimizer {
|
||||
final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
|
||||
try {
|
||||
return performDexOptLI(pkg, sharedLibraries, instructionSets,
|
||||
packageStats, isUsedByOtherApps, options);
|
||||
packageStats, packageUseInfo, options);
|
||||
} finally {
|
||||
releaseWakeLockLI(acquireTime);
|
||||
}
|
||||
@@ -134,21 +135,13 @@ public class PackageDexOptimizer {
|
||||
@GuardedBy("mInstallLock")
|
||||
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
|
||||
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
|
||||
boolean isUsedByOtherApps, DexoptOptions options) {
|
||||
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
|
||||
final String[] instructionSets = targetInstructionSets != null ?
|
||||
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
|
||||
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
|
||||
final List<String> paths = pkg.getAllCodePaths();
|
||||
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
|
||||
|
||||
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
|
||||
options.getCompilerFilter(), isUsedByOtherApps);
|
||||
final boolean profileUpdated = options.isCheckForProfileUpdates() &&
|
||||
isProfileUpdated(pkg, sharedGid, compilerFilter);
|
||||
|
||||
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
|
||||
final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
|
||||
|
||||
// Get the class loader context dependencies.
|
||||
// For each code path in the package, this array contains the class loader context that
|
||||
// needs to be passed to dexopt in order to ensure correct optimizations.
|
||||
@@ -172,6 +165,17 @@ public class PackageDexOptimizer {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
|
||||
|| packageUseInfo.isUsedByOtherApps(path);
|
||||
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
|
||||
options.getCompilerFilter(), isUsedByOtherApps);
|
||||
final boolean profileUpdated = options.isCheckForProfileUpdates() &&
|
||||
isProfileUpdated(pkg, sharedGid, compilerFilter);
|
||||
|
||||
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
|
||||
// flags.
|
||||
final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
|
||||
|
||||
for (String dexCodeIsa : dexCodeInstructionSets) {
|
||||
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
|
||||
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
|
||||
|
||||
@@ -9887,17 +9887,19 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
|
||||
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
|
||||
if (!deps.isEmpty()) {
|
||||
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
|
||||
options.getCompilerFilter(), options.getSplitName(),
|
||||
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
|
||||
for (PackageParser.Package depPackage : deps) {
|
||||
// TODO: Analyze and investigate if we (should) profile libraries.
|
||||
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
|
||||
getOrCreateCompilerPackageStats(depPackage),
|
||||
true /* isUsedByOtherApps */,
|
||||
options);
|
||||
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
|
||||
}
|
||||
}
|
||||
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
|
||||
getOrCreateCompilerPackageStats(p),
|
||||
mDexManager.isUsedByOtherApps(p.packageName), options);
|
||||
mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -10023,7 +10025,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
public void shutdown() {
|
||||
mPackageUsage.writeNow(mPackages);
|
||||
mCompilerStats.writeNow();
|
||||
mDexManager.savePackageDexUsageNow();
|
||||
mDexManager.writePackageDexUsageNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -18587,7 +18589,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
|
||||
null /* instructionSets */,
|
||||
getOrCreateCompilerPackageStats(pkg),
|
||||
mDexManager.isUsedByOtherApps(pkg.packageName),
|
||||
mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
|
||||
dexoptOptions);
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
@@ -25424,8 +25426,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
if (ps == null) {
|
||||
continue;
|
||||
}
|
||||
PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
|
||||
pkg.packageName);
|
||||
PackageDexUsage.PackageUseInfo packageUseInfo =
|
||||
getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
|
||||
if (PackageManagerServiceUtils
|
||||
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
|
||||
downgradeTimeThresholdMillis, packageUseInfo,
|
||||
|
||||
@@ -25,7 +25,6 @@ import static com.android.server.pm.PackageManagerService.TAG;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppGlobals;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
@@ -137,9 +136,11 @@ public class PackageManagerServiceUtils {
|
||||
sortTemp, packageManagerService);
|
||||
|
||||
// Give priority to apps used by other apps.
|
||||
DexManager dexManager = packageManagerService.getDexManager();
|
||||
applyPackageFilter((pkg) ->
|
||||
packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
|
||||
remainingPkgs, sortTemp, packageManagerService);
|
||||
dexManager.getPackageUseInfoOrDefault(pkg.packageName)
|
||||
.isAnyCodePathUsedByOtherApps(),
|
||||
result, remainingPkgs, sortTemp, packageManagerService);
|
||||
|
||||
// Filter out packages that aren't recently used, add all remaining apps.
|
||||
// TODO: add a property to control this?
|
||||
@@ -209,12 +210,10 @@ public class PackageManagerServiceUtils {
|
||||
|
||||
// If the app was active in background during the threshold period and was used
|
||||
// by other packages.
|
||||
// If packageUseInfo is null, it can be said that the package was not used by other
|
||||
// packages.
|
||||
boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
|
||||
- latestPackageUseTimeInMillis)
|
||||
< thresholdTimeinMillis)
|
||||
&& (packageUseInfo != null && packageUseInfo.isUsedByOtherApps());
|
||||
&& packageUseInfo.isAnyCodePathUsedByOtherApps();
|
||||
|
||||
return !isActiveInBackgroundAndUsedByOtherPackages;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ import com.android.server.pm.PackageManagerServiceCompilerMapping;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -81,6 +83,19 @@ 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(IPackageManager pms, PackageDexOptimizer pdo,
|
||||
Installer installer, Object installLock) {
|
||||
mPackageCodeLocationsCache = new HashMap<>();
|
||||
@@ -297,6 +312,8 @@ public class DexManager {
|
||||
|
||||
private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
|
||||
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
|
||||
Map<String, Set<String>> packageToCodePaths = new HashMap<>();
|
||||
|
||||
// Cache the code locations for the installed packages. This allows for
|
||||
// faster lookups (no locks) when finding what package owns the dex file.
|
||||
for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) {
|
||||
@@ -306,25 +323,53 @@ public class DexManager {
|
||||
// Cache the code locations.
|
||||
cachePackageInfo(pi, userId);
|
||||
|
||||
// Cache a map from package name to the set of user ids who installed the package.
|
||||
// Cache two maps:
|
||||
// - from package name to the set of user ids who installed the package.
|
||||
// - from package name to the set of code paths.
|
||||
// We will use it to sync the data and remove obsolete entries from
|
||||
// mPackageDexUsage.
|
||||
Set<Integer> users = putIfAbsent(
|
||||
packageToUsersMap, pi.packageName, new HashSet<>());
|
||||
users.add(userId);
|
||||
|
||||
Set<String> codePaths = putIfAbsent(
|
||||
packageToCodePaths, pi.packageName, new HashSet<>());
|
||||
codePaths.add(pi.applicationInfo.sourceDir);
|
||||
if (pi.applicationInfo.splitSourceDirs != null) {
|
||||
Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mPackageDexUsage.read();
|
||||
mPackageDexUsage.syncData(packageToUsersMap);
|
||||
mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package dex usage for the given package name.
|
||||
* @return the package data or null if there is no data available for this package.
|
||||
* If there is no usage info the method will return a default {@code PackageUseInfo} with
|
||||
* no data about secondary dex files and marked as not being used by other apps.
|
||||
*
|
||||
* Note that no use info means the package was not used or it was used but not by other apps.
|
||||
* Also, note that right now we might prune packages which are not used by other apps.
|
||||
* TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
|
||||
* to access the package use.
|
||||
*/
|
||||
public PackageUseInfo getPackageUseInfo(String packageName) {
|
||||
return mPackageDexUsage.getPackageUseInfo(packageName);
|
||||
public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
|
||||
PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
|
||||
return useInfo == null ? DEFAULT_USE_INFO : useInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the manager has usage information on the give package.
|
||||
*
|
||||
* Note that no use info means the package was not used or it was used but not by other apps.
|
||||
* Also, note that right now we might prune packages which are not used by other apps.
|
||||
* TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
|
||||
* to access the package use.
|
||||
*/
|
||||
/*package*/ boolean hasInfoOnPackage(String packageName) {
|
||||
return mPackageDexUsage.getPackageUseInfo(packageName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +388,7 @@ public class DexManager {
|
||||
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
|
||||
: mPackageDexOptimizer;
|
||||
String packageName = options.getPackageName();
|
||||
PackageUseInfo useInfo = getPackageUseInfo(packageName);
|
||||
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
|
||||
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "No secondary dex use for package:" + packageName);
|
||||
@@ -387,7 +432,7 @@ public class DexManager {
|
||||
* deleted, update the internal records and delete any generated oat files.
|
||||
*/
|
||||
public void reconcileSecondaryDexFiles(String packageName) {
|
||||
PackageUseInfo useInfo = getPackageUseInfo(packageName);
|
||||
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
|
||||
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "No secondary dex use for package:" + packageName);
|
||||
@@ -518,23 +563,6 @@ public class DexManager {
|
||||
return mPackageDexUsage.getAllPackagesWithSecondaryDexFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the profiling data collected for the given app indicate
|
||||
* that the apps's APK has been loaded by another app.
|
||||
* Note that this returns false for all apps without any collected profiling data.
|
||||
*/
|
||||
public boolean isUsedByOtherApps(String packageName) {
|
||||
PackageUseInfo useInfo = getPackageUseInfo(packageName);
|
||||
if (useInfo == null) {
|
||||
// No use info, means the package was not used or it was used but not by other apps.
|
||||
// Note that right now we might prune packages which are not used by other apps.
|
||||
// TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
|
||||
// to access the package use.
|
||||
return false;
|
||||
}
|
||||
return useInfo.isUsedByOtherApps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the package which owns the given dexPath.
|
||||
*/
|
||||
@@ -593,9 +621,9 @@ public class DexManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the in-memory package dex usage to disk right away.
|
||||
* Writes the in-memory package dex usage to disk right away.
|
||||
*/
|
||||
public void savePackageDexUsageNow() {
|
||||
public void writePackageDexUsageNow() {
|
||||
mPackageDexUsage.writeNow();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@ public final class DexoptOptions {
|
||||
// save disk space.
|
||||
public static final int DEXOPT_DOWNGRADE = 1 << 5;
|
||||
|
||||
// When set, dexopt will compile the dex file as a shared library even if it is not actually
|
||||
// used by other apps. This is used to force the compilation or shared libraries declared
|
||||
// with in the manifest with ''uses-library' before we have a chance to detect they are
|
||||
// actually shared at runtime.
|
||||
public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6;
|
||||
|
||||
// The name of package to optimize.
|
||||
private final String mPackageName;
|
||||
|
||||
@@ -79,7 +85,8 @@ public final class DexoptOptions {
|
||||
DEXOPT_BOOT_COMPLETE |
|
||||
DEXOPT_ONLY_SECONDARY_DEX |
|
||||
DEXOPT_ONLY_SHARED_DEX |
|
||||
DEXOPT_DOWNGRADE;
|
||||
DEXOPT_DOWNGRADE |
|
||||
DEXOPT_AS_SHARED_LIBRARY;
|
||||
if ((flags & (~validityMask)) != 0) {
|
||||
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
|
||||
}
|
||||
@@ -122,7 +129,15 @@ public final class DexoptOptions {
|
||||
return (mFlags & DEXOPT_DOWNGRADE) != 0;
|
||||
}
|
||||
|
||||
public boolean isDexoptAsSharedLibrary() {
|
||||
return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
|
||||
}
|
||||
|
||||
public String getSplitName() {
|
||||
return mSplitName;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
@@ -53,17 +54,21 @@ import libcore.util.Objects;
|
||||
public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
private final static String TAG = "PackageDexUsage";
|
||||
|
||||
// The last version update: add class loader contexts for secondary dex files.
|
||||
private final static int PACKAGE_DEX_USAGE_VERSION = 3;
|
||||
// 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.
|
||||
// Version 2 added:
|
||||
// - the list of packages that load the dex files
|
||||
// - class loader contexts for secondary dex files
|
||||
// - usage for all code paths (including splits)
|
||||
private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
|
||||
|
||||
private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
|
||||
|
||||
private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
|
||||
"PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
|
||||
|
||||
private final static String SPLIT_CHAR = ",";
|
||||
private final static String CODE_PATH_LINE_CHAR = "+";
|
||||
private final static String DEX_LINE_CHAR = "#";
|
||||
private final static String LOADING_PACKAGE_CHAR = "@";
|
||||
|
||||
@@ -130,9 +135,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
// If we have a primary or a split apk, set isUsedByOtherApps.
|
||||
// We do not need to record the loaderIsa or the owner because we compile
|
||||
// primaries for all users and all ISAs.
|
||||
packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
|
||||
maybeAddLoadingPackage(owningPackageName, loadingPackageName,
|
||||
packageUseInfo.mLoadingPackages);
|
||||
packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
|
||||
owningPackageName, loadingPackageName);
|
||||
} else {
|
||||
// For secondary dex files record the loaderISA and the owner. We'll need
|
||||
// to know under which user to compile and for what ISA.
|
||||
@@ -149,9 +153,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
if (primaryOrSplit) {
|
||||
// We have a possible update on the primary apk usage. Merge
|
||||
// isUsedByOtherApps information and return if there was an update.
|
||||
boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
|
||||
loadingPackageName, packageUseInfo.mLoadingPackages);
|
||||
return packageUseInfo.merge(isUsedByOtherApps) || updateLoadingPackages;
|
||||
return packageUseInfo.mergeCodePathUsedByOtherApps(
|
||||
dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
|
||||
} else {
|
||||
DexUseInfo newData = new DexUseInfo(
|
||||
isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
|
||||
@@ -230,22 +233,18 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
*
|
||||
* file_magic_version
|
||||
* package_name_1
|
||||
* +code_path1
|
||||
* @ loading_package_1_1, loading_package_1_2...
|
||||
* +code_path2
|
||||
* @ loading_package_2_1, loading_package_2_2...
|
||||
* #dex_file_path_1_1
|
||||
* @ loading_package_1_1_1, loading_package_1_1_2...
|
||||
* user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
|
||||
* @ loading_package_1_1_1, loading_package_1_1_2...
|
||||
* class_loader_context_1_1
|
||||
* #dex_file_path_1_2
|
||||
* @ loading_package_1_2_1, loading_package_1_2_2...
|
||||
* user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
|
||||
* ...
|
||||
* package_name_2
|
||||
* @ loading_package_2_1, loading_package_2_1_2...
|
||||
* #dex_file_path_2_1
|
||||
* @ loading_package_2_1_1, loading_package_2_1_2...
|
||||
* user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
|
||||
* #dex_file_path_2_2,
|
||||
* @ loading_package_2_2_1, loading_package_2_2_2...
|
||||
* user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
|
||||
* @ loading_package_1_2_1, loading_package_1_2_2...
|
||||
* class_loader_context_1_2
|
||||
* ...
|
||||
*/
|
||||
/* package */ void write(Writer out) {
|
||||
@@ -262,27 +261,31 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
// Write the package line.
|
||||
String packageName = pEntry.getKey();
|
||||
PackageUseInfo packageUseInfo = pEntry.getValue();
|
||||
fpw.println(packageName);
|
||||
|
||||
fpw.println(String.join(SPLIT_CHAR, packageName,
|
||||
writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
|
||||
fpw.println(LOADING_PACKAGE_CHAR +
|
||||
String.join(SPLIT_CHAR, packageUseInfo.mLoadingPackages));
|
||||
// Write the code paths used by other apps.
|
||||
for (Map.Entry<String, Set<String>> codeEntry :
|
||||
packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
|
||||
String codePath = codeEntry.getKey();
|
||||
Set<String> loadingPackages = codeEntry.getValue();
|
||||
fpw.println(CODE_PATH_LINE_CHAR + codePath);
|
||||
fpw.println(LOADING_PACKAGE_CHAR + String.join(SPLIT_CHAR, loadingPackages));
|
||||
}
|
||||
|
||||
// Write dex file lines.
|
||||
for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
|
||||
String dexPath = dEntry.getKey();
|
||||
DexUseInfo dexUseInfo = dEntry.getValue();
|
||||
fpw.println(DEX_LINE_CHAR + dexPath);
|
||||
fpw.println(LOADING_PACKAGE_CHAR +
|
||||
String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
|
||||
fpw.println(dexUseInfo.getClassLoaderContext());
|
||||
|
||||
fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
|
||||
writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
|
||||
writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
|
||||
for (String isa : dexUseInfo.mLoaderIsas) {
|
||||
fpw.print(SPLIT_CHAR + isa);
|
||||
}
|
||||
fpw.println();
|
||||
fpw.println(LOADING_PACKAGE_CHAR
|
||||
+ String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
|
||||
fpw.println(dexUseInfo.getClassLoaderContext());
|
||||
}
|
||||
}
|
||||
fpw.flush();
|
||||
@@ -324,7 +327,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
String s;
|
||||
String line;
|
||||
String currentPackage = null;
|
||||
PackageUseInfo currentPackageData = null;
|
||||
|
||||
@@ -332,8 +335,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
for (String abi : Build.SUPPORTED_ABIS) {
|
||||
supportedIsas.add(VMRuntime.getInstructionSet(abi));
|
||||
}
|
||||
while ((s = in.readLine()) != null) {
|
||||
if (s.startsWith(DEX_LINE_CHAR)) {
|
||||
while ((line = in.readLine()) != null) {
|
||||
if (line.startsWith(DEX_LINE_CHAR)) {
|
||||
// This is the start of the the dex lines.
|
||||
// We expect 4 lines for each dex entry:
|
||||
// #dexPaths
|
||||
@@ -345,25 +348,23 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
"Malformed PackageDexUsage file. Expected package line before dex line.");
|
||||
}
|
||||
|
||||
// First line is the dex path.
|
||||
String dexPath = s.substring(DEX_LINE_CHAR.length());
|
||||
// Line 1 is the dex path.
|
||||
String dexPath = line.substring(DEX_LINE_CHAR.length());
|
||||
|
||||
// In version 2 the second line contains the list of packages that loaded the file.
|
||||
List<String> loadingPackages = maybeReadLoadingPackages(in, version);
|
||||
// In version 3 the third line contains the class loader context.
|
||||
String classLoaderContext = maybeReadClassLoaderContext(in, version);
|
||||
|
||||
// Next line is the dex data.
|
||||
s = in.readLine();
|
||||
if (s == null) {
|
||||
// Line 2 is the dex data: (userId, isUsedByOtherApps, isa).
|
||||
line = in.readLine();
|
||||
if (line == null) {
|
||||
throw new IllegalStateException("Could not find dexUseInfo line");
|
||||
}
|
||||
|
||||
// We expect at least 3 elements (isUsedByOtherApps, userId, isa).
|
||||
String[] elems = s.split(SPLIT_CHAR);
|
||||
String[] elems = line.split(SPLIT_CHAR);
|
||||
if (elems.length < 3) {
|
||||
throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
|
||||
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);
|
||||
|
||||
int ownerUserId = Integer.parseInt(elems[0]);
|
||||
boolean isUsedByOtherApps = readBoolean(elems[1]);
|
||||
DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
|
||||
@@ -386,17 +387,35 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
continue;
|
||||
}
|
||||
currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
|
||||
} else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
|
||||
// This is a code path used by other apps line.
|
||||
if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
|
||||
throw new IllegalArgumentException("Unexpected code path line when parsing " +
|
||||
"PackageDexUseData: " + line);
|
||||
}
|
||||
|
||||
// Expects 2 lines:
|
||||
// +code_paths
|
||||
// @loading_packages
|
||||
String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
|
||||
Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
|
||||
currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
|
||||
} else {
|
||||
// This is a package line.
|
||||
// We expect it to be: `packageName,isUsedByOtherApps`.
|
||||
String[] elems = s.split(SPLIT_CHAR);
|
||||
if (elems.length != 2) {
|
||||
throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
|
||||
if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
|
||||
currentPackage = line;
|
||||
currentPackageData = new PackageUseInfo();
|
||||
} else {
|
||||
// Old version (<2)
|
||||
// We expect it to be: `packageName,isUsedByOtherApps`.
|
||||
String[] elems = line.split(SPLIT_CHAR);
|
||||
if (elems.length != 2) {
|
||||
throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
|
||||
}
|
||||
currentPackage = elems[0];
|
||||
currentPackageData = new PackageUseInfo();
|
||||
currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
|
||||
}
|
||||
currentPackage = elems[0];
|
||||
currentPackageData = new PackageUseInfo();
|
||||
currentPackageData.mIsUsedByOtherApps = readBoolean(elems[1]);
|
||||
currentPackageData.mLoadingPackages.addAll(maybeReadLoadingPackages(in, version));
|
||||
data.put(currentPackage, currentPackageData);
|
||||
}
|
||||
}
|
||||
@@ -413,7 +432,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
*/
|
||||
private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
|
||||
String context = null;
|
||||
if (version == PACKAGE_DEX_USAGE_VERSION) {
|
||||
if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
|
||||
context = in.readLine();
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("Could not find the classLoaderContext line.");
|
||||
@@ -429,7 +448,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
* Reads the list of loading packages from the buffer {@code in} if
|
||||
* {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
|
||||
*/
|
||||
private List<String> maybeReadLoadingPackages(BufferedReader in, int version)
|
||||
private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
|
||||
throws IOException {
|
||||
if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
|
||||
String line = in.readLine();
|
||||
@@ -438,13 +457,15 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
}
|
||||
// We expect that most of the times the list of loading packages will be empty.
|
||||
if (line.length() == LOADING_PACKAGE_CHAR.length()) {
|
||||
return Collections.emptyList();
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
return Arrays.asList(
|
||||
Set<String> result = new HashSet<>();
|
||||
Collections.addAll(result,
|
||||
line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,14 +479,15 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
}
|
||||
|
||||
private boolean isSupportedVersion(int version) {
|
||||
return version == PACKAGE_DEX_USAGE_VERSION ||
|
||||
version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1;
|
||||
return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
|
||||
|| version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs the existing data with the set of available packages by removing obsolete entries.
|
||||
*/
|
||||
public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
|
||||
/*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
|
||||
Map<String, Set<String>> packageToCodePaths) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
Iterator<Map.Entry<String, PackageUseInfo>> pIt =
|
||||
mPackageUseInfoMap.entrySet().iterator();
|
||||
@@ -489,8 +511,26 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
dIt.remove();
|
||||
}
|
||||
}
|
||||
if (!packageUseInfo.mIsUsedByOtherApps
|
||||
&& packageUseInfo.mDexUseInfoMap.isEmpty()) {
|
||||
|
||||
// Sync the code paths.
|
||||
Set<String> codePaths = packageToCodePaths.get(packageName);
|
||||
Iterator<Map.Entry<String, Set<String>>> codeIt =
|
||||
packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
|
||||
while (codeIt.hasNext()) {
|
||||
if (!codePaths.contains(codeIt.next().getKey())) {
|
||||
codeIt.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// In case the package was marked as used by other apps in a previous version
|
||||
// propagate the flag to all the code paths.
|
||||
// See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
|
||||
if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
|
||||
for (String codePath : codePaths) {
|
||||
packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
|
||||
}
|
||||
} else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
|
||||
&& packageUseInfo.mDexUseInfoMap.isEmpty()) {
|
||||
// The package is not used by other apps and we removed all its dex files
|
||||
// records. Remove the entire package record as well.
|
||||
pIt.remove();
|
||||
@@ -504,14 +544,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
* Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
|
||||
* @return true if the package usage info was updated.
|
||||
*/
|
||||
public boolean clearUsedByOtherApps(String packageName) {
|
||||
/*package*/ boolean clearUsedByOtherApps(String packageName) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
|
||||
if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
|
||||
if (packageUseInfo == null) {
|
||||
return false;
|
||||
}
|
||||
packageUseInfo.mIsUsedByOtherApps = false;
|
||||
return true;
|
||||
return packageUseInfo.clearCodePathUsedByOtherApps();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +571,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
* @return true if the record was found and actually deleted,
|
||||
* false if the record doesn't exist
|
||||
*/
|
||||
public boolean removeUserPackage(String packageName, int userId) {
|
||||
/*package*/ boolean removeUserPackage(String packageName, int userId) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
|
||||
if (packageUseInfo == null) {
|
||||
@@ -550,7 +589,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
}
|
||||
// If no secondary dex info is left and the package is not used by other apps
|
||||
// remove the data since it is now useless.
|
||||
if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
|
||||
if (packageUseInfo.mDexUseInfoMap.isEmpty()
|
||||
&& !packageUseInfo.isAnyCodePathUsedByOtherApps()) {
|
||||
mPackageUseInfoMap.remove(packageName);
|
||||
updated = true;
|
||||
}
|
||||
@@ -564,7 +604,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
* @return true if the record was found and actually deleted,
|
||||
* false if the record doesn't exist
|
||||
*/
|
||||
public boolean removeDexFile(String packageName, String dexFile, int userId) {
|
||||
/*package*/ boolean removeDexFile(String packageName, String dexFile, int userId) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
|
||||
if (packageUseInfo == null) {
|
||||
@@ -586,7 +626,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public PackageUseInfo getPackageUseInfo(String packageName) {
|
||||
/*package*/ PackageUseInfo getPackageUseInfo(String packageName) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
|
||||
// The useInfo contains a map for secondary dex files which could be modified
|
||||
@@ -601,7 +641,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
/**
|
||||
* Return all packages that contain records of secondary dex files.
|
||||
*/
|
||||
public Set<String> getAllPackagesWithSecondaryDexFiles() {
|
||||
/*package*/ Set<String> getAllPackagesWithSecondaryDexFiles() {
|
||||
Set<String> packages = new HashSet<>();
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
|
||||
@@ -639,15 +679,6 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
throw new IllegalArgumentException("Unknown bool encoding: " + bool);
|
||||
}
|
||||
|
||||
private boolean contains(int[] array, int elem) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (elem == array[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String dump() {
|
||||
StringWriter sw = new StringWriter();
|
||||
write(sw);
|
||||
@@ -658,46 +689,94 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
* Stores data on how a package and its dex files are used.
|
||||
*/
|
||||
public static class PackageUseInfo {
|
||||
// This flag is for the primary and split apks. It is set to true whenever one of them
|
||||
// is loaded by another app.
|
||||
private boolean mIsUsedByOtherApps;
|
||||
// The app's code paths that are used by other apps.
|
||||
// The key is the code path and the value is the set of loading packages.
|
||||
private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
|
||||
// Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
|
||||
private final Map<String, DexUseInfo> mDexUseInfoMap;
|
||||
// Packages who load this dex file.
|
||||
private final Set<String> mLoadingPackages;
|
||||
|
||||
// Keeps track of whether or not this package was used by other apps before
|
||||
// we upgraded to VERSION 4 which records the info for each code path separately.
|
||||
// This is unwanted complexity but without it we risk to profile guide compile
|
||||
// something that supposed to be shared. For example:
|
||||
// 1) we determine that chrome is used by another app
|
||||
// 2) we take an OTA which upgrades the way we keep track of usage data
|
||||
// 3) chrome doesn't get used until the background job executes
|
||||
// 4) as part of the backgound job we now think that chrome is not used by others
|
||||
// and we speed-profile.
|
||||
// 5) as a result the next time someone uses chrome it will extract from apk since
|
||||
// the compiled code will be private.
|
||||
private boolean mUsedByOtherAppsBeforeUpgrade;
|
||||
|
||||
public PackageUseInfo() {
|
||||
mIsUsedByOtherApps = false;
|
||||
mCodePathsUsedByOtherApps = new HashMap<>();
|
||||
mDexUseInfoMap = new HashMap<>();
|
||||
mLoadingPackages = new HashSet<>();
|
||||
}
|
||||
|
||||
// Creates a deep copy of the `other`.
|
||||
public PackageUseInfo(PackageUseInfo other) {
|
||||
mIsUsedByOtherApps = other.mIsUsedByOtherApps;
|
||||
mCodePathsUsedByOtherApps = new HashMap<>();
|
||||
for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
|
||||
mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
|
||||
}
|
||||
|
||||
mDexUseInfoMap = new HashMap<>();
|
||||
for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
|
||||
mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
|
||||
}
|
||||
mLoadingPackages = new HashSet<>(other.mLoadingPackages);
|
||||
}
|
||||
|
||||
private boolean merge(boolean isUsedByOtherApps) {
|
||||
boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
|
||||
mIsUsedByOtherApps = mIsUsedByOtherApps || isUsedByOtherApps;
|
||||
return oldIsUsedByOtherApps != this.mIsUsedByOtherApps;
|
||||
private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
|
||||
String owningPackageName, String loadingPackage) {
|
||||
if (!isUsedByOtherApps) {
|
||||
// Nothing to update if the the code path is not used by other apps.
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean newCodePath = false;
|
||||
Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
|
||||
if (loadingPackages == null) {
|
||||
loadingPackages = new HashSet<>();
|
||||
mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
|
||||
newCodePath = true;
|
||||
}
|
||||
boolean newLoadingPackage = loadingPackage != null
|
||||
&& !loadingPackage.equals(owningPackageName)
|
||||
&& loadingPackages.add(loadingPackage);
|
||||
return newCodePath || newLoadingPackage;
|
||||
}
|
||||
|
||||
public boolean isUsedByOtherApps() {
|
||||
return mIsUsedByOtherApps;
|
||||
public boolean isUsedByOtherApps(String codePath) {
|
||||
return mCodePathsUsedByOtherApps.containsKey(codePath);
|
||||
}
|
||||
|
||||
public Map<String, DexUseInfo> getDexUseInfoMap() {
|
||||
return mDexUseInfoMap;
|
||||
}
|
||||
|
||||
public Set<String> getLoadingPackages() {
|
||||
return mLoadingPackages;
|
||||
public Set<String> getLoadingPackages(String codePath) {
|
||||
return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
|
||||
}
|
||||
|
||||
public boolean isAnyCodePathUsedByOtherApps() {
|
||||
return !mCodePathsUsedByOtherApps.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the usedByOtherApps markers from all code paths.
|
||||
* Returns whether or not there was an update.
|
||||
*/
|
||||
/*package*/ boolean clearCodePathUsedByOtherApps() {
|
||||
// Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
|
||||
// the new data. This is not saved to disk so we don't need to return it.
|
||||
mUsedByOtherAppsBeforeUpgrade = true;
|
||||
|
||||
if (mCodePathsUsedByOtherApps.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
mCodePathsUsedByOtherApps.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
|
||||
|
||||
// Package is not used by others, so we should get nothing back.
|
||||
assertNull(getPackageUseInfo(mFooUser0));
|
||||
assertNoUseInfo(mFooUser0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,8 +116,7 @@ public class DexManagerTests {
|
||||
|
||||
// Bar is used by others now and should be in our records
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, true);
|
||||
assertTrue(pui.getDexUseInfoMap().isEmpty());
|
||||
}
|
||||
|
||||
@@ -128,8 +127,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mFooUser0, pui, false);
|
||||
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
}
|
||||
@@ -141,8 +139,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mFooUser0, barSecondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, false);
|
||||
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
|
||||
}
|
||||
@@ -165,8 +162,7 @@ public class DexManagerTests {
|
||||
|
||||
// Check bar usage. Should be used by other app (for primary and barSecondaries).
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, true);
|
||||
assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
|
||||
pui.getDexUseInfoMap().size());
|
||||
|
||||
@@ -176,8 +172,7 @@ public class DexManagerTests {
|
||||
|
||||
// Check foo usage. Should not be used by other app.
|
||||
pui = getPackageUseInfo(mFooUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mFooUser0, pui, false);
|
||||
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
}
|
||||
@@ -185,22 +180,22 @@ public class DexManagerTests {
|
||||
@Test
|
||||
public void testPackageUseInfoNotFound() {
|
||||
// Assert we don't get back data we did not previously record.
|
||||
assertNull(getPackageUseInfo(mFooUser0));
|
||||
assertNoUseInfo(mFooUser0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidIsa() {
|
||||
// Notifying with an invalid ISA should be ignored.
|
||||
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
|
||||
assertNull(getPackageUseInfo(mInvalidIsa));
|
||||
assertNoUseInfo(mInvalidIsa);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotExistingPackate() {
|
||||
public void testNotExistingPackage() {
|
||||
// Notifying about the load of a package which was previously not
|
||||
// register in DexManager#load should be ignored.
|
||||
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
|
||||
assertNull(getPackageUseInfo(mDoesNotExist));
|
||||
assertNoUseInfo(mDoesNotExist);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -208,7 +203,7 @@ public class DexManagerTests {
|
||||
// Bar from User1 tries to load secondary dex files from User0 Bar.
|
||||
// Request should be ignored.
|
||||
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
|
||||
assertNull(getPackageUseInfo(mBarUser1));
|
||||
assertNoUseInfo(mBarUser1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -217,7 +212,7 @@ public class DexManagerTests {
|
||||
// Note that the PackageManagerService already filters this out but we
|
||||
// still check that nothing goes unexpected in DexManager.
|
||||
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
|
||||
assertNull(getPackageUseInfo(mBarUser1));
|
||||
assertNoUseInfo(mBarUser1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -229,7 +224,7 @@ public class DexManagerTests {
|
||||
// Before we notify about the installation of the newPackage if mFoo
|
||||
// is trying to load something from it we should not find it.
|
||||
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
|
||||
assertNull(getPackageUseInfo(newPackage));
|
||||
assertNoUseInfo(newPackage);
|
||||
|
||||
// Notify about newPackage install and let mFoo load its dexes.
|
||||
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
|
||||
@@ -237,8 +232,7 @@ public class DexManagerTests {
|
||||
|
||||
// We should get back the right info.
|
||||
PackageUseInfo pui = getPackageUseInfo(newPackage);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(newPackage, pui, false);
|
||||
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
|
||||
}
|
||||
@@ -254,8 +248,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(newPackage, newSecondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(newPackage);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(newPackage, pui, false);
|
||||
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
}
|
||||
@@ -267,8 +260,7 @@ public class DexManagerTests {
|
||||
|
||||
// Bar is used by others now and should be in our records.
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, true);
|
||||
assertTrue(pui.getDexUseInfoMap().isEmpty());
|
||||
|
||||
// Notify that bar is updated.
|
||||
@@ -278,8 +270,7 @@ public class DexManagerTests {
|
||||
|
||||
// The usedByOtherApps flag should be clear now.
|
||||
pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -291,8 +282,7 @@ public class DexManagerTests {
|
||||
|
||||
// We shouldn't find yet the new split as we didn't notify the package update.
|
||||
notifyDexLoad(mFooUser0, newSplits, mUser0);
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNull(pui);
|
||||
assertNoUseInfo(mBarUser0);
|
||||
|
||||
// Notify that bar is updated. splitSourceDirs will contain the updated path.
|
||||
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
|
||||
@@ -301,9 +291,9 @@ public class DexManagerTests {
|
||||
|
||||
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
|
||||
notifyDexLoad(mFooUser0, newSplits, mUser0);
|
||||
pui = getPackageUseInfo(mBarUser0);
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(newSplits, pui, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -335,8 +325,7 @@ public class DexManagerTests {
|
||||
// Foo should still be around since it's used by other apps but with no
|
||||
// secondary dex info.
|
||||
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mFooUser0, pui, true);
|
||||
assertTrue(pui.getDexUseInfoMap().isEmpty());
|
||||
}
|
||||
|
||||
@@ -350,8 +339,7 @@ public class DexManagerTests {
|
||||
|
||||
// Foo should not be around since all its secondary dex info were deleted
|
||||
// and it is not used by other apps.
|
||||
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||
assertNull(pui);
|
||||
assertNoUseInfo(mFooUser0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -363,8 +351,7 @@ public class DexManagerTests {
|
||||
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
|
||||
|
||||
// Bar should not be around since it was removed for all users.
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNull(pui);
|
||||
assertNoUseInfo(mBarUser0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -373,7 +360,7 @@ public class DexManagerTests {
|
||||
// Load a dex file from framework.
|
||||
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
|
||||
// The dex file should not be recognized as a package.
|
||||
assertNull(mDexManager.getPackageUseInfo(frameworkDex));
|
||||
assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,8 +370,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mFooUser0, pui, false);
|
||||
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
}
|
||||
@@ -395,8 +381,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
|
||||
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
|
||||
// We expect that all the contexts are unsupported.
|
||||
String[] expectedContexts =
|
||||
@@ -413,8 +398,7 @@ public class DexManagerTests {
|
||||
|
||||
notifyDexLoad(mBarUser0, secondaries, mUser0);
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, false);
|
||||
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
|
||||
assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
|
||||
@@ -422,8 +406,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
|
||||
|
||||
pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0, pui, false);
|
||||
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
|
||||
// We expect that all the contexts to be changed to variable now.
|
||||
String[] expectedContexts =
|
||||
@@ -439,8 +422,7 @@ public class DexManagerTests {
|
||||
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
|
||||
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
|
||||
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
|
||||
// We expect that all the contexts are unsupported.
|
||||
String[] expectedContexts =
|
||||
@@ -484,6 +466,17 @@ public class DexManagerTests {
|
||||
expectedContexts);
|
||||
}
|
||||
|
||||
private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
|
||||
boolean isUsedByOtherApps) {
|
||||
assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
|
||||
}
|
||||
|
||||
private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
|
||||
boolean isUsedByOtherApps) {
|
||||
for (String codePath : codePaths) {
|
||||
assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
|
||||
}
|
||||
}
|
||||
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
|
||||
// By default, assume a single class loader in the chain.
|
||||
// This makes writing tests much easier.
|
||||
@@ -499,7 +492,12 @@ public class DexManagerTests {
|
||||
}
|
||||
|
||||
private PackageUseInfo getPackageUseInfo(TestData testData) {
|
||||
return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
|
||||
assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
|
||||
return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
|
||||
}
|
||||
|
||||
private void assertNoUseInfo(TestData testData) {
|
||||
assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
|
||||
}
|
||||
|
||||
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
|
||||
|
||||
@@ -62,7 +62,8 @@ public class DexoptOptionsTests {
|
||||
DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
|
||||
DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
|
||||
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
|
||||
DexoptOptions.DEXOPT_DOWNGRADE;
|
||||
DexoptOptions.DEXOPT_DOWNGRADE |
|
||||
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY;
|
||||
|
||||
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
|
||||
assertEquals(mPackageName, opt.getPackageName());
|
||||
@@ -74,6 +75,7 @@ public class DexoptOptionsTests {
|
||||
assertTrue(opt.isDexoptOnlySharedDex());
|
||||
assertTrue(opt.isDowngrade());
|
||||
assertTrue(opt.isForce());
|
||||
assertTrue(opt.isDexoptAsSharedLibrary());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,7 +91,7 @@ public class DexoptOptionsTests {
|
||||
PackageManagerService.REASON_INSTALL,
|
||||
PackageManagerService.REASON_BACKGROUND_DEXOPT,
|
||||
PackageManagerService.REASON_AB_OTA,
|
||||
PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE};
|
||||
PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,};
|
||||
|
||||
for (int reason : reasons) {
|
||||
DexoptOptions opt = new DexoptOptions(mPackageName, reason, flags);
|
||||
@@ -102,6 +104,7 @@ public class DexoptOptionsTests {
|
||||
assertFalse(opt.isDexoptOnlySharedDex());
|
||||
assertFalse(opt.isDowngrade());
|
||||
assertTrue(opt.isForce());
|
||||
assertFalse(opt.isDexoptAsSharedLibrary());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +122,7 @@ public class DexoptOptionsTests {
|
||||
assertFalse(opt.isDexoptOnlySharedDex());
|
||||
assertFalse(opt.isDowngrade());
|
||||
assertTrue(opt.isForce());
|
||||
assertFalse(opt.isDexoptAsSharedLibrary());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -249,7 +250,10 @@ public class PackageDexUsageTests {
|
||||
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
|
||||
packageToUsersMap.put(mBarSecondary2User1.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
|
||||
mPackageDexUsage.syncData(packageToUsersMap);
|
||||
Map<String, Set<String>> packageToCodePaths = new HashMap<>();
|
||||
packageToCodePaths.put(mBarBaseUser0.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
|
||||
mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
|
||||
|
||||
// Assert that only user 1 files are there.
|
||||
assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
|
||||
@@ -341,8 +345,8 @@ public class PackageDexUsageTests {
|
||||
Set<String> usersExtra = new HashSet<>(Arrays.asList(
|
||||
new String[] {"another.package.2", "another.package.3"}));
|
||||
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
|
||||
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
|
||||
@@ -351,7 +355,7 @@ public class PackageDexUsageTests {
|
||||
// Verify that the users were recorded.
|
||||
Set<String> userAll = new HashSet<>(users);
|
||||
userAll.addAll(usersExtra);
|
||||
assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooBaseUser0,
|
||||
assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
|
||||
mFooSecondary1User0);
|
||||
}
|
||||
|
||||
@@ -359,19 +363,19 @@ public class PackageDexUsageTests {
|
||||
public void testRecordDexFileUsersNotTheOwningPackage() {
|
||||
PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
|
||||
Set<String> users = new HashSet<>(Arrays.asList(
|
||||
new String[] {mFooBaseUser0.mPackageName}));
|
||||
new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
|
||||
Set<String> usersExtra = new HashSet<>(Arrays.asList(
|
||||
new String[] {"another.package.2", "another.package.3"}));
|
||||
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
|
||||
assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
|
||||
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, mFooBaseUser0,
|
||||
assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
|
||||
mFooSecondary1User0);
|
||||
}
|
||||
|
||||
@@ -428,7 +432,6 @@ public class PackageDexUsageTests {
|
||||
assertPackageDexUsage(null, mFooSecondary1User0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDexUsageClassLoaderContext() {
|
||||
final boolean isUsedByOtherApps = false;
|
||||
@@ -458,6 +461,80 @@ public class PackageDexUsageTests {
|
||||
assertFalse(unknownContext.isVariableClassLoaderContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadVersion1() {
|
||||
String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
|
||||
// Equivalent to
|
||||
// record(mFooSplit2UsedByOtherApps0);
|
||||
// record(mFooSecondary1User0);
|
||||
// record(mFooSecondary2UsedByOtherApps0);
|
||||
// record(mBarBaseUser0);
|
||||
// record(mBarSecondary1User0);
|
||||
String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
|
||||
+ "com.google.foo,1\n"
|
||||
+ "#/data/user/0/com.google.foo/sec-1.dex\n"
|
||||
+ "0,0," + isa + "\n"
|
||||
+ "#/data/user/0/com.google.foo/sec-2.dex\n"
|
||||
+ "0,1," + isa + "\n"
|
||||
+ "com.google.bar,0\n"
|
||||
+ "#/data/user/0/com.google.bar/sec-1.dex\n"
|
||||
+ "0,0," + isa + "\n";
|
||||
|
||||
PackageDexUsage packageDexUsage = new PackageDexUsage();
|
||||
try {
|
||||
packageDexUsage.read(new StringReader(content));
|
||||
} catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
|
||||
// After the read we must sync the data to fill the missing information on the code paths.
|
||||
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
|
||||
Map<String, Set<String>> packageToCodePaths = new HashMap<>();
|
||||
|
||||
// Handle foo package.
|
||||
packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
|
||||
packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
|
||||
mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
|
||||
// Handle bar package.
|
||||
packageToUsersMap.put(mBarBaseUser0.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
|
||||
packageToCodePaths.put(mBarBaseUser0.mPackageName,
|
||||
new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
|
||||
|
||||
// Sync the data.
|
||||
packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
|
||||
|
||||
// Update the class loaders to unknown before asserting if needed. Before version 2 we
|
||||
// didn't have any.
|
||||
String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
|
||||
TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
|
||||
TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
|
||||
TestData fooSplit2UsedByOtherApps0 =
|
||||
mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
|
||||
TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
|
||||
TestData fooSecondary2UsedByOtherApps0 =
|
||||
mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
|
||||
TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
|
||||
TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
|
||||
|
||||
// Assert foo code paths. Note that we ignore the users during upgrade.
|
||||
final Set<String> ignoredUsers = null;
|
||||
assertPackageDexUsage(packageDexUsage, ignoredUsers,
|
||||
fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
|
||||
// Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
|
||||
// share the same data.
|
||||
assertPackageDexUsage(packageDexUsage, ignoredUsers,
|
||||
fooSplit1User0.updateUseByOthers(true),
|
||||
fooSecondary1User0, fooSecondary2UsedByOtherApps0);
|
||||
assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
|
||||
fooSecondary1User0, fooSecondary2UsedByOtherApps0);
|
||||
|
||||
// Assert bar code paths. Note that we ignore the users during upgrade.
|
||||
assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
|
||||
}
|
||||
|
||||
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
|
||||
assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
|
||||
}
|
||||
@@ -470,9 +547,11 @@ public class PackageDexUsageTests {
|
||||
|
||||
// Check package use info
|
||||
assertNotNull(pInfo);
|
||||
assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
|
||||
if (users != null) {
|
||||
assertEquals(pInfo.getLoadingPackages(), users);
|
||||
if (primary != null) {
|
||||
assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
|
||||
if (users != null) {
|
||||
assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
|
||||
@@ -560,5 +639,10 @@ public class PackageDexUsageTests {
|
||||
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
|
||||
mPrimaryOrSplit, mUsedBy, newContext);
|
||||
}
|
||||
|
||||
private TestData updateUseByOthers(boolean newUsedByOthers) {
|
||||
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
|
||||
mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user