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

Commit 6ae54d8c authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Allow specifying which packages to send a broadcast to.

Add BroadcastOptions.setIncludedPackages() that allows
specifying which packages to send a broadcast to.

Bug: 421215310
Test: atest tests/app/BroadcastsTest/src/android/app/cts/broadcasts/BroadcastOptionsIncludedPackagesTest.java
Flag: EXEMPT hidden api - there shouldn't be any effect until a client uses it
Change-Id: I32d5ec6b031cfefb3a48868e5f243449a41a6c1e
parent 1d4abc78
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.Process;

import com.android.internal.util.ArrayUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -68,6 +70,7 @@ public class BroadcastOptions extends ComponentOptions {
    private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
    private @Nullable IntentFilter mDeliveryGroupMatchingFilter;
    private @DeferralPolicy int mDeferralPolicy;
    private @Nullable String[] mIncludedPackages;

    /** @hide */
    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
@@ -230,6 +233,12 @@ public class BroadcastOptions extends ComponentOptions {
    private static final String KEY_DEFERRAL_POLICY =
            "android:broadcast.deferralPolicy";

    /**
     * Corresponds to {@link #setIncludedPackages(String[])}
     */
    private static final String KEY_INCLUDED_PACKAGES =
            "android:broadcast.includedPackageNames";

    /**
     * The list of delivery group policies which specify how multiple broadcasts belonging to
     * the same delivery group has to be handled.
@@ -355,6 +364,7 @@ public class BroadcastOptions extends ComponentOptions {
        mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
                IntentFilter.class);
        mDeferralPolicy = opts.getInt(KEY_DEFERRAL_POLICY, DEFERRAL_POLICY_DEFAULT);
        mIncludedPackages = opts.getStringArray(KEY_INCLUDED_PACKAGES);
    }

    /** @hide */
@@ -1112,6 +1122,27 @@ public class BroadcastOptions extends ComponentOptions {
        return (mFlags & FLAG_DEBUG_LOG) != 0;
    }

    /**
     * Set the list of packages to send the broadcast to.
     *
     * @hide
     */
    public BroadcastOptions setIncludedPackages(@Nullable String[] packageNames) {
        mIncludedPackages = packageNames;
        return this;
    }

    /**
     * Get the list of packages to send the broadcast to, that was previously set using
     * {@link #setIncludedPackages(String[])}.
     *
     * @hide
     */
    @Nullable
    public String[] getIncludedPackages() {
        return mIncludedPackages;
    }

    /**
     * Returns the created options as a Bundle, which can be passed to
     * {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -1178,6 +1209,9 @@ public class BroadcastOptions extends ComponentOptions {
        if (mDeferralPolicy != DEFERRAL_POLICY_DEFAULT) {
            b.putInt(KEY_DEFERRAL_POLICY, mDeferralPolicy);
        }
        if (!ArrayUtils.isEmpty(mIncludedPackages)) {
            b.putStringArray(KEY_INCLUDED_PACKAGES, mIncludedPackages);
        }
        return b;
    }

+5 −1
Original line number Diff line number Diff line
@@ -369,10 +369,14 @@ public abstract class PackageManagerInternal {
     *                         of the calling UID.
     * @param forSend          true if the invocation is intended for sending broadcasts. The value
     *                         of this parameter affects how packages are filtered.
     * @param includedPackages An optional array of package names. If provided, the results
     *                         will be limited to receivers from these packages only.
     *                         If {@code null}, receivers from all packages can be included.
     */
    public abstract List<ResolveInfo> queryIntentReceivers(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            int filterCallingUid, int callingPid, int userId, boolean forSend);
            int filterCallingUid, int callingPid, int userId, boolean forSend,
            @Nullable String[] includedPackages);

    /**
     * Retrieve all services that can be performed for the given intent.
+2 −1
Original line number Diff line number Diff line
@@ -5839,7 +5839,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                        intent, matchFlags, uid, userId));
            case ActivityManager.INTENT_SENDER_BROADCAST:
                return new ParceledListSlice<>(mPackageManagerInt.queryIntentReceivers(
                        intent, resolvedType, matchFlags, uid, Process.INVALID_PID, userId, false));
                        intent, resolvedType, matchFlags, uid, Process.INVALID_PID, userId,
                        false /* forSend */, null /* includedPackages */));
            default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT
                throw new IllegalStateException("Unsupported intent sender type: " + res.key.type);
        }
