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

Commit 7274c3a4 authored by Atneya Nair's avatar Atneya Nair
Browse files

[audio] AudioService IPC invalidation listener changes

Remove the sysprop writing logic from the package/perm cache
invalidation listener relying on the new shared memory backing store for
the PIC/IPC cache. Instead, directly queue the permission sync task,
which is already throttled within AudioService.

This fixes issues with the permission updates during a prolonged
sequence of permission cache corks not triggering the perm sync task in
AudioService until the corking ended. This could happen during tests.

Note, this means that the sysprop listener within audioserver no longer
will be updated with the new memory backed IPC, which is tolerated since
both flags are moving to -next in the same release.

Test: atest CtsPermissionUiTestCases:android.permissionui.cts.CameraMicIndicatorsPermissionTest#testSafetyCenterMicIndicator
Flag: com.android.media.audio.audioserver_permissions
Fixes: 381370620
Fixes: 391175817
Change-Id: Ic30b5ade8456387a3877f6cf433ecf2e3aeb23ab
parent 5fef01f7
Loading
Loading
Loading
Loading
+28 −76
Original line number Original line Diff line number Diff line
@@ -826,6 +826,7 @@ public class AudioService extends IAudioService.Stub
    private final Executor mAudioServerLifecycleExecutor;
    private final Executor mAudioServerLifecycleExecutor;
    private long mSysPropListenerNativeHandle;
    private long mSysPropListenerNativeHandle;
    private CacheWatcher mCacheWatcher;
    private final List<Future> mScheduledPermissionTasks = new ArrayList();
    private final List<Future> mScheduledPermissionTasks = new ArrayList();
    private IMediaProjectionManager mProjectionService; // to validate projection token
    private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -11035,31 +11036,26 @@ public class AudioService extends IAudioService.Stub
                    }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
                    }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
                }
                }
            };
            };
            if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
                mCacheWatcher = new CacheWatcher(task);
                mCacheWatcher.start();
            } else {
                mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
                mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
                        PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                        PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                        task);
                        task);
            }
        } else {
        } else {
            mAudioSystem.listenForSystemPropertyChange(
            mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                    PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
                    () -> mAudioServerLifecycleExecutor.execute(
                    () -> mAudioServerLifecycleExecutor.execute(
                                mPermissionProvider::onPermissionStateChanged));
                                mPermissionProvider::onPermissionStateChanged));
        }
        }
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
            new PackageInfoTransducer().start();
        }
    }
    }
    /**
    /**
     * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
     * Listens for CACHE_KEY_PACKAGE_INFO_CACHE invalidations to trigger permission syncing
     * 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 {
    private static class CacheWatcher extends Thread {
        // The run/stop signal.
        // The run/stop signal.
        private final AtomicBoolean mRunning = new AtomicBoolean(false);
        private final AtomicBoolean mRunning = new AtomicBoolean(false);
@@ -11067,81 +11063,33 @@ public class AudioService extends IAudioService.Stub
        // The source of change information.
        // The source of change information.
        private final PropertyInvalidatedCache.NonceWatcher mWatcher;
        private final PropertyInvalidatedCache.NonceWatcher mWatcher;
        // The handler for scheduling delayed reactions to changes.
        // Task to trigger when cache changes
        private final Handler mHandler;
        private final Runnable mTask;
        // How long to soak changes: 50ms is the legacy choice.
        public CacheWatcher(Runnable r) {
        private final static long SOAK_TIME_MS = 50;
            mWatcher = PropertyInvalidatedCache.getNonceWatcher(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
        // The ubiquitous lock.
            mTask = r;
        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() {
        public void run() {
            mRunning.set(true);
            mRunning.set(true);
            while (mRunning.get()) {
            while (mRunning.get()) {
                doCheck();
                try {
                try {
                    final int changes = mWatcher.waitForChange();
                    mWatcher.waitForChange();
                    if (changes == 0 || !mRunning.get()) {
                        continue;
                    }
                } catch (InterruptedException e) {
                } catch (InterruptedException e) {
                    Log.wtf(TAG, "Unexpected Interrupt", e);
                    // We don't know why the exception occurred but keep running until told to
                    // We don't know why the exception occurred but keep running until told to
                    // stop.
                    // stop.
                    continue;
                    continue;
                }
                }
                trigger();
            }
            }
            }
        @GuardedBy("mLock")
        private void updateLocked() {
            String n = Long.toString(mToken++);
            SystemPropertySetter.setWithRetry(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
        }
        }
        private void trigger() {
        public synchronized void doCheck() {
            synchronized (mLock) {
            if (mWatcher.isChanged()) {
                boolean alreadyQueued = mSoakDeadlineMs >= 0;
                mTask.run();
                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();
            }
            }
        }
        }
@@ -15317,7 +15265,11 @@ public class AudioService extends IAudioService.Stub
    /** @see AudioManager#permissionUpdateBarrier() */
    /** @see AudioManager#permissionUpdateBarrier() */
    public void permissionUpdateBarrier() {
    public void permissionUpdateBarrier() {
        if (!audioserverPermissions()) return;
        if (!audioserverPermissions()) return;
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
            mCacheWatcher.doCheck();
        } else {
            mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
            mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
        }
        List<Future> snapshot;
        List<Future> snapshot;
        synchronized (mScheduledPermissionTasks) {
        synchronized (mScheduledPermissionTasks) {
            snapshot = List.copyOf(mScheduledPermissionTasks);
            snapshot = List.copyOf(mScheduledPermissionTasks);