diff options
| author | 2020-10-15 22:50:34 -0700 | |
|---|---|---|
| committer | 2020-10-26 14:33:05 -0700 | |
| commit | 8c1fc3ea3a6d2ccfbd369eb843809eb65bed2427 (patch) | |
| tree | d27714dd2a52873a75305fe9884ceb98b006d06e | |
| parent | 4f2f5f15478773261be6ab6158924ea1cb2b5efd (diff) | |
2/ Add support for dragging shortcuts and tasks
- Add mechanism for creating PendingIntents to launch apps in the same
way as starting through LauncherApps service
- Require callers starting a task launch to either be the recents
component or have the START_TASKS_FROM_RECENTS permission
Bug: 169894807
Test: atest LauncherAppsTest
Test: atest DragDropControllerTest
Change-Id: I2f9f622b4ef7a8aba06b0854a1549a2c07cf38e1
12 files changed, 336 insertions, 42 deletions
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index dedfce6f6b96..38ab8aa2dcbc 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -64,12 +64,29 @@ public class ClipDescription implements Parcelable { public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent"; /** - * The MIME type for an activity. + * The MIME type for an activity. The ClipData must include intents with required extras + * {@link #EXTRA_PENDING_INTENT} and {@link Intent#EXTRA_USER}, and an optional + * {@link #EXTRA_ACTIVITY_OPTIONS}. * @hide */ public static final String MIMETYPE_APPLICATION_ACTIVITY = "application/vnd.android.activity"; /** + * The MIME type for a shortcut. The ClipData must include intents with required extras + * {@link #EXTRA_PENDING_INTENT} and {@link Intent#EXTRA_USER}, and an optional + * {@link #EXTRA_ACTIVITY_OPTIONS}. + * @hide + */ + public static final String MIMETYPE_APPLICATION_SHORTCUT = "application/vnd.android.shortcut"; + + /** + * The MIME type for a task. The ClipData must include an intent with a required extra + * {@link Intent#EXTRA_TASK_ID} of the task to launch. + * @hide + */ + public static final String MIMETYPE_APPLICATION_TASK = "application/vnd.android.task"; + + /** * The MIME type for data whose type is otherwise unknown. * <p> * Per RFC 2046, the "application" media type is to be used for discrete diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index d9ecf46069cd..d688614f6caa 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -17,6 +17,7 @@ package android.content.pm; import android.app.IApplicationThread; +import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; @@ -55,6 +56,8 @@ 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, + in UserHandle user); void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage, String callingFeatureId, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index b7af397cd36a..2909d66d72ff 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -17,6 +17,7 @@ package android.content.pm; import static android.Manifest.permission; +import static android.app.PendingIntent.FLAG_IMMUTABLE; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -716,6 +717,29 @@ public class LauncherApps { } /** + * Returns a PendingIntent that would start the same activity started from + * {@link #startMainActivity(ComponentName, UserHandle, Rect, Bundle)}. + * + * @param component The ComponentName of the activity to launch + * @param startActivityOptions Options to pass to startActivity + * @param user The UserHandle of the profile + * @hide + */ + @Nullable + public PendingIntent getMainActivityLaunchIntent(@NonNull ComponentName component, + @Nullable Bundle startActivityOptions, @NonNull UserHandle user) { + logErrorForInvalidProfileAccess(user); + if (DEBUG) { + Log.i(TAG, "GetMainActivityLaunchIntent " + component + " " + user); + } + try { + return mService.getActivityLaunchIntent(component, startActivityOptions, user); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it * returns null. * diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 358d440686dd..f55e91e6ec40 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -145,6 +145,12 @@ "group": "WM_SHELL_DRAG_AND_DROP", "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java" }, + "1842752748": { + "message": "Clip description: handlingDrag=%b mimeTypes=%s", + "level": "VERBOSE", + "group": "WM_SHELL_DRAG_AND_DROP", + "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java" + }, "1862198614": { "message": "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 8f8b98bbfbae..bf5b1d8a4bcc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -16,8 +16,17 @@ package com.android.wm.shell.draganddrop; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS; +import static android.content.ClipDescription.EXTRA_PENDING_INTENT; 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 android.content.Intent.EXTRA_PACKAGE_NAME; +import static android.content.Intent.EXTRA_SHORTCUT_ID; +import static android.content.Intent.EXTRA_TASK_ID; +import static android.content.Intent.EXTRA_USER; import static android.view.DragEvent.ACTION_DRAG_ENDED; import static android.view.DragEvent.ACTION_DRAG_ENTERED; import static android.view.DragEvent.ACTION_DRAG_EXITED; @@ -33,15 +42,24 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.app.PendingIntent; +import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherApps; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.RemoteException; +import android.os.UserHandle; import android.util.Slog; import android.util.SparseArray; import android.view.DragEvent; @@ -68,6 +86,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange private static final String TAG = DragAndDropController.class.getSimpleName(); + private final Context mContext; private final DisplayController mDisplayController; private SplitScreen mSplitScreen; @@ -76,7 +95,8 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange private DragLayout mDragLayout; private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); - public DragAndDropController(DisplayController displayController) { + public DragAndDropController(Context context, DisplayController displayController) { + mContext = context; mDisplayController = displayController; mDisplayController.addDisplayWindowListener(this); } @@ -135,13 +155,16 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange event.getOffsetX(), event.getOffsetY()); final int displayId = target.getDisplay().getDisplayId(); final PerDisplay pd = mDisplayDropTargets.get(displayId); + final ClipDescription description = event.getClipDescription(); if (event.getAction() == ACTION_DRAG_STARTED) { - final ClipDescription description = event.getClipDescription(); - final boolean hasValidClipData = description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY); + final boolean hasValidClipData = description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY) + || description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT) + || description.hasMimeType(MIMETYPE_APPLICATION_TASK); mIsHandlingDrag = hasValidClipData; - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Clip description: %s", - getMimeTypes(description)); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Clip description: handlingDrag=%b mimeTypes=%s", + mIsHandlingDrag, getMimeTypes(description)); } if (!mIsHandlingDrag) { @@ -163,31 +186,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mDragLayout.update(event); break; case ACTION_DROP: { - final SurfaceControl dragSurface = event.getDragSurface(); - final View dragLayout = mDragLayout; - final ClipData data = event.getClipData(); - return mDragLayout.drop(event, dragSurface, (dropTargetBounds) -> { - if (dropTargetBounds != null) { - // TODO(b/169894807): Properly handle the drop, for now just launch it - if (data.getItemCount() > 0) { - Intent intent = data.getItemAt(0).getIntent(); - PendingIntent pi = intent.getParcelableExtra( - ClipDescription.EXTRA_PENDING_INTENT); - try { - pi.send(); - } catch (PendingIntent.CanceledException e) { - Slog.e(TAG, "Failed to launch activity", e); - } - } - } - - setDropTargetWindowVisibility(pd, View.INVISIBLE); - pd.dropTarget.removeView(dragLayout); - - // Clean up the drag surface - mTransaction.reparent(dragSurface, null); - mTransaction.apply(); - }); + return handleDrop(event, pd); } case ACTION_DRAG_EXITED: { // Either one of DROP or EXITED will happen, and when EXITED we won't consume @@ -211,6 +210,62 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return true; } + /** + * Handles dropping on the drop target. + */ + private boolean handleDrop(DragEvent event, PerDisplay pd) { + final ClipData data = event.getClipData(); + final ClipDescription description = event.getClipDescription(); + final SurfaceControl dragSurface = event.getDragSurface(); + final View dragLayout = mDragLayout; + final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); + final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); + return mDragLayout.drop(event, dragSurface, (dropTargetBounds) -> { + if (dropTargetBounds != null && data.getItemCount() > 0) { + final Intent intent = data.getItemAt(0).getIntent(); + // TODO(b/169894807): Properly handle the drop, for now just launch it + if (isTask) { + int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); + try { + ActivityTaskManager.getService().startActivityFromRecents( + taskId, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to launch task", e); + } + } else if (isShortcut) { + try { + Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS) + ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) + : null; + LauncherApps launcherApps = + mContext.getSystemService(LauncherApps.class); + launcherApps.startShortcut( + intent.getStringExtra(EXTRA_PACKAGE_NAME), + intent.getStringExtra(EXTRA_SHORTCUT_ID), + null /* sourceBounds */, opts, + intent.getParcelableExtra(EXTRA_USER)); + } catch (ActivityNotFoundException e) { + Slog.e(TAG, "Failed to launch shortcut", e); + } + } else { + PendingIntent pi = intent.getParcelableExtra(EXTRA_PENDING_INTENT); + try { + pi.send(); + } catch (PendingIntent.CanceledException e) { + Slog.e(TAG, "Failed to launch activity", e); + } + } + } + + setDropTargetWindowVisibility(pd, View.INVISIBLE); + pd.dropTarget.removeView(dragLayout); + + // Clean up the drag surface + mTransaction.reparent(dragSurface, null); + mTransaction.apply(); + }); + } + private void setDropTargetWindowVisibility(PerDisplay pd, int visibility) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Set drop target window visibility: displayId=%d visibility=%d", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java new file mode 100644 index 000000000000..0b1141ee9b30 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.content.ClipDescription; +import android.content.Intent; + +/** + * Wrapper around ClipDescription. + */ +public abstract class ClipDescriptionCompat { + + public static String MIMETYPE_APPLICATION_ACTIVITY = + ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; + + public static String MIMETYPE_APPLICATION_SHORTCUT = + ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; + + public static String MIMETYPE_APPLICATION_TASK = + ClipDescription.MIMETYPE_APPLICATION_TASK; + + public static String EXTRA_PENDING_INTENT = ClipDescription.EXTRA_PENDING_INTENT; + + public static String EXTRA_TASK_ID = Intent.EXTRA_TASK_ID; +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java new file mode 100644 index 000000000000..d24c779b1416 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.pm.LauncherApps; +import android.os.Bundle; +import android.os.UserHandle; + +/** + * Wrapper around LauncherApps. + */ +public abstract class LauncherAppsCompat { + + public static PendingIntent getMainActivityLaunchIntent(LauncherApps launcherApps, + ComponentName component, Bundle startActivityOptions, UserHandle user) { + return launcherApps.getMainActivityLaunchIntent(component, startActivityOptions, user); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index ead2fc1cd9dc..cff1c9d086ea 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -102,8 +102,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static DragAndDropController provideDragAndDropController(DisplayController displayController) { - return new DragAndDropController(displayController); + static DragAndDropController provideDragAndDropController(Context context, + DisplayController displayController) { + return new DragAndDropController(context, displayController); } @WMSingleton diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 8eebf2a8d9d9..b679c0fbab83 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS; @@ -32,6 +33,7 @@ import android.app.IApplicationThread; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.usage.UsageStatsManagerInternal; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -1018,6 +1020,32 @@ public class LauncherAppsService extends SystemService { } @Override + public PendingIntent getActivityLaunchIntent(ComponentName component, Bundle opts, + UserHandle user) { + if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) { + throw new ActivityNotFoundException("Activity could not be found"); + } + + final Intent launchIntent = getMainActivityLaunchIntent(component, user); + if (launchIntent == null) { + throw new SecurityException("Attempt to launch activity without " + + " category Intent.CATEGORY_LAUNCHER " + component); + } + + final long ident = Binder.clearCallingIdentity(); + try { + // If we reach here, we've verified that the caller has access to the profile and + // is launching an exported activity with CATEGORY_LAUNCHER so we can clear the + // calling identity to mirror the startActivityAsUser() call which does not validate + // the calling user + return PendingIntent.getActivityAsUser(mContext, 0 /* requestCode */, launchIntent, + FLAG_IMMUTABLE, opts, user); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { @@ -1025,9 +1053,24 @@ public class LauncherAppsService extends SystemService { return; } + Intent launchIntent = getMainActivityLaunchIntent(component, user); + if (launchIntent == null) { + throw new SecurityException("Attempt to launch activity without " + + " category Intent.CATEGORY_LAUNCHER " + component); + } + launchIntent.setSourceBounds(sourceBounds); + + mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, + callingFeatureId, launchIntent, /* resultTo= */ null, + Intent.FLAG_ACTIVITY_NEW_TASK, opts, user.getIdentifier()); + } + + /** + * Returns the main activity launch intent for the given component package. + */ + private Intent getMainActivityLaunchIntent(ComponentName component, UserHandle user) { Intent launchIntent = new Intent(Intent.ACTION_MAIN); launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); - launchIntent.setSourceBounds(sourceBounds); launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); launchIntent.setPackage(component.getPackageName()); @@ -1066,15 +1109,12 @@ public class LauncherAppsService extends SystemService { } } if (!canLaunch) { - throw new SecurityException("Attempt to launch activity without " - + " category Intent.CATEGORY_LAUNCHER " + component); + return null; } } finally { Binder.restoreCallingIdentity(ident); } - mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, - callingFeatureId, launchIntent, /* resultTo= */ null, - Intent.FLAG_ACTIVITY_NEW_TASK, opts, user.getIdentifier()); + return launchIntent; } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8e5add916620..0fac8358c9c4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3606,7 +3606,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** This can be called with or without the global lock held. */ - private void enforceCallerIsRecentsOrHasPermission(String permission, String func) { + void enforceCallerIsRecentsOrHasPermission(String permission, String func) { if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) { mAmInternal.enforceCallingPermission(permission, func); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 72edb86283d2..6fbd35164874 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -20,7 +20,12 @@ import static android.Manifest.permission.DEVICE_POWER; import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; 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 android.content.Intent.EXTRA_SHORTCUT_ID; +import static android.content.Intent.EXTRA_TASK_ID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -49,6 +54,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArraySet; import android.util.MergedConfiguration; import android.util.Slog; @@ -297,18 +303,25 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { */ @VisibleForTesting public void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + throw new IllegalStateException("Need to validate before calling identify is cleared"); + } final ClipDescription desc = data != null ? data.getDescription() : null; if (desc == null) { return; } // Ensure that only one of the app mime types are set final boolean hasActivity = desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY); - int appMimeTypeCount = (hasActivity ? 1 : 0); + final boolean hasShortcut = desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); + final boolean hasTask = desc.hasMimeType(MIMETYPE_APPLICATION_TASK); + int appMimeTypeCount = (hasActivity ? 1 : 0) + + (hasShortcut ? 1 : 0) + + (hasTask ? 1 : 0); if (appMimeTypeCount == 0) { return; } else if (appMimeTypeCount > 1) { throw new IllegalArgumentException("Can not specify more than one of activity, " - + "or task mime types"); + + "shortcut, or task mime types"); } // Ensure that data is provided and that they are intents if (data.getItemCount() == 0) { @@ -344,6 +357,28 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } finally { Binder.restoreCallingIdentity(origId); } + } else if (hasShortcut) { + mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, + "performDrag"); + for (int i = 0; i < data.getItemCount(); i++) { + final Intent intent = data.getItemAt(i).getIntent(); + final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); + if (!intent.hasExtra(EXTRA_SHORTCUT_ID) + || TextUtils.isEmpty(intent.getStringExtra(EXTRA_SHORTCUT_ID)) + || user == null) { + throw new IllegalArgumentException("Clip item must include the shortcut id and " + + "the user to launch for."); + } + } + } else if (hasTask) { + mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, + "performDrag"); + for (int i = 0; i < data.getItemCount(); i++) { + final Intent intent = data.getItemAt(i).getIntent(); + if (intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID) == INVALID_TASK_ID) { + throw new IllegalArgumentException("Clip item must include the task id."); + } + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 64a05bb361e9..747fb8999276 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 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 android.view.DragEvent.ACTION_DRAG_STARTED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -82,6 +84,8 @@ import java.util.concurrent.TimeUnit; @RunWith(WindowTestRunner.class) public class DragDropControllerTests extends WindowTestsBase { private static final int TIMEOUT_MS = 3000; + private static final int TEST_UID = 12345; + private TestDragDropController mTarget; private WindowState mWindow; private IBinder mToken; @@ -278,6 +282,42 @@ public class DragDropControllerTests extends WindowTestsBase { return clipData; } + @Test + public void testValidateAppShortcutArguments() { + final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float scale) {} + }); + try { + final ClipData clipData = new ClipData( + new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_SHORTCUT }), + new ClipData.Item(new Intent())); + + session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID); + fail("Expected failure without shortcut id"); + } catch (IllegalArgumentException e) { + // Expected failure + } + } + + @Test + public void testValidateAppTaskArguments() { + final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float scale) {} + }); + try { + final ClipData clipData = new ClipData( + new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }), + new ClipData.Item(new Intent())); + + session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID); + fail("Expected failure without task id"); + } catch (IllegalArgumentException e) { + // Expected failure + } + } + private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) { startDrag(flags, data, () -> { mTarget.handleMotionEvent(false, dropX, dropY); |