[PM] Clean up logic for secondary dex oat files
am: e69fba3f7f
Change-Id: I223b27221acf25e628003dde0f63cffe66779753
This commit is contained in:
@@ -509,6 +509,13 @@ interface IPackageManager {
|
|||||||
|
|
||||||
void forceDexOpt(String packageName);
|
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
|
* Update status of external media on the package manager to scan and
|
||||||
* install packages installed on the external media. Like say the
|
* install packages installed on the external media. Like say the
|
||||||
|
|||||||
@@ -433,6 +433,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertValidInstructionSet(String instructionSet)
|
private static void assertValidInstructionSet(String instructionSet)
|
||||||
throws InstallerException {
|
throws InstallerException {
|
||||||
for (String abi : Build.SUPPORTED_ABIS) {
|
for (String abi : Build.SUPPORTED_ABIS) {
|
||||||
|
|||||||
@@ -2133,7 +2133,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
|||||||
mInstaller = installer;
|
mInstaller = installer;
|
||||||
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
|
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
|
||||||
"*dexopt*");
|
"*dexopt*");
|
||||||
mDexManager = new DexManager(this, mPackageDexOptimizer);
|
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
|
||||||
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
|
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
|
||||||
|
|
||||||
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
|
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
|
||||||
@@ -7549,6 +7549,16 @@ public class PackageManagerService extends IPackageManager.Stub {
|
|||||||
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
|
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
|
||||||
if (p.usesLibraries != null || p.usesOptionalLibraries != null) {
|
if (p.usesLibraries != null || p.usesOptionalLibraries != null) {
|
||||||
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
|
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
return runInstallWrite();
|
return runInstallWrite();
|
||||||
case "compile":
|
case "compile":
|
||||||
return runCompile();
|
return runCompile();
|
||||||
|
case "reconcile-secondary-dex-files":
|
||||||
|
return runreconcileSecondaryDexFiles();
|
||||||
case "dump-profiles":
|
case "dump-profiles":
|
||||||
return runDumpProfiles();
|
return runDumpProfiles();
|
||||||
case "list":
|
case "list":
|
||||||
@@ -423,6 +425,12 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int runreconcileSecondaryDexFiles() throws RemoteException {
|
||||||
|
String packageName = getNextArg();
|
||||||
|
mInterface.reconcileSecondaryDexFiles(packageName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private int runDumpProfiles() throws RemoteException {
|
private int runDumpProfiles() throws RemoteException {
|
||||||
String packageName = getNextArg();
|
String packageName = getNextArg();
|
||||||
mInterface.dumpProfiles(packageName);
|
mInterface.dumpProfiles(packageName);
|
||||||
@@ -1475,6 +1483,8 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
pw.println(" -3: filter to only show third party packages");
|
pw.println(" -3: filter to only show third party packages");
|
||||||
pw.println(" -i: see the installer for the packages");
|
pw.println(" -i: see the installer for the packages");
|
||||||
pw.println(" -u: also include uninstalled packages");
|
pw.println(" -u: also include uninstalled packages");
|
||||||
|
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(" list permission-groups");
|
||||||
pw.println(" Prints all known permission groups.");
|
pw.println(" Prints all known permission groups.");
|
||||||
pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");
|
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.PackageInfo;
|
||||||
import android.content.pm.PackageParser;
|
import android.content.pm.PackageParser;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
|
|
||||||
import android.util.Slog;
|
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.PackageDexOptimizer;
|
||||||
import com.android.server.pm.PackageManagerServiceUtils;
|
import com.android.server.pm.PackageManagerServiceUtils;
|
||||||
import com.android.server.pm.PackageManagerServiceCompilerMapping;
|
import com.android.server.pm.PackageManagerServiceCompilerMapping;
|
||||||
@@ -62,6 +67,9 @@ public class DexManager {
|
|||||||
|
|
||||||
private final IPackageManager mPackageManager;
|
private final IPackageManager mPackageManager;
|
||||||
private final PackageDexOptimizer mPackageDexOptimizer;
|
private final PackageDexOptimizer mPackageDexOptimizer;
|
||||||
|
private final Object mInstallLock;
|
||||||
|
@GuardedBy("mInstallLock")
|
||||||
|
private final Installer mInstaller;
|
||||||
|
|
||||||
// Possible outcomes of a dex search.
|
// Possible outcomes of a dex search.
|
||||||
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
|
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_SPLIT = 2; // dex file is a split apk
|
||||||
private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
|
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<>();
|
mPackageCodeLocationsCache = new HashMap<>();
|
||||||
mPackageDexUsage = new PackageDexUsage();
|
mPackageDexUsage = new PackageDexUsage();
|
||||||
mPackageManager = pms;
|
mPackageManager = pms;
|
||||||
mPackageDexOptimizer = pdo;
|
mPackageDexOptimizer = pdo;
|
||||||
|
mInstaller = installer;
|
||||||
|
mInstallLock = installLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,7 +266,7 @@ public class DexManager {
|
|||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
|
Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
|
||||||
+ " for user " + dexUseInfo.getOwnerUserId());
|
+ " for user " + dexUseInfo.getOwnerUserId());
|
||||||
// Skip over it, another user might still have the package.
|
mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
|
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
|
||||||
@@ -265,6 +276,73 @@ public class DexManager {
|
|||||||
return success;
|
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.
|
* 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) {
|
public PackageUseInfo getPackageUseInfo(String packageName) {
|
||||||
synchronized (mPackageUseInfoMap) {
|
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);
|
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
|
||||||
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
|
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.
|
// Foo and Bar are available to user0.
|
||||||
// Only Bar is available to user1;
|
// Only Bar is available to user1;
|
||||||
|
|||||||
@@ -256,6 +256,32 @@ public class PackageDexUsageTests {
|
|||||||
assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
|
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) {
|
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
|
||||||
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
|
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
|
||||||
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
|
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
|
||||||
|
|||||||
Reference in New Issue
Block a user