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

Commit 102e1422 authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Limit the scope of receiver priorities to within a process.

With this change, a receiver's priority value will only
affect the order in which broadcasts are delivered within
the same process that the receiver is running in.

Bug: 371307720
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
Flag: com.android.server.am.limit_priority_scope
Change-Id: I23df1426ca9f493b82574fbcfffc36de29f2b7dd
parent 6349ad59
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -593,7 +593,7 @@ class BroadcastController {
                            originalStickyCallingUid, BackgroundStartPrivileges.NONE,
                            originalStickyCallingUid, BackgroundStartPrivileges.NONE,
                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
                            null /* filterExtrasForReceiver */,
                            null /* filterExtrasForReceiver */,
                            broadcast.originalCallingAppProcessState);
                            broadcast.originalCallingAppProcessState, mService.mPlatformCompat);
                    queue.enqueueBroadcastLocked(r);
                    queue.enqueueBroadcastLocked(r);
                }
                }
            }
            }
@@ -1631,7 +1631,7 @@ class BroadcastController {
                    receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
                    receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
                    ordered, sticky, false, userId,
                    ordered, sticky, false, userId,
                    backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
                    backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
                    callerAppProcessState);
                    callerAppProcessState, mService.mPlatformCompat);
            broadcastSentEventRecord.setBroadcastRecord(r);
            broadcastSentEventRecord.setBroadcastRecord(r);


            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
+125 −24
Original line number Original line Diff line number Diff line
@@ -42,6 +42,9 @@ import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions.DeliveryGroupPolicy;
import android.app.BroadcastOptions.DeliveryGroupPolicy;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.Intent;
@@ -55,10 +58,12 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.IntArray;
import android.util.PrintWriterPrinter;
import android.util.PrintWriterPrinter;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoOutputStream;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.compat.PlatformCompat;


import dalvik.annotation.optimization.NeverCompile;
import dalvik.annotation.optimization.NeverCompile;


@@ -77,6 +82,16 @@ import java.util.function.BiFunction;
 * An active intent broadcast.
 * An active intent broadcast.
 */
 */
