diff options
| author | 2023-08-23 14:40:40 +0200 | |
|---|---|---|
| committer | 2023-09-01 08:49:54 +0200 | |
| commit | a4269d0b8c636c807fef1c12a29aacc5821f1be2 (patch) | |
| tree | b897c42ff031af29bf61042d524509b63aee22ec | |
| parent | 9d287d852a976b6c3ff51a88f1356179fbe68948 (diff) | |
Intercept HOME intents for secondary home displays.
Compute (the likely) launch TaskDisplayArea early in ActivityStarter
before intercepting the intent and pass it to
ActiivtyStartInterceptor. If the launch display doesn't support
primary home but it supports secondary home, replace the intent with
the secondary home intent.
The existing RootWindowController#resolveSecondaryHomeActivity is
reused to consolidate the resolution logic into a single place. This
has the side effect that the ResolverActivity is skipped if there are
more than one CATEGORY_SECONDARY_HOME activities available but the
secondary home component that is started on new displays is the one
that is resolved, following the existing resolution logic. Note that
this is only the case if a CATEGORY_HOME intent is sent to the virtual
display. CATEGORY_SECONDARY_HOME intents are still resolved in the
old way. This is super nice as it complies with both the existing
intent handling logic and allows for a seamless home experience on VD
This also nicely allows for adding the display's custom home component
to the intent at a single place in the future.
Fix: 297168712
Test: atest ActivityStartInterceptorTest
Change-Id: I59e24f97b5df11372c17e50c9ea5751dbf973eb7
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)); |