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

Commit ec7ac1f0 authored by yutingfang's avatar yutingfang
Browse files

Add cache for AppOpsManager#checkPackage

IAppOpsService#checkPackage(uid, packageName) is a highly frequently called binder API. It
mainly checks whether provided packageName belongs to the uid and if
calling uid can access the package. The result doesn't change often so
we can cache it on the client side to reduce binder calls.

Bug: 423030916
Test: presubmit
Flag: android.permission.flags.check_package_caching_enabled
Change-Id: If1de93269b7f9f8cfd031c4c1cea986923ce8ef1
parent bc4ddbb1
Loading
Loading
Loading
Loading
+65 −1
Original line number Diff line number Diff line
@@ -8258,6 +8258,10 @@ public class AppOpsManager {
    private static final String APP_OP_MODE_CACHING_NAME = "appOpModeCache";
    private static final int APP_OP_MODE_CACHING_SIZE = 2048;

    private static final String CHECK_PACKAGE_CACHING_API = "checkPackage";
    private static final String CHECK_PACKAGE_CACHING_NAME = "checkPackageCache";
    private static final int CHECK_PACKAGE_CACHING_SIZE = 512;

    private static final IpcDataCache.QueryHandler<AppOpModeQuery, Integer> sGetAppOpModeQuery =
            new IpcDataCache.QueryHandler<>() {
                @Override
@@ -8278,12 +8282,36 @@ public class AppOpsManager {
                }
            };

    private static final IpcDataCache.QueryHandler<CheckPackageQuery, Integer> sCheckPackageQuery =
            new IpcDataCache.QueryHandler<>() {
                @Override
                public Integer apply(CheckPackageQuery query) {
                    IAppOpsService service = getService();
                    try {
                        return service.checkPackage(query.uid, query.packageName);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }

                @Override
                public boolean shouldBypassCache(@NonNull CheckPackageQuery query) {
                    // If the flag to enable the new caching behavior is off, bypass the cache.
                    return !Flags.checkPackageCachingEnabled();
                }
            };


    // A LRU cache on binder clients that caches AppOp mode by uid, packageName, virtualDeviceId
    // and attributionTag.
    private static final IpcDataCache<AppOpModeQuery, Integer> sAppOpModeCache =
            new IpcDataCache<>(APP_OP_MODE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
                    APP_OP_MODE_CACHING_API, APP_OP_MODE_CACHING_NAME, sGetAppOpModeQuery);

    private static final IpcDataCache<CheckPackageQuery, Integer> sCheckPackageCache =
            new IpcDataCache<>(CHECK_PACKAGE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
                    CHECK_PACKAGE_CACHING_API, CHECK_PACKAGE_CACHING_NAME, sCheckPackageQuery);

    // Ops that we don't want to cache due to:
    // 1) Discrepancy of attributionTag support in checkOp and noteOp that determines if a package
    //    can bypass user restriction of an op: b/240617242. COARSE_LOCATION and FINE_LOCATION are
@@ -8321,6 +8349,34 @@ public class AppOpsManager {
        }
    }

    /**
     * @hide
     */
    public static void invalidateCheckPackageCache() {
        if (Flags.checkPackageCachingEnabled()) {
            IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, CHECK_PACKAGE_CACHING_API);
        }
    }

    /**
     * Bypass CheckPackageCache in the local process
     *
     * @hide
     */
    public static void disableCheckPackageCache() {
        if (Flags.checkPackageCachingEnabled()) {
            sCheckPackageCache.disableLocal();
        }
    }

    private record CheckPackageQuery(int uid, @NonNull String packageName) {
        @Override
        public String toString() {
            return TextUtils.formatSimple("CheckPackageQuery(uid=%d, packageName=%s)", uid,
                    packageName);
        }
    }

    private static final class AppOpModeQuery {
        final int op;
        final int uid;
@@ -10122,7 +10178,15 @@ public class AppOpsManager {
    @Deprecated
    public void checkPackage(int uid, @NonNull String packageName) {
        try {
            if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
            int mode;
            if (Flags.checkPackageCachingEnabled()) {
                mode = sCheckPackageCache.query(
                        new CheckPackageQuery(uid, packageName));
            } else {
                mode = mService.checkPackage(uid, packageName);
            }

            if (mode != MODE_ALLOWED) {
                throw new SecurityException(
                        "Package " + packageName + " does not belong to " + uid);
            }
+8 −0
Original line number Diff line number Diff line
@@ -612,3 +612,11 @@ flag {
    description: "This flags app ops cell reporting tied to ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION"
    bug: "420985100"
}

flag {
    name: "check_package_caching_enabled"
    is_fixed_read_only: true
    namespace: "permissions"
    description: "Enable checkPackage caching in AppOpsManager"
    bug: "423030916"
}
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -180,6 +181,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
        PackageManagerService.invalidatePackageInfoCache(invalidationReason);
        PackageManagerService.invalidateGetPackagesForUidCache(invalidationReason);
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        AppOpsManager.invalidateCheckPackageCache();
        dispatchChange(this);
    }

+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -265,6 +266,7 @@ final class DeletePackageHelper {
            }
            PackageManagerService.invalidateGetPackagesForUidCache(
                    PackageMetrics.INVALIDATION_REASON_DELETE_PACKAGE);
            AppOpsManager.invalidateCheckPackageCache();
        }

        if (res) {
+1 −0
Original line number Diff line number Diff line
@@ -2528,6 +2528,7 @@ final class InstallPackageHelper {
        }
        PackageManagerService.invalidateGetPackagesForUidCache(
                PackageMetrics.INVALIDATION_REASON_INSTALL_PACKAGE);
        AppOpsManager.invalidateCheckPackageCache();
    }

    @GuardedBy("mPm.mLock")
Loading