Merge "[PM] Clean up logic for secondary dex oat files"

This commit is contained in:
Calin Juravle
2017-01-30 20:00:12 +00:00
committed by Android (Google) Code Review
8 changed files with 205 additions and 5 deletions

View File

@@ -517,6 +517,13 @@ interface IPackageManager {
void forceDexOpt(String packageName);
/**
* 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
* deleted, update the internal records and delete the generated oat files.
*/
void reconcileSecondaryDexFiles(String packageName);
/**
* Update status of external media on the package manager to scan and
* install packages installed on the external media. Like say the

View File

@@ -442,6 +442,20 @@ public class Installer extends SystemService {
}
}
public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
for (int i = 0; i < isas.length; i++) {
assertValidInstructionSet(isas[i]);
}
if (!checkBeforeRemote()) return false;
try {
return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas,
volumeUuid, flags);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
public void invalidateMounts() throws InstallerException {
if (!checkBeforeRemote()) return;
try {

View File

@@ -2227,7 +2227,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(this, mPackageDexOptimizer);
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -8235,6 +8235,16 @@ public class PackageManagerService extends IPackageManager.Stub {
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, 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
* deleted, update the internal records and delete the generated oat files.
*/
@Override
public void reconcileSecondaryDexFiles(String packageName) {
mDexManager.reconcileSecondaryDexFiles(packageName);
}
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
if (p.usesLibraries != null || p.usesOptionalLibraries != null
|| p.usesStaticLibraries != null) {

View File

@@ -118,6 +118,8 @@ class PackageManagerShellCommand extends ShellCommand {
return runInstallWrite();
case "compile":
return runCompile();
case "reconcile-secondary-dex-files":
return runreconcileSecondaryDexFiles();
case "dump-profiles":
return runDumpProfiles();
case "list":
@@ -441,6 +443,12 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
private int runreconcileSecondaryDexFiles() throws RemoteException {
String packageName = getNextArg();
mInterface.reconcileSecondaryDexFiles(packageName);
return 0;
}
private int runDumpProfiles() throws RemoteException {
String packageName = getNextArg();
mInterface.dumpProfiles(packageName);
@@ -1547,6 +1555,8 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -u: also include uninstalled packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE");
pw.println(" Reconciles the package secondary dex files with the generated oat files.");
pw.println(" list permission-groups");
pw.println(" Prints all known permission groups.");
pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");

View File

@@ -21,8 +21,13 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.os.RemoteException;
import android.os.storage.StorageManager;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageDexOptimizer;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
@@ -62,6 +67,9 @@ public class DexManager {
private final IPackageManager mPackageManager;
private final PackageDexOptimizer mPackageDexOptimizer;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
@@ -69,11 +77,14 @@ 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
public DexManager(IPackageManager pms, PackageDexOptimizer pdo) {
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
Installer installer, Object installLock) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
mInstaller = installer;
mInstallLock = installLock;
}
/**
@@ -255,7 +266,7 @@ public class DexManager {
if (pkg == null) {
Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
+ " for user " + dexUseInfo.getOwnerUserId());
// Skip over it, another user might still have the package.
mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
continue;
}
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
@@ -265,6 +276,73 @@ public class DexManager {
return success;
}
/**
* 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
* deleted, update the internal records and delete any generated oat files.
*/
public void reconcileSecondaryDexFiles(String packageName) {
PackageUseInfo useInfo = getPackageUseInfo(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
// Nothing to reconcile.
return;
}
Set<String> dexFilesToRemove = new HashSet<>();
for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
String dexPath = entry.getKey();
DexUseInfo dexUseInfo = entry.getValue();
PackageInfo pkg = null;
try {
// Note that we look for the package in the PackageManager just to be able
// to get back the real app uid and its storage kind. These are only used
// to perform extra validation in installd.
// TODO(calin): maybe a bit overkill.
pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
dexUseInfo.getOwnerUserId());
} catch (RemoteException ignore) {
// Can't happen, DexManager is local.
}
if (pkg == null) {
// It may be that the package was uninstalled while we process the secondary
// dex files.
Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
+ " for user " + dexUseInfo.getOwnerUserId());
// Update the usage and continue, another user might still have the package.
mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
continue;
}
ApplicationInfo info = pkg.applicationInfo;
int flags = 0;
if (info.dataDir.equals(info.deviceProtectedDataDir)) {
flags |= StorageManager.FLAG_STORAGE_DE;
} else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
flags |= StorageManager.FLAG_STORAGE_CE;
} else {
Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
continue;
}
boolean dexStillExists = true;
synchronized(mInstallLock) {
try {
String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
} catch (InstallerException e) {
Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
" : " + e.getMessage());
}
}
if (!dexStillExists) {
mPackageDexUsage.removeDexFile(packageName, dexPath, dexUseInfo.getOwnerUserId());
}
}
}
/**
* Retrieves the package which owns the given dexPath.
*/

