blob: 08952eab071f31560546f1047f1a89a1936c47dc [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static android.content.Intent.ACTION_LOCKED_BOOT_COMPLETED;
import static android.content.Intent.ACTION_TIME_CHANGED;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
import static com.android.server.am.BroadcastRecord.DELIVERY_SKIPPED;
import static com.android.server.am.BroadcastRecord.DELIVERY_TIMEOUT;
import static com.android.server.am.BroadcastRecord.calculateBlockedUntilBeyondCount;
import static com.android.server.am.BroadcastRecord.calculateDeferUntilActive;
import static com.android.server.am.BroadcastRecord.calculateUrgent;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import android.app.ActivityManagerInternal;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
/**
* Test class for {@link BroadcastRecord}.
*
* Build/Install/Run:
* atest FrameworksServicesTests:BroadcastRecordTest
*/
@SmallTest
@RunWith(MockitoJUnitRunner.class)
public class BroadcastRecordTest {
private static final String TAG = "BroadcastRecordTest";
private static final int USER0 = UserHandle.USER_SYSTEM;
private static final int USER1 = USER0 + 1;
private static final int[] USER_LIST = new int[] {USER0, USER1};
private static final String PACKAGE1 = "pkg1";
private static final String PACKAGE2 = "pkg2";
private static final String PACKAGE3 = "pkg3";
private static final String PACKAGE4 = "pkg4";
private static final String[] PACKAGE_LIST = new String[] {PACKAGE1, PACKAGE2, PACKAGE3,
PACKAGE4};
private static final int SYSTEM_UID = android.os.Process.SYSTEM_UID;
private static final int APP_UID = android.os.Process.FIRST_APPLICATION_UID;
private static final BroadcastOptions OPT_DEFAULT = BroadcastOptions.makeBasic();
private static final BroadcastOptions OPT_NONE = BroadcastOptions.makeBasic()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
private static final BroadcastOptions OPT_UNTIL_ACTIVE = BroadcastOptions.makeBasic()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
@Mock ActivityManagerInternal mActivityManagerInternal;
@Mock BroadcastQueue mQueue;
@Mock ProcessRecord mProcess;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void testIsPrioritized_Empty() {
assertFalse(isPrioritized(List.of()));
}
@Test
public void testIsPrioritized_Single() {
assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), 0))));
assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), -10))));
assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), 10))));
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0)), false));
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), -10)), false));
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10)), false));
}
@Test
public void testIsPrioritized_No() {
assertFalse(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0))));
assertFalse(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 10),
createResolveInfo(PACKAGE3, getAppId(3), 10))));
assertArrayEquals(new int[] {-1,-1,-1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
assertArrayEquals(new int[] {-1,-1,-1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 10),
createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
}
@Test
public void testIsPrioritized_Yes() {
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), -10))));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0))));
assertArrayEquals(new int[] {0,1,2},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), -10)), false));
assertArrayEquals(new int[] {0,0,2,3,3},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 20),
createResolveInfo(PACKAGE2, getAppId(2), 20),
createResolveInfo(PACKAGE3, getAppId(3), 10),
createResolveInfo(PACKAGE3, getAppId(3), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
}
@Test
public void testSetDeliveryState_Single() {
final BroadcastRecord r = createBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfoWithPriority(0)));
assertEquals(DELIVERY_PENDING, r.getDeliveryState(0));
assertBlocked(r, false);
assertTerminalDeferredBeyond(r, 0, 0, 0);
r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
assertBlocked(r, false);
assertTerminalDeferredBeyond(r, 0, 1, 1);
// Identical state change has no effect
r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
assertBlocked(r, false);
assertTerminalDeferredBeyond(r, 0, 1, 1);
// Moving to terminal state updates counters
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
assertBlocked(r, false);
assertTerminalDeferredBeyond(r, 1, 0, 1);
// Trying to change terminal state has no effect
r.setDeliveryState(0, DELIVERY_TIMEOUT, TAG);
assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
assertBlocked(r, false);
assertTerminalDeferredBeyond(r, 1, 0, 1);
}
@Test
public void testSetDeliveryState_Unordered() {
final BroadcastRecord r = createBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0)));
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 0, 0, 0);
// Even though we finish a middle item in the tranche, we're not
// "beyond" it because there is still unfinished work before it
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 1, 0, 0);
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 2, 0, 2);
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 3, 0, 3);
}
@Test
public void testSetDeliveryState_Ordered() {
final BroadcastRecord r = createOrderedBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0)));
assertBlocked(r, false, true, true);
assertTerminalDeferredBeyond(r, 0, 0, 0);
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, true);
assertTerminalDeferredBeyond(r, 1, 0, 1);
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 2, 0, 2);
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false);
assertTerminalDeferredBeyond(r, 3, 0, 3);
}
@Test
public void testSetDeliveryState_DeferUntilActive() {
final BroadcastRecord r = createBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfoWithPriority(10),
createResolveInfoWithPriority(10),
createResolveInfoWithPriority(10),
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(0),
createResolveInfoWithPriority(-10),
createResolveInfoWithPriority(-10),
createResolveInfoWithPriority(-10)));
assertBlocked(r, false, false, false, true, true, true, true, true, true);
assertTerminalDeferredBeyond(r, 0, 0, 0);
r.setDeliveryState(0, DELIVERY_PENDING, TAG);
r.setDeliveryState(1, DELIVERY_DEFERRED, TAG);
r.setDeliveryState(2, DELIVERY_PENDING, TAG);
r.setDeliveryState(3, DELIVERY_DEFERRED, TAG);
r.setDeliveryState(4, DELIVERY_DEFERRED, TAG);
r.setDeliveryState(5, DELIVERY_DEFERRED, TAG);
r.setDeliveryState(6, DELIVERY_DEFERRED, TAG);
r.setDeliveryState(7, DELIVERY_PENDING, TAG);
r.setDeliveryState(8, DELIVERY_DEFERRED, TAG);
// Verify deferred counts ratchet up, but we're not "beyond" the first
// still-pending receiver
assertBlocked(r, false, false, false, true, true, true, true, true, true);
assertTerminalDeferredBeyond(r, 0, 6, 0);
// We're still not "beyond" the first still-pending receiver, even when
// we finish a receiver later in the first tranche
r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false, true, true, true, true, true, true);
assertTerminalDeferredBeyond(r, 1, 6, 0);
// Completing that last item in first tranche means we now unblock the
// second tranche, and since it's entirely deferred, the third traunche
// is unblocked too
r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false, false, false, false, false, false, false);
assertTerminalDeferredBeyond(r, 2, 6, 7);
// Moving a deferred item in an earlier tranche back to being pending
// doesn't change the fact that we've already moved beyond it
r.setDeliveryState(1, DELIVERY_PENDING, TAG);
assertBlocked(r, false, false, false, false, false, false, false, false, false);
assertTerminalDeferredBeyond(r, 2, 5, 7);
r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false, false, false, false, false, false, false);
assertTerminalDeferredBeyond(r, 3, 5, 7);
// Completing middle pending item is enough to fast-forward to end
r.setDeliveryState(7, DELIVERY_DELIVERED, TAG);
assertBlocked(r, false, false, false, false, false, false, false, false, false);
assertTerminalDeferredBeyond(r, 4, 5, 9);
// Moving everyone else directly into a finished state updates all the
// terminal counters
r.setDeliveryState(3, DELIVERY_SKIPPED, TAG);
r.setDeliveryState(4, DELIVERY_SKIPPED, TAG);
r.setDeliveryState(5, DELIVERY_SKIPPED, TAG);
r.setDeliveryState(6, DELIVERY_SKIPPED, TAG);
r.setDeliveryState(8, DELIVERY_SKIPPED, TAG);
assertBlocked(r, false, false, false, false, false, false, false, false, false);
assertTerminalDeferredBeyond(r, 9, 0, 9);
}
@Test
public void testGetReceiverIntent_Simple() {
final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastRecord r = createBroadcastRecord(
List.of(createResolveInfo(PACKAGE1, getAppId(1))), UserHandle.USER_ALL, intent);
final Intent actual = r.getReceiverIntent(r.receivers.get(0));
assertEquals(PACKAGE1, actual.getComponent().getPackageName());
assertNull(r.intent.getComponent());
}
@Test
public void testGetReceiverIntent_Filtered_Partial() {
final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra(Intent.EXTRA_INDEX, 42);
final BroadcastRecord r = createBroadcastRecord(
List.of(createResolveInfo(PACKAGE1, getAppId(1))), UserHandle.USER_ALL, intent,
(uid, extras) -> Bundle.EMPTY,
null /* options */);
final Intent actual = r.getReceiverIntent(r.receivers.get(0));
assertEquals(PACKAGE1, actual.getComponent().getPackageName());
assertEquals(-1, actual.getIntExtra(Intent.EXTRA_INDEX, -1));
assertNull(r.intent.getComponent());
assertEquals(42, r.intent.getIntExtra(Intent.EXTRA_INDEX, -1));
}
@Test
public void testGetReceiverIntent_Filtered_Complete() {
final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra(Intent.EXTRA_INDEX, 42);
final BroadcastRecord r = createBroadcastRecord(
List.of(createResolveInfo(PACKAGE1, getAppId(1))), UserHandle.USER_ALL, intent,
(uid, extras) -> null,
null /* options */);
final Intent actual = r.getReceiverIntent(r.receivers.get(0));
assertNull(actual);
assertNull(r.intent.getComponent());
assertEquals(42, r.intent.getIntExtra(Intent.EXTRA_INDEX, -1));
}
@Test
public void testIsReceiverEquals() {
final ResolveInfo info = createResolveInfo(PACKAGE1, getAppId(1));
assertTrue(isReceiverEquals(info, info));
assertTrue(isReceiverEquals(info, createResolveInfo(PACKAGE1, getAppId(1))));
assertFalse(isReceiverEquals(info, createResolveInfo(PACKAGE2, getAppId(2))));
}
@Test
public void testCalculateUrgent() {
final Intent intent = new Intent();
final Intent intentForeground = new Intent()
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
assertFalse(calculateUrgent(intent, null));
assertTrue(calculateUrgent(intentForeground, null));
{
final BroadcastOptions opts = BroadcastOptions.makeBasic();
assertFalse(calculateUrgent(intent, opts));
}
{
final BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setInteractive(true);
assertTrue(calculateUrgent(intent, opts));
}
{
final BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setAlarmBroadcast(true);
assertTrue(calculateUrgent(intent, opts));
}
}
@Test
public void testCalculateDeferUntilActive_App() {
// Verify non-urgent behavior
assertFalse(calculateDeferUntilActive(APP_UID, null, null, false, false));
assertFalse(calculateDeferUntilActive(APP_UID, OPT_DEFAULT, null, false, false));
assertFalse(calculateDeferUntilActive(APP_UID, OPT_NONE, null, false, false));
assertTrue(calculateDeferUntilActive(APP_UID, OPT_UNTIL_ACTIVE, null, false, false));
// Verify urgent behavior
assertFalse(calculateDeferUntilActive(APP_UID, null, null, false, true));
assertFalse(calculateDeferUntilActive(APP_UID, OPT_DEFAULT, null, false, true));
assertFalse(calculateDeferUntilActive(APP_UID, OPT_NONE, null, false, true));
assertTrue(calculateDeferUntilActive(APP_UID, OPT_UNTIL_ACTIVE, null, false, true));
}
@Test
public void testCalculateDeferUntilActive_System() {
BroadcastRecord.CORE_DEFER_UNTIL_ACTIVE = true;
// Verify non-urgent behavior
assertTrue(calculateDeferUntilActive(SYSTEM_UID, null, null, false, false));
assertTrue(calculateDeferUntilActive(SYSTEM_UID, OPT_DEFAULT, null, false, false));
assertFalse(calculateDeferUntilActive(SYSTEM_UID, OPT_NONE, null, false, false));
assertTrue(calculateDeferUntilActive(SYSTEM_UID, OPT_UNTIL_ACTIVE, null, false, false));
// Verify urgent behavior
assertFalse(calculateDeferUntilActive(SYSTEM_UID, null, null, false, true));
assertFalse(calculateDeferUntilActive(SYSTEM_UID, OPT_DEFAULT, null, false, true));
assertFalse(calculateDeferUntilActive(SYSTEM_UID, OPT_NONE, null, false, true));
assertTrue(calculateDeferUntilActive(SYSTEM_UID, OPT_UNTIL_ACTIVE, null, false, true));
}
@Test
public void testCalculateDeferUntilActive_Overrides() {
final IIntentReceiver resultTo = new IIntentReceiver.Default();
// Ordered broadcasts never deferred; requested option is ignored
assertFalse(calculateDeferUntilActive(APP_UID, OPT_UNTIL_ACTIVE, null, true, false));
assertFalse(calculateDeferUntilActive(APP_UID, OPT_UNTIL_ACTIVE, resultTo, true, false));
// Unordered with result is always deferred; requested option is ignored
assertTrue(calculateDeferUntilActive(APP_UID, OPT_NONE, resultTo, false, false));
}
@Test
public void testCleanupDisabledPackageReceivers() {
final int user0 = UserHandle.USER_SYSTEM;
final int user1 = user0 + 1;
final String pkgToCleanup = "pkg.a";
final String pkgOther = "pkg.b";
// Receivers contain multiple-user (contains [pkg.a@u0, pkg.a@u1, pkg.b@u0, pkg.b@u1]).
final List<ResolveInfo> receiversM = createReceiverInfos(
new String[] { pkgToCleanup, pkgOther },
new int[] { user0, user1 });
// Receivers only contain one user (contains [pkg.a@u0, pkg.b@u0]).
final List<ResolveInfo> receiversU0 = excludeReceivers(
receiversM, null /* packageName */, user1);
// With given package:
// Send to all users, cleanup a package of all users.
final BroadcastRecord recordAllAll = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
new Intent());
cleanupDisabledPackageReceivers(recordAllAll, pkgToCleanup, UserHandle.USER_ALL);
assertNull(verifyRemaining(recordAllAll, excludeReceivers(receiversM, pkgToCleanup, -1)));
// Send to all users, cleanup a package of one user.
final BroadcastRecord recordAllOne = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
new Intent());
cleanupDisabledPackageReceivers(recordAllOne, pkgToCleanup, user0);
assertNull(verifyRemaining(recordAllOne,
excludeReceivers(receiversM, pkgToCleanup, user0)));
// Send to one user, cleanup a package of all users.
final BroadcastRecord recordOneAll = createBroadcastRecord(receiversU0, user0,
new Intent());
cleanupDisabledPackageReceivers(recordOneAll, pkgToCleanup, UserHandle.USER_ALL);
assertNull(verifyRemaining(recordOneAll, excludeReceivers(receiversU0, pkgToCleanup, -1)));
// Send to one user, cleanup a package one user.
final BroadcastRecord recordOneOne = createBroadcastRecord(receiversU0, user0,
new Intent());
cleanupDisabledPackageReceivers(recordOneOne, pkgToCleanup, user0);
assertNull(verifyRemaining(recordOneOne, excludeReceivers(receiversU0, pkgToCleanup, -1)));
// Without given package (e.g. stop user):
// Send to all users, cleanup one user.
final BroadcastRecord recordAllM = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
new Intent());
cleanupDisabledPackageReceivers(recordAllM, null /* packageName */, user1);
assertNull(verifyRemaining(recordAllM,
excludeReceivers(receiversM, null /* packageName */, user1)));
// Send to one user, cleanup one user.
final BroadcastRecord recordU0 = createBroadcastRecord(receiversU0, user0, new Intent());
cleanupDisabledPackageReceivers(recordU0, null /* packageName */, user0);
assertNull(verifyRemaining(recordU0, Collections.emptyList()));
}
// Test defer BOOT_COMPLETED and LOCKED_BOOT_COMPLETED broaddcasts.
@Test
public void testDeferBootCompletedBroadcast() {
testDeferBootCompletedBroadcast_defer_none(ACTION_BOOT_COMPLETED);
testDeferBootCompletedBroadcast_defer_all(ACTION_BOOT_COMPLETED);
testDeferBootCompletedBroadcast_defer_background_restricted_only(ACTION_BOOT_COMPLETED);
testDeferBootCompletedBroadcast_defer_none(ACTION_LOCKED_BOOT_COMPLETED);
testDeferBootCompletedBroadcast_defer_all(ACTION_LOCKED_BOOT_COMPLETED);
testDeferBootCompletedBroadcast_defer_background_restricted_only(
ACTION_LOCKED_BOOT_COMPLETED);
}
// non-BOOT_COMPLETED broadcast does not get deferred.
@Test
public void testNoDeferOtherBroadcast() {
// no split for non-BOOT_COMPLETED broadcasts.
final BroadcastRecord br = createBootCompletedBroadcastRecord(ACTION_TIME_CHANGED);
final int origReceiversSize = br.receivers.size();
SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
// No receivers get deferred.
assertEquals(0, deferred.size());
assertEquals(origReceiversSize, br.receivers.size());
}
@Test
public void testMatchesDeliveryGroup() {
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent1, options1);
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent2, options2);
assertTrue(record2.matchesDeliveryGroup(record1));
}
@Test
public void testMatchesDeliveryGroup_withMatchingKey() {
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
options1.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key1");
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent1, options1);
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
options2.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key2");
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent2, options2);
final Intent intent3 = new Intent(Intent.ACTION_SERVICE_STATE);
intent3.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 1);
intent3.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 3);
final BroadcastOptions options3 = BroadcastOptions.makeBasic();
options3.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key1");
final BroadcastRecord record3 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent3, options3);
// record2 and record1 have different matching keys, so their delivery groups
// shouldn't match
assertFalse(record2.matchesDeliveryGroup(record1));
// record3 and record2 have different matching keys, so their delivery groups
// shouldn't match
assertFalse(record3.matchesDeliveryGroup(record2));
// record3 and record1 have same matching keys, so their delivery groups should match even
// if the intent has different extras.
assertTrue(record3.matchesDeliveryGroup(record1));
}
@Test
public void testMatchesDeliveryGroup_withMatchingFilter() {
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
intent1.putExtra(Intent.EXTRA_REASON, "reason1");
final IntentFilter filter1 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
final PersistableBundle bundle1 = new PersistableBundle();
bundle1.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
bundle1.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
filter1.setExtras(bundle1);
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
options1.setDeliveryGroupMatchingFilter(filter1);
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent1, options1);
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
intent2.putExtra(Intent.EXTRA_REASON, "reason2");
final IntentFilter filter2 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
final PersistableBundle bundle2 = new PersistableBundle();
bundle2.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
bundle2.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
filter2.setExtras(bundle2);
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
options2.setDeliveryGroupMatchingFilter(filter2);
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent2, options2);
final Intent intent3 = new Intent(Intent.ACTION_SERVICE_STATE);
intent3.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 1);
intent3.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 3);
intent3.putExtra(Intent.EXTRA_REASON, "reason3");
final IntentFilter filter3 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
final PersistableBundle bundle3 = new PersistableBundle();
bundle3.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
bundle3.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
filter3.setExtras(bundle3);
final BroadcastOptions options3 = BroadcastOptions.makeBasic();
options3.setDeliveryGroupMatchingFilter(filter3);
final BroadcastRecord record3 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
intent3, options3);
// record2's matchingFilter doesn't match record1's intent, so their delivery groups
// shouldn't match
assertFalse(record2.matchesDeliveryGroup(record1));
// record3's matchingFilter doesn't match record2's intent, so their delivery groups
// shouldn't match
assertFalse(record3.matchesDeliveryGroup(record2));
// record3's matchingFilter matches record1's intent, so their delivery groups should match.
assertTrue(record3.matchesDeliveryGroup(record1));
}
private BroadcastRecord createBootCompletedBroadcastRecord(String action) {
final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, USER_LIST);
final BroadcastRecord br = createBroadcastRecord(receivers, UserHandle.USER_ALL,
new Intent(action));
assertEquals(PACKAGE_LIST.length * USER_LIST.length, br.receivers.size());
return br;
}
// Test type DEFER_BOOT_COMPLETED_BROADCAST_NONE, this type does not defer any receiver.
private void testDeferBootCompletedBroadcast_defer_none(String action) {
final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
final int origReceiversSize = br.receivers.size();
SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_NONE);
// No receivers get deferred.
assertEquals(0, deferred.size());
assertEquals(origReceiversSize, br.receivers.size());
}
// Test type DEFER_BOOT_COMPLETED_BROADCAST_ALL, this type defer all receivers.
private void testDeferBootCompletedBroadcast_defer_all(String action) {
final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
// original BroadcastRecord receivers list is empty now.
assertTrue(br.receivers.isEmpty());
assertEquals(PACKAGE_LIST.length * USER_LIST.length, deferred.size());
for (int i = 0; i < PACKAGE_LIST.length; i++) {
for (final int userId : USER_LIST) {
final int uid = UserHandle.getUid(userId, getAppId(i));
assertTrue(deferred.contains(uid));
assertEquals(1, deferred.get(uid).receivers.size());
final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
assertEquals(uid, info.activityInfo.applicationInfo.uid);
}
}
}
// Test type DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY,
// This type defers receiver whose app package is background restricted.
private void testDeferBootCompletedBroadcast_defer_background_restricted_only(String action) {
final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
final int origReceiversSize = br.receivers.size();
// First half packages in PACKAGE_LIST, return BACKGROUND_RESTRICTED.
for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
for (int u = 0; u < USER_LIST.length; u++) {
final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED).when(mActivityManagerInternal)
.getRestrictionLevel(eq(uid));
}
}
// the second half packages in PACKAGE_LIST, return not BACKGROUND_RESTRICTED.
for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
for (int u = 0; u < USER_LIST.length; u++) {
final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED - 10).when(
mActivityManagerInternal).getRestrictionLevel(eq(uid));
}
}
SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
mActivityManagerInternal,
DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY);
// original BroadcastRecord receivers list is half now.
assertEquals(origReceiversSize / 2, br.receivers.size());
assertEquals(origReceiversSize / 2, deferred.size());
for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
for (int u = 0; u < USER_LIST.length; u++) {
final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
assertTrue(deferred.contains(uid));
assertEquals(1, deferred.get(uid).receivers.size());
final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
assertEquals(uid, info.activityInfo.applicationInfo.uid);
}
}
for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
for (int u = 0; u < USER_LIST.length; u++) {
final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
boolean found = false;
for (int r = 0; r < br.receivers.size(); r++) {
final ResolveInfo info = (ResolveInfo) br.receivers.get(r);
if (uid == info.activityInfo.applicationInfo.uid) {
found = true;
break;
}
}
assertTrue(found);
}
}
}
/**
* Test the class {@link BroadcastDispatcher#DeferredBootCompletedBroadcastPerUser}
*/
@Test
public void testDeferBootCompletedBroadcast_dispatcher() {
testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, false);
testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, false);
testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, true);
testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, true);
}
private void testDeferBootCompletedBroadcast_dispatcher_internal(String action,
boolean isAllUidReady) {
final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, new int[] {USER0});
final BroadcastRecord br = createBroadcastRecord(receivers, USER0, new Intent(action));
assertEquals(PACKAGE_LIST.length, br.receivers.size());
SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
// original BroadcastRecord receivers list is empty now.
assertTrue(br.receivers.isEmpty());
assertEquals(PACKAGE_LIST.length, deferred.size());
DeferredBootCompletedBroadcastPerUser deferredPerUser =
new DeferredBootCompletedBroadcastPerUser(USER0);
deferredPerUser.enqueueBootCompletedBroadcasts(action, deferred);
if (action.equals(ACTION_LOCKED_BOOT_COMPLETED)) {
assertEquals(PACKAGE_LIST.length,
deferredPerUser.mDeferredLockedBootCompletedBroadcasts.size());
assertTrue(deferredPerUser.mLockedBootCompletedBroadcastReceived);
for (int i = 0; i < PACKAGE_LIST.length; i++) {
final int uid = UserHandle.getUid(USER0, getAppId(i));
if (!isAllUidReady) {
deferredPerUser.updateUidReady(uid);
}
BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
isAllUidReady);
final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
assertEquals(uid, info.activityInfo.applicationInfo.uid);
}
assertEquals(0, deferredPerUser.mUidReadyForLockedBootCompletedBroadcast.size());
} else if (action.equals(ACTION_BOOT_COMPLETED)) {
assertEquals(PACKAGE_LIST.length,
deferredPerUser.mDeferredBootCompletedBroadcasts.size());
assertTrue(deferredPerUser.mBootCompletedBroadcastReceived);
for (int i = 0; i < PACKAGE_LIST.length; i++) {
final int uid = UserHandle.getUid(USER0, getAppId(i));
if (!isAllUidReady) {
deferredPerUser.updateUidReady(uid);
}
BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
isAllUidReady);
final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
assertEquals(uid, info.activityInfo.applicationInfo.uid);
}
assertEquals(0, deferredPerUser.mUidReadyForBootCompletedBroadcast.size());
}
}
private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
String packageName, int userId) {
record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */,
userId, true /* doit */);
}
private static String verifyRemaining(BroadcastRecord record,
List<ResolveInfo> expectedRemainingReceivers) {
final StringBuilder errorMsg = new StringBuilder();
for (final Object receiver : record.receivers) {
final ResolveInfo resolveInfo = (ResolveInfo) receiver;
final ApplicationInfo appInfo = resolveInfo.activityInfo.applicationInfo;
boolean foundExpected = false;
for (final ResolveInfo expectedReceiver : expectedRemainingReceivers) {
final ApplicationInfo expectedAppInfo =
expectedReceiver.activityInfo.applicationInfo;
if (appInfo.packageName.equals(expectedAppInfo.packageName)
&& UserHandle.getUserId(appInfo.uid) == UserHandle
.getUserId(expectedAppInfo.uid)) {
foundExpected = true;
break;
}
}
if (!foundExpected) {
errorMsg.append(appInfo.packageName).append("@")
.append('u').append(UserHandle.getUserId(appInfo.uid)).append(' ');
}
}
return errorMsg.length() == 0 ? null
: errorMsg.insert(0, "Contains unexpected receiver: ").toString();
}
private static ResolveInfo createResolveInfoWithPriority(int priority) {
return createResolveInfo(PACKAGE1, getAppId(1), priority);
}
private static ResolveInfo createResolveInfo(String packageName, int uid) {
return createResolveInfo(packageName, uid, 0);
}
private static ResolveInfo createResolveInfo(String packageName, int uid, int priority) {
final ResolveInfo resolveInfo = new ResolveInfo();
final ActivityInfo activityInfo = new ActivityInfo();
final ApplicationInfo appInfo = new ApplicationInfo();
appInfo.packageName = packageName;
appInfo.uid = uid;
activityInfo.applicationInfo = appInfo;
activityInfo.packageName = packageName;
activityInfo.name = packageName + ".MyReceiver";
resolveInfo.activityInfo = activityInfo;
resolveInfo.priority = priority;
return resolveInfo;
}
/**
* Generate (packages.length * userIds.length) receivers.
*/
private static List<ResolveInfo> createReceiverInfos(String[] packages, int[] userIds) {
final List<ResolveInfo> receivers = new ArrayList<>();
for (int i = 0; i < packages.length; i++) {
for (final int userId : userIds) {
receivers.add(createResolveInfo(packages[i],
UserHandle.getUid(userId, getAppId(i))));
}
}
return receivers;
}
/**
* Create a new list which filters out item if package name or user id is matched.
* Null package name or user id < 0 will be considered as don't care.
*/
private static List<ResolveInfo> excludeReceivers(List<ResolveInfo> receivers,
String packageName, int userId) {
final List<ResolveInfo> excludedList = new ArrayList<>();
for (final ResolveInfo receiver : receivers) {
if ((packageName != null
&& !packageName.equals(receiver.activityInfo.applicationInfo.packageName))
|| (userId > -1 && userId != UserHandle
.getUserId(receiver.activityInfo.applicationInfo.uid))) {
excludedList.add(receiver);
}
}
return excludedList;
}
private BroadcastRecord createBroadcastRecord(Intent intent,
List<ResolveInfo> receivers) {
return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
null /* options */, false);
}
private BroadcastRecord createOrderedBroadcastRecord(Intent intent,
List<ResolveInfo> receivers) {
return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
null /* options */, true);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent) {
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
null /* options */, false);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent, BroadcastOptions options) {
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
options, false);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
BroadcastOptions options) {
return createBroadcastRecord(receivers, userId, intent, filterExtrasForReceiver,
options, false);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
BroadcastOptions options, boolean ordered) {
return new BroadcastRecord(
mQueue /* queue */,
intent,
mProcess /* callerApp */,
PACKAGE1 /* callerPackage */,
null /* callerFeatureId */,
0 /* callingPid */,
0 /* callingUid */,
false /* callerInstantApp */,
null /* resolvedType */,
null /* requiredPermissions */,
null /* excludedPermissions */,
null /* excludedPackages */,
0 /* appOp */,
options,
new ArrayList<>(receivers), // Make a copy to not affect the original list.
null /* resultToApp */,
null /* resultTo */,
0 /* resultCode */,
null /* resultData */,
null /* resultExtras */,
ordered /* serialized */,
false /* sticky */,
false /* initialSticky */,
userId,
BackgroundStartPrivileges.NONE,
false /* timeoutExempt */,
filterExtrasForReceiver);
}
private static int getAppId(int i) {
return Process.FIRST_APPLICATION_UID + i;
}
private static boolean isPrioritized(List<Object> receivers) {
return BroadcastRecord.isPrioritized(
calculateBlockedUntilBeyondCount(receivers, false), false);
}
private static void assertBlocked(BroadcastRecord r, boolean... blocked) {
assertEquals(r.receivers.size(), blocked.length);
for (int i = 0; i < blocked.length; i++) {
assertEquals("blocked " + i, blocked[i], r.isBlocked(i));
}
}
private static void assertTerminalDeferredBeyond(BroadcastRecord r,
int expectedTerminalCount, int expectedDeferredCount, int expectedBeyondCount) {
assertEquals("terminal", expectedTerminalCount, r.terminalCount);
assertEquals("deferred", expectedDeferredCount, r.deferredCount);
assertEquals("beyond", expectedBeyondCount, r.beyondCount);
}
}