diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 54b39bd215bfa..0bc459a19e7d4 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -179,6 +179,10 @@ public class ClipData implements Parcelable { final ArrayList mItems; + // This is false by default unless the ClipData is obtained via + // {@link #copyForTransferWithActivityInfo}. + private boolean mParcelItemActivityInfos; + /** * Description of a single item in a ClipData. * @@ -204,9 +208,11 @@ public class ClipData implements Parcelable { final Intent mIntent; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) Uri mUri; - // Additional activity info resolved by the system - ActivityInfo mActivityInfo; private TextLinks mTextLinks; + // Additional activity info resolved by the system. This is only parceled with the ClipData + // if the data is obtained from {@link #copyForTransferWithActivityInfo} + private ActivityInfo mActivityInfo; + /** @hide */ public Item(Item other) { @@ -214,6 +220,8 @@ public class ClipData implements Parcelable { mHtmlText = other.mHtmlText; mIntent = other.mIntent; mUri = other.mUri; + mActivityInfo = other.mActivityInfo; + mTextLinks = other.mTextLinks; } /** @@ -816,6 +824,24 @@ public class ClipData implements Parcelable { mItems = new ArrayList(other.mItems); } + /** + * Returns a copy of the ClipData which will parcel the Item's activity infos. + * @hide + */ + public ClipData copyForTransferWithActivityInfo() { + ClipData copy = new ClipData(this); + copy.mParcelItemActivityInfos = true; + return copy; + } + + /** + * Returns whether this clip data will parcel the Item's activity infos. + * @hide + */ + public boolean willParcelWithActivityInfo() { + return mParcelItemActivityInfos; + } + /** * Create a new ClipData holding data of the type * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}. @@ -1208,7 +1234,7 @@ public class ClipData implements Parcelable { dest.writeString8(item.mHtmlText); dest.writeTypedObject(item.mIntent, flags); dest.writeTypedObject(item.mUri, flags); - dest.writeTypedObject(item.mActivityInfo, flags); + dest.writeTypedObject(mParcelItemActivityInfos ? item.mActivityInfo : null, flags); dest.writeTypedObject(item.mTextLinks, flags); } } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index c674cb85a79ea..18ea738b08ce1 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -316,7 +316,7 @@ class DragState { final int myPid = Process.myPid(); final IBinder clientToken = touchedWin.mClient.asBinder(); final DragEvent event = obtainDragEvent(DragEvent.ACTION_DROP, x, y, - true /* includeData */, targetInterceptsGlobalDrag(touchedWin), + mData, targetInterceptsGlobalDrag(touchedWin), dragAndDropPermissions); try { touchedWin.mClient.dispatchDragEvent(event); @@ -462,8 +462,10 @@ class DragState { boolean containsAppExtras) { final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin); if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) { + // Only allow the extras to be dispatched to a global-intercepting drag target + ClipData data = interceptsGlobalDrag ? mData.copyForTransferWithActivityInfo() : null; DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY, - interceptsGlobalDrag, false /* includeDragSurface */, + data, false /* includeDragSurface */, null /* dragAndDropPermission */); try { newWin.mClient.dispatchDragEvent(event); @@ -614,11 +616,11 @@ class DragState { return mDragInProgress; } - private DragEvent obtainDragEvent(int action, float x, float y, boolean includeData, + private DragEvent obtainDragEvent(int action, float x, float y, ClipData data, boolean includeDragSurface, IDragAndDropPermissions dragAndDropPermissions) { return DragEvent.obtain(action, x, y, mThumbOffsetX, mThumbOffsetY, - null /* localState */, mDataDescription, - includeData ? mData : null, includeDragSurface ? mSurfaceControl : null, + null /* localState */, mDataDescription, data, + includeDragSurface ? mSurfaceControl : null, dragAndDropPermissions, false /* result */); } 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 8981ab1a0e8ac..223dc3121cd6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -22,6 +22,7 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.DragEvent.ACTION_DRAG_STARTED; +import static android.view.DragEvent.ACTION_DROP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -31,7 +32,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; @@ -267,12 +270,32 @@ public class DragDropControllerTests extends WindowTestsBase { assertTrue(globalInterceptWindowDragEvents.get(0).getAction() == ACTION_DRAG_STARTED); + // Verify that only the global intercept window receives the clip data with the + // resolved activity info for the drag + assertNull(localWindowDragEvents.get(0).getClipData()); + assertTrue(globalInterceptWindowDragEvents.get(0).getClipData() + .willParcelWithActivityInfo()); + mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0); mTarget.handleMotionEvent(false, 0, 0); mToken = globalInterceptWindow.mClient.asBinder(); + + // Verify the drop event is only sent for the global intercept window + assertTrue(nonLocalWindowDragEvents.isEmpty()); + assertTrue(last(localWindowDragEvents).getAction() != ACTION_DROP); + assertTrue(last(globalInterceptWindowDragEvents).getAction() == ACTION_DROP); + + // Verify that item extras were not sent with the drop event + assertNull(last(localWindowDragEvents).getClipData()); + assertFalse(last(globalInterceptWindowDragEvents).getClipData() + .willParcelWithActivityInfo()); }); } + private DragEvent last(ArrayList list) { + return list.get(list.size() - 1); + } + @Test public void testValidateAppActivityArguments() { final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {