Loading core/java/android/app/BroadcastOptions.java +10 −17 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +1 −1 Original line number Diff line number Diff line Loading @@ -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(); Loading services/core/java/com/android/server/am/ActivityManagerService.java +92 −45 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -10844,12 +10868,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); Loading Loading @@ -11004,22 +11028,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 { Loading Loading @@ -13635,7 +13665,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; Loading Loading @@ -13711,14 +13741,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); } } } Loading Loading @@ -13799,12 +13830,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) { Loading @@ -13816,15 +13848,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; Loading Loading @@ -13906,10 +13938,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 */, Loading Loading @@ -14288,7 +14321,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!"); Loading Loading @@ -14753,15 +14787,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"); Loading @@ -14770,27 +14804,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)); } } Loading Loading @@ -14984,6 +15021,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 */ Loading Loading @@ -15165,14 +15212,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; } services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java→services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java→services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +95 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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()); Loading Loading @@ -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); Loading Loading @@ -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; } Loading Loading
core/java/android/app/BroadcastOptions.java +10 −17 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading
packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +1 −1 Original line number Diff line number Diff line Loading @@ -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(); Loading
services/core/java/com/android/server/am/ActivityManagerService.java +92 −45 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -10844,12 +10868,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); Loading Loading @@ -11004,22 +11028,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 { Loading Loading @@ -13635,7 +13665,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; Loading Loading @@ -13711,14 +13741,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); } } } Loading Loading @@ -13799,12 +13830,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) { Loading @@ -13816,15 +13848,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; Loading Loading @@ -13906,10 +13938,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 */, Loading Loading @@ -14288,7 +14321,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!"); Loading Loading @@ -14753,15 +14787,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"); Loading @@ -14770,27 +14804,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)); } } Loading Loading @@ -14984,6 +15021,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 */ Loading Loading @@ -15165,14 +15212,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; }
services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java→services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java→services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +95 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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()); Loading Loading @@ -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); Loading Loading @@ -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; } Loading