final class BroadcastRecord extends Binder {
final class BroadcastRecord extends Binder {
    /**
     * Limit the scope of the priority values to the process level. This means that priority values
     * will only influence the order of broadcast delivery within the same process.
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
    @Overridable
    @VisibleForTesting
    static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L;

    final @NonNull Intent intent;    // the original intent that generated us
    final @NonNull Intent intent;    // the original intent that generated us
    final @Nullable ComponentName targetComp; // original component name set on the intent
    final @Nullable ComponentName targetComp; // original component name set on the intent
    final @Nullable ProcessRecord callerApp; // process that sent this
    final @Nullable ProcessRecord callerApp; // process that sent this
@@ -417,13 +432,13 @@ final class BroadcastRecord extends Binder {
            @NonNull BackgroundStartPrivileges backgroundStartPrivileges,
            @NonNull BackgroundStartPrivileges backgroundStartPrivileges,
            boolean timeoutExempt,
            boolean timeoutExempt,
            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
            int callerAppProcessState) {
            int callerAppProcessState, PlatformCompat platformCompat) {
        this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid,
        this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid,
                callingUid, callerInstantApp, resolvedType, requiredPermissions,
                callingUid, callerInstantApp, resolvedType, requiredPermissions,
                excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp,
                excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp,
                resultTo, resultCode, resultData, resultExtras, serialized, sticky,
                resultTo, resultCode, resultData, resultExtras, serialized, sticky,
                initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt,
                initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt,
                filterExtrasForReceiver, callerAppProcessState);
                filterExtrasForReceiver, callerAppProcessState, platformCompat);
    }
    }


    BroadcastRecord(BroadcastQueue _queue,
    BroadcastRecord(BroadcastQueue _queue,
@@ -439,7 +454,7 @@ final class BroadcastRecord extends Binder {
            @NonNull BackgroundStartPrivileges backgroundStartPrivileges,
            @NonNull BackgroundStartPrivileges backgroundStartPrivileges,
            boolean timeoutExempt,
            boolean timeoutExempt,
            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
            int callerAppProcessState) {
            int callerAppProcessState, PlatformCompat platformCompat) {
        if (_intent == null) {
        if (_intent == null) {
            throw new NullPointerException("Can't construct with a null intent");
            throw new NullPointerException("Can't construct with a null intent");
        }
        }
@@ -466,7 +481,8 @@ final class BroadcastRecord extends Binder {
        urgent = calculateUrgent(_intent, _options);
        urgent = calculateUrgent(_intent, _options);
        deferUntilActive = calculateDeferUntilActive(_callingUid,
        deferUntilActive = calculateDeferUntilActive(_callingUid,
                _options, _resultTo, _serialized, urgent);
                _options, _resultTo, _serialized, urgent);
        blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized);
        blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(
                receivers, _serialized, platformCompat);
        scheduledTime = new long[delivery.length];
        scheduledTime = new long[delivery.length];
        terminalTime = new long[delivery.length];
        terminalTime = new long[delivery.length];
        resultToApp = _resultToApp;
        resultToApp = _resultToApp;
@@ -730,7 +746,8 @@ final class BroadcastRecord extends Binder {
    }
    }


    /**
    /**
     * Determine if the result of {@link #calculateBlockedUntilBeyondCount(List, boolean)}
     * Determine if the result of
     * {@link #calculateBlockedUntilBeyondCount(List, boolean, PlatformCompat)}
     * has prioritized tranches of receivers.
     * has prioritized tranches of receivers.
     */
     */
    @VisibleForTesting
    @VisibleForTesting
@@ -754,19 +771,80 @@ final class BroadcastRecord extends Binder {
     */
     */
    @VisibleForTesting
    @VisibleForTesting
    static @NonNull int[] calculateBlockedUntilBeyondCount(
    static @NonNull int[] calculateBlockedUntilBeyondCount(
            @NonNull List<Object> receivers, boolean ordered) {
            @NonNull List<Object> receivers, boolean ordered, PlatformCompat platformCompat) {
        final int N = receivers.size();
        final int N = receivers.size();
        final int[] blockedUntilBeyondCount = new int[N];
        final int[] blockedUntilBeyondCount = new int[N];
        int lastPriority = 0;
        int lastPriorityIndex = 0;
        for (int i = 0; i < N; i++) {
        if (ordered) {
        if (ordered) {
            // When sending an ordered broadcast, we need to block this
            // When sending an ordered broadcast, we need to block this
            // receiver until all previous receivers have terminated
            // receiver until all previous receivers have terminated
            for (int i = 0; i < N; i++) {
                blockedUntilBeyondCount[i] = i;
                blockedUntilBeyondCount[i] = i;
            }
        } else {
            if (Flags.limitPriorityScope()) {
                final boolean[] changeEnabled = calculateChangeStateForReceivers(
                        receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat);

                // Priority of the previous tranche
                int lastTranchePriority = 0;
                // Priority of the current tranche
                int currentTranchePriority = 0;
                // Index of the last receiver in the previous tranche
                int lastTranchePriorityIndex = -1;
                // Index of the last receiver with change disabled in the previous tranche
                int lastTrancheChangeDisabledIndex = -1;
                // Index of the last receiver with change disabled in the current tranche
                int currentTrancheChangeDisabledIndex = -1;

                for (int i = 0; i < N; i++) {
                    final int thisPriority = getReceiverPriority(receivers.get(i));
                    if (i == 0) {
                        currentTranchePriority = thisPriority;
                        if (!changeEnabled[i]) {
                            currentTrancheChangeDisabledIndex = i;
                        }
                        continue;
                    }

                    // Check if a new priority tranche has started
                    if (thisPriority != currentTranchePriority) {
                        // Update tranche boundaries and reset the disabled index.
                        if (currentTrancheChangeDisabledIndex != -1) {
                            lastTrancheChangeDisabledIndex = currentTrancheChangeDisabledIndex;
                        }
                        lastTranchePriority = currentTranchePriority;
                        lastTranchePriorityIndex = i - 1;
                        currentTranchePriority = thisPriority;
                        currentTrancheChangeDisabledIndex = -1;
                    }
                    if (!changeEnabled[i]) {
                        currentTrancheChangeDisabledIndex = i;

                        // Since the change is disabled, block the current receiver until the
                        // last receiver in the previous tranche.
                        blockedUntilBeyondCount[i] = lastTranchePriorityIndex + 1;
                    } else if (thisPriority != lastTranchePriority) {
                        // If the changeId was disabled for an earlier receiver and the current
                        // receiver has a different priority, block the current receiver
                        // until that earlier receiver.
                        if (lastTrancheChangeDisabledIndex != -1) {
                            blockedUntilBeyondCount[i] = lastTrancheChangeDisabledIndex + 1;
                        }
                    }
                }
                // If the entire list is in the same priority tranche or no receivers had
                // changeId disabled, mark as -1 to indicate that none of them need to wait
                if (N > 0 && (lastTranchePriorityIndex == -1
                        || (lastTrancheChangeDisabledIndex == -1
                                && currentTrancheChangeDisabledIndex == -1))) {
                    Arrays.fill(blockedUntilBeyondCount, -1);
                }
            } else {
            } else {
                // When sending a prioritized broadcast, we only need to wait
                // When sending a prioritized broadcast, we only need to wait
                // for the previous tranche of receivers to be terminated
                // for the previous tranche of receivers to be terminated
                int lastPriority = 0;
                int lastPriorityIndex = 0;
                for (int i = 0; i < N; i++) {
                    final int thisPriority = getReceiverPriority(receivers.get(i));
                    final int thisPriority = getReceiverPriority(receivers.get(i));
                    if ((i == 0) || (thisPriority != lastPriority)) {
                    if ((i == 0) || (thisPriority != lastPriority)) {
                        lastPriority = thisPriority;
                        lastPriority = thisPriority;
@@ -776,15 +854,38 @@ final class BroadcastRecord extends Binder {
                        blockedUntilBeyondCount[i] = lastPriorityIndex;
                        blockedUntilBeyondCount[i] = lastPriorityIndex;
                    }
                    }
                }
                }
        }
                // If the entire list is in the same priority tranche, mark as -1 to
                // If the entire list is in the same priority tranche, mark as -1 to
                // indicate that none of them need to wait
                // indicate that none of them need to wait
                if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
                if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
                    Arrays.fill(blockedUntilBeyondCount, -1);
                    Arrays.fill(blockedUntilBeyondCount, -1);
                }
                }
            }
        }
        return blockedUntilBeyondCount;
        return blockedUntilBeyondCount;
    }
    }


