Loading services/core/java/com/android/server/am/BroadcastController.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -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); } } } } Loading Loading @@ -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); Loading services/core/java/com/android/server/am/BroadcastRecord.java +125 −24 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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, Loading @@ -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"); } } Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading services/core/java/com/android/server/am/broadcasts_flags.aconfig +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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 services/tests/mockingservicestests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -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", Loading services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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 Loading
services/core/java/com/android/server/am/BroadcastController.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -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); } } } } Loading Loading @@ -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); Loading
services/core/java/com/android/server/am/BroadcastRecord.java +125 −24 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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, Loading @@ -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"); } } Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading
services/core/java/com/android/server/am/broadcasts_flags.aconfig +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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
services/tests/mockingservicestests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -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", Loading
services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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