+26 −8
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
@@ -207,8 +208,7 @@ class BroadcastController {
     * Resolver for broadcast intents to registered receivers.
     * Holds BroadcastFilter (subclass of IntentFilter).
     */
    final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver =
            new IntentResolver<>() {
    final class BroadcastIntentResolver extends IntentResolver<BroadcastFilter, BroadcastFilter> {
        @Override
        protected boolean allowFilterResult(
                BroadcastFilter filter, List<BroadcastFilter> dest) {
@@ -245,7 +245,21 @@ class BroadcastController {
        protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
            return packageName.equals(filter.packageName);
        }

        public List<BroadcastFilter> queryIntent(@NonNull PackageDataSnapshot snapshot,
                Intent intent, String resolvedType, boolean defaultOnly, @UserIdInt int userId,
                @Nullable String[] includedPackages) {
            final List<BroadcastFilter> infos = super.queryIntent(snapshot, intent,
                    resolvedType, defaultOnly, userId);
            // TODO: b/428262517 - filter out packages that are not in includedPackages close to
            // intent resolution.
            if (includedPackages != null) {
                infos.removeIf(info -> !ArrayUtils.contains(includedPackages, info.packageName));
            }
            return infos;
        }
    };
    private final BroadcastIntentResolver mReceiverResolver = new BroadcastIntentResolver();

    BroadcastController(Context context, ActivityManagerService service, BroadcastQueue queue) {
        mContext = context;
@@ -1509,10 +1523,13 @@ class BroadcastController {
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
            receivers = collectReceiverComponents(
                    intent, resolvedType, callingUid, callingPid, users, broadcastAllowList);
                    intent, resolvedType, callingUid, callingPid, users, broadcastAllowList,
                    brOptions == null ? null : brOptions.getIncludedPackages());
        }
        if (intent.getComponent() == null) {
            final PackageDataSnapshot snapshot = mService.getPackageManagerInternal().snapshot();
            final String[] includedPackages = brOptions != null
                    ? brOptions.getIncludedPackages() : null;
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
@@ -1521,8 +1538,8 @@ class BroadcastController {
                        continue;
                    }
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(snapshot, intent,
                                    resolvedType, false /*defaultOnly*/, users[i]);
                            mReceiverResolver.queryIntent(snapshot, intent, resolvedType,
                                    false /*defaultOnly*/, users[i], includedPackages);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
@@ -1531,7 +1548,7 @@ class BroadcastController {
                }
            } else {
                registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
                        resolvedType, false /*defaultOnly*/, userId);
                        resolvedType, false /*defaultOnly*/, userId, includedPackages);
            }
            if (registeredReceivers != null) {
                SaferIntentUtils.blockNullAction(args, registeredReceivers);
@@ -1959,7 +1976,7 @@ class BroadcastController {

    private List<ResolveInfo> collectReceiverComponents(
            Intent intent, String resolvedType, int callingUid, int callingPid,
            int[] users, int[] broadcastAllowList) {
            int[] users, int[] broadcastAllowList, String[] includedPackages) {
        // TODO: come back and remove this assumption to triage all broadcasts
        long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;

@@ -1974,7 +1991,8 @@ class BroadcastController {
                continue;
            }
            List<ResolveInfo> newReceivers = mService.mPackageManagerInt.queryIntentReceivers(
                    intent, resolvedType, pmFlags, callingUid, callingPid, user, /* forSend */true);
                    intent, resolvedType, pmFlags, callingUid, callingPid, user,
                    /* forSend */ true, includedPackages);
            if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
                // If this is not the system user, we need to check for
                // any receivers that should be filtered out.
+2 −1
Original line number Diff line number Diff line
@@ -481,7 +481,8 @@ public class ComponentAliasResolver {
        i.setComponent(resolution.getTarget());

        List<ResolveInfo> resolved = pmi.queryIntentReceivers(
                i, resolvedType, packageFlags, callingUid, callingPid, userId, /*forSend*/ true);
                i, resolvedType, packageFlags, callingUid, callingPid, userId, /* forSend */ true,
                /* includedPackages */ null);
        if (resolved == null || resolved.size() == 0) {
            // Target component not found.
            Slog.w(TAG, "Alias target " + target.flattenToShortString() + " not found");
Loading