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

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

Merge "audio: Fix permission update race" into main

parents d9f00bdf 2e148f18
Loading
Loading
Loading
Loading
+45 −38
Original line number Original line Diff line number Diff line
@@ -790,8 +790,7 @@ public class AudioService extends IAudioService.Stub
    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    private final Executor mAudioServerLifecycleExecutor;
    private final Executor mAudioServerLifecycleExecutor;
    private final ConcurrentLinkedQueue<Future> mScheduledPermissionTasks =
    private final List<Future> mScheduledPermissionTasks = new ArrayList();
            new ConcurrentLinkedQueue();
    private IMediaProjectionManager mProjectionService; // to validate projection token
    private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -10600,20 +10599,24 @@ public class AudioService extends IAudioService.Stub
        // instanceof to simplify the construction requirements of AudioService for testing: no
        // instanceof to simplify the construction requirements of AudioService for testing: no
        // delayed execution during unit tests.
        // delayed execution during unit tests.
        if (mAudioServerLifecycleExecutor instanceof ScheduledExecutorService exec) {
        if (mAudioServerLifecycleExecutor instanceof ScheduledExecutorService exec) {
            // We schedule and add from a this callback thread only (serially), so the task order on
            // The order on the task list is an embedding on the scheduling order of the executor,
            // the serial executor matches the order on the task list.  This list should almost
            // since we synchronously add the scheduled task to our local queue. This list should
            // always have only two elements, except in cases of serious system contention.
            // almost always have only two elements, except in cases of serious system contention.
            Runnable task = () -> mScheduledPermissionTasks.add(exec.schedule(() -> {
            Runnable task = () -> {
                synchronized (mScheduledPermissionTasks) {
                    mScheduledPermissionTasks.add(exec.schedule(() -> {
                        try {
                        try {
                        // Clean up completed tasks before us to bound the queue length.  Cancel any
                            // Our goal is to remove all tasks which don't correspond to ourselves
                        // pending permission refresh tasks, after our own, since we are about to
                            // on this queue. Either they are already done (ahead of us), or we
                        // fulfill all of them.  We must be the first non-completed task in the
                            // should cancel them (behind us), since their work is redundant after
                        // queue, since the execution order matches the queue order.  Note, this
                            // we fire.
                        // task is the only writer on elements in the queue, and the task is
                            // We must be the first non-completed task in the queue, since the
                        // serialized, so
                            // execution order matches the queue order. Note, this task is the only
                            // writer on elements in the queue, and the task is serialized, so
                            //  => no in-flight cancellation
                            //  => no in-flight cancellation
                            //  => exists at least one non-completed task (ourselves)
                            //  => exists at least one non-completed task (ourselves)
                            //  => the queue is non-empty (only completed tasks removed)
                            //  => the queue is non-empty (only completed tasks removed)
                            synchronized (mScheduledPermissionTasks) {
                                final var iter = mScheduledPermissionTasks.iterator();
                                final var iter = mScheduledPermissionTasks.iterator();
                                while (iter.next().isDone()) {
                                while (iter.next().isDone()) {
                                    iter.remove();
                                    iter.remove();
@@ -10627,19 +10630,19 @@ public class AudioService extends IAudioService.Stub
                                    }
                                    }
                                    iter.remove();
                                    iter.remove();
                                }
                                }
                            }
                            mPermissionProvider.onPermissionStateChanged();
                            mPermissionProvider.onPermissionStateChanged();
                        } catch (Exception e) {
                        } catch (Exception e) {
                            // Handle executor routing exceptions to nowhere
                            // Handle executor routing exceptions to nowhere
                            Thread.getDefaultUncaughtExceptionHandler()
                            Thread.getDefaultUncaughtExceptionHandler()
                                    .uncaughtException(Thread.currentThread(), e);
                                    .uncaughtException(Thread.currentThread(), e);
                        }
                        }
                },
                    }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
                UPDATE_DELAY_MS,
                }
                TimeUnit.MILLISECONDS));
            };
            mAudioSystem.listenForSystemPropertyChange(
            mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    task);
                    task);
            task.run();
        } else {
        } else {
            mAudioSystem.listenForSystemPropertyChange(
            mAudioSystem.listenForSystemPropertyChange(
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
@@ -14710,7 +14713,11 @@ public class AudioService extends IAudioService.Stub
    @Override
    @Override
    /** @see AudioManager#permissionUpdateBarrier() */
    /** @see AudioManager#permissionUpdateBarrier() */
    public void permissionUpdateBarrier() {
    public void permissionUpdateBarrier() {
        for (var x : List.copyOf(mScheduledPermissionTasks)) {
        List<Future> snapshot;
        synchronized (mScheduledPermissionTasks) {
            snapshot = List.copyOf(mScheduledPermissionTasks);
        }
        for (var x : snapshot) {
            try {
            try {
                x.get();
                x.get();
            } catch (CancellationException e) {
            } catch (CancellationException e) {