diff options
| author | 2023-09-01 10:34:56 +0000 | |
|---|---|---|
| committer | 2023-09-01 10:34:56 +0000 | |
| commit | d58b2734ddfe18c91bb4c133b8b323fd9cb7f563 (patch) | |
| tree | 05b8649efc75111ade0c7329a800aa4d045bc1f1 | |
| parent | 12666090a2f3882af02e85dfa2f695397a139cce (diff) | |
| parent | a4269d0b8c636c807fef1c12a29aacc5821f1be2 (diff) | |
Merge "Intercept HOME intents for secondary home displays." into main
6 files changed, 204 insertions, 70 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 90e67df65fec..582536b662ce 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2929,7 +2929,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A reparent(newTaskFrag, position); } - private boolean isHomeIntent(Intent intent) { + static boolean isHomeIntent(Intent intent) { return ACTION_MAIN.equals(intent.getAction()) && (intent.hasCategory(CATEGORY_HOME) || intent.hasCategory(CATEGORY_SECONDARY_HOME)) diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 1eb56f1b7d1c..a5b1132fe499 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -55,6 +55,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -77,7 +78,6 @@ class ActivityStartInterceptor { private final ActivityTaskManagerService mService; private final ActivityTaskSupervisor mSupervisor; - private final RootWindowContainer mRootWindowContainer; private final Context mServiceContext; // UserManager cannot be final as it's not ready when this class is instantiated during boot @@ -110,17 +110,23 @@ class ActivityStartInterceptor { TaskFragment mInTaskFragment; ActivityOptions mActivityOptions; + /* + * Note that this is just a hint of what the launch display area will be as it is + * based only on the information at the early pre-interception stage of starting the + * intent. The real launch display area calculated later may be different from this one. + */ + TaskDisplayArea mPresumableLaunchDisplayArea; + ActivityStartInterceptor( ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) { - this(service, supervisor, service.mRootWindowContainer, service.mContext); + this(service, supervisor, service.mContext); } @VisibleForTesting ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor, - RootWindowContainer root, Context context) { + Context context) { mService = service; mSupervisor = supervisor; - mRootWindowContainer = root; mServiceContext = context; } @@ -162,7 +168,7 @@ class ActivityStartInterceptor { /** * A helper function to obtain the targeted {@link TaskFragment} during * {@link #intercept(Intent, ResolveInfo, ActivityInfo, String, Task, TaskFragment, int, int, - * ActivityOptions)} if any. + * ActivityOptions, TaskDisplayArea)} if any. */ @Nullable private TaskFragment getLaunchTaskFragment() { @@ -187,7 +193,7 @@ class ActivityStartInterceptor { */ boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid, - ActivityOptions activityOptions) { + ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) { mUserManager = UserManager.get(mServiceContext); mIntent = intent; @@ -199,6 +205,7 @@ class ActivityStartInterceptor { mInTask = inTask; mInTaskFragment = inTaskFragment; mActivityOptions = activityOptions; + mPresumableLaunchDisplayArea = presumableLaunchDisplayArea; if (interceptQuietProfileIfNeeded()) { // If work profile is turned off, skip the work challenge since the profile can only @@ -221,6 +228,11 @@ class ActivityStartInterceptor { if (interceptLockedManagedProfileIfNeeded()) { return true; } + if (interceptHomeIfNeeded()) { + // Replace primary home intents directed at displays that do not support primary home + // but support secondary home with the relevant secondary home activity. + return true; + } final SparseArray<ActivityInterceptorCallback> callbacks = mService.getActivityInterceptorCallbacks(); @@ -470,6 +482,47 @@ class ActivityStartInterceptor { return true; } + private boolean interceptHomeIfNeeded() { + if (mPresumableLaunchDisplayArea == null || mService.mRootWindowContainer == null) { + return false; + } + if (!ActivityRecord.isHomeIntent(mIntent)) { + return false; + } + if (!mIntent.hasCategory(Intent.CATEGORY_HOME)) { + // Already a secondary home intent, leave it alone. + return false; + } + if (mService.mRootWindowContainer.shouldPlacePrimaryHomeOnDisplay( + mPresumableLaunchDisplayArea.getDisplayId())) { + // Primary home can be launched to the display area. + return false; + } + if (!mService.mRootWindowContainer.shouldPlaceSecondaryHomeOnDisplayArea( + mPresumableLaunchDisplayArea)) { + // Secondary home cannot be launched on the display area. + return false; + } + + // At this point we have a primary home intent for a display that does not support primary + // home activity but it supports secondary home one. So replace it with secondary home. + Pair<ActivityInfo, Intent> info = mService.mRootWindowContainer + .resolveSecondaryHomeActivity(mUserId, mPresumableLaunchDisplayArea); + mIntent = info.second; + // The new task flag is needed because the home activity should already be in the root task + // and should not be moved to the caller's task. Also, activities cannot change their type, + // e.g. a standard activity cannot become a home activity. + mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK); + mCallingPid = mRealCallingPid; + mCallingUid = mRealCallingUid; + mResolvedType = null; + + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0, + mRealCallingUid, mRealCallingPid); + mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/ null); + return true; + } + private boolean isPackageSuspended() { return mAInfo != null && mAInfo.applicationInfo != null && (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 1bc78d6e0820..458d1e8fa04b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1154,10 +1154,12 @@ class ActivityStarter { } } + final TaskDisplayArea suggestedLaunchDisplayArea = + computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions); mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, - callingPid, callingUid, checkedOptions)) { + callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) { // activity start was intercepted, e.g. because the target user is currently in quiet // mode (turn off work) or the target application is suspended intent = mInterceptor.mIntent; @@ -1890,6 +1892,15 @@ class ActivityStarter { mPreferredWindowingMode = mLaunchParams.mWindowingMode; } + private TaskDisplayArea computeSuggestedLaunchDisplayArea( + Task task, ActivityRecord source, ActivityOptions options) { + mSupervisor.getLaunchParamsController().calculate(task, /*layout=*/null, + /*activity=*/ null, source, options, mRequest, PHASE_DISPLAY, mLaunchParams); + return mLaunchParams.hasPreferredTaskDisplayArea() + ? mLaunchParams.mPreferredTaskDisplayArea + : mRootWindowContainer.getDefaultTaskDisplayArea(); + } + @VisibleForTesting int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) { if (r.packageName == null) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 57f8268b2fdc..d56acaa00f00 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1608,6 +1608,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** + * Check if the display is valid for primary home activity. + * + * @param displayId The target display ID + * @return {@code true} if allowed to launch, {@code false} otherwise. + */ + boolean shouldPlacePrimaryHomeOnDisplay(int displayId) { + // No restrictions to default display, vr 2d display or main display for visible users. + return displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY + && (displayId == mService.mVr2dDisplayId + || mWmService.shouldPlacePrimaryHomeOnDisplay(displayId))); + } + + /** * Check if the display area is valid for secondary home activity. * * @param taskDisplayArea The target display area. @@ -1680,10 +1693,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int displayId = taskDisplayArea != null ? taskDisplayArea.getDisplayId() : INVALID_DISPLAY; - if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY - && (displayId == mService.mVr2dDisplayId - || mWmService.shouldPlacePrimaryHomeOnDisplay(displayId)))) { - // No restrictions to default display, vr 2d display or main display for visible users. + if (shouldPlacePrimaryHomeOnDisplay(displayId)) { return true; } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index ad46770432a1..fa620db23306 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -118,12 +118,14 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { root = activity; } - if (root == null) { + if (root == null && phase != PHASE_DISPLAY) { // There is a case that can lead us here. The caller is moving the top activity that is // in a task that has multiple activities to PIP mode. For that the caller is creating a // new task to host the activity so that we only move the top activity to PIP mode and // keep other activities in the previous task. There is no point to apply the launch // logic in this case. + // However, for PHASE_DISPLAY the root may be null, but we still want to get a hint of + // what the suggested launch display area would be. return RESULT_SKIP; } @@ -395,8 +397,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task, - @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams, - @NonNull ActivityRecord activityRecord, @Nullable Request request) { + @Nullable ActivityOptions options, @Nullable ActivityRecord source, + @Nullable LaunchParams currentParams, @Nullable ActivityRecord activityRecord, + @Nullable Request request) { TaskDisplayArea taskDisplayArea = null; final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null @@ -438,8 +441,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // If the source activity is a no-display activity, pass on the launch display area token // from source activity as currently preferred. - if (taskDisplayArea == null && source != null - && source.noDisplay) { + if (taskDisplayArea == null && source != null && source.noDisplay) { taskDisplayArea = source.mHandoverTaskDisplayArea; if (taskDisplayArea != null) { if (DEBUG) appendLog("display-area-from-no-display-source=" + taskDisplayArea); @@ -478,21 +480,24 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } } - if (taskDisplayArea == null) { + if (taskDisplayArea == null && currentParams != null) { taskDisplayArea = currentParams.mPreferredTaskDisplayArea; + if (DEBUG) appendLog("display-area-from-current-params=" + taskDisplayArea); } // Re-route to default display if the device didn't declare support for multi-display if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) { taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + if (DEBUG) appendLog("display-area-from-no-multidisplay=" + taskDisplayArea); } // Re-route to default display if the home activity doesn't support multi-display - if (taskDisplayArea != null && activityRecord.isActivityTypeHome() + if (taskDisplayArea != null && activityRecord != null && activityRecord.isActivityTypeHome() && !mSupervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info, taskDisplayArea, false /* allowInstrumenting */)) { taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + if (DEBUG) appendLog("display-area-from-home=" + taskDisplayArea); } return (taskDisplayArea != null) @@ -516,34 +521,56 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { * @return {@link TaskDisplayArea} to house the task */ private TaskDisplayArea getFallbackDisplayAreaForActivity( - @NonNull ActivityRecord activityRecord, @Nullable Request request) { - - WindowProcessController controllerFromLaunchingRecord = mSupervisor.mService - .getProcessController(activityRecord.launchedFromPid, - activityRecord.launchedFromUid); - final TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null - ? null : controllerFromLaunchingRecord.getTopActivityDisplayArea(); - if (displayAreaForLaunchingRecord != null) { - return displayAreaForLaunchingRecord; - } + @Nullable ActivityRecord activityRecord, @Nullable Request request) { + if (activityRecord != null) { + WindowProcessController controllerFromLaunchingRecord = + mSupervisor.mService.getProcessController( + activityRecord.launchedFromPid, activityRecord.launchedFromUid); + if (controllerFromLaunchingRecord != null) { + final TaskDisplayArea taskDisplayAreaForLaunchingRecord = + controllerFromLaunchingRecord.getTopActivityDisplayArea(); + if (taskDisplayAreaForLaunchingRecord != null) { + if (DEBUG) { + appendLog("display-area-for-launching-record=" + + taskDisplayAreaForLaunchingRecord); + } + return taskDisplayAreaForLaunchingRecord; + } + } - WindowProcessController controllerFromProcess = mSupervisor.mService.getProcessController( - activityRecord.getProcessName(), activityRecord.getUid()); - final TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null - : controllerFromProcess.getTopActivityDisplayArea(); - if (displayAreaForRecord != null) { - return displayAreaForRecord; + WindowProcessController controllerFromProcess = + mSupervisor.mService.getProcessController( + activityRecord.getProcessName(), activityRecord.getUid()); + if (controllerFromProcess != null) { + final TaskDisplayArea displayAreaForRecord = + controllerFromProcess.getTopActivityDisplayArea(); + if (displayAreaForRecord != null) { + if (DEBUG) appendLog("display-area-for-record=" + displayAreaForRecord); + return displayAreaForRecord; + } + } } - WindowProcessController controllerFromRequest = request == null ? null : mSupervisor - .mService.getProcessController(request.realCallingPid, request.realCallingUid); - final TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null - : controllerFromRequest.getTopActivityDisplayArea(); - if (displayAreaFromSourceProcess != null) { - return displayAreaFromSourceProcess; + if (request != null) { + WindowProcessController controllerFromRequest = + mSupervisor.mService.getProcessController( + request.realCallingPid, request.realCallingUid); + if (controllerFromRequest != null) { + final TaskDisplayArea displayAreaFromSourceProcess = + controllerFromRequest.getTopActivityDisplayArea(); + if (displayAreaFromSourceProcess != null) { + if (DEBUG) { + appendLog("display-area-source-process=" + displayAreaFromSourceProcess); + } + return displayAreaFromSourceProcess; + } + } } - return mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + final TaskDisplayArea defaultTaskDisplayArea = + mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + if (DEBUG) appendLog("display-area-from-default-fallback=" + defaultTaskDisplayArea); + return defaultTaskDisplayArea; } private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index bcb0c6b5c269..0989db4c25ac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -59,6 +59,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.testing.DexmakerShareClassLoaderRule; +import android.util.Pair; import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -128,6 +129,8 @@ public class ActivityStartInterceptorTest { private ActivityManagerInternal mAmInternal; @Mock private LockTaskController mLockTaskController; + @Mock + private TaskDisplayArea mTaskDisplayArea; private ActivityStartInterceptor mInterceptor; private ActivityInfo mAInfo = new ActivityInfo(); @@ -139,8 +142,8 @@ public class ActivityStartInterceptorTest { public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mService.mAmInternal = mAmInternal; - mInterceptor = new ActivityStartInterceptor( - mService, mSupervisor, mRootWindowContainer, mContext); + mService.mRootWindowContainer = mRootWindowContainer; + mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext); mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID, TEST_START_FLAGS, TEST_CALLING_PACKAGE, null); @@ -201,7 +204,7 @@ public class ActivityStartInterceptorTest { .thenReturn(PLATFORM_PACKAGE_NAME); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // THEN the returned intent is the admin support intent assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent); @@ -212,7 +215,7 @@ public class ActivityStartInterceptorTest { final String suspendingPackage = "com.test.suspending.package"; final SuspendDialogInfo dialogInfo = suspendPackage(suspendingPackage); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // Check intent parameters assertEquals(dialogInfo, @@ -243,7 +246,7 @@ public class ActivityStartInterceptorTest { TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)) .thenReturn(false); - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME) .filterEquals(mInterceptor.mIntent)); @@ -257,7 +260,8 @@ public class ActivityStartInterceptorTest { when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true); // THEN calling intercept returns false because package also has to be suspended. - assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertFalse( + mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); } @Test @@ -268,7 +272,7 @@ public class ActivityStartInterceptorTest { when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // THEN the returned intent is the quiet mode intent assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID) @@ -284,7 +288,7 @@ public class ActivityStartInterceptorTest { when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // THEN the returned intent is the quiet mode intent assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID) @@ -300,7 +304,7 @@ public class ActivityStartInterceptorTest { when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // THEN the returned intent is the quiet mode intent assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID) @@ -313,7 +317,7 @@ public class ActivityStartInterceptorTest { when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true); // THEN calling intercept returns true - mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null); // THEN the returned intent is the confirm credentials intent assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent)); @@ -329,7 +333,7 @@ public class ActivityStartInterceptorTest { mAInfo.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED; // THEN calling intercept returns true - mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null, null); // THEN the returned intent is original intent assertSame(originalIntent, mInterceptor.mIntent); @@ -345,7 +349,7 @@ public class ActivityStartInterceptorTest { mAInfo.directBootAware = false; // THEN calling intercept returns true - mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null); // THEN the returned intent is the confirm credentials intent assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent)); @@ -362,7 +366,7 @@ public class ActivityStartInterceptorTest { mAInfo.directBootAware = true; // THEN calling intercept returns true - mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null, null); // THEN the returned intent is original intent assertSame(originalIntent, mInterceptor.mIntent); @@ -375,7 +379,7 @@ public class ActivityStartInterceptorTest { .thenReturn("This app is bad"); // THEN calling intercept returns true - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); // THEN the returned intent is the harmful app warning intent assertEquals(HarmfulAppWarningActivity.class.getName(), @@ -383,11 +387,40 @@ public class ActivityStartInterceptorTest { } @Test + public void testHomeIntentInterception() { + // GIVEN a primary home intent and a display area that doesn't support it but supports + // secondary home activities + Intent originalIntent = new Intent(Intent.ACTION_MAIN); + originalIntent.addCategory(Intent.CATEGORY_HOME); + + Intent expectedIntent = new Intent(Intent.ACTION_MAIN); + expectedIntent.addCategory(Intent.CATEGORY_SECONDARY_HOME); + expectedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + final int secondaryDisplayId = 7; + when(mTaskDisplayArea.getDisplayId()).thenReturn(secondaryDisplayId); + when(mRootWindowContainer.shouldPlacePrimaryHomeOnDisplay(eq(secondaryDisplayId))) + .thenReturn(false); + when(mRootWindowContainer.shouldPlaceSecondaryHomeOnDisplayArea(eq(mTaskDisplayArea))) + .thenReturn(true); + when(mRootWindowContainer.resolveSecondaryHomeActivity( + eq(TEST_USER_ID), eq(mTaskDisplayArea))) + .thenReturn(Pair.create(null, expectedIntent)); + + // THEN calling intercept returns true + assertTrue(mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, + null, mTaskDisplayArea)); + + // THEN the returned intent is the secondary home intent + assertSame(expectedIntent, mInterceptor.mIntent); + } + + @Test public void testNoInterception() { // GIVEN that none of the interception conditions are met // THEN calling intercept returns false - assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); } public void addMockInterceptorCallback( @@ -420,7 +453,7 @@ public class ActivityStartInterceptorTest { new Intent("android.test.foo"), ActivityOptions.makeBasic().setLaunchDisplayId(3)); - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); assertEquals("android.test.foo", mInterceptor.mIntent.getAction()); assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId()); } @@ -429,7 +462,7 @@ public class ActivityStartInterceptorTest { public void testInterceptionCallback_singleCallbackReturnsNull() { addMockInterceptorCallback(null, null); - assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); } @Test @@ -437,7 +470,7 @@ public class ActivityStartInterceptorTest { addMockInterceptorCallback(null, null); addMockInterceptorCallback(new Intent("android.test.second"), null); - assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); assertEquals("android.test.second", mInterceptor.mIntent.getAction()); } @@ -447,7 +480,7 @@ public class ActivityStartInterceptorTest { new Intent("android.test.foo"), ActivityOptions.makeBasic().setLaunchDisplayId(3), true); ActivityInfo aInfo = mAInfo; - assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null, null)); assertEquals("android.test.foo", mInterceptor.mIntent.getAction()); assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId()); assertEquals(aInfo, mInterceptor.mAInfo); // mAInfo should not be resolved @@ -459,7 +492,7 @@ public class ActivityStartInterceptorTest { new Intent("android.test.foo"), ActivityOptions.makeBasic().setLaunchDisplayId(3)); ActivityInfo aInfo = mAInfo; - assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null)); + assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null, null)); assertEquals("android.test.foo", mInterceptor.mIntent.getAction()); assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId()); assertNotEquals(aInfo, mInterceptor.mAInfo); // mAInfo should be resolved after intercept @@ -488,7 +521,7 @@ public class ActivityStartInterceptorTest { when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock); Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); verify(spyCallback, times(1)).onInterceptActivityLaunch( any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); @@ -505,7 +538,7 @@ public class ActivityStartInterceptorTest { when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock); Intent intent = new Intent().setPackage(sandboxPackageNameMock); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); verify(spyCallback, times(1)).onInterceptActivityLaunch( any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); @@ -522,7 +555,7 @@ public class ActivityStartInterceptorTest { when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock); Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, "")); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); verify(spyCallback, times(1)).onInterceptActivityLaunch( any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); @@ -539,23 +572,23 @@ public class ActivityStartInterceptorTest { when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock); // Intent: null - mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null); // Action: null, Package: null, ComponentName: null Intent intent = new Intent(); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); // Wrong Action intent = new Intent().setAction(Intent.ACTION_VIEW); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); // Wrong Package intent = new Intent().setPackage("Random"); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); // Wrong ComponentName's package intent = new Intent().setComponent(new ComponentName("Random", "")); - mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null); verify(spyCallback, never()).onInterceptActivityLaunch( any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); |