diff options
9 files changed, 142 insertions, 34 deletions
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index cb8988eb5b92..08cfbf76a040 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -58,7 +58,7 @@ interface ILauncherApps { void startActivityAsUser(in IApplicationThread caller, String callingPackage, String callingFeatureId, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); - PendingIntent getActivityLaunchIntent(in ComponentName component, in Bundle opts, + PendingIntent getActivityLaunchIntent(String callingPackage, in ComponentName component, in UserHandle user); void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage, String callingFeatureId, in ComponentName component, in Rect sourceBounds, diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index e35c2f48038d..301d1bbc8e9d 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -749,24 +749,29 @@ public class LauncherApps { } /** - * Returns a PendingIntent that would start the same activity started from - * {@link #startMainActivity(ComponentName, UserHandle, Rect, Bundle)}. + * Returns a mutable PendingIntent that would start the same activity started from + * {@link #startMainActivity(ComponentName, UserHandle, Rect, Bundle)}. The caller needs to + * take care in ensuring that the mutable intent returned is not passed to untrusted parties. * * @param component The ComponentName of the activity to launch * @param startActivityOptions This parameter is no longer supported * @param user The UserHandle of the profile * @hide */ + @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) @Nullable public PendingIntent getMainActivityLaunchIntent(@NonNull ComponentName component, @Nullable Bundle startActivityOptions, @NonNull UserHandle user) { + if (mContext.checkSelfPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "Only allowed for recents."); + } logErrorForInvalidProfileAccess(user); if (DEBUG) { Log.i(TAG, "GetMainActivityLaunchIntent " + component + " " + user); } try { - // due to b/209607104, startActivityOptions will be ignored - return mService.getActivityLaunchIntent(component, null /* opts */, user); + return mService.getActivityLaunchIntent(mContext.getPackageName(), component, user); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 5ebdceba135b..e8bae0f94bf0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -45,10 +45,12 @@ import android.app.WindowConfiguration; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipDescription; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.LauncherApps; +import android.content.pm.ResolveInfo; import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; @@ -62,9 +64,11 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; import java.lang.annotation.Retention; @@ -106,12 +110,19 @@ public class DragAndDropPolicy { */ void start(DisplayLayout displayLayout, ClipData data, InstanceId loggerSessionId) { mLoggerSessionId = loggerSessionId; - mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data); + mSession = new DragSession(mActivityTaskManager, displayLayout, data); // TODO(b/169894807): Also update the session data with task stack changes mSession.update(); } /** + * Returns the last running task. + */ + ActivityManager.RunningTaskInfo getLatestRunningTask() { + return mSession.runningTaskInfo; + } + + /** * Returns the target's regions based on the current state of the device and display. */ @NonNull @@ -248,32 +259,68 @@ public class DragAndDropPolicy { final UserHandle user = intent.getParcelableExtra(EXTRA_USER); mStarter.startShortcut(packageName, id, position, opts, user); } else { - mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), - null, position, opts); + final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT); + mStarter.startIntent(launchIntent, getStartIntentFillInIntent(launchIntent, position), + position, opts); + } + } + + /** + * Returns the fill-in intent to use when starting an app from a drop. + */ + @VisibleForTesting + Intent getStartIntentFillInIntent(PendingIntent launchIntent, @SplitPosition int position) { + // Get the drag app + final List<ResolveInfo> infos = launchIntent.queryIntentComponents(0 /* flags */); + final ComponentName dragIntentActivity = !infos.isEmpty() + ? infos.get(0).activityInfo.getComponentName() + : null; + + // Get the current app (either fullscreen or the remaining app post-drop if in splitscreen) + final boolean inSplitScreen = mSplitScreen != null + && mSplitScreen.isSplitScreenVisible(); + final ComponentName currentActivity; + if (!inSplitScreen) { + currentActivity = mSession.runningTaskInfo != null + ? mSession.runningTaskInfo.baseActivity + : null; + } else { + final int nonReplacedSplitPosition = position == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT + : SPLIT_POSITION_TOP_OR_LEFT; + ActivityManager.RunningTaskInfo nonReplacedTaskInfo = + mSplitScreen.getTaskInfo(nonReplacedSplitPosition); + currentActivity = nonReplacedTaskInfo.baseActivity; + } + + if (currentActivity.equals(dragIntentActivity)) { + // Only apply MULTIPLE_TASK if we are dragging the same activity + final Intent fillInIntent = new Intent(); + fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Adding MULTIPLE_TASK"); + return fillInIntent; } + return null; } /** * Per-drag session data. */ private static class DragSession { - private final Context mContext; private final ActivityTaskManager mActivityTaskManager; private final ClipData mInitialDragData; final DisplayLayout displayLayout; Intent dragData; - int runningTaskId; + ActivityManager.RunningTaskInfo runningTaskInfo; @WindowConfiguration.WindowingMode int runningTaskWinMode = WINDOWING_MODE_UNDEFINED; @WindowConfiguration.ActivityType int runningTaskActType = ACTIVITY_TYPE_STANDARD; - boolean runningTaskIsResizeable; boolean dragItemSupportsSplitscreen; - DragSession(Context context, ActivityTaskManager activityTaskManager, + DragSession(ActivityTaskManager activityTaskManager, DisplayLayout dispLayout, ClipData data) { - mContext = context; mActivityTaskManager = activityTaskManager; mInitialDragData = data; displayLayout = dispLayout; @@ -287,10 +334,9 @@ public class DragAndDropPolicy { mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */); if (!tasks.isEmpty()) { final ActivityManager.RunningTaskInfo task = tasks.get(0); + runningTaskInfo = task; runningTaskWinMode = task.getWindowingMode(); runningTaskActType = task.getActivityType(); - runningTaskId = task.taskId; - runningTaskIsResizeable = task.isResizeable; } final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 7307ba30fd67..d395f956a41c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -26,7 +26,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.app.ActivityManager; -import android.app.ActivityTaskManager; import android.app.StatusBarManager; import android.content.ClipData; import android.content.Context; @@ -35,7 +34,6 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.RemoteException; import android.view.DragEvent; import android.view.SurfaceControl; import android.view.WindowInsets; @@ -51,7 +49,6 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.ArrayList; -import java.util.List; /** * Coordinates the visible drop targets for the current drag. @@ -166,17 +163,8 @@ public class DragLayout extends LinearLayout { boolean alreadyInSplit = mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible(); if (!alreadyInSplit) { - List<ActivityManager.RunningTaskInfo> tasks = null; - // Figure out the splashscreen info for the existing task. - try { - tasks = ActivityTaskManager.getService().getTasks(1, - false /* filterOnlyVisibleRecents */, - false /* keepIntentExtra */); - } catch (RemoteException e) { - // don't show an icon / will just use the defaults - } - if (tasks != null && !tasks.isEmpty()) { - ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0); + ActivityManager.RunningTaskInfo taskInfo1 = mPolicy.getLatestRunningTask(); + if (taskInfo1 != null) { Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo); int bgColor1 = getResizingBackgroundColor(taskInfo1); mDropZoneView1.setAppInfo(bgColor1, icon1); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java index 6080f3ae78e8..403dbf9d9554 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java @@ -22,7 +22,7 @@ import android.content.Context; import android.hardware.display.DisplayManager; import android.testing.TestableContext; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; import org.junit.Before; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 35e498262707..bb6026c36c97 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -23,6 +23,8 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -32,6 +34,7 @@ import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPL import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -50,9 +53,11 @@ import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; @@ -177,6 +182,12 @@ public class DragAndDropPolicyTest { info.configuration.windowConfiguration.setActivityType(actType); info.configuration.windowConfiguration.setWindowingMode(winMode); info.isResizeable = true; + info.baseActivity = new ComponentName(getInstrumentation().getContext().getPackageName(), + ".ActivityWithMode" + winMode); + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.packageName = info.baseActivity.getPackageName(); + activityInfo.name = info.baseActivity.getClassName(); + info.topActivityInfo = activityInfo; return info; } @@ -252,6 +263,62 @@ public class DragAndDropPolicyTest { } } + @Test + public void testLaunchMultipleTask_differentActivity() { + setRunningTask(mFullscreenAppTask); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); + Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0); + assertNull(fillInIntent); + } + + @Test + public void testLaunchMultipleTask_differentActivity_inSplitscreen() { + setRunningTask(mFullscreenAppTask); + doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible(); + doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt()); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); + Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0); + assertNull(fillInIntent); + } + + @Test + public void testLaunchMultipleTask_sameActivity() { + setRunningTask(mFullscreenAppTask); + + // Replace the mocked drag pending intent and ensure it resolves to the same activity + PendingIntent launchIntent = mock(PendingIntent.class); + ResolveInfo launchInfo = new ResolveInfo(); + launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo; + doReturn(Collections.singletonList(launchInfo)) + .when(launchIntent).queryIntentComponents(anyInt()); + mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT, + launchIntent); + + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); + Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0); + assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0); + } + + @Test + public void testLaunchMultipleTask_sameActivity_inSplitScreen() { + setRunningTask(mFullscreenAppTask); + + // Replace the mocked drag pending intent and ensure it resolves to the same activity + PendingIntent launchIntent = mock(PendingIntent.class); + ResolveInfo launchInfo = new ResolveInfo(); + launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo; + doReturn(Collections.singletonList(launchInfo)) + .when(launchIntent).queryIntentComponents(anyInt()); + mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT, + launchIntent); + + doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible(); + doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt()); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); + Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0); + assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0); + } + private Target filterTargetByType(ArrayList<Target> targets, int type) { for (Target t : targets) { if (type == t.type) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java index f10dc16fae5c..b976c1287aca 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java @@ -24,8 +24,8 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.common.ShellExecutor; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java index 078e2b6cf574..16e92395c85e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java @@ -45,8 +45,8 @@ import android.window.DisplayAreaOrganizer; import android.window.IWindowContainerToken; import android.window.WindowContainerToken; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index e65783865659..6b3ce773fb63 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME; import static android.app.PendingIntent.FLAG_IMMUTABLE; +import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; @@ -1218,8 +1219,9 @@ public class LauncherAppsService extends SystemService { } @Override - public PendingIntent getActivityLaunchIntent(ComponentName component, Bundle opts, + public PendingIntent getActivityLaunchIntent(String callingPackage, ComponentName component, UserHandle user) { + ensureShortcutPermission(callingPackage); if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) { throw new ActivityNotFoundException("Activity could not be found"); } @@ -1237,7 +1239,7 @@ public class LauncherAppsService extends SystemService { // calling identity to mirror the startActivityAsUser() call which does not validate // the calling user return PendingIntent.getActivityAsUser(mContext, 0 /* requestCode */, launchIntent, - FLAG_IMMUTABLE, null /* options */, user); + FLAG_MUTABLE, null /* opts */, user); } finally { Binder.restoreCallingIdentity(ident); } |