blob: 36607e15d98d221cb6469c9198e017b177ecf1e3 [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.server.wm;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.WindowManagerState.STATE_INITIALIZING;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_BACKGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_FOREGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_SECOND_BACKGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_SEND_PENDING_INTENT_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_SIMPLE_ADMIN_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_START_ACTIVITY_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.APP_A_VIRTUAL_DISPLAY_RECEIVER;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.ACTION_FINISH_ACTIVITY;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.ACTION_LAUNCH_BACKGROUND_ACTIVITIES;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_BACKGROUND_ACTIVITY_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_INTENTS_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.RELAUNCH_FOREGROUND_ACTIVITY_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.ForegroundActivity.START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.SendPendingIntentReceiver.IS_BROADCAST_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.VirtualDisplayReceiver.USE_PUBLIC_PRESENTATION;
import static android.server.wm.backgroundactivity.appb.Components.APP_B_FOREGROUND_ACTIVITY;
import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.google.common.truth.Truth.assertThat;
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 android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.ResultReceiver;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.SystemUserOnly;
import android.provider.Settings;
import android.server.wm.backgroundactivity.common.CommonComponents.Event;
import android.server.wm.backgroundactivity.common.EventReceiver;
import androidx.annotation.Nullable;
import androidx.test.filters.FlakyTest;
import com.android.compatibility.common.util.AppOpsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
/**
* This class covers all test cases for starting/blocking background activities.
* As instrumentation tests started by shell are whitelisted to allow starting background activity,
* tests can't be done in this app alone.
* Hence, there are 2 extra apps, appA and appB. This class will send commands to appA/appB, for
* example, send a broadcast to appA and ask it to start a background activity, and we will monitor
* the result and see if it starts an activity successfully.
*/
@Presubmit
public class BackgroundActivityLaunchTest extends ActivityManagerTestBase {
private static final int ACTIVITY_FOCUS_TIMEOUT_MS = 3000;
private static final String APP_A_PACKAGE_NAME = APP_A_FOREGROUND_ACTIVITY.getPackageName();
private static final long ACTIVITY_BG_START_GRACE_PERIOD_MS = 10 * 1000;
private static final String TEST_PACKAGE_APP_A = "android.server.wm.backgroundactivity.appa";
private static final String TEST_PACKAGE_APP_B = "android.server.wm.backgroundactivity.appb";
private static final int ACTIVITY_START_TIMEOUT_MS = 5000;
private static final int ALARM_MANAGER_MAX_DELAY_MS = 60000;
public static final ComponentName APP_A_ALARM_MANAGER_ACTIVITY =
new ComponentName(TEST_PACKAGE_APP_A,
"android.server.wm.backgroundactivity.appa.AlarmManagerActivity");
/**
* Tests can be executed as soon as the device has booted. When that happens the broadcast queue
* is long and it takes some time to process the broadcast we just sent.
*/
private static final int BROADCAST_DELIVERY_TIMEOUT_MS = 60000;
@Override
@Before
public void setUp() throws Exception {
// disable SAW appopp for AppA (it's granted autonatically when installed in CTS)
AppOpsUtils.setOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window", MODE_ERRORED);
assertEquals(AppOpsUtils.getOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window"),
MODE_ERRORED);
super.setUp();
assertNull(mWmState.getTaskByActivity(APP_A_BACKGROUND_ACTIVITY));
assertNull(mWmState.getTaskByActivity(APP_A_FOREGROUND_ACTIVITY));
assertNull(mWmState.getTaskByActivity(APP_B_FOREGROUND_ACTIVITY));
runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
+ APP_A_FOREGROUND_ACTIVITY.getPackageName());
runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
+ APP_B_FOREGROUND_ACTIVITY.getPackageName());
}
@After
public void tearDown() throws Exception {
// We do this before anything else, because having an active device owner can prevent us
// from being able to force stop apps. (b/142061276)
runWithShellPermissionIdentity(() -> {
runShellCommand("dpm remove-active-admin --user current "
+ APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
});
stopTestPackage(TEST_PACKAGE_APP_A);
stopTestPackage(TEST_PACKAGE_APP_B);
AppOpsUtils.reset(APP_A_PACKAGE_NAME);
}
@Test
public void testBackgroundActivityBlocked() throws Exception {
// Start AppA background activity and blocked
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
public void testBackgroundActivityBlocked_VirtualDisplay() throws Exception {
mContext.sendBroadcast(new Intent().setComponent(APP_A_VIRTUAL_DISPLAY_RECEIVER));
assertTrue(mWmState.waitFor((wmState) ->
wmState.getWindowByPackageName(APP_A_PACKAGE_NAME, TYPE_PRIVATE_PRESENTATION)
!= null, "Private presentation was never created"));
// Start AppA background activity and blocked
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
public void testBackgroundActivityBlocked_PublicVirtualDisplay() throws Exception {
mContext.sendBroadcast(new Intent().setComponent(APP_A_VIRTUAL_DISPLAY_RECEIVER)
.putExtra(USE_PUBLIC_PRESENTATION, true));
int TYPE_PRESENTATION = 2037; // constant is @hide
assertTrue(mWmState.waitFor((wmState) ->
wmState.getWindowByPackageName(APP_A_PACKAGE_NAME, TYPE_PRESENTATION)
!= null, "Private presentation was never created"));
// Start AppA background activity and blocked
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@FlakyTest(bugId = 155454710)
public void testBackgroundActivityNotBlockedWithinGracePeriod() throws Exception {
// Start AppA foreground activity
Intent firstIntent = new Intent();
firstIntent.setComponent(APP_A_FOREGROUND_ACTIVITY);
firstIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(firstIntent);
boolean firstResult = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", firstResult);
// Don't press home button to avoid stop app switches
mContext.sendBroadcast(new Intent(ACTION_FINISH_ACTIVITY));
mWmState.waitForHomeActivityVisible();
Thread.sleep(ACTIVITY_BG_START_GRACE_PERIOD_MS / 2);
Intent secondIntent = new Intent();
secondIntent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(secondIntent);
boolean secondResult = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Should be able to launch background activity", secondResult);
}
@Test
public void testBackgroundActivityNotBlockedWhenSystemAlertWindowGranted() throws Exception {
// enable appopp for SAW for this test
AppOpsUtils.setOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window", MODE_ALLOWED);
assertEquals(AppOpsUtils.getOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window"),
MODE_ALLOWED);
// Start AppA background activity successfully as the package has SAW
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
}
@Test
public void testBackgroundActivityNotBlockedWhenForegroundActivityExists() throws Exception {
// Start AppA foreground activity
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// Start AppA background activity successfully as there's a foreground activity
intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
mContext.sendBroadcast(intent);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
@Test
public void testActivityNotBlockedWhenForegroundActivityLaunch() throws Exception {
// Start foreground activity, and foreground activity able to launch background activity
// successfully
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
APP_A_FOREGROUND_ACTIVITY);
}
@Test
public void testActivityBroughtToTopOfTaskWhenLaunchedInTheBackground() throws Exception {
// Start foreground activity, and foreground activity able to launch background activity
// successfully
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
pressHomeAndResumeAppSwitch();
mContext.sendBroadcast(getLaunchActivitiesBroadcast(APP_A_BACKGROUND_ACTIVITY));
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
assertTaskStack(new ComponentName[] {APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
APP_A_FOREGROUND_ACTIVITY);
}
@Test
@FlakyTest(bugId = 143522449)
public void testActivityNotBlockedWhenForegroundActivityLaunchInDifferentTask()
throws Exception {
// Start foreground activity, and foreground activity able to launch background activity
// successfully
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA, true);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
pressHomeAndResumeAppSwitch();
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@FlakyTest(bugId = 130800326)
@Ignore // TODO(b/145981637): Make this test work
public void testActivityBlockedWhenForegroundActivityRestartsItself() throws Exception {
// Start AppA foreground activity
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(RELAUNCH_FOREGROUND_ACTIVITY_EXTRA, true);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
pressHomeAndResumeAppSwitch();
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
}
@Test
public void testSecondActivityNotBlockedWhenForegroundActivityLaunch() throws Exception {
// Start AppA foreground activity, which will immediately launch one activity
// and then the second.
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
intent.putExtra(LAUNCH_SECOND_BACKGROUND_ACTIVITY_EXTRA, true);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_A_SECOND_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch second background activity", result);
waitAndAssertActivityState(APP_A_BACKGROUND_ACTIVITY, STATE_INITIALIZING,
"First activity should have been created");
assertTaskStack(
new ComponentName[]{APP_A_SECOND_BACKGROUND_ACTIVITY, APP_A_BACKGROUND_ACTIVITY,
APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
}
@Test
@FlakyTest(bugId = 143522449)
public void testSecondActivityBlockedWhenBackgroundActivityLaunch() throws Exception {
Intent baseActivityIntent = new Intent();
baseActivityIntent.setComponent(APP_A_FOREGROUND_ACTIVITY);
baseActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(baseActivityIntent);
boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
pressHomeAndResumeAppSwitch();
// The activity, now in the background, will attempt to start 2 activities in quick
// succession
mContext.sendBroadcast(getLaunchActivitiesBroadcast(APP_A_BACKGROUND_ACTIVITY,
APP_A_SECOND_BACKGROUND_ACTIVITY));
// There should be 2 activities in the background (not focused) INITIALIZING
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Activity should not have been launched in the foreground", result);
result = waitForActivityFocused(APP_A_SECOND_BACKGROUND_ACTIVITY);
assertFalse("Second activity should not have been launched in the foreground", result);
assertTaskStack(
new ComponentName[]{APP_A_SECOND_BACKGROUND_ACTIVITY, APP_A_BACKGROUND_ACTIVITY,
APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
}
@Test
public void testPendingIntentActivityBlocked() throws Exception {
// Cannot start activity by pending intent, as both appA and appB are in background
sendPendingIntentActivity();
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@FlakyTest(bugId = 130800326)
public void testPendingIntentActivityNotBlocked_appAIsForeground() throws Exception {
// Start AppA foreground activity
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
sendPendingIntentActivity();
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
@Test
public void testPendingIntentBroadcastActivityNotBlocked_appBIsForeground() throws Exception {
// Start AppB foreground activity
Intent intent = new Intent();
intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
sendPendingIntentActivity();
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
}
@Test
public void testPendingIntentBroadcastTimeout_noDelay() throws Exception {
assertPendingIntentBroadcastTimeoutTest(0, true);
}
@Test
@FlakyTest(bugId = 141344170)
public void testPendingIntentBroadcastTimeout_delay1s() throws Exception {
assertPendingIntentBroadcastTimeoutTest(1000, true);
}
@Test
public void testPendingIntentBroadcastTimeout_delay12s() throws Exception {
assertPendingIntentBroadcastTimeoutTest(12000, false);
}
@Test
public void testPendingIntentBroadcast_appBIsBackground() throws Exception {
EventReceiver receiver = new EventReceiver(
Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
sendPendingIntentBroadcast(0, receiver.getNotifier());
// Waits for final hoop in AppA to start looking for activity, otherwise it could succeed
// if the broadcast took long time to get executed (which may happen after boot).
receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
@Test
@SystemUserOnly(reason = "Device owner must be SYSTEM user")
public void testDeviceOwner() throws Exception {
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
return;
}
String cmdResult = runShellCommand("dpm set-device-owner --user cur "
+ APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
assertThat(cmdResult).contains("Success");
EventReceiver receiver = new EventReceiver(
Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
intent.putExtra(EVENT_NOTIFIER_EXTRA, receiver.getNotifier());
mContext.sendBroadcast(intent);
// Waits for final hoop in AppA to start looking for activity
receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
@Test
public void testAlarmManagerCannotStartBgActivity() throws Exception {
// Start the alarm activity which will create a pending intent and ask alarm manager to
// run after 30s.
Intent intent = new Intent();
intent.setComponent(APP_A_ALARM_MANAGER_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
assertTrue("Alarm activity not started", waitUntilForegroundChanged(
TEST_PACKAGE_APP_A, true, ACTIVITY_START_TIMEOUT_MS));
// Start Settings activity.
intent = new Intent(Settings.ACTION_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
assertTrue("Settings activity not started", waitUntilForegroundChanged(
TEST_PACKAGE_APP_A, false, ACTIVITY_START_TIMEOUT_MS));
// Even Settings app is in foreground, alarm manager should not use Settings foreground
// state to launch the pending intent from alarm manager.
assertFalse("Test activity is resumed",
waitUntilForegroundChanged(TEST_PACKAGE_APP_A, true,
ALARM_MANAGER_MAX_DELAY_MS));
}
private Intent getLaunchActivitiesBroadcast(ComponentName... componentNames) {
Intent broadcastIntent = new Intent(ACTION_LAUNCH_BACKGROUND_ACTIVITIES);
Intent[] intents = Stream.of(componentNames)
.map(c -> {
Intent intent = new Intent();
intent.setComponent(c);
return intent;
})
.toArray(Intent[]::new);
broadcastIntent.putExtra(LAUNCH_INTENTS_EXTRA, intents);
return broadcastIntent;
}
private void pressHomeAndResumeAppSwitch() {
// Press home key to ensure stopAppSwitches is called because the last-stop-app-switch-time
// is a criteria of allowing background start.
pressHomeButton();
// Resume the stopped state (it won't affect last-stop-app-switch-time) so we don't need to
// wait extra time to prevent the next launch from being delayed.
resumeAppSwitches();
mWmState.waitForHomeActivityVisible();
// Resuming app switches again after home became visible because the previous call might
// have raced with pressHomeButton().
// TODO(b/155454710): Remove previous call after making sure all the tests don't depend on
// the timing here.
resumeAppSwitches();
}
private void assertTaskStack(ComponentName[] expectedComponents,
ComponentName sourceComponent) {
if (expectedComponents == null) {
assertNull(mWmState.getTaskByActivity(sourceComponent));
return;
}
List<WindowManagerState.Activity> actual = mWmState.getTaskByActivity(
sourceComponent).mActivities;
assertEquals(expectedComponents.length, actual.size());
int size = expectedComponents.length;
for (int i = 0; i < size; i++) {
assertEquals(expectedComponents[i].flattenToShortString(), actual.get(i).getName());
}
}
private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult)
throws TimeoutException {
// Start AppB foreground activity
Intent intent = new Intent();
intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
EventReceiver receiver = new EventReceiver(
Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
sendPendingIntentBroadcast(delayMs, receiver.getNotifier());
// Waits for final hoop in AppA to start looking for activity
receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS + delayMs,
APP_A_BACKGROUND_ACTIVITY);
assertEquals(expectedResult, result);
if (expectedResult) {
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY},
APP_A_BACKGROUND_ACTIVITY);
} else {
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
}
}
private boolean waitForActivityFocused(ComponentName componentName) {
return waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName);
}
private void sendPendingIntentActivity() {
Intent intent = new Intent();
intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
intent.putExtra(IS_BROADCAST_EXTRA, false);
mContext.sendBroadcast(intent);
}
private void sendPendingIntentBroadcast(int delayMs, @Nullable ResultReceiver eventNotifier) {
Intent intent = new Intent();
intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
intent.putExtra(IS_BROADCAST_EXTRA, true);
if (delayMs > 0) {
intent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, delayMs);
}
intent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
mContext.sendBroadcast(intent);
}
private boolean waitUntilForegroundChanged(String targetPkg, boolean toBeResumed, int timeout)
throws Exception {
long startTime = System.currentTimeMillis();
while (checkPackageResumed(targetPkg) != toBeResumed) {
if (System.currentTimeMillis() - startTime < timeout) {
Thread.sleep(100);
} else {
return false;
}
}
return true;
}
private boolean checkPackageResumed(String pkg) {
WindowManagerStateHelper helper = new WindowManagerStateHelper();
helper.computeState();
return ComponentName.unflattenFromString(
helper.getFocusedActivity()).getPackageName().equals(pkg);
}
}