diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java index 51bd4ea75005c..857aaf57f6407 100644 --- a/core/java/android/os/BundleMerger.java +++ b/core/java/android/os/BundleMerger.java @@ -85,37 +85,43 @@ public class BundleMerger implements Parcelable { /** * Merge strategy that numerically adds both conflicting values. */ - public static final int STRATEGY_NUMBER_ADD = 5; + public static final int STRATEGY_NUMBER_ADD = 10; /** * Merge strategy that numerically increments the first conflicting value by * {@code 1} and ignores the last conflicting value. */ - public static final int STRATEGY_NUMBER_INCREMENT_FIRST = 6; + public static final int STRATEGY_NUMBER_INCREMENT_FIRST = 20; + + /** + * Merge strategy that numerically increments the first conflicting value by + * {@code 1} and also numerically adds both conflicting values. + */ + public static final int STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD = 25; /** * Merge strategy that combines conflicting values using a boolean "and" * operation. */ - public static final int STRATEGY_BOOLEAN_AND = 7; + public static final int STRATEGY_BOOLEAN_AND = 30; /** * Merge strategy that combines conflicting values using a boolean "or" * operation. */ - public static final int STRATEGY_BOOLEAN_OR = 8; + public static final int STRATEGY_BOOLEAN_OR = 40; /** * Merge strategy that combines two conflicting array values by appending * the last array after the first array. */ - public static final int STRATEGY_ARRAY_APPEND = 9; + public static final int STRATEGY_ARRAY_APPEND = 50; /** * Merge strategy that combines two conflicting {@link ArrayList} values by * appending the last {@link ArrayList} after the first {@link ArrayList}. */ - public static final int STRATEGY_ARRAY_LIST_APPEND = 10; + public static final int STRATEGY_ARRAY_LIST_APPEND = 60; @IntDef(flag = false, prefix = { "STRATEGY_" }, value = { STRATEGY_REJECT, @@ -125,6 +131,7 @@ public class BundleMerger implements Parcelable { STRATEGY_COMPARABLE_MAX, STRATEGY_NUMBER_ADD, STRATEGY_NUMBER_INCREMENT_FIRST, + STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, STRATEGY_BOOLEAN_AND, STRATEGY_BOOLEAN_OR, STRATEGY_ARRAY_APPEND, @@ -282,6 +289,8 @@ public class BundleMerger implements Parcelable { return numberAdd(first, last); case STRATEGY_NUMBER_INCREMENT_FIRST: return numberIncrementFirst(first, last); + case STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD: + return numberAdd(numberIncrementFirst(first, last), last); case STRATEGY_BOOLEAN_AND: return booleanAnd(first, last); case STRATEGY_BOOLEAN_OR: diff --git a/core/tests/coretests/src/android/os/BundleMergerTest.java b/core/tests/coretests/src/android/os/BundleMergerTest.java index b7012ba66124c..43ed821e647d8 100644 --- a/core/tests/coretests/src/android/os/BundleMergerTest.java +++ b/core/tests/coretests/src/android/os/BundleMergerTest.java @@ -26,6 +26,7 @@ import static android.os.BundleMerger.STRATEGY_FIRST; import static android.os.BundleMerger.STRATEGY_LAST; import static android.os.BundleMerger.STRATEGY_NUMBER_ADD; import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST; +import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD; import static android.os.BundleMerger.STRATEGY_REJECT; import static android.os.BundleMerger.merge; @@ -150,6 +151,14 @@ public class BundleMergerTest { assertEquals(21L, merge(STRATEGY_NUMBER_INCREMENT_FIRST, 20L, 10L)); } + @Test + public void testStrategyNumberIncrementFirstAndAdd() throws Exception { + assertEquals(31, merge(STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, 10, 20)); + assertEquals(31, merge(STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, 20, 10)); + assertEquals(31L, merge(STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, 10L, 20L)); + assertEquals(31L, merge(STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, 20L, 10L)); + } + @Test public void testStrategyBooleanAnd() throws Exception { assertEquals(false, merge(STRATEGY_BOOLEAN_AND, false, false)); @@ -214,6 +223,29 @@ public class BundleMergerTest { }); } + @Test + public void testSetDefaultMergeStrategy() throws Exception { + final BundleMerger merger = new BundleMerger(); + merger.setDefaultMergeStrategy(STRATEGY_FIRST); + merger.setMergeStrategy(Intent.EXTRA_INDEX, STRATEGY_COMPARABLE_MAX); + + Bundle a = new Bundle(); + a.putString(Intent.EXTRA_SUBJECT, "SubjectA"); + a.putInt(Intent.EXTRA_INDEX, 10); + + Bundle b = new Bundle(); + b.putString(Intent.EXTRA_SUBJECT, "SubjectB"); + b.putInt(Intent.EXTRA_INDEX, 20); + + Bundle ab = merger.merge(a, b); + assertEquals("SubjectA", ab.getString(Intent.EXTRA_SUBJECT)); + assertEquals(20, ab.getInt(Intent.EXTRA_INDEX)); + + Bundle ba = merger.merge(b, a); + assertEquals("SubjectB", ba.getString(Intent.EXTRA_SUBJECT)); + assertEquals(20, ba.getInt(Intent.EXTRA_INDEX)); + } + @Test public void testMerge_Simple() throws Exception { final BundleMerger merger = new BundleMerger(); @@ -323,7 +355,7 @@ public class BundleMergerTest { merger.setMergeStrategy(DropBoxManager.EXTRA_TIME, STRATEGY_COMPARABLE_MAX); merger.setMergeStrategy(DropBoxManager.EXTRA_DROPPED_COUNT, - STRATEGY_NUMBER_INCREMENT_FIRST); + STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD); final long now = System.currentTimeMillis(); final Bundle a = new Bundle(); @@ -341,6 +373,11 @@ public class BundleMergerTest { c.putLong(DropBoxManager.EXTRA_TIME, now + 2000); c.putInt(DropBoxManager.EXTRA_DROPPED_COUNT, 0); + final Bundle d = new Bundle(); + d.putString(DropBoxManager.EXTRA_TAG, "system_server_strictmode"); + d.putLong(DropBoxManager.EXTRA_TIME, now + 3000); + d.putInt(DropBoxManager.EXTRA_DROPPED_COUNT, 5); + final Bundle ab = merger.merge(a, b); assertEquals("system_server_strictmode", ab.getString(DropBoxManager.EXTRA_TAG)); assertEquals(now + 1000, ab.getLong(DropBoxManager.EXTRA_TIME)); @@ -350,6 +387,11 @@ public class BundleMergerTest { assertEquals("system_server_strictmode", abc.getString(DropBoxManager.EXTRA_TAG)); assertEquals(now + 2000, abc.getLong(DropBoxManager.EXTRA_TIME)); assertEquals(2, abc.getInt(DropBoxManager.EXTRA_DROPPED_COUNT)); + + final Bundle abcd = merger.merge(abc, d); + assertEquals("system_server_strictmode", abcd.getString(DropBoxManager.EXTRA_TAG)); + assertEquals(now + 3000, abcd.getLong(DropBoxManager.EXTRA_TIME)); + assertEquals(8, abcd.getInt(DropBoxManager.EXTRA_DROPPED_COUNT)); } private static ArrayList arrayListOf(Object... values) { diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 27ee627d1b48f..639f1472ff53e 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -19,6 +19,7 @@ package com.android.server; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -29,6 +30,8 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; +import android.os.BundleMerger; import android.os.Debug; import android.os.DropBoxManager; import android.os.FileUtils; @@ -264,7 +267,7 @@ public final class DropBoxManagerService extends SystemService { public void handleMessage(Message msg) { switch (msg.what) { case MSG_SEND_BROADCAST: - prepareAndSendBroadcast((Intent) msg.obj); + prepareAndSendBroadcast((Intent) msg.obj, null); break; case MSG_SEND_DEFERRED_BROADCAST: Intent deferredIntent; @@ -272,27 +275,50 @@ public final class DropBoxManagerService extends SystemService { deferredIntent = mDeferredMap.remove((String) msg.obj); } if (deferredIntent != null) { - prepareAndSendBroadcast(deferredIntent); + prepareAndSendBroadcast(deferredIntent, + createBroadcastOptions(deferredIntent)); } break; } } - private void prepareAndSendBroadcast(Intent intent) { + private void prepareAndSendBroadcast(Intent intent, Bundle options) { if (!DropBoxManagerService.this.mBooted) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } getContext().sendBroadcastAsUser(intent, UserHandle.ALL, - android.Manifest.permission.READ_LOGS); + android.Manifest.permission.READ_LOGS, options); } private Intent createIntent(String tag, long time) { final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED); dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag); dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time); + dropboxIntent.putExtra(DropBoxManager.EXTRA_DROPPED_COUNT, 0); return dropboxIntent; } + private Bundle createBroadcastOptions(Intent intent) { + final BundleMerger extrasMerger = new BundleMerger(); + extrasMerger.setDefaultMergeStrategy(BundleMerger.STRATEGY_FIRST); + extrasMerger.setMergeStrategy(DropBoxManager.EXTRA_TIME, + BundleMerger.STRATEGY_COMPARABLE_MAX); + extrasMerger.setMergeStrategy(DropBoxManager.EXTRA_DROPPED_COUNT, + BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD); + + final String tag = intent.getStringExtra(DropBoxManager.EXTRA_TAG); + final IntentFilter matchingFilter = new IntentFilter( + DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED); + matchingFilter.addExtra(DropBoxManager.EXTRA_TAG, tag); + + return BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED) + .setDeliveryGroupMatchingFilter(matchingFilter) + .setDeliveryGroupExtrasMerger(extrasMerger) + .setDeferUntilActive(true) + .toBundle(); + } + /** * Schedule a dropbox broadcast to be sent asynchronously. */ 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 e0f9be4be188e..ad224df5b9443 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -60,11 +60,13 @@ import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.os.Bundle; import android.os.BundleMerger; +import android.os.DropBoxManager; import android.os.HandlerThread; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.IndentingPrintWriter; +import android.util.Pair; import androidx.test.filters.SmallTest; @@ -977,6 +979,59 @@ public class BroadcastQueueModernImplTest { List.of(musicVolumeChanged, alarmVolumeChanged, timeTick)); } + @Test + public void testDeliveryGroupPolicy_merged_matchingFilter() { + final long now = SystemClock.elapsedRealtime(); + final Pair dropboxEntryBroadcast1 = createDropboxBroadcast( + "TAG_A", now, 2); + final Pair dropboxEntryBroadcast2 = createDropboxBroadcast( + "TAG_B", now + 1000, 4); + final Pair dropboxEntryBroadcast3 = createDropboxBroadcast( + "TAG_A", now + 2000, 7); + + // Halt all processing so that we get a consistent view + mHandlerThread.getLooper().getQueue().postSyncBarrier(); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first, + dropboxEntryBroadcast1.second)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first, + dropboxEntryBroadcast2.second)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast3.first, + dropboxEntryBroadcast3.second)); + + final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + // dropboxEntryBroadcast1 and dropboxEntryBroadcast3 should be merged as they use the same + // tag and there shouldn't be a change to dropboxEntryBroadcast2. + final Pair expectedMergedBroadcast = createDropboxBroadcast( + "TAG_A", now + 2000, 10); + verifyPendingRecords(queue, List.of( + dropboxEntryBroadcast2.first, expectedMergedBroadcast.first)); + } + + private Pair createDropboxBroadcast(String tag, long timestampMs, + int droppedCount) { + final Intent dropboxEntryAdded = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED); + dropboxEntryAdded.putExtra(DropBoxManager.EXTRA_TAG, tag); + dropboxEntryAdded.putExtra(DropBoxManager.EXTRA_TIME, timestampMs); + dropboxEntryAdded.putExtra(DropBoxManager.EXTRA_DROPPED_COUNT, droppedCount); + + final BundleMerger extrasMerger = new BundleMerger(); + extrasMerger.setDefaultMergeStrategy(BundleMerger.STRATEGY_FIRST); + extrasMerger.setMergeStrategy(DropBoxManager.EXTRA_TIME, + BundleMerger.STRATEGY_COMPARABLE_MAX); + extrasMerger.setMergeStrategy(DropBoxManager.EXTRA_DROPPED_COUNT, + BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD); + final IntentFilter matchingFilter = new IntentFilter( + DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED); + matchingFilter.addExtra(DropBoxManager.EXTRA_TAG, tag); + final BroadcastOptions optionsDropboxEntryAdded = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED) + .setDeliveryGroupMatchingFilter(matchingFilter) + .setDeliveryGroupExtrasMerger(extrasMerger); + return Pair.create(dropboxEntryAdded, optionsDropboxEntryAdded); + } + @Test public void testVerifyEnqueuedTime_withReplacePending() { final Intent userPresent = new Intent(Intent.ACTION_USER_PRESENT); diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java index a25f4920030c2..1c79b503c2d52 100644 --- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java +++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java @@ -20,6 +20,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.os.Bundle; import android.os.DropBoxManager; import android.os.Looper; import android.os.Parcel; @@ -66,7 +67,7 @@ public class DropBoxTest extends AndroidTestCase { mContext = new ContextWrapper(super.getContext()) { @Override public void sendBroadcastAsUser(Intent intent, - UserHandle user, String receiverPermission) { + UserHandle user, String receiverPermission, Bundle options) { // Don't actually send broadcasts. } };