Loading core/java/android/content/pm/RegisteredServicesCache.java +73 −52 Original line number Diff line number Diff line Loading @@ -85,6 +85,8 @@ public abstract class RegisteredServicesCache<V> { private static final boolean DEBUG = false; protected static final String REGISTERED_SERVICES_DIR = "registered_services"; static final long SERVICE_INFO_CACHES_TIMEOUT_MILLIS = 30000; // 30 seconds public final Context mContext; private final String mInterfaceName; private final String mMetaDataName; Loading @@ -96,8 +98,18 @@ public abstract class RegisteredServicesCache<V> { @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2); @GuardedBy("mServicesLock") private final ArrayMap<String, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>(); @GuardedBy("mServiceInfoCaches") private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>(); private final Handler mBackgroundHandler; private final Runnable mClearServiceInfoCachesRunnable = new Runnable() { public void run() { synchronized (mServiceInfoCaches) { mServiceInfoCaches.clear(); } } }; private static class UserServices<V> { @GuardedBy("mServicesLock") Loading Loading @@ -172,9 +184,9 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } Handler handler = BackgroundThread.getHandler(); mBackgroundHandler = BackgroundThread.getHandler(); mContext.registerReceiverAsUser( mPackageReceiver, UserHandle.ALL, intentFilter, null, handler); mPackageReceiver, UserHandle.ALL, intentFilter, null, mBackgroundHandler); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); Loading @@ -183,7 +195,7 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { sdFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } mContext.registerReceiver(mExternalReceiver, sdFilter, null, handler); mContext.registerReceiver(mExternalReceiver, sdFilter, null, mBackgroundHandler); // Register for user-related events IntentFilter userFilter = new IntentFilter(); Loading @@ -191,7 +203,7 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, handler); mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, mBackgroundHandler); } private void handlePackageEvent(Intent intent, int userId) { Loading Loading @@ -328,6 +340,10 @@ public abstract class RegisteredServicesCache<V> { public final ComponentName componentName; @UnsupportedAppUsage public final int uid; /** * The last update time of the package that contains the service. * It's from {@link PackageInfo#lastUpdateTime}. */ public final long lastUpdateTime; /** @hide */ Loading @@ -342,7 +358,8 @@ public abstract class RegisteredServicesCache<V> { @Override public String toString() { return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid; return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid + ", lastUpdateTime " + lastUpdateTime; } } Loading Loading @@ -496,19 +513,56 @@ public abstract class RegisteredServicesCache<V> { final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); final List<ResolveInfo> resolveInfos = queryIntentServices(userId); final PackageManager pm = mContext.getPackageManager(); for (ResolveInfo resolveInfo : resolveInfos) { // Check if the service has been in the service cache. long lastUpdateTime = -1; final android.content.pm.ServiceInfo si = resolveInfo.serviceInfo; final ComponentName componentName = si.getComponentName(); if (Flags.optimizeParsingInRegisteredServicesCache()) { try { PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); lastUpdateTime = packageInfo.lastUpdateTime; } catch (NameNotFoundException | SecurityException e) { Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e); continue; } ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName, lastUpdateTime); if (serviceInfo != null) { serviceInfos.add(serviceInfo); continue; } } try { ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId); ServiceInfo<V> info = parseServiceInfo(resolveInfo, lastUpdateTime); if (info == null) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); continue; } serviceInfos.add(info); if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServiceInfoCaches) { mServiceInfoCaches.put(componentName, info); } } } catch (XmlPullParserException | IOException e) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); } } if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServiceInfoCaches) { if (!mServiceInfoCaches.isEmpty()) { mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable); mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable, SERVICE_INFO_CACHES_TIMEOUT_MILLIS); } } } synchronized (mServicesLock) { final UserServices<V> user = findOrCreateUserLocked(userId); final boolean firstScan = user.services == null; Loading Loading @@ -645,32 +699,18 @@ public abstract class RegisteredServicesCache<V> { return false; } /** * If the service has already existed in the caches, this method will not be called to parse * the service. */ @VisibleForTesting protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId) protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, long lastUpdateTime) throws XmlPullParserException, IOException { android.content.pm.ServiceInfo si = service.serviceInfo; ComponentName componentName = new ComponentName(si.packageName, si.name); PackageManager pm = mContext.getPackageManager(); // Check if the service has been in the service cache. long lastUpdateTime = -1; if (Flags.optimizeParsingInRegisteredServicesCache()) { try { PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); lastUpdateTime = packageInfo.lastUpdateTime; ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(si, lastUpdateTime); if (serviceInfo != null) { return serviceInfo; } } catch (NameNotFoundException | SecurityException e) { Slog.d(TAG, "Fail to get the PackageInfo in parseServiceInfo: " + e); } } XmlResourceParser parser = null; try { parser = si.loadXmlMetaData(pm, mMetaDataName); Loading @@ -696,13 +736,7 @@ public abstract class RegisteredServicesCache<V> { if (v == null) { return null; } ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime); if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServicesLock) { mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo); } } return serviceInfo; return new ServiceInfo<V>(v, si, componentName, lastUpdateTime); } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to load resources for pacakge " + si.packageName); Loading Loading @@ -873,26 +907,13 @@ public abstract class RegisteredServicesCache<V> { mContext.unregisterReceiver(mUserRemovedReceiver); } private static String getServiceCacheKey(@NonNull android.content.pm.ServiceInfo serviceInfo) { StringBuilder sb = new StringBuilder(serviceInfo.packageName); sb.append('-'); sb.append(serviceInfo.name); return sb.toString(); } private ServiceInfo<V> getServiceInfoFromServiceCache( @NonNull android.content.pm.ServiceInfo serviceInfo, long lastUpdateTime) { String serviceCacheKey = getServiceCacheKey(serviceInfo); synchronized (mServicesLock) { ServiceInfo<V> serviceCache = mServiceInfoCaches.get(serviceCacheKey); if (serviceCache == null) { return null; } if (serviceCache.lastUpdateTime == lastUpdateTime) { private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName, long lastUpdateTime) { synchronized (mServiceInfoCaches) { ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName); if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) { return serviceCache; } // The service is not latest, remove it from the cache. mServiceInfoCaches.remove(serviceCacheKey); return null; } } Loading core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +14 −2 Original line number Diff line number Diff line Loading @@ -76,6 +76,10 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { mUsers = new ArrayList<>(); mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN)); mUsers.add(new UserInfo(1, "User1", 0)); addServiceInfoIntoResolveInfo(r1, "r1.package.name" /* packageName */, "r1.service.name" /* serviceName */); addServiceInfoIntoResolveInfo(r2, "r2.package.name" /* packageName */, "r2.service.name" /* serviceName */); } public void testGetAllServicesHappyPath() { Loading Loading @@ -218,6 +222,14 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { assertTrue("File should be created at " + file, file.length() > 0); } private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName, String serviceName) { final ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.packageName = packageName; serviceInfo.name = serviceName; resolveInfo.serviceInfo = serviceInfo; } /** * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing */ Loading Loading @@ -301,8 +313,8 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { } @Override protected ServiceInfo<TestServiceType> parseServiceInfo( ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException { protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo, long lastUpdateTime) throws XmlPullParserException, IOException { int size = mServices.size(); for (int i = 0; i < size; i++) { Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); Loading Loading
core/java/android/content/pm/RegisteredServicesCache.java +73 −52 Original line number Diff line number Diff line Loading @@ -85,6 +85,8 @@ public abstract class RegisteredServicesCache<V> { private static final boolean DEBUG = false; protected static final String REGISTERED_SERVICES_DIR = "registered_services"; static final long SERVICE_INFO_CACHES_TIMEOUT_MILLIS = 30000; // 30 seconds public final Context mContext; private final String mInterfaceName; private final String mMetaDataName; Loading @@ -96,8 +98,18 @@ public abstract class RegisteredServicesCache<V> { @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2); @GuardedBy("mServicesLock") private final ArrayMap<String, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>(); @GuardedBy("mServiceInfoCaches") private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>(); private final Handler mBackgroundHandler; private final Runnable mClearServiceInfoCachesRunnable = new Runnable() { public void run() { synchronized (mServiceInfoCaches) { mServiceInfoCaches.clear(); } } }; private static class UserServices<V> { @GuardedBy("mServicesLock") Loading Loading @@ -172,9 +184,9 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } Handler handler = BackgroundThread.getHandler(); mBackgroundHandler = BackgroundThread.getHandler(); mContext.registerReceiverAsUser( mPackageReceiver, UserHandle.ALL, intentFilter, null, handler); mPackageReceiver, UserHandle.ALL, intentFilter, null, mBackgroundHandler); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); Loading @@ -183,7 +195,7 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { sdFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } mContext.registerReceiver(mExternalReceiver, sdFilter, null, handler); mContext.registerReceiver(mExternalReceiver, sdFilter, null, mBackgroundHandler); // Register for user-related events IntentFilter userFilter = new IntentFilter(); Loading @@ -191,7 +203,7 @@ public abstract class RegisteredServicesCache<V> { if (isCore) { userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, handler); mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, mBackgroundHandler); } private void handlePackageEvent(Intent intent, int userId) { Loading Loading @@ -328,6 +340,10 @@ public abstract class RegisteredServicesCache<V> { public final ComponentName componentName; @UnsupportedAppUsage public final int uid; /** * The last update time of the package that contains the service. * It's from {@link PackageInfo#lastUpdateTime}. */ public final long lastUpdateTime; /** @hide */ Loading @@ -342,7 +358,8 @@ public abstract class RegisteredServicesCache<V> { @Override public String toString() { return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid; return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid + ", lastUpdateTime " + lastUpdateTime; } } Loading Loading @@ -496,19 +513,56 @@ public abstract class RegisteredServicesCache<V> { final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); final List<ResolveInfo> resolveInfos = queryIntentServices(userId); final PackageManager pm = mContext.getPackageManager(); for (ResolveInfo resolveInfo : resolveInfos) { // Check if the service has been in the service cache. long lastUpdateTime = -1; final android.content.pm.ServiceInfo si = resolveInfo.serviceInfo; final ComponentName componentName = si.getComponentName(); if (Flags.optimizeParsingInRegisteredServicesCache()) { try { PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); lastUpdateTime = packageInfo.lastUpdateTime; } catch (NameNotFoundException | SecurityException e) { Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e); continue; } ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName, lastUpdateTime); if (serviceInfo != null) { serviceInfos.add(serviceInfo); continue; } } try { ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId); ServiceInfo<V> info = parseServiceInfo(resolveInfo, lastUpdateTime); if (info == null) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); continue; } serviceInfos.add(info); if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServiceInfoCaches) { mServiceInfoCaches.put(componentName, info); } } } catch (XmlPullParserException | IOException e) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); } } if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServiceInfoCaches) { if (!mServiceInfoCaches.isEmpty()) { mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable); mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable, SERVICE_INFO_CACHES_TIMEOUT_MILLIS); } } } synchronized (mServicesLock) { final UserServices<V> user = findOrCreateUserLocked(userId); final boolean firstScan = user.services == null; Loading Loading @@ -645,32 +699,18 @@ public abstract class RegisteredServicesCache<V> { return false; } /** * If the service has already existed in the caches, this method will not be called to parse * the service. */ @VisibleForTesting protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId) protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, long lastUpdateTime) throws XmlPullParserException, IOException { android.content.pm.ServiceInfo si = service.serviceInfo; ComponentName componentName = new ComponentName(si.packageName, si.name); PackageManager pm = mContext.getPackageManager(); // Check if the service has been in the service cache. long lastUpdateTime = -1; if (Flags.optimizeParsingInRegisteredServicesCache()) { try { PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); lastUpdateTime = packageInfo.lastUpdateTime; ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(si, lastUpdateTime); if (serviceInfo != null) { return serviceInfo; } } catch (NameNotFoundException | SecurityException e) { Slog.d(TAG, "Fail to get the PackageInfo in parseServiceInfo: " + e); } } XmlResourceParser parser = null; try { parser = si.loadXmlMetaData(pm, mMetaDataName); Loading @@ -696,13 +736,7 @@ public abstract class RegisteredServicesCache<V> { if (v == null) { return null; } ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime); if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mServicesLock) { mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo); } } return serviceInfo; return new ServiceInfo<V>(v, si, componentName, lastUpdateTime); } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to load resources for pacakge " + si.packageName); Loading Loading @@ -873,26 +907,13 @@ public abstract class RegisteredServicesCache<V> { mContext.unregisterReceiver(mUserRemovedReceiver); } private static String getServiceCacheKey(@NonNull android.content.pm.ServiceInfo serviceInfo) { StringBuilder sb = new StringBuilder(serviceInfo.packageName); sb.append('-'); sb.append(serviceInfo.name); return sb.toString(); } private ServiceInfo<V> getServiceInfoFromServiceCache( @NonNull android.content.pm.ServiceInfo serviceInfo, long lastUpdateTime) { String serviceCacheKey = getServiceCacheKey(serviceInfo); synchronized (mServicesLock) { ServiceInfo<V> serviceCache = mServiceInfoCaches.get(serviceCacheKey); if (serviceCache == null) { return null; } if (serviceCache.lastUpdateTime == lastUpdateTime) { private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName, long lastUpdateTime) { synchronized (mServiceInfoCaches) { ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName); if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) { return serviceCache; } // The service is not latest, remove it from the cache. mServiceInfoCaches.remove(serviceCacheKey); return null; } } Loading
core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +14 −2 Original line number Diff line number Diff line Loading @@ -76,6 +76,10 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { mUsers = new ArrayList<>(); mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN)); mUsers.add(new UserInfo(1, "User1", 0)); addServiceInfoIntoResolveInfo(r1, "r1.package.name" /* packageName */, "r1.service.name" /* serviceName */); addServiceInfoIntoResolveInfo(r2, "r2.package.name" /* packageName */, "r2.service.name" /* serviceName */); } public void testGetAllServicesHappyPath() { Loading Loading @@ -218,6 +222,14 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { assertTrue("File should be created at " + file, file.length() > 0); } private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName, String serviceName) { final ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.packageName = packageName; serviceInfo.name = serviceName; resolveInfo.serviceInfo = serviceInfo; } /** * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing */ Loading Loading @@ -301,8 +313,8 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { } @Override protected ServiceInfo<TestServiceType> parseServiceInfo( ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException { protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo, long lastUpdateTime) throws XmlPullParserException, IOException { int size = mServices.size(); for (int i = 0; i < size; i++) { Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); Loading