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

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

Merge "Update optimizing RegisteredServicesCache ServiceInfo parsing" into main

parents 3672fa9f 9d5eed31
Loading
Loading
Loading
Loading
+73 −52
Original line number Diff line number Diff line
@@ -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;
@@ -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")
@@ -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();
@@ -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();
@@ -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) {
@@ -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 */
@@ -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;
        }
    }

@@ -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;
@@ -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);
@@ -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);
@@ -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;
        }
    }
+14 −2
Original line number Diff line number Diff line
@@ -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() {
@@ -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
     */
@@ -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);