From adbadd5577d2b1291d10146b6ffb5577cf236528 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 28 Mar 2017 18:19:15 -0700 Subject: [PATCH] Keep track of protected data dirs in DexManager Apps may store data in any of their protected dirs (deviceProtectedDataDir or credentialProtectedDataDir). DexManager used to keep track of only the default data directory, which could be any of them. The CL adds support for all protected dirs. Test: runtest -x services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java check that all of gmscore modules get recorded and compiled with adb shell cmd package compile --secondary com.android.google.gms Bug: 32871170 Change-Id: Id98904ce9e9fc8bb060b01c6fbb9ccce8f7f5328 --- .../server/pm/PackageDexOptimizer.java | 7 ++- .../com/android/server/pm/dex/DexManager.java | 52 +++++++++++++------ .../server/pm/dex/DexManagerTests.java | 22 ++++++++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index d9ea7284616d2..75e89953c88a7 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.os.Environment; +import android.os.FileUtils; import android.os.PowerManager; import android.os.UserHandle; import android.os.WorkSource; @@ -256,9 +257,11 @@ public class PackageDexOptimizer { String compilerFilter, boolean isUsedByOtherApps) { int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX; // Check the app storage and add the appropriate flags. - if (info.dataDir.equals(info.deviceProtectedDataDir)) { + if (info.deviceProtectedDataDir != null && + FileUtils.contains(info.deviceProtectedDataDir, path)) { dexoptFlags |= DEXOPT_STORAGE_DE; - } else if (info.dataDir.equals(info.credentialProtectedDataDir)) { + } else if (info.credentialProtectedDataDir != null && + FileUtils.contains(info.credentialProtectedDataDir, path)) { dexoptFlags |= DEXOPT_STORAGE_CE; } else { Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index c693a475ed591..3d7cedce522af 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -19,7 +19,7 @@ package com.android.server.pm.dex; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.PackageParser; +import android.os.FileUtils; import android.os.RemoteException; import android.os.storage.StorageManager; import android.os.UserHandle; @@ -93,7 +93,7 @@ public class DexManager { * Note that this method is invoked when apps load dex files and it should * return as fast as possible. * - * @param loadingPackage the package performing the load + * @param loadingAppInfo the package performing the load * @param dexPaths the list of dex files being loaded * @param loaderIsa the ISA of the app loading the dex files * @param loaderUserId the user id which runs the code loading the dex files @@ -191,8 +191,7 @@ public class DexManager { throw new IllegalArgumentException( "notifyPackageInstalled called with USER_ALL"); } - cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir, - pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId); + cachePackageInfo(pi, userId); } /** @@ -231,13 +230,32 @@ public class DexManager { } } - public void cachePackageCodeLocation(String packageName, String baseCodePath, - String[] splitCodePaths, String dataDir, int userId) { + /** + * Caches the code location from the given package info. + */ + private void cachePackageInfo(PackageInfo pi, int userId) { + ApplicationInfo ai = pi.applicationInfo; + String[] dataDirs = new String[] {ai.dataDir, ai.deviceProtectedDataDir, + ai.credentialProtectedDataDir}; + cachePackageCodeLocation(pi.packageName, ai.sourceDir, ai.splitSourceDirs, + dataDirs, userId); + } + + private void cachePackageCodeLocation(String packageName, String baseCodePath, + String[] splitCodePaths, String[] dataDirs, int userId) { PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName, new PackageCodeLocations(packageName, baseCodePath, splitCodePaths)); pcl.updateCodeLocation(baseCodePath, splitCodePaths); - if (dataDir != null) { - pcl.mergeAppDataDirs(dataDir, userId); + if (dataDirs != null) { + for (String dataDir : dataDirs) { + // The set of data dirs includes deviceProtectedDataDir and + // credentialProtectedDataDir which might be null for shared + // libraries. Currently we don't track these but be lenient + // and check in case we ever decide to store their usage data. + if (dataDir != null) { + pcl.mergeAppDataDirs(dataDir, userId); + } + } } } @@ -250,8 +268,7 @@ public class DexManager { int userId = entry.getKey(); for (PackageInfo pi : packageInfoList) { // Cache the code locations. - cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir, - pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId); + cachePackageInfo(pi, 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 @@ -329,6 +346,7 @@ public class DexManager { mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId()); continue; } + int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath, dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps()); success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED); @@ -350,7 +368,7 @@ public class DexManager { // Nothing to reconcile. return; } - Set dexFilesToRemove = new HashSet<>(); + boolean updated = false; for (Map.Entry entry : useInfo.getDexUseInfoMap().entrySet()) { String dexPath = entry.getKey(); @@ -378,14 +396,16 @@ public class DexManager { } ApplicationInfo info = pkg.applicationInfo; int flags = 0; - if (info.dataDir.equals(info.deviceProtectedDataDir)) { + if (info.deviceProtectedDataDir != null && + FileUtils.contains(info.deviceProtectedDataDir, dexPath)) { flags |= StorageManager.FLAG_STORAGE_DE; - } else if (info.dataDir.equals(info.credentialProtectedDataDir)) { + } else if (info.credentialProtectedDataDir!= null && + FileUtils.contains(info.credentialProtectedDataDir, dexPath)) { flags |= StorageManager.FLAG_STORAGE_CE; } else { - Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); - updated = mPackageDexUsage.removeUserPackage( - packageName, dexUseInfo.getOwnerUserId()) || updated; + Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath); + updated = mPackageDexUsage.removeDexFile( + packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; continue; } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 72fb78e89ea21..afc0f67fe9930 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -360,6 +360,19 @@ public class DexManagerTests { assertNull(mDexManager.getPackageUseInfo(frameworkDex)); } + @Test + public void testNotifySecondaryFromProtected() { + // Foo loads its own secondary files. + List fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs(); + notifyDexLoad(mFooUser0, fooSecondaries, mUser0); + + PackageUseInfo pui = getPackageUseInfo(mFooUser0); + assertNotNull(pui); + assertFalse(pui.isUsedByOtherApps()); + assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size()); + assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0); + } + private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List secondaries, boolean isUsedByOtherApps, int ownerUserId) { for (String dex : secondaries) { @@ -394,6 +407,8 @@ public class DexManagerTests { ai.setBaseCodePath(codeDir + "/base.dex"); ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"}); ai.dataDir = "/data/user/" + userId + "/" + packageName; + ai.deviceProtectedDataDir = "/data/user_de/" + userId + "/" + packageName; + ai.credentialProtectedDataDir = "/data/user_ce/" + userId + "/" + packageName; ai.packageName = packageName; return ai; } @@ -426,6 +441,13 @@ public class DexManagerTests { return paths; } + List getSecondaryDexPathsFromProtectedDirs() { + List paths = new ArrayList<>(); + paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex"); + paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex"); + return paths; + } + List getBaseAndSplitDexPaths() { List paths = new ArrayList<>(); paths.add(mPackageInfo.applicationInfo.sourceDir);