diff options
| author | 2025-02-07 08:26:59 -0800 | |
|---|---|---|
| committer | 2025-02-07 08:26:59 -0800 | |
| commit | 18978b0f199b9a333f17bea365338fac3d81cd03 (patch) | |
| tree | be93fa0df0eec45a8bfb9b7a6b1afda0c09b509a | |
| parent | 4a26f673299fe71d4c86553fe4df0348bd9dd02c (diff) | |
| parent | e6751e09f53d04d3a62e1ff5609d20f9c5a166ad (diff) | |
Merge "PM caches now cache nulls" into main
5 files changed, 121 insertions, 26 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 2dead565fa85..f2e7e8513116 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -17,7 +17,6 @@ package android.app; import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM; -import static android.app.PropertyInvalidatedCache.createSystemCacheKey; import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED; import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_NOT_COLORED; import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; @@ -1146,12 +1145,16 @@ public class ApplicationPackageManager extends PackageManager { } } - private static final String CACHE_KEY_PACKAGES_FOR_UID_PROPERTY = - createSystemCacheKey("get_packages_for_uid"); - private static final PropertyInvalidatedCache<Integer, GetPackagesForUidResult> - mGetPackagesForUidCache = - new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>( - 1024, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) { + private static final String CACHE_KEY_PACKAGES_FOR_UID_API = "get_packages_for_uid"; + + /** @hide */ + @VisibleForTesting + public static final PropertyInvalidatedCache<Integer, GetPackagesForUidResult> + sGetPackagesForUidCache = new PropertyInvalidatedCache<>( + new PropertyInvalidatedCache.Args(MODULE_SYSTEM) + .maxEntries(1024).api(CACHE_KEY_PACKAGES_FOR_UID_API).cacheNulls(true), + CACHE_KEY_PACKAGES_FOR_UID_API, null) { + @Override public GetPackagesForUidResult recompute(Integer uid) { try { @@ -1170,17 +1173,17 @@ public class ApplicationPackageManager extends PackageManager { @Override public String[] getPackagesForUid(int uid) { - return mGetPackagesForUidCache.query(uid).value(); + return sGetPackagesForUidCache.query(uid).value(); } /** @hide */ public static void disableGetPackagesForUidCache() { - mGetPackagesForUidCache.disableLocal(); + sGetPackagesForUidCache.disableLocal(); } /** @hide */ public static void invalidateGetPackagesForUidCache() { - PropertyInvalidatedCache.invalidateCache(CACHE_KEY_PACKAGES_FOR_UID_PROPERTY); + sGetPackagesForUidCache.invalidateCache(); } @Override diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index bfb33f2b7cb1..c573161f30cc 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -1163,6 +1163,17 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Return the current cache nonce. + * @hide + */ + @VisibleForTesting + public long getNonce() { + synchronized (mLock) { + return mNonce.getNonce(); + } + } + + /** * Complete key prefixes. */ private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + "."; @@ -1314,7 +1325,7 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Burst a property name into module and api. Throw if the key is invalid. This method is - * used in to transition legacy cache constructors to the args constructor. + * used to transition legacy cache constructors to the Args constructor. */ private static Args argsFromProperty(@NonNull String name) { throwIfInvalidCacheKey(name); @@ -1327,6 +1338,15 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Return the API porting of a legacy property. This method is used to transition caches to + * the Args constructor. + * @hide + */ + public static String apiFromProperty(@NonNull String name) { + return argsFromProperty(name).mApi; + } + + /** * Make a new property invalidated cache. This constructor names the cache after the * property name. New clients should prefer the constructor that takes an explicit * cache name. @@ -2036,11 +2056,11 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * Disable all caches in the local process. This is primarily useful for testing when - * the test needs to bypass the cache or when the test is for a server, and the test - * process does not have privileges to write SystemProperties. Once disabled it is not - * possible to re-enable caching in the current process. If a client wants to - * temporarily disable caching, use the corking mechanism. + * Disable all caches in the local process. This is primarily useful for testing when the + * test needs to bypass the cache or when the test is for a server, and the test process does + * not have privileges to write the nonce. Once disabled it is not possible to re-enable + * caching in the current process. See {@link #testPropertyName} for a more focused way to + * bypass caches when the test is for a server. * @hide */ public static void disableForTestMode() { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0369b7d9bc28..6ae2df2cd7a2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,6 +16,7 @@ package android.content.pm; +import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM; import static android.content.pm.SigningInfo.AppSigningSchemeVersion; import static android.media.audio.Flags.FLAG_FEATURE_SPATIAL_AUDIO_HEADTRACKING_LOW_LATENCY; @@ -11659,11 +11660,22 @@ public abstract class PackageManager { } } - private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo> - sApplicationInfoCache = - new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>( - 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE, - "getApplicationInfo") { + private static String packageInfoApi() { + return PropertyInvalidatedCache.apiFromProperty( + PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE); + } + + // The maximum number of entries to keep in the packageInfo and applicationInfo caches. + private final static int MAX_INFO_CACHE_ENTRIES = 2048; + + /** @hide */ + @VisibleForTesting + public static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo> + sApplicationInfoCache = new PropertyInvalidatedCache<>( + new PropertyInvalidatedCache.Args(MODULE_SYSTEM) + .maxEntries(MAX_INFO_CACHE_ENTRIES).api(packageInfoApi()).cacheNulls(true), + "getApplicationInfo", null) { + @Override public ApplicationInfo recompute(ApplicationInfoQuery query) { return getApplicationInfoAsUserUncached( @@ -11749,10 +11761,11 @@ public abstract class PackageManager { } private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo> - sPackageInfoCache = - new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>( - 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE, - "getPackageInfo") { + sPackageInfoCache = new PropertyInvalidatedCache<>( + new PropertyInvalidatedCache.Args(MODULE_SYSTEM) + .maxEntries(MAX_INFO_CACHE_ENTRIES).api(packageInfoApi()).cacheNulls(true), + "getPackageInfo", null) { + @Override public PackageInfo recompute(PackageInfoQuery query) { return getPackageInfoAsUserUncached( diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index cc4c2b5bf893..068d68d25017 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -40,6 +40,7 @@ import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ApplicationPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.SigningDetails; @@ -173,6 +174,10 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, * Report a change to observers. */ private void onChanged() { + // App visibility may have changed, which means that earlier fetches from these caches may + // be invalid. + PackageManager.invalidatePackageInfoCache(); + ApplicationPackageManager.invalidateGetPackagesForUidCache(); dispatchChange(this); } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java index 66aaa562b873..a01df8bf108d 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java @@ -37,6 +37,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.UserInfo; +import android.app.PropertyInvalidatedCache; import android.os.Build; import android.os.Handler; import android.os.Message; @@ -50,6 +51,8 @@ import android.util.SparseArray; import androidx.annotation.NonNull; +import android.app.ApplicationPackageManager; +import android.content.pm.PackageManager; import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedActivity; @@ -64,8 +67,10 @@ import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.utils.Watchable; import com.android.server.utils.WatchableTester; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -244,6 +249,55 @@ public class AppsFilterImplTest { (Answer<Boolean>) invocation -> ((AndroidPackage) invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() >= Build.VERSION_CODES.R); + PropertyInvalidatedCache.setTestMode(true); + PackageManager.sApplicationInfoCache.testPropertyName(); + ApplicationPackageManager.sGetPackagesForUidCache.testPropertyName(); + } + + @After + public void tearDown() { + PropertyInvalidatedCache.setTestMode(false); + } + + /** + * A class to make it easier to verify that PM caches are properly invalidated by + * AppsFilterImpl operations. This extends WatchableTester to test the cache nonces along + * with change reporting. + */ + private static class NonceTester extends WatchableTester { + // The nonces from caches under consideration. The no-parameter constructor fetches the + // values from the cacches. + private static record Nonces(long applicationInfo, long packageInfo) { + Nonces() { + this(ApplicationPackageManager.sGetPackagesForUidCache.getNonce(), + PackageManager.sApplicationInfoCache.getNonce()); + } + } + + // Track the latest cache nonces. + private Nonces mNonces; + + NonceTester(Watchable w, String k) { + super(w, k); + mNonces = new Nonces(); + } + + @Override + public void verifyChangeReported(String msg) { + super.verifyChangeReported(msg); + Nonces update = new Nonces(); + assertTrue(msg, update.applicationInfo != mNonces.applicationInfo); + assertTrue(msg, update.packageInfo != mNonces.packageInfo); + mNonces = update; + } + + @Override + public void verifyNoChangeReported(String msg) { + super.verifyNoChangeReported(msg); + Nonces update = new Nonces(); + assertTrue(msg, update.applicationInfo == mNonces.applicationInfo); + assertTrue(msg, update.packageInfo == mNonces.packageInfo); + } } @Test @@ -1167,7 +1221,7 @@ public class AppsFilterImplTest { final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, mMockHandler); - final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + final WatchableTester watcher = new NonceTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); watcher.verifyChangeReported("addBasicAndroid"); |