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

Commit 8eacf0f4 authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "Separate permission notifications from cache updates" into main

parents 64fd93f3 80fd09f1
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -79,6 +79,17 @@ import java.util.concurrent.atomic.AtomicLong;
@TestApi
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PropertyInvalidatedCache<Query, Result> {
    /**
     * A method to report if the PermissionManager notifications can be separated from cache
     * invalidation.  The feature relies on a series of flags; the dependency is captured in this
     * method.
     * @hide
     */
    public static boolean separatePermissionNotificationsEnabled() {
        return isSharedMemoryAvailable()
                && Flags.picSeparatePermissionNotifications();
    }

    /**
     * This is a configuration class that customizes a cache instance.
     * @hide
@@ -1921,6 +1932,10 @@ public class PropertyInvalidatedCache<Query, Result> {
        }

        public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
            if (separatePermissionNotificationsEnabled()) {
                throw new IllegalStateException("AutoCorking is unavailable");
            }

            mPropertyName = propertyName;
            mAutoCorkDelayMs = autoCorkDelayMs;
            // We can't initialize mHandler here: when we're created, the main loop might not
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,14 @@ flag {
     bug: "373752556"
}

flag {
     namespace: "system_performance"
     name: "pic_separate_permission_notifications"
     is_fixed_read_only: true
     description: "Seperate PermissionManager notifications from cache udpates"
     bug: "373752556"
}

flag {
     namespace: "system_performance"
     name: "pic_cache_nulls"
+29 −18
Original line number Diff line number Diff line
@@ -11651,7 +11651,7 @@ public abstract class PackageManager {
    private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
            sApplicationInfoCache =
            new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
                    2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
                    "getApplicationInfo") {
                @Override
                public ApplicationInfo recompute(ApplicationInfoQuery query) {
@@ -11682,18 +11682,6 @@ public abstract class PackageManager {
        sApplicationInfoCache.disableLocal();
    }

    private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
            new PropertyInvalidatedCache.AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO);

    /**
     * Invalidate caches of package and permission information system-wide.
     *
     * @hide
     */
    public static void invalidatePackageInfoCache() {
        sCacheAutoCorker.autoCork();
    }

    // Some of the flags don't affect the query result, but let's be conservative and cache
    // each combination of flags separately.

@@ -11752,7 +11740,7 @@ public abstract class PackageManager {
    private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
            sPackageInfoCache =
            new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
                    2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
                    "getPackageInfo") {
                @Override
                public PackageInfo recompute(PackageInfoQuery query) {
@@ -11784,17 +11772,40 @@ public abstract class PackageManager {
    /**
     * Inhibit package info cache invalidations when correct.
     *
     * @hide */
     * @hide
     */
    public static void corkPackageInfoCache() {
        PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
        sPackageInfoCache.corkInvalidations();
    }

    /**
     * Enable package info cache invalidations.
     *
     * @hide */
     * @hide
     */
    public static void uncorkPackageInfoCache() {
        PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
        sPackageInfoCache.uncorkInvalidations();
    }

    // This auto-corker is obsolete once the separate permission notifications feature is
    // committed.
    private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
            PropertyInvalidatedCache.separatePermissionNotificationsEnabled()
            ? null
            : new PropertyInvalidatedCache
                    .AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);

    /**
     * Invalidate caches of package and permission information system-wide.
     *
     * @hide
     */
    public static void invalidatePackageInfoCache() {
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
            sPackageInfoCache.invalidateCache();
        } else {
            sCacheAutoCorker.autoCork();
        }
    }

    /**
+35 −4
Original line number Diff line number Diff line
@@ -1795,14 +1795,45 @@ public final class PermissionManager {
        }
    }

    /** @hide */
    public static final String CACHE_KEY_PACKAGE_INFO =
    // The legacy system property "package_info" had two purposes: to invalidate PIC caches and to
    // signal that package information, and therefore permissions, might have changed.
    // AudioSystem is the only client of the signaling behavior.  The "separate permissions
    // notification" feature splits the two behaviors into two system property names.
    //
    // If the feature is disabled (legacy behavior) then the two system property names have the
    // same value.  This means there is only one system property in use.
    //
    // If the feature is enabled, then the two system property names have different values, which
    // means there is a system property used by PIC and a system property used for signaling.  The
    // legacy value is hard-coded in native code that relies on the signaling behavior, so the
    // system property name for signaling is the legacy property name, and the system property
    // name for PIC is new.
    private static String getPackageInfoCacheKey() {
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
            return PropertyInvalidatedCache.createSystemCacheKey("package_info_cache");
        } else {
            return CACHE_KEY_PACKAGE_INFO_NOTIFY;
        }
    }

    /**
     * The system property that is used to notify clients that package information, and therefore
     * permissions, may have changed.
     * @hide
     */
    public static final String CACHE_KEY_PACKAGE_INFO_NOTIFY =
            PropertyInvalidatedCache.createSystemCacheKey("package_info");

    /**
     * The system property that is used to invalidate PIC caches.
     * @hide
     */
    public static final String CACHE_KEY_PACKAGE_INFO_CACHE = getPackageInfoCacheKey();

    /** @hide */
    private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
            new PropertyInvalidatedCache<PermissionQuery, Integer>(
                    2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
                    2048, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPermission") {
                @Override
                public Integer recompute(PermissionQuery query) {
                    return checkPermissionUncached(query.permission, query.pid, query.uid,
@@ -1920,7 +1951,7 @@ public final class PermissionManager {
    private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
            sPackageNamePermissionCache =
            new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
                    16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
                    16, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPackageNamePermission") {
                @Override
                public Integer recompute(PackageNamePermissionQuery query) {
                    return checkPackageNamePermissionUncached(
+112 −2
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IUidObserver;
import android.app.NotificationManager;
import android.app.PropertyInvalidatedCache;
import android.app.UidObserver;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -248,6 +249,7 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -10941,14 +10943,122 @@ public class AudioService extends IAudioService.Stub
                }
            };
            mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                    task);
        } else {
            mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                    () -> mAudioServerLifecycleExecutor.execute(
                                mPermissionProvider::onPermissionStateChanged));
        }
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
            new PackageInfoTransducer().start();
        }
    }
    /**
     * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
     * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
     * property.  This operates on the popcorn principle: changes in the source are done when the
     * source has been quiet for the soak interval.
     *
     * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
     * sniffing.  It should be cleaned up.
     */
    private static class PackageInfoTransducer extends Thread {
        // The run/stop signal.
        private final AtomicBoolean mRunning = new AtomicBoolean(false);
        // The source of change information.
        private final PropertyInvalidatedCache.NonceWatcher mWatcher;
        // The handler for scheduling delayed reactions to changes.
        private final Handler mHandler;
        // How long to soak changes: 50ms is the legacy choice.
        private final static long SOAK_TIME_MS = 50;
        // The ubiquitous lock.
        private final Object mLock = new Object();
        // If positive, this is the soak expiration time.
        @GuardedBy("mLock")
        private long mSoakDeadlineMs = -1;
        // A source of unique long values.
        @GuardedBy("mLock")
        private long mToken = 0;
        PackageInfoTransducer() {
            mWatcher = PropertyInvalidatedCache
                       .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
            mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        PackageInfoTransducer.this.handleMessage(msg);
                    }};
        }
        public void run() {
            mRunning.set(true);
            while (mRunning.get()) {
                try {
                    final int changes = mWatcher.waitForChange();
                    if (changes == 0 || !mRunning.get()) {
                        continue;
                    }
                } catch (InterruptedException e) {
                    // We don't know why the exception occurred but keep running until told to
                    // stop.
                    continue;
                }
                trigger();
            }
        }
        @GuardedBy("mLock")
        private void updateLocked() {
            String n = Long.toString(mToken++);
            SystemProperties.set(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
        }
        private void trigger() {
            synchronized (mLock) {
                boolean alreadyQueued = mSoakDeadlineMs >= 0;
                final long nowMs = SystemClock.uptimeMillis();
                mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
                if (!alreadyQueued) {
                    mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
                    updateLocked();
                }
            }
        }
        private void handleMessage(Message msg) {
            synchronized (mLock) {
                if (mSoakDeadlineMs < 0) {
                    return;  // ???
                }
                final long nowMs = SystemClock.uptimeMillis();
                if (mSoakDeadlineMs > nowMs) {
                    mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
                    mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
                    return;
                }
                mSoakDeadlineMs = -1;
                updateLocked();
            }
        }
        /**
         * Cause the thread to exit.  Running is set to false and the watcher is awakened.
         */
        public void done() {
            mRunning.set(false);
            mWatcher.wakeUp();
        }
    }
    //==========================================================================================
Loading