diff options
3 files changed, 73 insertions, 9 deletions
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 0333942b7f3e..9d11710a2cad 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -17,6 +17,7 @@ package android.content.pm; import android.Manifest; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -30,6 +31,7 @@ import android.os.Environment; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArrayMap; import android.util.AtomicFile; import android.util.AttributeSet; import android.util.IntArray; @@ -45,11 +47,11 @@ import com.android.internal.util.ArrayUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import libcore.io.IoUtils; - import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -94,6 +96,9 @@ 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<>(); + private static class UserServices<V> { @GuardedBy("mServicesLock") final Map<V, Integer> persistentServices = Maps.newHashMap(); @@ -323,13 +328,16 @@ public abstract class RegisteredServicesCache<V> { public final ComponentName componentName; @UnsupportedAppUsage public final int uid; + public final long lastUpdateTime; /** @hide */ - public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) { + public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName, + long lastUpdateTime) { this.type = type; this.componentInfo = componentInfo; this.componentName = componentName; this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1; + this.lastUpdateTime = lastUpdateTime; } @Override @@ -490,7 +498,7 @@ public abstract class RegisteredServicesCache<V> { final List<ResolveInfo> resolveInfos = queryIntentServices(userId); for (ResolveInfo resolveInfo : resolveInfos) { try { - ServiceInfo<V> info = parseServiceInfo(resolveInfo); + ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId); if (info == null) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); continue; @@ -638,13 +646,31 @@ public abstract class RegisteredServicesCache<V> { } @VisibleForTesting - protected ServiceInfo<V> parseServiceInfo(ResolveInfo service) + protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId) 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); @@ -670,8 +696,13 @@ public abstract class RegisteredServicesCache<V> { if (v == null) { return null; } - final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo; - return new ServiceInfo<V>(v, serviceInfo, componentName); + ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime); + if (Flags.optimizeParsingInRegisteredServicesCache()) { + synchronized (mServicesLock) { + mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo); + } + } + return serviceInfo; } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to load resources for pacakge " + si.packageName); @@ -841,4 +872,28 @@ public abstract class RegisteredServicesCache<V> { mContext.unregisterReceiver(mExternalReceiver); 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) { + return serviceCache; + } + // The service is not latest, remove it from the cache. + mServiceInfoCaches.remove(serviceCacheKey); + return null; + } + } } diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 7bba06c87813..e4b8c90d381d 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -383,3 +383,11 @@ flag { bug: "334024639" description: "Feature flag to check whether a given UID can access a content provider" } + +flag { + name: "optimize_parsing_in_registered_services_cache" + namespace: "package_manager_service" + description: "Feature flag to optimize RegisteredServicesCache ServiceInfo parsing by using caches." + bug: "319137634" + is_fixed_read_only: true +} diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index 37ef6cba8814..939bf2ec4b0a 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -207,7 +207,8 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { final ComponentInfo info = new ComponentInfo(); info.applicationInfo = new ApplicationInfo(); info.applicationInfo.uid = uid; - return new RegisteredServicesCache.ServiceInfo<>(type, info, null); + return new RegisteredServicesCache.ServiceInfo<>(type, info, null /* componentName */, + 0 /* lastUpdateTime */); } private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) { @@ -301,7 +302,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { @Override protected ServiceInfo<TestServiceType> parseServiceInfo( - ResolveInfo resolveInfo) throws XmlPullParserException, IOException { + ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException { int size = mServices.size(); for (int i = 0; i < size; i++) { Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); |