summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sudheer Shanka <sudheersai@google.com> 2023-04-30 22:06:10 +0000
committer Sudheer Shanka <sudheersai@google.com> 2023-05-02 17:02:49 -0700
commitfd41bfccf0201e2c706af16d7424721036dceea8 (patch)
treea10050ab952e7872455d3daf6657a30337e102be
parent2d094275ea4dc297e84066210a388fc9a8e1b31c (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.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java137
-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;
}