diff options
10 files changed, 195 insertions, 32 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 1edd7f5e429d..af3da0cbf5ee 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -243,6 +243,8 @@ public abstract class ActivityManagerInternal {      public abstract void ensureBootCompleted();      public abstract void updateOomLevelsForDisplay(int displayId);      public abstract boolean isActivityStartsLoggingEnabled(); +    /** Returns true if the background activity starts is enabled. */ +    public abstract boolean isBackgroundActivityStartsEnabled();      public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);      /** Input dispatch timeout to a window, start the ANR process. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 848b041007c2..83be63c6cb22 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11035,6 +11035,16 @@ public final class Settings {                  = "activity_starts_logging_enabled";          /** +         * Feature flag to enable or disable the background activity starts. +         * When disabled, apps aren't allowed to start activities unless they're in the foreground. +         * Type: int (0 for false, 1 for true) +         * Default: 1 +         * @hide +         */ +        public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED = +                "background_activity_starts_enabled"; + +        /**           * @hide           * @see com.android.server.appbinding.AppBindingConstants           */ diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 8f583307adab..21e7b9c64c5c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -126,6 +126,7 @@ public class SettingsBackupTest {                      Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,                      Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,                      Settings.Global.AUTOMATIC_POWER_SAVER_MODE, +                    Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,                      Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,                      Settings.Global.BATTERY_DISCHARGE_THRESHOLD,                      Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 5c77f0a3ad47..8571ae6b07f6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -16,6 +16,8 @@  package com.android.server.am; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; +  import android.content.ContentResolver;  import android.database.ContentObserver;  import android.net.Uri; @@ -26,8 +28,6 @@ import android.util.Slog;  import java.io.PrintWriter; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; -  /**   * Settings constants that can modify the activity manager's behavior.   */ @@ -222,6 +222,10 @@ final class ActivityManagerConstants extends ContentObserver {      // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED      volatile boolean mFlagActivityStartsLoggingEnabled; +    // Indicates whether the background activity starts is enabled. +    // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED +    volatile boolean mFlagBackgroundActivityStartsEnabled; +      private final ActivityManagerService mService;      private ContentResolver mResolver;      private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -256,6 +260,10 @@ final class ActivityManagerConstants extends ContentObserver {      private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(                  Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED); +    private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI = +                Settings.Global.getUriFor( +                        Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED); +      public ActivityManagerConstants(ActivityManagerService service, Handler handler) {          super(handler);          mService = service; @@ -266,8 +274,10 @@ final class ActivityManagerConstants extends ContentObserver {          mResolver = resolver;          mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);          mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this); +        mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);          updateConstants();          updateActivityStartsLoggingEnabled(); +        updateBackgroundActivityStartsEnabled();      }      public void setOverrideMaxCachedProcesses(int value) { @@ -290,6 +300,8 @@ final class ActivityManagerConstants extends ContentObserver {              updateConstants();          } else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {              updateActivityStartsLoggingEnabled(); +        } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) { +            updateBackgroundActivityStartsEnabled();          }      } @@ -373,6 +385,11 @@ final class ActivityManagerConstants extends ContentObserver {                  Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;      } +    private void updateBackgroundActivityStartsEnabled() { +        mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver, +                Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1; +    } +      private void updateMaxCachedProcesses() {          CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0                  ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 80e73132f542..b62f648956dd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19204,6 +19204,10 @@ public class ActivityManagerService extends IActivityManager.Stub              return mConstants.mFlagActivityStartsLoggingEnabled;          } +        public boolean isBackgroundActivityStartsEnabled() { +            return mConstants.mFlagBackgroundActivityStartsEnabled; +        } +          public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {              synchronized(ActivityManagerService.this) {                  ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 1c08d039207b..7c7553f34a3a 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -850,9 +850,10 @@ class ActivityMetricsLogger {          builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,                  targetUidHasAnyVisibleWindow ? 1 : 0);          builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag); -        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);          builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0); -        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction()); +        if (intent != null) { +            builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction()); +        }          if (callerApp != null) {              builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);              builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE, @@ -881,29 +882,34 @@ class ActivityMetricsLogger {                          (nowUptime - callerApp.getWhenUnimportant()));              }          } -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString()); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0); -        if (r.lastVisibleTime != 0) { -            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE, -                    (nowUptime - r.lastVisibleTime)); -        } -        if (r.resultTo != null) { -            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName); -            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME, -                    r.resultTo.shortComponentName); -        } -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0); -        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD, -                r.visibleIgnoringKeyguard ? 1 : 0); -        if (r.lastLaunchTime != 0) { -            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH, -                    (nowUptime - r.lastLaunchTime)); +        if (r != null) { +            builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, +                    r.realActivity.toShortString()); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0); +            if (r.lastVisibleTime != 0) { +                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE, +                        (nowUptime - r.lastVisibleTime)); +            } +            if (r.resultTo != null) { +                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, +                        r.resultTo.packageName); +                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME, +                        r.resultTo.shortComponentName); +            } +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0); +            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD, +                    r.visibleIgnoringKeyguard ? 1 : 0); +            if (r.lastLaunchTime != 0) { +                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH, +                        (nowUptime - r.lastLaunchTime)); +            }          }          mMetricsLogger.write(builder);      } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 90f3ff84a027..1735ef89918a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -98,6 +98,7 @@ import android.graphics.Rect;  import android.os.Binder;  import android.os.Bundle;  import android.os.IBinder; +import android.os.Process;  import android.os.RemoteException;  import android.os.SystemClock;  import android.os.Trace; @@ -108,6 +109,7 @@ import android.text.TextUtils;  import android.util.EventLog;  import android.util.Pools.SynchronizedPool;  import android.util.Slog; +import android.widget.Toast;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.app.HeavyWeightSwitcherActivity; @@ -731,6 +733,12 @@ class ActivityStarter {          abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,                  callingPid, resolvedType, aInfo.applicationInfo); +        // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking +        // on START_ABORTED +        if (!abort) { +            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, callerApp); +        } +          // Merge the two options bundles, while realCallerOptions takes precedence.          ActivityOptions checkedOptions = options != null                  ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; @@ -774,6 +782,8 @@ class ActivityStarter {              // We pretend to the caller that it was really started, but              // they will just get a cancel result.              ActivityOptions.abort(checkedOptions); +            maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, +                    /* r= */ null, originatingPendingIntent, /* abortedStart= */ true);              return START_ABORTED;          } @@ -866,19 +876,50 @@ class ActivityStarter {          mController.doPendingActivityLaunches(false);          maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r, -                originatingPendingIntent); +                originatingPendingIntent, /* abortedStart= */ false);          return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,                  true /* doResume */, checkedOptions, inTask, outActivity);      } +    private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage, +            WindowProcessController callerApp) { +        if (mService.isBackgroundActivityStartsEnabled()) { +            return false; +        } +        // don't abort for the most important UIDs +        if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) { +            return false; +        } +        // don't abort if the callerApp has any visible activity +        if (callerApp != null && callerApp.hasForegroundActivities()) { +            return false; +        } +        // don't abort if the callingUid's process is important enough +        if (mService.getUidStateLocked(callingUid) <= ActivityManager.PROCESS_STATE_TOP) { +            return false; +        } +        // don't abort if the callingUid has any visible window +        if (mService.mWindowManager.isAnyWindowVisibleForUid(callingUid)) { +            return false; +        } +        // anything that has fallen through will currently be aborted +        // TODO: remove this toast after feature development is done +        mService.mUiHandler.post(() -> { +            Toast.makeText(mService.mContext, +                    "Blocking background activity start for " + callingPackage, +                    Toast.LENGTH_SHORT).show(); +        }); +        return true; +    } +      private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,              Intent intent, WindowProcessController callerApp, ActivityRecord r, -            PendingIntentRecord originatingPendingIntent) { +            PendingIntentRecord originatingPendingIntent, boolean abortedStart) {          boolean callerAppHasForegroundActivity =                  callerApp != null && callerApp.hasForegroundActivities();          if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity -                || r == null) { +                || (!abortedStart && r == null)) {              // skip logging in this case              return;          } @@ -894,8 +935,8 @@ class ActivityStarter {              final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)                      ? callingUidHasAnyVisibleWindow                      : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid); -            final String targetPackage = r.packageName; -            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1; +            final String targetPackage = (r != null) ? r.packageName : null; +            final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;              final int targetUidProcState = mService.getUidStateLocked(targetUid);              final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)                      ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid) diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d0e3fb47730e..054b105fbfa4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5016,6 +5016,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {          return mAmInternal.isActivityStartsLoggingEnabled();      } +    boolean isBackgroundActivityStartsEnabled() { +        return mAmInternal.isBackgroundActivityStartsEnabled(); +    } +      void enableScreenAfterBoot(boolean booted) {          EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,                  SystemClock.uptimeMillis()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 50aa541a9549..f6ff05b077d1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -16,6 +16,7 @@  package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_TOP;  import static android.app.ActivityManager.START_ABORTED;  import static android.app.ActivityManager.START_CLASS_NOT_FOUND;  import static android.app.ActivityManager.START_DELIVERED_TO_TOP; @@ -69,6 +70,7 @@ import android.content.pm.IPackageManager;  import android.content.pm.PackageManagerInternal;  import android.graphics.Rect;  import android.os.IBinder; +import android.os.Process;  import android.os.RemoteException;  import android.platform.test.annotations.Presubmit;  import android.service.voice.IVoiceInteractionSession; @@ -110,6 +112,7 @@ public class ActivityStarterTests extends ActivityTestsBase {      private static final int FAKE_CALLING_UID = 666;      private static final int FAKE_REAL_CALLING_UID = 667;      private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude"; +    private static final int UNIMPORTANT_UID = 12345;      @Before      public void setUp() throws Exception { @@ -551,6 +554,79 @@ public class ActivityStarterTests extends ActivityTestsBase {      }      /** +     * This test ensures that unsupported usecases aren't aborted when background starts are +     * allowed. +     */ +    @Test +    public void testBackgroundActivityStartsAllowed_noStartsAborted() { +        doReturn(true).when(mService).isBackgroundActivityStartsEnabled(); + +        runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", +                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false); +    } + +    /** +     * This test ensures that unsupported usecases are aborted when background starts are +     * disallowed. +     */ +    @Test +    public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() { +        doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); + +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_unsupportedUsecase_aborted", +                true, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false); +    } + +    /** +     * This test ensures that supported usecases aren't aborted when background starts are +     * disallowed. +     * The scenarios each have only one condidion that makes them supported. +     */ +    @Test +    public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() { +        doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); + +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", +                false, Process.ROOT_UID, false, PROCESS_STATE_TOP + 1, false); +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", +                false, Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, false); +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasVisibleWindow_notAborted", +                false, UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, false); +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_processStateTop_notAborted", +                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP, false); +        runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasForegroundActivities_notAborted", +                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, true); +    } + +    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, +            int testCallingUid, boolean hasVisibleWindow, int procState, +            boolean hasForegroundActivities) { +        // window visibility +        doReturn(hasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid( +                testCallingUid); +        // process importance +        doReturn(procState).when(mService).getUidStateLocked(testCallingUid); +        // foreground activities +        final IApplicationThread caller = mock(IApplicationThread.class); +        final ApplicationInfo ai = new ApplicationInfo(); +        ai.uid = testCallingUid; +        final WindowProcessController callerApp = +                new WindowProcessController(mService, ai, null, testCallingUid, -1, null, null); +        callerApp.setHasForegroundActivities(hasForegroundActivities); +        doReturn(callerApp).when(mService).getProcessController(caller); + +        final ActivityOptions options = spy(ActivityOptions.makeBasic()); +        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK).setCaller(caller) +                .setCallingUid(testCallingUid).setActivityOptions(new SafeActivityOptions(options)); + +        final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute(); + +        assertEquals(ActivityStarter.getExternalResult( +                shouldHaveAborted ? START_ABORTED : START_SUCCESS), result); +        verify(options, times(shouldHaveAborted ? 1 : 0)).abort(); +    } + +    /**       * This test ensures that when starting an existing single task activity on secondary display       * which is not the top focused display, it should deliver new intent to the activity and not       * create a new stack. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index c2ab3acd4933..cc0ae948b054 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -402,6 +402,8 @@ class ActivityTestsBase {              spyOn(getLifecycleManager());              spyOn(getLockTaskController());              doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); +            // allow background activity starts by default +            doReturn(true).when(this).isBackgroundActivityStartsEnabled();          }          void setActivityManagerService(IntentFirewall intentFirewall,  |