    @VisibleForTesting
    static @NonNull boolean[] calculateChangeStateForReceivers(@NonNull List<Object> receivers,
            long changeId, PlatformCompat platformCompat) {
        final SparseBooleanArray changeStateForUids = new SparseBooleanArray();
        final int count = receivers.size();
        final boolean[] changeStateForReceivers = new boolean[count];
        for (int i = 0; i < count; ++i) {
            final int receiverUid = getReceiverUid(receivers.get(i));
            final boolean isChangeEnabled;
            final int idx = changeStateForUids.indexOfKey(receiverUid);
            if (idx >= 0) {
                isChangeEnabled = changeStateForUids.valueAt(idx);
            } else {
                isChangeEnabled = platformCompat.isChangeEnabledByUidInternalNoLogging(
                        changeId, receiverUid);
                changeStateForUids.put(receiverUid, isChangeEnabled);
            }
            changeStateForReceivers[i] = isChangeEnabled;
        }
        return changeStateForReceivers;
    }

    static int getReceiverUid(@NonNull Object receiver) {
    static int getReceiverUid(@NonNull Object receiver) {
        if (receiver instanceof BroadcastFilter) {
        if (receiver instanceof BroadcastFilter) {
            return ((BroadcastFilter) receiver).owningUid;
            return ((BroadcastFilter) receiver).owningUid;
+8 −0
Original line number Original line Diff line number Diff line
@@ -8,3 +8,11 @@ flag {
    is_fixed_read_only: true
    is_fixed_read_only: true
    bug: "369487976"
    bug: "369487976"
}
}

flag {
    name: "limit_priority_scope"
    namespace: "backstage_power"
    description: "Limit the scope of receiver priorities to within a process"
    is_fixed_read_only: true
    bug: "369487976"
}
 No newline at end of file
+1 −0
Original line number Original line Diff line number Diff line
@@ -134,6 +134,7 @@ android_ravenwood_test {
        "androidx.annotation_annotation",
        "androidx.annotation_annotation",
        "androidx.test.rules",
        "androidx.test.rules",
        "services.core",
        "services.core",
        "servicestests-utils-mockito-extended",
    ],
    ],
    srcs: [
    srcs: [
        "src/com/android/server/am/BroadcastRecordTest.java",
        "src/com/android/server/am/BroadcastRecordTest.java",
+11 −0
Original line number Original line Diff line number Diff line
@@ -41,6 +41,7 @@ import android.os.TestLooperManager;
import android.os.UserHandle;
import android.os.UserHandle;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings;
import android.util.SparseArray;
import android.util.SparseArray;


@@ -97,6 +98,9 @@ public abstract class BaseBroadcastQueueTest {
            .spyStatic(ProcessList.class)
            .spyStatic(ProcessList.class)
            .build();
            .build();



    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    @Rule
    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();


@@ -112,6 +116,8 @@ public abstract class BaseBroadcastQueueTest {
    AlarmManagerInternal mAlarmManagerInt;
    AlarmManagerInternal mAlarmManagerInt;
    @Mock
    @Mock
    ProcessList mProcessList;
    ProcessList mProcessList;
    @Mock
    PlatformCompat mPlatformCompat;


    @Mock
    @Mock
    AppStartInfoTracker mAppStartInfoTracker;
    AppStartInfoTracker mAppStartInfoTracker;
@@ -178,6 +184,11 @@ public abstract class BaseBroadcastQueueTest {
        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());


        doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker();
        doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker();

        doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
                eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
        doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
                eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
    }
    }


    public void tearDown() throws Exception {
    public void tearDown() throws Exception {
Loading