diff options
| author | 2023-04-30 22:06:10 +0000 | |
|---|---|---|
| committer | 2023-05-02 17:02:49 -0700 | |
| commit | fd41bfccf0201e2c706af16d7424721036dceea8 (patch) | |
| tree | a10050ab952e7872455d3daf6657a30337e102be | |
| parent | 2d094275ea4dc297e84066210a388fc9a8e1b31c (diff) | |
Allow deferring initial delivery of sticky broadcasts.
If a sticky broadcast is marked as deferrable-until-active
while sending, store and use that while delivering the
broadcast to newly registered receivers for it.
Bug: 278016040
Test: atest services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
Change-Id: Ibc874a6c4296ad0962def5e46bd7b6bfa77266c5
| -rw-r--r-- | core/java/android/app/BroadcastOptions.java | 27 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 137 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java (renamed from services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java) | 3 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java) | 98 |
5 files changed, 200 insertions, 67 deletions
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 6357798a6cab..9549ebf698a8 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -351,6 +351,16 @@ public class BroadcastOptions extends ComponentOptions { mDeferralPolicy = opts.getInt(KEY_DEFERRAL_POLICY, DEFERRAL_POLICY_DEFAULT); } + /** @hide */ + @NonNull + public static BroadcastOptions makeWithDeferUntilActive(boolean deferUntilActive) { + final BroadcastOptions opts = BroadcastOptions.makeBasic(); + if (deferUntilActive) { + opts.setDeferralPolicy(DEFERRAL_POLICY_UNTIL_ACTIVE); + } + return opts; + } + /** * Set a duration for which the system should temporary place an application on the * power allowlist when this broadcast is being delivered to it. @@ -774,23 +784,6 @@ public class BroadcastOptions extends ComponentOptions { return mIdForResponseEvent; } - /** {@hide} */ - @Deprecated - public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) { - if (shouldDefer) { - setDeferralPolicy(DEFERRAL_POLICY_UNTIL_ACTIVE); - } else { - setDeferralPolicy(DEFERRAL_POLICY_NONE); - } - return this; - } - - /** {@hide} */ - @Deprecated - public boolean isDeferUntilActive() { - return (mDeferralPolicy == DEFERRAL_POLICY_UNTIL_ACTIVE); - } - /** * Sets deferral policy for this broadcast that specifies how this broadcast * can be deferred for delivery at some future point. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 1a126d72f9e9..471c0a8899e2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -400,7 +400,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private static final Bundle USER_PRESENT_INTENT_OPTIONS = BroadcastOptions.makeBasic() - .setDeferUntilActive(true) + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .toBundle(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a54e8e95b155..c5116f682521 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -44,7 +44,6 @@ import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED; import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED; -import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN; @@ -1169,8 +1168,26 @@ public class ActivityManagerService extends IActivityManager.Stub * for stickies that are sent to all users. */ @GuardedBy("this") - final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts = - new SparseArray<ArrayMap<String, ArrayList<Intent>>>(); + final SparseArray<ArrayMap<String, ArrayList<StickyBroadcast>>> mStickyBroadcasts = + new SparseArray<>(); + + @VisibleForTesting + static final class StickyBroadcast { + public Intent intent; + public boolean deferUntilActive; + + public static StickyBroadcast create(Intent intent, boolean deferUntilActive) { + final StickyBroadcast b = new StickyBroadcast(); + b.intent = intent; + b.deferUntilActive = deferUntilActive; + return b; + } + + @Override + public String toString() { + return "{intent=" + intent + ", defer=" + deferUntilActive + "}"; + } + } final ActiveServices mServices; @@ -2349,6 +2366,13 @@ public class ActivityManagerService extends IActivityManager.Stub /** Provides the basic functionality for unit tests. */ @VisibleForTesting ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread) { + this(injector, handlerThread, null); + } + + /** Provides the basic functionality for unit tests. */ + @VisibleForTesting + ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread, + @Nullable UserController userController) { mInjector = injector; mContext = mInjector.getContext(); mUiContext = null; @@ -2375,7 +2399,7 @@ public class ActivityManagerService extends IActivityManager.Stub mSystemThread = null; mUiHandler = injector.getUiHandler(null /* service */); mUidObserverController = new UidObserverController(mUiHandler); - mUserController = new UserController(this); + mUserController = userController == null ? new UserController(this) : userController; mInjector.mUserController = mUserController; mPendingIntentController = new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants); @@ -10831,12 +10855,12 @@ public class ActivityManagerService extends IActivityManager.Stub for (int user=0; user<mStickyBroadcasts.size(); user++) { long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS); proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user)); - for (Map.Entry<String, ArrayList<Intent>> ent + for (Map.Entry<String, ArrayList<StickyBroadcast>> ent : mStickyBroadcasts.valueAt(user).entrySet()) { long actionToken = proto.start(StickyBroadcastProto.ACTIONS); proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey()); - for (Intent intent : ent.getValue()) { - intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS, + for (StickyBroadcast broadcast : ent.getValue()) { + broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS, false, true, true, false); } proto.end(actionToken); @@ -10991,22 +11015,28 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" Sticky broadcasts for user "); pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":"); StringBuilder sb = new StringBuilder(128); - for (Map.Entry<String, ArrayList<Intent>> ent + for (Map.Entry<String, ArrayList<StickyBroadcast>> ent : mStickyBroadcasts.valueAt(user).entrySet()) { pw.print(" * Sticky action "); pw.print(ent.getKey()); if (dumpAll) { pw.println(":"); - ArrayList<Intent> intents = ent.getValue(); - final int N = intents.size(); + ArrayList<StickyBroadcast> broadcasts = ent.getValue(); + final int N = broadcasts.size(); for (int i=0; i<N; i++) { + final Intent intent = broadcasts.get(i).intent; + final boolean deferUntilActive = broadcasts.get(i).deferUntilActive; sb.setLength(0); sb.append(" Intent: "); - intents.get(i).toShortString(sb, false, true, false, false); - pw.println(sb.toString()); - Bundle bundle = intents.get(i).getExtras(); + intent.toShortString(sb, false, true, false, false); + pw.print(sb); + if (deferUntilActive) { + pw.print(" [D]"); + } + pw.println(); + Bundle bundle = intent.getExtras(); if (bundle != null) { - pw.print(" "); - pw.println(bundle.toString()); + pw.print(" extras: "); + pw.println(bundle); } } } else { @@ -13622,7 +13652,7 @@ public class ActivityManagerService extends IActivityManager.Stub String callerFeatureId, String receiverId, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { enforceNotIsolatedCaller("registerReceiver"); - ArrayList<Intent> stickyIntents = null; + ArrayList<StickyBroadcast> stickyBroadcasts = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0; @@ -13698,14 +13728,15 @@ public class ActivityManagerService extends IActivityManager.Stub while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { - ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); + ArrayMap<String, ArrayList<StickyBroadcast>> stickies = + mStickyBroadcasts.get(id); if (stickies != null) { - ArrayList<Intent> intents = stickies.get(action); - if (intents != null) { - if (stickyIntents == null) { - stickyIntents = new ArrayList<Intent>(); + ArrayList<StickyBroadcast> broadcasts = stickies.get(action); + if (broadcasts != null) { + if (stickyBroadcasts == null) { + stickyBroadcasts = new ArrayList<>(); } - stickyIntents.addAll(intents); + stickyBroadcasts.addAll(broadcasts); } } } @@ -13786,12 +13817,13 @@ public class ActivityManagerService extends IActivityManager.Stub // Dynamic receivers are exported by default for versions prior to T final boolean exported = (flags & Context.RECEIVER_EXPORTED) != 0; - ArrayList<Intent> allSticky = null; - if (stickyIntents != null) { + ArrayList<StickyBroadcast> allSticky = null; + if (stickyBroadcasts != null) { final ContentResolver resolver = mContext.getContentResolver(); // Look for any matching sticky broadcasts... - for (int i = 0, N = stickyIntents.size(); i < N; i++) { - Intent intent = stickyIntents.get(i); + for (int i = 0, N = stickyBroadcasts.size(); i < N; i++) { + final StickyBroadcast broadcast = stickyBroadcasts.get(i); + Intent intent = broadcast.intent; // Don't provided intents that aren't available to instant apps. if (instantApp && (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { @@ -13803,15 +13835,15 @@ public class ActivityManagerService extends IActivityManager.Stub // cannot lock ActivityManagerService here. if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { - allSticky = new ArrayList<Intent>(); + allSticky = new ArrayList<>(); } - allSticky.add(intent); + allSticky.add(broadcast); } } } // The first sticky in the list is returned directly back to the client. - Intent sticky = allSticky != null ? allSticky.get(0) : null; + Intent sticky = allSticky != null ? allSticky.get(0).intent : null; if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky); if (receiver == null) { return sticky; @@ -13893,10 +13925,11 @@ public class ActivityManagerService extends IActivityManager.Stub final int stickyCount = allSticky.size(); for (int i = 0; i < stickyCount; i++) { - Intent intent = allSticky.get(i); - BroadcastQueue queue = broadcastQueueForIntent(intent); - BroadcastRecord r = new BroadcastRecord(queue, intent, null, - null, null, -1, -1, false, null, null, null, null, OP_NONE, null, + final StickyBroadcast broadcast = allSticky.get(i); + BroadcastQueue queue = broadcastQueueForIntent(broadcast.intent); + BroadcastRecord r = new BroadcastRecord(queue, broadcast.intent, null, + null, null, -1, -1, false, null, null, null, null, OP_NONE, + BroadcastOptions.makeWithDeferUntilActive(broadcast.deferUntilActive), receivers, null, null, 0, null, null, false, true, true, -1, BackgroundStartPrivileges.NONE, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */, @@ -14274,7 +14307,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent - + " ordered=" + ordered + " userid=" + userId); + + " ordered=" + ordered + " userid=" + userId + + " options=" + (brOptions == null ? "null" : brOptions.toBundle())); if ((resultTo != null) && !ordered) { if (!mEnableModernQueue) { Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); @@ -14739,15 +14773,15 @@ public class ActivityManagerService extends IActivityManager.Stub // But first, if this is not a broadcast to all users, then // make sure it doesn't conflict with an existing broadcast to // all users. - ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get( + ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get( UserHandle.USER_ALL); if (stickies != null) { - ArrayList<Intent> list = stickies.get(intent.getAction()); + ArrayList<StickyBroadcast> list = stickies.get(intent.getAction()); if (list != null) { int N = list.size(); int i; for (i=0; i<N; i++) { - if (intent.filterEquals(list.get(i))) { + if (intent.filterEquals(list.get(i).intent)) { throw new IllegalArgumentException( "Sticky broadcast " + intent + " for user " + userId + " conflicts with existing global broadcast"); @@ -14756,27 +14790,30 @@ public class ActivityManagerService extends IActivityManager.Stub } } } - ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); + ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId); if (stickies == null) { stickies = new ArrayMap<>(); mStickyBroadcasts.put(userId, stickies); } - ArrayList<Intent> list = stickies.get(intent.getAction()); + ArrayList<StickyBroadcast> list = stickies.get(intent.getAction()); if (list == null) { list = new ArrayList<>(); stickies.put(intent.getAction(), list); } + final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive( + callingUid, brOptions, resultTo, ordered, + BroadcastRecord.calculateUrgent(intent, brOptions)); final int stickiesCount = list.size(); int i; for (i = 0; i < stickiesCount; i++) { - if (intent.filterEquals(list.get(i))) { + if (intent.filterEquals(list.get(i).intent)) { // This sticky already exists, replace it. - list.set(i, new Intent(intent)); + list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive)); break; } } if (i >= stickiesCount) { - list.add(new Intent(intent)); + list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive)); } } @@ -14970,6 +15007,16 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManager.BROADCAST_SUCCESS; } + @VisibleForTesting + ArrayList<StickyBroadcast> getStickyBroadcasts(String action, int userId) { + final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts = + mStickyBroadcasts.get(userId); + if (stickyBroadcasts == null) { + return null; + } + return stickyBroadcasts.get(action); + } + /** * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1 */ @@ -15151,14 +15198,14 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); + ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId); if (stickies != null) { - ArrayList<Intent> list = stickies.get(intent.getAction()); + ArrayList<StickyBroadcast> list = stickies.get(intent.getAction()); if (list != null) { int N = list.size(); int i; for (i=0; i<N; i++) { - if (intent.filterEquals(list.get(i))) { + if (intent.filterEquals(list.get(i).intent)) { list.remove(i); break; } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java index 4167c7e89758..64cc3979f181 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java @@ -49,7 +49,8 @@ public class ActivityManagerInternalTest { private static final long TEST_PROC_STATE_SEQ1 = 1111; private static final long TEST_PROC_STATE_SEQ2 = 1112; - @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); + @Rule public final ApplicationExitInfoTest.ServiceThreadRule + mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); @Mock private ActivityManagerService.Injector mMockInjector; diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 8c7b0c5a569a..f82d246d68bd 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -48,8 +48,11 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -59,13 +62,16 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.IApplicationThread; import android.app.IUidObserver; import android.app.SyncNotedAppOp; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManagerInternal; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -84,6 +90,7 @@ import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import com.android.server.LocalServices; +import com.android.server.am.ActivityManagerService.StickyBroadcast; import com.android.server.am.ProcessList.IsolatedUidRange; import com.android.server.am.ProcessList.IsolatedUidRangeAllocator; import com.android.server.am.UidObserverController.ChangeRecord; @@ -102,10 +109,12 @@ import org.mockito.MockitoAnnotations; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -120,6 +129,15 @@ import java.util.function.Function; public class ActivityManagerServiceTest { private static final String TAG = ActivityManagerServiceTest.class.getSimpleName(); + private static final int TEST_USER = 11; + + private static final String TEST_ACTION1 = "com.android.server.am.TEST_ACTION1"; + private static final String TEST_ACTION2 = "com.android.server.am.TEST_ACTION2"; + private static final String TEST_ACTION3 = "com.android.server.am.TEST_ACTION3"; + + private static final String TEST_EXTRA_KEY1 = "com.android.server.am.TEST_EXTRA_KEY1"; + private static final String TEST_EXTRA_VALUE1 = "com.android.server.am.TEST_EXTRA_VALUE1"; + private static final int TEST_UID = 11111; private static final int USER_ID = 666; @@ -150,11 +168,14 @@ public class ActivityManagerServiceTest { LocalServices.removeServiceForTest(PackageManagerInternal.class); } - @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); + @Rule + public final ApplicationExitInfoTest.ServiceThreadRule + mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); private Context mContext = getInstrumentation().getTargetContext(); @Mock private AppOpsService mAppOpsService; + @Mock private UserController mUserController; private TestInjector mInjector; private ActivityManagerService mAms; @@ -169,7 +190,14 @@ public class ActivityManagerServiceTest { mHandlerThread.start(); mHandler = new TestHandler(mHandlerThread.getLooper()); mInjector = new TestInjector(mContext); - mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread()); + doAnswer(invocation -> { + final int userId = invocation.getArgument(2); + return userId; + }).when(mUserController).handleIncomingUser(anyInt(), anyInt(), anyInt(), anyBoolean(), + anyInt(), any(), any()); + doReturn(true).when(mUserController).isUserOrItsParentRunning(anyInt()); + mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread(), + mUserController); mAms.mConstants.mNetworkAccessTimeoutMs = 2000; mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext); mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper()); @@ -311,7 +339,8 @@ public class ActivityManagerServiceTest { } private void verifyUidRangesNoOverlap(IsolatedUidRange uidRange1, IsolatedUidRange uidRange2) { - IsolatedUidRange lowRange = uidRange1.mFirstUid <= uidRange2.mFirstUid ? uidRange1 : uidRange2; + IsolatedUidRange lowRange = uidRange1.mFirstUid <= uidRange2.mFirstUid + ? uidRange1 : uidRange2; IsolatedUidRange highRange = lowRange == uidRange1 ? uidRange2 : uidRange1; assertTrue(highRange.mFirstUid > lowRange.mLastUid); @@ -609,6 +638,69 @@ public class ActivityManagerServiceTest { } } + @Test + public void testBroadcastStickyIntent() { + final Intent intent1 = new Intent(TEST_ACTION1); + final Intent intent2 = new Intent(TEST_ACTION2) + .putExtra(TEST_EXTRA_KEY1, TEST_EXTRA_VALUE1); + final Intent intent3 = new Intent(TEST_ACTION3); + final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true); + + broadcastIntent(intent1, null, true); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), + StickyBroadcast.create(intent1, false)); + assertNull(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER)); + assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); + + broadcastIntent(intent2, options.toBundle(), true); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), + StickyBroadcast.create(intent1, false)); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), + StickyBroadcast.create(intent2, true)); + assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); + + broadcastIntent(intent3, null, true); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), + StickyBroadcast.create(intent1, false)); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), + StickyBroadcast.create(intent2, true)); + assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER), + StickyBroadcast.create(intent3, false)); + } + + @SuppressWarnings("GuardedBy") + private void broadcastIntent(Intent intent, Bundle options, boolean sticky) { + final int res = mAms.broadcastIntentLocked(null, null, null, intent, null, null, 0, + null, null, null, null, null, 0, options, false, sticky, + Process.myPid(), Process.myUid(), Process.myUid(), Process.myPid(), TEST_USER); + assertEquals(ActivityManager.BROADCAST_SUCCESS, res); + } + + private void assertStickyBroadcasts(ArrayList<StickyBroadcast> actualBroadcasts, + StickyBroadcast... expectedBroadcasts) { + final String errMsg = "Expected: " + Arrays.toString(expectedBroadcasts) + + "; Actual: " + Arrays.toString(actualBroadcasts.toArray()); + assertEquals(errMsg, expectedBroadcasts.length, actualBroadcasts.size()); + for (int i = 0; i < expectedBroadcasts.length; ++i) { + final StickyBroadcast expected = expectedBroadcasts[i]; + final StickyBroadcast actual = actualBroadcasts.get(i); + assertTrue(errMsg, areEquals(expected, actual)); + } + } + + private boolean areEquals(StickyBroadcast a, StickyBroadcast b) { + if (!Objects.equals(a.intent.getAction(), b.intent.getAction())) { + return false; + } + if (!Bundle.kindofEquals(a.intent.getExtras(), b.intent.getExtras())) { + return false; + } + if (a.deferUntilActive != b.deferUntilActive) { + return false; + } + return true; + } + private interface ObserverChangesVerifier { void verify(IUidObserver observer, ChangeRecord changeItem) throws RemoteException; } |