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

Commit 05ec4717 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

BroadcastQueue: skip disabled apps/components.

The OS can request that a package, sub-components, or an entire user
be "cleaned up", typically when something is disabled.  This change
implements this in the modern broadcast stack, along with tests.

We're treating "cleaned up" recievers as being "skipped", since we
can't change the indexes of BroadcastRecord.receivers after they've
been enqueued.

Bug: 245771249
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Change-Id: Ic4061288c72ac0528e9662cb973a189e4572163d
parent 6c42dde4
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -4515,7 +4515,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        // Clean-up disabled broadcast receivers.
        for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
            mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
                    packageName, disabledClasses, userId, true);
                    packageName, disabledClasses, userId);
        }
    }
@@ -4524,7 +4524,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        boolean didSomething = false;
        for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
            didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
                    null, null, userId, true);
                    null, null, userId);
        }
        return didSomething;
    }
@@ -4660,7 +4660,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (doit) {
            for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
                didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
                        packageName, null, userId, doit);
                        packageName, null, userId);
            }
        }
+36 −1
Original line number Diff line number Diff line
@@ -27,8 +27,11 @@ import android.util.IndentingPrintWriter;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.server.am.BroadcastRecord.DeliveryState;

import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.function.BiPredicate;

/**
 * Queue of pending {@link BroadcastRecord} entries intended for delivery to a
@@ -153,6 +156,33 @@ class BroadcastProcessQueue {
        mPending.addLast(args);
    }

    /**
     * Skip any broadcasts matching the given predicate, marking them as
     * {@link BroadcastRecord#DELIVERY_SKIPPED}. Typically used when a package
     * or components have been disabled.
     * <p>
     * Note that we carefully preserve the broadcast in our queue to ensure that
     * we follow our normal flow for "finishing" a broadcast, which is where we
     * handle things like ordered broadcasts.
     */
    public boolean skipMatchingBroadcasts(@NonNull BiPredicate<BroadcastRecord, Object> predicate) {
        boolean didSomething = false;
        final Iterator<SomeArgs> it = mPending.iterator();
        while (it.hasNext()) {
            final SomeArgs args = it.next();
            final BroadcastRecord record = (BroadcastRecord) args.arg1;
            final int index = args.argi1;
            final Object receiver = record.receivers.get(index);
            if (predicate.test(record, receiver)) {
                record.setDeliveryState(index, BroadcastRecord.DELIVERY_SKIPPED);
                didSomething = true;
            }
        }
        // TODO: also check any active broadcast once we have a better "nonce"
        // representing each scheduled broadcast to avoid races
        return didSomething;
    }

    /**
     * Update if this process is in the "cached" state, typically signaling that
     * broadcast dispatch should be paused or delayed.
@@ -240,7 +270,12 @@ class BroadcastProcessQueue {
                traceTrackName, hashCode());
    }

    public void setActiveDeliveryState(int deliveryState) {
    public @DeliveryState int getActiveDeliveryState() {
        checkState(isActive(), "isActive");
        return mActive.delivery[mActiveIndex];
    }

    public void setActiveDeliveryState(@DeliveryState int deliveryState) {
        checkState(isActive(), "isActive");
        mActive.setDeliveryState(mActiveIndex, deliveryState);

+1 −1
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ public abstract class BroadcastQueue {
     */
    @GuardedBy("mService")
    public abstract boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
            @Nullable Set<String> filterByClasses, int userId, boolean doit);
            @Nullable Set<String> filterByClasses, int userId);

    /**
     * Quickly determine if this queue has broadcasts that are still waiting to
+3 −6
Original line number Diff line number Diff line
@@ -1693,18 +1693,15 @@ public class BroadcastQueueImpl extends BroadcastQueue {
    }

    public boolean cleanupDisabledPackageReceiversLocked(
            String packageName, Set<String> filterByClasses, int userId, boolean doit) {
            String packageName, Set<String> filterByClasses, int userId) {
        boolean didSomething = false;
        for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
            didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
                    packageName, filterByClasses, userId, doit);
            if (!doit && didSomething) {
                return true;
            }
                    packageName, filterByClasses, userId, true);
        }

        didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
                filterByClasses, userId, doit);
                filterByClasses, userId, true);

        return didSomething;
    }
+65 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Handler;
@@ -43,6 +44,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -60,6 +62,8 @@ import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
 * Alternative {@link BroadcastQueue} implementation which pivots broadcasts to
@@ -466,6 +470,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

    private void scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
        checkState(queue.isActive(), "isActive");

        // If someone already skipped us, finish immediately; typically due to a
        // component being disabled
        if (queue.getActiveDeliveryState() == BroadcastRecord.DELIVERY_SKIPPED) {
            finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
            return;
        }

        queue.setActiveDeliveryState(BroadcastRecord.DELIVERY_SCHEDULED);

        final ProcessRecord app = queue.app;
@@ -579,12 +591,61 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        }
    }

    private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY =
            (q) -> true;
    private static final BiPredicate<BroadcastRecord, Object> BROADCAST_PREDICATE_ANY =
            (r, o) -> true;

    @Override
    public boolean cleanupDisabledPackageReceiversLocked(String packageName,
            Set<String> filterByClasses, int userId, boolean doit) {
        // TODO: implement
    public boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
            @Nullable Set<String> filterByClasses, int userId) {
        final Predicate<BroadcastProcessQueue> queuePredicate;
        final BiPredicate<BroadcastRecord, Object> broadcastPredicate;
        if (packageName != null) {
            // Caller provided a package and user ID, so we're focused on queues
            // belonging to a specific UID
            final int uid = mService.mPackageManagerInt.getPackageUid(
                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
            queuePredicate = (q) -> {
                return q.uid == uid;
            };

            // If caller provided a set of classes, filter to skip only those;
            // otherwise we skip all broadcasts
            if (filterByClasses != null) {
                broadcastPredicate = (r, o) -> {
                    if (o instanceof ResolveInfo) {
                        return filterByClasses.contains(((ResolveInfo) o).activityInfo.name);
                    } else {
                        return false;
                    }
                };
            } else {
                broadcastPredicate = BROADCAST_PREDICATE_ANY;
            }
        } else {
            // Caller is cleaning up an entire user ID; skip all broadcasts
            queuePredicate = (q) -> {
                return UserHandle.getUserId(q.uid) == userId;
            };
            broadcastPredicate = BROADCAST_PREDICATE_ANY;
        }

        // Note that we carefully preserve any "skipped" broadcasts in their
        // queues so that we follow our normal flow for "finishing" a broadcast,
        // which is where we handle things like ordered broadcasts.
        boolean didSomething = false;
        for (int i = 0; i < mProcessQueues.size(); i++) {
            BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
            while (leaf != null) {
                if (queuePredicate.test(leaf)) {
                    didSomething |= leaf.skipMatchingBroadcasts(broadcastPredicate);
                }
                leaf = leaf.processNameNext;
            }
        }
        return didSomething;
    }

    @Override
    public void start(@NonNull ContentResolver resolver) {
Loading