Loading services/core/java/com/android/server/pm/PackageManagerService.java +6 −0 Original line number Diff line number Diff line Loading @@ -8719,6 +8719,7 @@ public class PackageManagerService extends IPackageManager.Stub { } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } Loading Loading @@ -16134,6 +16135,8 @@ public class PackageManagerService extends IPackageManager.Stub { setInstantAppForUser(ps, user.getIdentifier(), instantApp, fullApp); 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); } Loading Loading @@ -16283,6 +16286,9 @@ public class PackageManagerService extends IPackageManager.Stub { updateSettingsLI(newPackage, installerPackageName, allUsers, res, user, installReason); prepareAppDataAfterInstallLIF(newPackage); mDexManager.notifyPackageUpdated(newPackage.packageName, newPackage.baseCodePath, newPackage.splitCodePaths); } } catch (PackageManagerException e) { res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR); services/core/java/com/android/server/pm/dex/DexManager.java +77 −19 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } Loading @@ -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 Loading Loading @@ -425,27 +474,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) { Loading services/core/java/com/android/server/pm/dex/PackageDexUsage.java +33 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; } } Loading services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +117 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -57,6 +58,7 @@ public class DexManagerTests { private int mUser0; private int mUser1; @Before public void setup() { Loading Loading @@ -243,6 +245,113 @@ 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); } private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) { for (String dex : secondaries) { Loading Loading @@ -317,5 +426,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]; } } } services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +50 −0 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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; Loading Loading
services/core/java/com/android/server/pm/PackageManagerService.java +6 −0 Original line number Diff line number Diff line Loading @@ -8719,6 +8719,7 @@ public class PackageManagerService extends IPackageManager.Stub { } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } Loading Loading @@ -16134,6 +16135,8 @@ public class PackageManagerService extends IPackageManager.Stub { setInstantAppForUser(ps, user.getIdentifier(), instantApp, fullApp); 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); } Loading Loading @@ -16283,6 +16286,9 @@ public class PackageManagerService extends IPackageManager.Stub { updateSettingsLI(newPackage, installerPackageName, allUsers, res, user, installReason); prepareAppDataAfterInstallLIF(newPackage); mDexManager.notifyPackageUpdated(newPackage.packageName, newPackage.baseCodePath, newPackage.splitCodePaths); } } catch (PackageManagerException e) { res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
services/core/java/com/android/server/pm/dex/DexManager.java +77 −19 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } Loading @@ -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 Loading Loading @@ -425,27 +474,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) { Loading
services/core/java/com/android/server/pm/dex/PackageDexUsage.java +33 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; } } Loading
services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +117 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -57,6 +58,7 @@ public class DexManagerTests { private int mUser0; private int mUser1; @Before public void setup() { Loading Loading @@ -243,6 +245,113 @@ 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); } private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) { for (String dex : secondaries) { Loading Loading @@ -317,5 +426,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]; } } }
services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +50 −0 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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; Loading