Loading core/java/android/content/pm/RegisteredServicesCache.java +37 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.content.pm; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -558,7 +559,8 @@ public abstract class RegisteredServicesCache<V> { if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mUserIdToServiceInfoCaches) { if (mUserIdToServiceInfoCaches.numElementsForKey(userId) > 0) { final Integer token = Integer.valueOf(userId); final ServiceInfoCachesToken<V> token = new ServiceInfoCachesToken<V>( this /* RegisteredServicesCache<V> */, userId); mBackgroundHandler.removeCallbacksAndEqualMessages(token); mBackgroundHandler.postDelayed( new ClearServiceInfoCachesTimeoutRunnable(userId), token, Loading Loading @@ -962,4 +964,38 @@ public abstract class RegisteredServicesCache<V> { } } } /** * Use the token to make sure the service info caches to be cleared in the different instances. * @param <V> The type of the value. * * @hide */ public static final class ServiceInfoCachesToken<V> { public final RegisteredServicesCache<V> mRegisteredServicesCache; public final int mUserId; public ServiceInfoCachesToken(RegisteredServicesCache<V> registeredServicesCache, int userId) { this.mRegisteredServicesCache = registeredServicesCache; this.mUserId = userId; } @Override public boolean equals(@Nullable Object o) { if (o instanceof ServiceInfoCachesToken) { final ServiceInfoCachesToken<V> other = (ServiceInfoCachesToken<V>) o; return mRegisteredServicesCache == other.mRegisteredServicesCache && mUserId == other.mUserId; } return false; } @Override public int hashCode() { int result = mRegisteredServicesCache != null ? mRegisteredServicesCache.hashCode() : 0; result = 31 * result + mUserId; return result; } } } core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java +88 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading Loading @@ -344,9 +345,13 @@ public class RegisteredServicesCacheUnitTest { // of U1 user. doAnswer(invocation -> { Message message = invocation.getArgument(0); if (!message.obj.equals(Integer.valueOf(U0))) { if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; if (token.mUserId == U1) { message.getCallback().run(); } } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); Loading @@ -369,6 +374,87 @@ public class RegisteredServicesCacheUnitTest { verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L)); } @Test public void testClearServiceInfoCachesForTwoDifferentInstancesAfterTimeout() throws Exception { PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0))).thenReturn(packageInfo1); TestRegisteredServicesCache testServicesCache1 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo( mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(), 1000L /* lastUpdateTime */); testServicesCache1.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); TestRegisteredServicesCache testServicesCache2 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); testServicesCache2.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); final ArrayList<RegisteredServicesCache<TestServiceType>> registeredServicesCacheArrayList = new ArrayList<>(); doAnswer(invocation -> { Message message = invocation.getArgument(0); if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; Log.d(TAG, "RegisteredServicesCache token: " + token.mRegisteredServicesCache); registeredServicesCacheArrayList.add(token.mRegisteredServicesCache); } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache2.getAllServices(U0); verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong()); assertThat(registeredServicesCacheArrayList.size()).isEqualTo(2); assertThat(registeredServicesCacheArrayList.get(0)).isNotEqualTo( registeredServicesCacheArrayList.get(1)); } @Test public void testClearServiceInfoCachesForTwoSameInstancesAfterTimeout() throws Exception { PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0))).thenReturn(packageInfo1); TestRegisteredServicesCache testServicesCache1 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo( mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(), 1000L /* lastUpdateTime */); testServicesCache1.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); final ArrayList<RegisteredServicesCache<TestServiceType>> registeredServicesCacheArrayList = new ArrayList<>(); doAnswer(invocation -> { Message message = invocation.getArgument(0); if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; Log.d(TAG, "RegisteredServicesCache token: " + token.mRegisteredServicesCache); registeredServicesCacheArrayList.add(token.mRegisteredServicesCache); } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.invalidateCache(U0); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong()); assertThat(registeredServicesCacheArrayList.size()).isEqualTo(2); assertThat(registeredServicesCacheArrayList.get(0)).isEqualTo( registeredServicesCacheArrayList.get(1)); } private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo( TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) { final ComponentInfo info = new ComponentInfo(); Loading Loading
core/java/android/content/pm/RegisteredServicesCache.java +37 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.content.pm; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -558,7 +559,8 @@ public abstract class RegisteredServicesCache<V> { if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mUserIdToServiceInfoCaches) { if (mUserIdToServiceInfoCaches.numElementsForKey(userId) > 0) { final Integer token = Integer.valueOf(userId); final ServiceInfoCachesToken<V> token = new ServiceInfoCachesToken<V>( this /* RegisteredServicesCache<V> */, userId); mBackgroundHandler.removeCallbacksAndEqualMessages(token); mBackgroundHandler.postDelayed( new ClearServiceInfoCachesTimeoutRunnable(userId), token, Loading Loading @@ -962,4 +964,38 @@ public abstract class RegisteredServicesCache<V> { } } } /** * Use the token to make sure the service info caches to be cleared in the different instances. * @param <V> The type of the value. * * @hide */ public static final class ServiceInfoCachesToken<V> { public final RegisteredServicesCache<V> mRegisteredServicesCache; public final int mUserId; public ServiceInfoCachesToken(RegisteredServicesCache<V> registeredServicesCache, int userId) { this.mRegisteredServicesCache = registeredServicesCache; this.mUserId = userId; } @Override public boolean equals(@Nullable Object o) { if (o instanceof ServiceInfoCachesToken) { final ServiceInfoCachesToken<V> other = (ServiceInfoCachesToken<V>) o; return mRegisteredServicesCache == other.mRegisteredServicesCache && mUserId == other.mUserId; } return false; } @Override public int hashCode() { int result = mRegisteredServicesCache != null ? mRegisteredServicesCache.hashCode() : 0; result = 31 * result + mUserId; return result; } } }
core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java +88 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading Loading @@ -344,9 +345,13 @@ public class RegisteredServicesCacheUnitTest { // of U1 user. doAnswer(invocation -> { Message message = invocation.getArgument(0); if (!message.obj.equals(Integer.valueOf(U0))) { if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; if (token.mUserId == U1) { message.getCallback().run(); } } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); Loading @@ -369,6 +374,87 @@ public class RegisteredServicesCacheUnitTest { verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L)); } @Test public void testClearServiceInfoCachesForTwoDifferentInstancesAfterTimeout() throws Exception { PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0))).thenReturn(packageInfo1); TestRegisteredServicesCache testServicesCache1 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo( mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(), 1000L /* lastUpdateTime */); testServicesCache1.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); TestRegisteredServicesCache testServicesCache2 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); testServicesCache2.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); final ArrayList<RegisteredServicesCache<TestServiceType>> registeredServicesCacheArrayList = new ArrayList<>(); doAnswer(invocation -> { Message message = invocation.getArgument(0); if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; Log.d(TAG, "RegisteredServicesCache token: " + token.mRegisteredServicesCache); registeredServicesCacheArrayList.add(token.mRegisteredServicesCache); } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache2.getAllServices(U0); verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong()); assertThat(registeredServicesCacheArrayList.size()).isEqualTo(2); assertThat(registeredServicesCacheArrayList.get(0)).isNotEqualTo( registeredServicesCacheArrayList.get(1)); } @Test public void testClearServiceInfoCachesForTwoSameInstancesAfterTimeout() throws Exception { PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0))).thenReturn(packageInfo1); TestRegisteredServicesCache testServicesCache1 = spy( new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo( mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(), 1000L /* lastUpdateTime */); testServicesCache1.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); final ArrayList<RegisteredServicesCache<TestServiceType>> registeredServicesCacheArrayList = new ArrayList<>(); doAnswer(invocation -> { Message message = invocation.getArgument(0); if (message.obj instanceof RegisteredServicesCache.ServiceInfoCachesToken) { final RegisteredServicesCache.ServiceInfoCachesToken token = (RegisteredServicesCache.ServiceInfoCachesToken) message.obj; Log.d(TAG, "RegisteredServicesCache token: " + token.mRegisteredServicesCache); registeredServicesCacheArrayList.add(token.mRegisteredServicesCache); } return true; }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); testServicesCache1.invalidateCache(U0); testServicesCache1.getAllServices(U0); verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong()); assertThat(registeredServicesCacheArrayList.size()).isEqualTo(2); assertThat(registeredServicesCacheArrayList.get(0)).isEqualTo( registeredServicesCacheArrayList.get(1)); } private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo( TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) { final ComponentInfo info = new ComponentInfo(); Loading