Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8e3f367 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Optimize RegisteredServicesCache ServiceInfo parsing" into main

parents 03d36ec9 b6f95a01
Loading
Loading
Loading
Loading
+62 −7
Original line number Diff line number Diff line
@@ -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;
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -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
}
+3 −2
Original line number Diff line number Diff line
@@ -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);