Merge "[PM] Clean up logic for secondary dex oat files"
This commit is contained in:
committed by
Android (Google) Code Review
commit
2b4f8731ca
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]");
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user