Merge changes from topic 'dex'
am: f665781c74
Change-Id: I31c202a56d8f92634cc1d5d84106e27cb8c23bfd
This commit is contained in:
@@ -144,6 +144,7 @@ import libcore.io.DropBox;
|
||||
import libcore.io.EventLogger;
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.net.event.NetworkEventDispatcher;
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
import dalvik.system.CloseGuard;
|
||||
import dalvik.system.VMDebug;
|
||||
import dalvik.system.VMRuntime;
|
||||
@@ -5348,6 +5349,16 @@ public final class ActivityThread {
|
||||
}
|
||||
}
|
||||
|
||||
// If we use profiles, setup the dex reporter to notify package manager
|
||||
// of any relevant dex loads. The idle maintenance job will use the information
|
||||
// reported to optimize the loaded dex files.
|
||||
// Note that we only need one global reporter per app.
|
||||
// Make sure we do this before calling onCreate so that we can capture the
|
||||
// complete application startup.
|
||||
if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
|
||||
BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
|
||||
}
|
||||
|
||||
// Install the Network Security Config Provider. This must happen before the application
|
||||
// code is loaded to prevent issues with instances of TLS objects being created before
|
||||
// the provider is installed.
|
||||
|
||||
168
core/java/android/app/DexLoadReporter.java
Normal file
168
core/java/android/app/DexLoadReporter.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.os.FileUtils;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A dex load reporter which will notify package manager of any dex file loaded
|
||||
* with {@code BaseDexClassLoader}.
|
||||
* The goals are:
|
||||
* 1) discover secondary dex files so that they can be optimized during the
|
||||
* idle maintenance job.
|
||||
* 2) determine whether or not a dex file is used by an app which does not
|
||||
* own it (in order to select the optimal compilation method).
|
||||
* @hide
|
||||
*/
|
||||
/*package*/ class DexLoadReporter implements BaseDexClassLoader.Reporter {
|
||||
private static final String TAG = "DexLoadReporter";
|
||||
|
||||
private static final DexLoadReporter INSTANCE = new DexLoadReporter();
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// We must guard the access to the list of data directories because
|
||||
// we might have concurrent accesses. Apps might load dex files while
|
||||
// new data dirs are registered (due to creation of LoadedApks via
|
||||
// create createApplicationContext).
|
||||
@GuardedBy("mDataDirs")
|
||||
private final Set<String> mDataDirs;
|
||||
|
||||
private DexLoadReporter() {
|
||||
mDataDirs = new HashSet<>();
|
||||
}
|
||||
|
||||
/*package*/ static DexLoadReporter getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an application data directory with the reporter.
|
||||
* The data directories are used to determine if a dex file is secondary dex or not.
|
||||
* Note that this method may be called multiple times for the same app, registering
|
||||
* different data directories. This may happen when apps share the same user id
|
||||
* ({@code android:sharedUserId}). For example, if app1 and app2 share the same user
|
||||
* id, and app1 loads app2 apk, then both data directories will be registered.
|
||||
*/
|
||||
/*package*/ void registerAppDataDir(String packageName, String dataDir) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Package " + packageName + " registering data dir: " + dataDir);
|
||||
}
|
||||
// TODO(calin): A few code paths imply that the data dir
|
||||
// might be null. Investigate when that can happen.
|
||||
if (dataDir != null) {
|
||||
synchronized (mDataDirs) {
|
||||
mDataDirs.add(dataDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(List<String> dexPaths) {
|
||||
if (dexPaths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Notify the package manager about the dex loads unconditionally.
|
||||
// The load might be for either a primary or secondary dex file.
|
||||
notifyPackageManager(dexPaths);
|
||||
// Check for secondary dex files and register them for profiling if
|
||||
// possible.
|
||||
registerSecondaryDexForProfiling(dexPaths);
|
||||
}
|
||||
|
||||
private void notifyPackageManager(List<String> dexPaths) {
|
||||
String packageName = ActivityThread.currentPackageName();
|
||||
try {
|
||||
ActivityThread.getPackageManager().notifyDexLoad(
|
||||
packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerSecondaryDexForProfiling(List<String> dexPaths) {
|
||||
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
|
||||
return;
|
||||
}
|
||||
// Make a copy of the current data directories so that we don't keep the lock
|
||||
// while registering for profiling. The registration will perform I/O to
|
||||
// check for or create the profile.
|
||||
String[] dataDirs;
|
||||
synchronized (mDataDirs) {
|
||||
dataDirs = mDataDirs.toArray(new String[0]);
|
||||
}
|
||||
for (String dexPath : dexPaths) {
|
||||
registerSecondaryDexForProfiling(dexPath, dataDirs);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerSecondaryDexForProfiling(String dexPath, String[] dataDirs) {
|
||||
if (!isSecondaryDexFile(dexPath, dataDirs)) {
|
||||
// The dex path is not a secondary dex file. Nothing to do.
|
||||
return;
|
||||
}
|
||||
File secondaryProfile = getSecondaryProfileFile(dexPath);
|
||||
try {
|
||||
// Create the profile if not already there.
|
||||
// Returns true if the file was created, false if the file already exists.
|
||||
// or throws exceptions in case of errors.
|
||||
boolean created = secondaryProfile.createNewFile();
|
||||
if (DEBUG && created) {
|
||||
Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
|
||||
":" + ex.getMessage());
|
||||
// Don't move forward with the registration if we failed to create the profile.
|
||||
return;
|
||||
}
|
||||
|
||||
VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
|
||||
}
|
||||
|
||||
// A dex file is a secondary dex file if it is in any of the registered app
|
||||
// data directories.
|
||||
private boolean isSecondaryDexFile(String dexPath, String[] dataDirs) {
|
||||
for (String dataDir : dataDirs) {
|
||||
if (FileUtils.contains(dataDir, dexPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Secondary dex profiles are stored next to the dex file and have the same
|
||||
// name with '.prof' appended.
|
||||
// NOTE: Keep in sync with installd.
|
||||
private File getSecondaryProfileFile(String dexPath) {
|
||||
return new File(dexPath + ".prof");
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,6 @@ import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.io.File;
|
||||
@@ -610,39 +609,10 @@ public final class LoadedApk {
|
||||
VMRuntime.registerAppInfo(profileFile.getPath(),
|
||||
codePaths.toArray(new String[codePaths.size()]));
|
||||
|
||||
// Setup the reporter to notify package manager of any relevant dex loads.
|
||||
// At this point the primary apk is loaded and will not be reported.
|
||||
// Anything loaded from now on will be tracked as a potential secondary
|
||||
// or foreign dex file. The goal is to enable:
|
||||
// 1) monitoring and compilation of secondary dex file
|
||||
// 2) track whether or not a dex file is used by other apps (used to
|
||||
// determined the compilation filter of apks).
|
||||
if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
|
||||
// Set the dex load reporter if not already set.
|
||||
// Note that during the app's life cycle different LoadedApks may be
|
||||
// created and loaded (e.g. if two different apps share the same runtime).
|
||||
BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DexLoadReporter implements BaseDexClassLoader.Reporter {
|
||||
private static final DexLoadReporter INSTANCE = new DexLoadReporter();
|
||||
|
||||
private DexLoadReporter() {}
|
||||
|
||||
@Override
|
||||
public void report(List<String> dexPaths) {
|
||||
if (dexPaths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String packageName = ActivityThread.currentPackageName();
|
||||
try {
|
||||
ActivityThread.getPackageManager().notifyDexLoad(
|
||||
packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
|
||||
}
|
||||
}
|
||||
// Register the app data directory with the reporter. It will
|
||||
// help deciding whether or not a dex file is the primary apk or a
|
||||
// secondary dex.
|
||||
DexLoadReporter.getInstance().registerAppDataDir(mPackageName, mDataDir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -428,14 +428,13 @@ public class FileUtils {
|
||||
*/
|
||||
public static boolean contains(File dir, File file) {
|
||||
if (dir == null || file == null) return false;
|
||||
return contains(dir.getAbsolutePath(), file.getAbsolutePath());
|
||||
}
|
||||
|
||||
String dirPath = dir.getAbsolutePath();
|
||||
String filePath = file.getAbsolutePath();
|
||||
|
||||
public static boolean contains(String dirPath, String filePath) {
|
||||
if (dirPath.equals(filePath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!dirPath.endsWith("/")) {
|
||||
dirPath += "/";
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ public class BackgroundDexOptService extends JobService {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
|
||||
|
||||
private static final int JOB_IDLE_OPTIMIZE = 800;
|
||||
private static final int JOB_POST_BOOT_UPDATE = 801;
|
||||
|
||||
@@ -292,8 +290,8 @@ public class BackgroundDexOptService extends JobService {
|
||||
PackageManagerService.REASON_BACKGROUND_DEXOPT,
|
||||
/* force */ false)
|
||||
: pm.performDexOptSecondary(pkg,
|
||||
PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
|
||||
/* force */ true);
|
||||
PackageManagerService.REASON_BACKGROUND_DEXOPT,
|
||||
/* force */ false);
|
||||
if (success) {
|
||||
// Dexopt succeeded, remove package from the list of failing ones.
|
||||
synchronized (failedPackageNames) {
|
||||
|
||||
@@ -7548,6 +7548,11 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
|
||||
}
|
||||
|
||||
public boolean performDexOptSecondary(String packageName, int compileReason,
|
||||
boolean force) {
|
||||
return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile the information we have about the secondary dex files belonging to
|
||||
* {@code packagName} and the actual dex files. For all dex files that were
|
||||
@@ -7770,6 +7775,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
} catch (InstallerException e) {
|
||||
Slog.w(TAG, String.valueOf(e));
|
||||
}
|
||||
mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14499,6 +14505,8 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
}
|
||||
prepareAppDataAfterInstallLIF(newPackage);
|
||||
addedPkg = true;
|
||||
mDexManager.notifyPackageUpdated(newPackage.packageName,
|
||||
newPackage.baseCodePath, newPackage.splitCodePaths);
|
||||
} catch (PackageManagerException e) {
|
||||
res.setError("Package couldn't be installed in " + pkg.codePath, e);
|
||||
}
|
||||
@@ -14646,6 +14654,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
|
||||
updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
|
||||
prepareAppDataAfterInstallLIF(newPackage);
|
||||
|
||||
mDexManager.notifyPackageUpdated(newPackage.packageName,
|
||||
newPackage.baseCodePath, newPackage.splitCodePaths);
|
||||
}
|
||||
} catch (PackageManagerException e) {
|
||||
res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.os.RemoteException;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -179,17 +180,64 @@ public class DexManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyPackageInstalled(PackageInfo info, int userId) {
|
||||
cachePackageCodeLocation(info, userId);
|
||||
/**
|
||||
* Notifies that a new package was installed for {@code userId}.
|
||||
* {@code userId} must not be {@code UserHandle.USER_ALL}.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code userId} is {@code UserHandle.USER_ALL}.
|
||||
*/
|
||||
public void notifyPackageInstalled(PackageInfo pi, int userId) {
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
throw new IllegalArgumentException(
|
||||
"notifyPackageInstalled called with USER_ALL");
|
||||
}
|
||||
cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
|
||||
pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
|
||||
}
|
||||
|
||||
private void cachePackageCodeLocation(PackageInfo info, int userId) {
|
||||
PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
|
||||
if (pcl != null) {
|
||||
pcl.mergeAppDataDirs(info.applicationInfo, userId);
|
||||
} else {
|
||||
mPackageCodeLocationsCache.put(info.packageName,
|
||||
new PackageCodeLocations(info.applicationInfo, userId));
|
||||
/**
|
||||
* Notifies that package {@code packageName} was updated.
|
||||
* This will clear the UsedByOtherApps mark if it exists.
|
||||
*/
|
||||
public void notifyPackageUpdated(String packageName, String baseCodePath,
|
||||
String[] splitCodePaths) {
|
||||
cachePackageCodeLocation(packageName, baseCodePath, splitCodePaths, null, /*userId*/ -1);
|
||||
// In case there was an update, write the package use info to disk async.
|
||||
// Note that we do the writing here and not in PackageDexUsage in order to be
|
||||
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
|
||||
// multiple updates in PackaeDexUsage before writing it).
|
||||
if (mPackageDexUsage.clearUsedByOtherApps(packageName)) {
|
||||
mPackageDexUsage.maybeWriteAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that the user {@code userId} data for package {@code packageName}
|
||||
* was destroyed. This will remove all usage info associated with the package
|
||||
* for the given user.
|
||||
* {@code userId} is allowed to be {@code UserHandle.USER_ALL} in which case
|
||||
* all usage information for the package will be removed.
|
||||
*/
|
||||
public void notifyPackageDataDestroyed(String packageName, int userId) {
|
||||
boolean updated = userId == UserHandle.USER_ALL
|
||||
? mPackageDexUsage.removePackage(packageName)
|
||||
: mPackageDexUsage.removeUserPackage(packageName, userId);
|
||||
// In case there was an update, write the package use info to disk async.
|
||||
// Note that we do the writing here and not in PackageDexUsage in order to be
|
||||
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
|
||||
// multiple updates in PackaeDexUsage before writing it).
|
||||
if (updated) {
|
||||
mPackageDexUsage.maybeWriteAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void cachePackageCodeLocation(String packageName, String baseCodePath,
|
||||
String[] splitCodePaths, String dataDir, int userId) {
|
||||
PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
|
||||
new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
|
||||
pcl.updateCodeLocation(baseCodePath, splitCodePaths);
|
||||
if (dataDir != null) {
|
||||
pcl.mergeAppDataDirs(dataDir, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +250,8 @@ public class DexManager {
|
||||
int userId = entry.getKey();
|
||||
for (PackageInfo pi : packageInfoList) {
|
||||
// Cache the code locations.
|
||||
cachePackageCodeLocation(pi, userId);
|
||||
cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
|
||||
pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
|
||||
|
||||
// Cache a map from package name to the set of user ids who installed the package.
|
||||
// We will use it to sync the data and remove obsolete entries from
|
||||
@@ -225,6 +274,17 @@ public class DexManager {
|
||||
return mPackageDexUsage.getPackageUseInfo(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform dexopt on the package {@code packageName} secondary dex files.
|
||||
* @return true if all secondary dex files were processed successfully (compiled or skipped
|
||||
* because they don't need to be compiled)..
|
||||
*/
|
||||
public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
|
||||
return dexoptSecondaryDex(packageName,
|
||||
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
|
||||
force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform dexopt on the package {@code packageName} secondary dex files.
|
||||
* @return true if all secondary dex files were processed successfully (compiled or skipped
|
||||
@@ -383,7 +443,7 @@ public class DexManager {
|
||||
// Ignore framework code.
|
||||
// TODO(calin): is there a better way to detect it?
|
||||
if (dexPath.startsWith("/system/framework/")) {
|
||||
new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
|
||||
return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
|
||||
}
|
||||
|
||||
// First, check if the package which loads the dex file actually owns it.
|
||||
@@ -425,27 +485,36 @@ public class DexManager {
|
||||
*/
|
||||
private static class PackageCodeLocations {
|
||||
private final String mPackageName;
|
||||
private final String mBaseCodePath;
|
||||
private String mBaseCodePath;
|
||||
private final Set<String> mSplitCodePaths;
|
||||
// Maps user id to the application private directory.
|
||||
private final Map<Integer, Set<String>> mAppDataDirs;
|
||||
|
||||
public PackageCodeLocations(ApplicationInfo ai, int userId) {
|
||||
mPackageName = ai.packageName;
|
||||
mBaseCodePath = ai.sourceDir;
|
||||
this(ai.packageName, ai.sourceDir, ai.splitSourceDirs);
|
||||
mergeAppDataDirs(ai.dataDir, userId);
|
||||
}
|
||||
public PackageCodeLocations(String packageName, String baseCodePath,
|
||||
String[] splitCodePaths) {
|
||||
mPackageName = packageName;
|
||||
mSplitCodePaths = new HashSet<>();
|
||||
if (ai.splitSourceDirs != null) {
|
||||
for (String split : ai.splitSourceDirs) {
|
||||
mAppDataDirs = new HashMap<>();
|
||||
updateCodeLocation(baseCodePath, splitCodePaths);
|
||||
}
|
||||
|
||||
public void updateCodeLocation(String baseCodePath, String[] splitCodePaths) {
|
||||
mBaseCodePath = baseCodePath;
|
||||
mSplitCodePaths.clear();
|
||||
if (splitCodePaths != null) {
|
||||
for (String split : splitCodePaths) {
|
||||
mSplitCodePaths.add(split);
|
||||
}
|
||||
}
|
||||
mAppDataDirs = new HashMap<>();
|
||||
mergeAppDataDirs(ai, userId);
|
||||
}
|
||||
|
||||
public void mergeAppDataDirs(ApplicationInfo ai, int userId) {
|
||||
public void mergeAppDataDirs(String dataDir, int userId) {
|
||||
Set<String> dataDirs = putIfAbsent(mAppDataDirs, userId, new HashSet<>());
|
||||
dataDirs.add(ai.dataDir);
|
||||
dataDirs.add(dataDir);
|
||||
}
|
||||
|
||||
public int searchDex(String dexPath, int userId) {
|
||||
|
||||
@@ -376,8 +376,35 @@ 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) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
|
||||
if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
|
||||
return false;
|
||||
}
|
||||
packageUseInfo.mIsUsedByOtherApps = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the usage data associated with package {@code packageName}.
|
||||
* @return true if the package usage was found and removed successfully.
|
||||
*/
|
||||
public boolean removePackage(String packageName) {
|
||||
synchronized (mPackageUseInfoMap) {
|
||||
return mPackageUseInfoMap.remove(packageName) != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the records about package {@code packageName} belonging to user {@code userId}.
|
||||
* If the package is left with no records of secondary dex usage and is not used by other
|
||||
* apps it will be removed as well.
|
||||
* @return true if the record was found and actually deleted,
|
||||
* false if the record doesn't exist
|
||||
*/
|
||||
@@ -397,6 +424,12 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
// 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) {
|
||||
mPackageUseInfoMap.remove(packageName);
|
||||
updated = true;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package com.android.server.pm.dex;
|
||||
|
||||
import android.os.Build;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -57,9 +58,9 @@ public class DexManagerTests {
|
||||
|
||||
private int mUser0;
|
||||
private int mUser1;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
mUser0 = 0;
|
||||
mUser1 = 1;
|
||||
|
||||
@@ -243,6 +244,122 @@ public class DexManagerTests {
|
||||
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageUpdated() {
|
||||
// Foo loads Bar main apks.
|
||||
notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
|
||||
|
||||
// Bar is used by others now and should be in our records.
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
assertTrue(pui.getDexUseInfoMap().isEmpty());
|
||||
|
||||
// Notify that bar is updated.
|
||||
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
|
||||
mBarUser0.mPackageInfo.applicationInfo.sourceDir,
|
||||
mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
|
||||
|
||||
// The usedByOtherApps flag should be clear now.
|
||||
pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertFalse(pui.isUsedByOtherApps());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageUpdatedCodeLocations() {
|
||||
// Simulate a split update.
|
||||
String newSplit = mBarUser0.replaceLastSplit();
|
||||
List<String> newSplits = new ArrayList<>();
|
||||
newSplits.add(newSplit);
|
||||
|
||||
// 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);
|
||||
|
||||
// Notify that bar is updated. splitSourceDirs will contain the updated path.
|
||||
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
|
||||
mBarUser0.mPackageInfo.applicationInfo.sourceDir,
|
||||
mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
|
||||
|
||||
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
|
||||
notifyDexLoad(mFooUser0, newSplits, mUser0);
|
||||
pui = getPackageUseInfo(mBarUser0);
|
||||
assertNotNull(pui);
|
||||
assertTrue(pui.isUsedByOtherApps());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageDataDestroyForOne() {
|
||||
// Bar loads its own secondary files.
|
||||
notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
|
||||
notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
|
||||
|
||||
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
|
||||
|
||||
// Bar should not be around since it was removed for all users.
|
||||
PackageUseInfo pui = getPackageUseInfo(mBarUser1);
|
||||
assertNotNull(pui);
|
||||
assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
|
||||
/*isUsedByOtherApps*/false, mUser1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageDataDestroyForeignUse() {
|
||||
// Foo loads its own secondary files.
|
||||
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
|
||||
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
|
||||
|
||||
// Bar loads Foo main apks.
|
||||
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
|
||||
|
||||
mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
|
||||
|
||||
// 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());
|
||||
assertTrue(pui.getDexUseInfoMap().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageDataDestroyComplete() {
|
||||
// Foo loads its own secondary files.
|
||||
List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
|
||||
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
|
||||
|
||||
mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyPackageDataDestroyForAll() {
|
||||
// Foo loads its own secondary files.
|
||||
notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
|
||||
notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyFrameworkLoad() {
|
||||
String frameworkDex = "/system/framework/com.android.location.provider.jar";
|
||||
// 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));
|
||||
}
|
||||
|
||||
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
|
||||
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
|
||||
for (String dex : secondaries) {
|
||||
@@ -317,5 +434,12 @@ public class DexManagerTests {
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
String replaceLastSplit() {
|
||||
int length = mPackageInfo.applicationInfo.splitSourceDirs.length;
|
||||
// Add an extra bogus dex extension to simulate a new split name.
|
||||
mPackageInfo.applicationInfo.splitSourceDirs[length - 1] += ".dex";
|
||||
return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +256,30 @@ public class PackageDexUsageTests {
|
||||
assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovePackage() {
|
||||
// Record Bar secondaries for two different users.
|
||||
assertTrue(record(mBarSecondary1User0));
|
||||
assertTrue(record(mBarSecondary2User1));
|
||||
|
||||
// Remove the package.
|
||||
assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
|
||||
// Assert that we can't find the package anymore.
|
||||
assertNull(mPackageDexUsage.getPackageUseInfo(mBarSecondary1User0.mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNonexistentPackage() {
|
||||
// Record Bar secondaries for two different users.
|
||||
assertTrue(record(mBarSecondary1User0));
|
||||
|
||||
// Remove the package.
|
||||
assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
|
||||
// Remove the package again. It should return false because the package no longer
|
||||
// has a record in the use info.
|
||||
assertFalse(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveUserPackage() {
|
||||
// Record Bar secondaries for two different users.
|
||||
@@ -282,6 +306,32 @@ public class PackageDexUsageTests {
|
||||
assertPackageDexUsage(null, mBarSecondary2User1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUsedByOtherApps() {
|
||||
// Write a package which is used by other apps.
|
||||
assertTrue(record(mFooSplit2UsedByOtherApps0));
|
||||
assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
|
||||
|
||||
// Check that the package is no longer used by other apps.
|
||||
TestData noLongerUsedByOtherApps = new TestData(
|
||||
mFooSplit2UsedByOtherApps0.mPackageName,
|
||||
mFooSplit2UsedByOtherApps0.mDexFile,
|
||||
mFooSplit2UsedByOtherApps0.mOwnerUserId,
|
||||
mFooSplit2UsedByOtherApps0.mLoaderIsa,
|
||||
/*mIsUsedByOtherApps*/false,
|
||||
mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
|
||||
assertPackageDexUsage(noLongerUsedByOtherApps);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUsedByOtherAppsNonexistent() {
|
||||
// Write a package which is used by other apps.
|
||||
assertTrue(record(mFooSplit2UsedByOtherApps0));
|
||||
assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
|
||||
// Clearing again should return false as there should be no update on the use info.
|
||||
assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
|
||||
}
|
||||
|
||||
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
|
||||
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
|
||||
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
|
||||
|
||||
Reference in New Issue
Block a user