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

Commit 66e20ef0 authored by lpeter's avatar lpeter
Browse files

Implement client side caching for queryIntentActivities

Several packages frequently call queryIntentActivities method.
This causes high CPU usage, so implement client side caching for
queryIntentActivities.

Flag: android.content.pm.cache_query_intent_activities_in_client_side

Bug: 339648913
Test: atest ApplicationPackageManagerTest
Change-Id: I0a180c77fdbcbd4fb0d4b2c95a13090df755e692
parent 5884003c
Loading
Loading
Loading
Loading
+118 −10
Original line number Diff line number Diff line
@@ -1552,6 +1552,16 @@ public class ApplicationPackageManager extends PackageManager {
    @SuppressWarnings("unchecked")
    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags,
            int userId) {
        if (android.content.pm.Flags.cacheQueryIntentActivitiesInClientSide()) {
            List<ResolveInfo> resolveInfos = sQueryIntentActivitiesCache.query(
                    new IntentActivitiesQuery(intent,
                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                            updateFlagsForComponent(flags.getValue(), userId, intent), userId));
            if (resolveInfos == null) {
                return Collections.emptyList();
            }
            return resolveInfos;
        } else {
            try {
                ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivities(
                        intent,
@@ -1566,6 +1576,7 @@ public class ApplicationPackageManager extends PackageManager {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    @Override
    public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
@@ -4258,4 +4269,101 @@ public class ApplicationPackageManager extends PackageManager {
            return null;
        }
    }

    private static final class IntentActivitiesQuery {
        final Intent mIntent;
        final String mResolvedType;
        final long mFlags;
        final int mUserId;

        IntentActivitiesQuery(Intent intent, String resolvedType, long flags, int userId) {
            this.mIntent = intent;
            this.mResolvedType = resolvedType;
            this.mFlags = flags;
            this.mUserId = userId;
        }

        @Override
        public int hashCode() {
            int hash = mIntent.filterHashCode();
            hash = hash * 13 + Objects.hashCode(mResolvedType);
            hash = hash * 13 + Long.hashCode(mFlags);
            hash = hash * 13 + mUserId;
            return hash;
        }

        @Override
        public boolean equals(@Nullable Object obj) {
            if (obj == null) {
                return false;
            }
            ApplicationPackageManager.IntentActivitiesQuery other;
            try {
                other = (ApplicationPackageManager.IntentActivitiesQuery) obj;
            } catch (ClassCastException ex) {
                return false;
            }
            return mIntent.filterEquals(other.mIntent)
                    && Objects.equals(mResolvedType, other.mResolvedType)
                    && mFlags == other.mFlags
                    && mUserId == other.mUserId;
        }
    }

    private static final String CACHE_KEY_QUERY_INTENT_ACTIVITIES_API = "query_intent_activities";

    /** @hide */
    @VisibleForTesting
    public static final PropertyInvalidatedCache<IntentActivitiesQuery, List<ResolveInfo>>
            sQueryIntentActivitiesCache = new PropertyInvalidatedCache<>(
                    new PropertyInvalidatedCache.Args(MODULE_SYSTEM).maxEntries(1024).api(
                            CACHE_KEY_QUERY_INTENT_ACTIVITIES_API).cacheNulls(true),
                    CACHE_KEY_QUERY_INTENT_ACTIVITIES_API, null) {
                @Override
                public List<ResolveInfo> recompute(IntentActivitiesQuery query) {
                    try {
                        ParceledListSlice<ResolveInfo> parceledList =
                                ActivityThread.currentActivityThread().getPackageManager()
                                        .queryIntentActivities(
                                        query.mIntent,
                                        query.mResolvedType,
                                        query.mFlags,
                                        query.mUserId);
                        if (parceledList == null) {
                            return Collections.emptyList();
                        }
                        return parceledList.getList();
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }

                @Override
                public String queryToString(IntentActivitiesQuery query) {
                    StringBuilder b = new StringBuilder(128);
                    b.append("Query { ");
                    b.append("intent=").append(query.mIntent.toString());
                    b.append(", resolvedType=").append(query.mResolvedType);
                    b.append(", flags=").append(Long.toHexString(query.mFlags));
                    b.append(", userId=").append(query.mUserId);
                    b.append(" }");
                    return b.toString();
                }
            };

    /** @hide */
    public static void disableQueryIntentActivitiesCacheForCurrentProcess() {
        if (!android.content.pm.Flags.cacheQueryIntentActivitiesInClientSide()) {
            return;
        }
        sQueryIntentActivitiesCache.disableForCurrentProcess();
    }

    /** @hide */
    public static void invalidateQueryIntentActivitiesCache() {
        if (!android.content.pm.Flags.cacheQueryIntentActivitiesInClientSide()) {
            return;
        }
        sQueryIntentActivitiesCache.invalidateCache();
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -332,3 +332,11 @@ flag {
    is_fixed_read_only: true
}

flag {
    name: "cache_query_intent_activities_in_client_side"
    namespace: "package_manager_service"
    description: "Feature flag to implement client side caching for queryIntentActivities."
    bug: "339648913"
    is_fixed_read_only: true
}
+1 −0
Original line number Diff line number Diff line
@@ -178,6 +178,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
        // be invalid.
        PackageManager.invalidatePackageInfoCache();
        ApplicationPackageManager.invalidateGetPackagesForUidCache();
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        dispatchChange(this);
    }

+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.app.ApplicationPackageManager;
import android.content.pm.DataLoaderType;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfoLite;
@@ -626,6 +627,7 @@ class InstallingSession {
            request.setError("APEX installation failed", e);
        }
        PackageManagerService.invalidatePackageInfoCache();
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        mPm.notifyInstallObserver(request);
    }

+4 −0
Original line number Diff line number Diff line
@@ -772,6 +772,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            ApplicationPackageManager.disableGetPackagesForUidCache();
            ApplicationPackageManager.invalidateHasSystemFeatureCache();
            PackageManager.corkPackageInfoCache();
            ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
            ApplicationPackageManager.disableQueryIntentActivitiesCacheForCurrentProcess();
        }

        @Override
@@ -1580,6 +1582,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        // coalesce settings writes, this strategy would have us invalidate the cache too late.
        // Invalidating on schedule addresses this problem.
        invalidatePackageInfoCache();
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        if (!mHandler.hasMessages(WRITE_SETTINGS)) {
            mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
        }
@@ -1587,6 +1590,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService

    void scheduleWritePackageListLocked(int userId) {
        invalidatePackageInfoCache();
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
            Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
            msg.arg1 = userId;
Loading