From 13b8910d23e5d43c8097f369e89bdb4f318f2fdb Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Tue, 27 Dec 2022 03:38:08 +0000 Subject: [PATCH] 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 --- .../server/am/BroadcastProcessQueue.java | 1 + .../android/server/am/BroadcastRecord.java | 18 +++++++++++- .../am/BroadcastQueueModernImplTest.java | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index d53f8fb1c8822..bec2a2e5f53a0 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -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; diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 24cf3d2800638..9f31f64a5c26d 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -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; } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 8d78cd6fb0124..60f86219e6ab3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -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 componentNameList) { final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED); packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);