Preserve enqueue time of replaced broadcast records.

When a broadcast is sent with FLAG_RECEIVER_REPLACE_PENDING,
we perform in-place swap with older broadcasts but we don't
update the enqueue times and this could result in other
processes getting prioritized even though they have broadcasts
that have been enqueued at a later time.

Bug: 263803500
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
Change-Id: Ie4524a6b05d66be0725650be888f45d2903e1daa
This commit is contained in:
Sudheer Shanka
2022-12-27 03:38:08 +00:00
parent 09183fb168
commit 13b8910d23
3 changed files with 47 additions and 1 deletions

View File

@@ -266,6 +266,7 @@ class BroadcastProcessQueue {
// Exact match found; perform in-place swap
args.arg1 = record;
args.argi1 = recordIndex;
record.copyEnqueueTimeFrom(testRecord);
onBroadcastDequeued(testRecord, testRecordIndex);
onBroadcastEnqueued(record, recordIndex);
return true;

View File

@@ -106,6 +106,10 @@ final class BroadcastRecord extends Binder {
@UptimeMillisLong long enqueueTime; // when broadcast enqueued
@ElapsedRealtimeLong long enqueueRealTime; // when broadcast enqueued
@CurrentTimeMillisLong long enqueueClockTime; // when broadcast enqueued
// When broadcast is originally enqueued. Only used in case of replacing broadcasts
// with FLAG_RECEIVER_REPLACE_PENDING. If it is 0, then 'enqueueClockTime' is the original
// enqueue time.
@UptimeMillisLong long originalEnqueueClockTime;
@UptimeMillisLong long dispatchTime; // when broadcast dispatch started
@ElapsedRealtimeLong long dispatchRealTime; // when broadcast dispatch started
@CurrentTimeMillisLong long dispatchClockTime; // when broadcast dispatch started
@@ -252,7 +256,12 @@ final class BroadcastRecord extends Binder {
pw.print(prefix); pw.print("enqueueClockTime=");
pw.print(sdf.format(new Date(enqueueClockTime)));
pw.print(" dispatchClockTime=");
pw.println(sdf.format(new Date(dispatchClockTime)));
pw.print(sdf.format(new Date(dispatchClockTime)));
if (originalEnqueueClockTime > 0) {
pw.print(" originalEnqueueClockTime=");
pw.print(sdf.format(new Date(originalEnqueueClockTime)));
}
pw.println();
pw.print(prefix); pw.print("dispatchTime=");
TimeUtils.formatDuration(dispatchTime, now, pw);
pw.print(" (");
@@ -615,6 +624,13 @@ final class BroadcastRecord extends Binder {
return delivery[index];
}
void copyEnqueueTimeFrom(@NonNull BroadcastRecord replacedBroadcast) {
originalEnqueueClockTime = enqueueClockTime;
enqueueTime = replacedBroadcast.enqueueTime;
enqueueRealTime = replacedBroadcast.enqueueRealTime;
enqueueClockTime = replacedBroadcast.enqueueClockTime;
}
boolean isForeground() {
return (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
}

View File

@@ -946,6 +946,35 @@ public class BroadcastQueueModernImplTest {
List.of(musicVolumeChanged, alarmVolumeChanged, timeTick));
}
@Test
public void testVerifyEnqueuedTime_withReplacePending() {
final Intent userPresent = new Intent(Intent.ACTION_USER_PRESENT);
userPresent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
// Halt all processing so that we get a consistent view
mHandlerThread.getLooper().getQueue().postSyncBarrier();
final BroadcastRecord userPresentRecord1 = makeBroadcastRecord(userPresent);
final BroadcastRecord userPresentRecord2 = makeBroadcastRecord(userPresent);
mImpl.enqueueBroadcastLocked(userPresentRecord1);
mImpl.enqueueBroadcastLocked(userPresentRecord2);
final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN,
getUidForPackage(PACKAGE_GREEN));
queue.makeActiveNextPending();
// Verify that there is only one record pending and its enqueueTime is
// same as that of userPresentRecord1.
final BroadcastRecord activeRecord = queue.getActive();
assertEquals(userPresentRecord1.enqueueTime, activeRecord.enqueueTime);
assertEquals(userPresentRecord1.enqueueRealTime, activeRecord.enqueueRealTime);
assertEquals(userPresentRecord1.enqueueClockTime, activeRecord.enqueueClockTime);
assertThat(activeRecord.originalEnqueueClockTime)
.isGreaterThan(activeRecord.enqueueClockTime);
assertTrue(queue.isEmpty());
}
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);