View File

@@ -376,9 +376,64 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
}
}
/**
* Remove all the records about package {@code packageName} belonging to user {@code userId}.
*/
public boolean removeUserPackage(String packageName, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
return false;
}
boolean updated = false;
Iterator<Map.Entry<String, DexUseInfo>> dIt =
packageUseInfo.mDexUseInfoMap.entrySet().iterator();
while (dIt.hasNext()) {
DexUseInfo dexUseInfo = dIt.next().getValue();
if (dexUseInfo.mOwnerUserId == userId) {
dIt.remove();
updated = true;
}
}
return updated;
}
}
/**
* Remove the secondary dex file record belonging to the package {@code packageName}
* and user {@code userId}.
*/
public boolean removeDexFile(String packageName, String dexFile, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
return false;
}
return removeDexFile(packageUseInfo, dexFile, userId);
}
}
private boolean removeDexFile(PackageUseInfo packageUseInfo, String dexFile, int userId) {
DexUseInfo dexUseInfo = packageUseInfo.mDexUseInfoMap.get(dexFile);
if (dexUseInfo == null) {
return false;
}
if (dexUseInfo.mOwnerUserId == userId) {
packageUseInfo.mDexUseInfoMap.remove(dexFile);
return true;
}
return false;
}
public PackageUseInfo getPackageUseInfo(String packageName) {
synchronized (mPackageUseInfoMap) {
return mPackageUseInfoMap.get(packageName);
PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
// The useInfo contains a map for secondary dex files which could be modified
// concurrently after this method returns and thus outside the locking we do here.
// (i.e. the map is updated when new class loaders are created, which can happen anytime
// after this method returns)
// Make a defensive copy to be sure we don't get concurrent modifications.
return useInfo == null ? null : new PackageUseInfo(useInfo);
}
}

View File

@@ -73,7 +73,7 @@ public class DexManagerTests {
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
mDexManager = new DexManager(null, null);
mDexManager = new DexManager(null, null, null, null);
// Foo and Bar are available to user0.
// Only Bar is available to user1;

View File

@@ -256,6 +256,32 @@ public class PackageDexUsageTests {
assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
}
@Test
public void testRemoveUserPackage() {
// Record Bar secondaries for two different users.
assertTrue(record(mBarSecondary1User0));
assertTrue(record(mBarSecondary2User1));
// Remove user 0 files.
assertTrue(mPackageDexUsage.removeUserPackage(mBarSecondary1User0.mPackageName,
mBarSecondary1User0.mOwnerUserId));
// Assert that only user 1 files are there.
assertPackageDexUsage(null, mBarSecondary2User1);
}
@Test
public void testRemoveDexFile() {
// Record Bar secondaries for two different users.
assertTrue(record(mBarSecondary1User0));
assertTrue(record(mBarSecondary2User1));
// Remove mBarSecondary1User0 file.
assertTrue(mPackageDexUsage.removeDexFile(mBarSecondary1User0.mPackageName,
mBarSecondary1User0.mDexFile, mBarSecondary1User0.mOwnerUserId));
// Assert that only user 1 files are there.
assertPackageDexUsage(null, mBarSecondary2User1);
}
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;