From a1fb9be437b672f52ad64e1506038757ec060dcf Mon Sep 17 00:00:00 2001 From: Daichi Hirono Date: Mon, 18 Dec 2017 17:02:54 +0900 Subject: [PATCH] Create a drag shadow surface in app process, Previoulsy a drag shadow surface is created in the system process. App needs to call one more binder call (prepareDrag) to obtain the surface from the system process. The CL lets an app to create a drag shadow surface by itself. Then app transfer the surface to system server by using reparent API. Bug: 70818582 Test: com.android.server.wm.DragDropControllerTests, android.server.wm.CrossAppDragAndDropTests, manually check the drag and drop behavior on test app. Change-Id: I72796efffbefe78a802d7c441dea308d1cdea572 --- core/java/android/view/IWindowSession.aidl | 13 +++-- core/java/android/view/SurfaceControl.aidl | 19 ++++++ core/java/android/view/View.java | 30 ++++++---- .../com/android/server/wm/DisplayContent.java | 7 +++ .../android/server/wm/DragDropController.java | 58 ++++++++----------- .../java/com/android/server/wm/Session.java | 15 +++-- .../server/wm/DragDropControllerTests.java | 30 ++++++---- 7 files changed, 102 insertions(+), 70 deletions(-) create mode 100644 core/java/android/view/SurfaceControl.aidl diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index ed167c812be18..a7112ba09fed8 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -29,6 +29,7 @@ import android.view.IWindowId; import android.view.MotionEvent; import android.view.WindowManager; import android.view.Surface; +import android.view.SurfaceControl; /** * System private per-application interface to the window manager. @@ -154,21 +155,21 @@ interface IWindowSession { * the drag to the OS and passes that as the return value. A return value of * null indicates failure. */ - IBinder prepareDrag(IWindow window, int flags, - int thumbnailWidth, int thumbnailHeight, out Surface outSurface); + IBinder prepareDrag(IWindow window, int flags, int thumbnailWidth, int thumbnailHeight); /** * Initiate the drag operation itself */ - boolean performDrag(IWindow window, IBinder dragToken, int touchSource, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); + boolean performDrag(IWindow window, IBinder dragToken, in SurfaceControl surface, + int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, + in ClipData data); - /** + /** * Report the result of a drop action targeted to the given window. * consumed is 'true' when the drop was accepted by a valid recipient, * 'false' otherwise. */ - void reportDropResult(IWindow window, boolean consumed); + void reportDropResult(IWindow window, boolean consumed); /** * Cancel the current drag operation. diff --git a/core/java/android/view/SurfaceControl.aidl b/core/java/android/view/SurfaceControl.aidl new file mode 100644 index 0000000000000..744ead2be6432 --- /dev/null +++ b/core/java/android/view/SurfaceControl.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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 android.view; + +parcelable SurfaceControl; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0bf690457e1e9..34ad91c717475 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23502,9 +23502,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Point shadowTouchPoint = new Point(); shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint); - if ((shadowSize.x < 0) || (shadowSize.y < 0) || - (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) { - throw new IllegalStateException("Drag shadow dimensions must not be negative"); + if ((shadowSize.x <= 0) || (shadowSize.y <= 0) + || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) { + throw new IllegalStateException("Drag shadow dimensions must be positive"); } if (ViewDebug.DEBUG_DRAG) { @@ -23515,13 +23515,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mDragSurface.release(); } mAttachInfo.mDragSurface = new Surface(); + + final ViewRootImpl root = mAttachInfo.mViewRootImpl; + final SurfaceSession session = new SurfaceSession(root.mSurface); try { mAttachInfo.mDragToken = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow, - flags, shadowSize.x, shadowSize.y, mAttachInfo.mDragSurface); + flags, shadowSize.x, shadowSize.y); if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" + mAttachInfo.mDragToken + " surface=" + mAttachInfo.mDragSurface); if (mAttachInfo.mDragToken != null) { - Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null); + final SurfaceControl surface = new SurfaceControl.Builder(session) + .setName("drag surface") + .setSize(shadowSize.x, shadowSize.y) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); + mAttachInfo.mDragSurface.copyFrom(surface); + final Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null); try { canvas.drawColor(0, PorterDuff.Mode.CLEAR); shadowBuilder.onDrawShadow(canvas); @@ -23529,23 +23538,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas); } - final ViewRootImpl root = getViewRootImpl(); - // Cache the local state object for delivery with DragEvents root.setLocalDragState(myLocalState); // repurpose 'shadowSize' for the last touch point root.getLastTouchPoint(shadowSize); - okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, mAttachInfo.mDragToken, - root.getLastTouchSource(), shadowSize.x, shadowSize.y, - shadowTouchPoint.x, shadowTouchPoint.y, data); + okay = mAttachInfo.mSession.performDrag( + mAttachInfo.mWindow, mAttachInfo.mDragToken, surface, + root.getLastTouchSource(), shadowSize.x, shadowSize.y, shadowTouchPoint.x, + shadowTouchPoint.y, data); if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay); } } catch (Exception e) { Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e); mAttachInfo.mDragSurface.destroy(); mAttachInfo.mDragSurface = null; + } finally { + session.kill(); } return okay; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a8e00dd53526f..77367db94e356 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3690,6 +3690,13 @@ class DisplayContent extends WindowContainer>> OPEN TRANSACTION performDrag"); - mService.openSurfaceTransaction(); - try { - surfaceControl.setPosition(touchX - thumbCenterX, - touchY - thumbCenterY); - surfaceControl.setLayer(mDragState.getDragLayerLocked()); - surfaceControl.setLayerStack(display.getLayerStack()); - surfaceControl.show(); - } finally { - mService.closeSurfaceTransaction("performDrag"); - if (SHOW_LIGHT_TRANSACTIONS) { - Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); - } + + final SurfaceControl.Transaction transaction = + callingWin.getPendingTransaction(); + transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha); + transaction.setPosition( + surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY); + transaction.show(surfaceControl); + displayContent.reparentToOverlay(transaction, surfaceControl); + callingWin.scheduleAnimation(); + + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); } mDragState.notifyLocationLocked(touchX, touchY); } finally { + if (surface != null) { + surface.release(); + } if (mDragState != null && !mDragState.isInProgress()) { mDragState.closeLocked(); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 192d6c84e1909..928062086d777 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -51,6 +51,7 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; @@ -309,26 +310,24 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { /* Drag/drop */ @Override - public IBinder prepareDrag(IWindow window, int flags, int width, int height, - Surface outSurface) { + public IBinder prepareDrag(IWindow window, int flags, int width, int height) { final int callerPid = Binder.getCallingPid(); final int callerUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - return mDragDropController.prepareDrag( - mSurfaceSession, callerPid, callerUid, window, flags, width, height, - outSurface); + return mDragDropController.prepareDrag(mSurfaceSession, callerPid, callerUid, window, + flags, width, height); } finally { Binder.restoreCallingIdentity(ident); } } @Override - public boolean performDrag(IWindow window, IBinder dragToken, + public boolean performDrag(IWindow window, IBinder dragToken, SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { - return mDragDropController.performDrag(window, dragToken, touchSource, - touchX, touchY, thumbCenterX, thumbCenterY, data); + return mDragDropController.performDrag(mSurfaceSession, window, dragToken, surface, + touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data); } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java index ac291632c8772..9a0d40038403c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.ClipData; +import android.graphics.PixelFormat; import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; @@ -37,6 +38,7 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.InputChannel; import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import com.android.internal.annotations.GuardedBy; @@ -147,9 +149,7 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testPrepareDrag_ZeroSizeSurface() throws Exception { - final Surface surface = new Surface(); - mToken = mTarget.prepareDrag( - new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface); + mToken = mTarget.prepareDrag(new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0); assertNull(mToken); } @@ -169,16 +169,24 @@ public class DragDropControllerTests extends WindowTestsBase { } private void dragFlow(int flag, ClipData data, float dropX, float dropY) { - final Surface surface = new Surface(); - mToken = mTarget.prepareDrag( - new SurfaceSession(), 0, 0, mWindow.mClient, flag, 100, 100, surface); + mToken = mTarget.prepareDrag(new SurfaceSession(), 0, 0, mWindow.mClient, flag, 100, 100); assertNotNull(mToken); + final SurfaceSession appSession = new SurfaceSession(); + try { + final SurfaceControl surface = new SurfaceControl.Builder(appSession) + .setName("drag surface") + .setSize(100, 100) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); - assertTrue(sWm.mInputManager.transferTouchFocus(null, null)); - assertTrue(mTarget.performDrag( - mWindow.mClient, mToken, 0, 0, 0, 0, 0, data)); + assertTrue(sWm.mInputManager.transferTouchFocus(null, null)); + assertNotNull(mTarget.performDrag( + new SurfaceSession(), mWindow.mClient, mToken, surface, 0, 0, 0, 0, 0, data)); - mTarget.handleMotionEvent(false, dropX, dropY); - mToken = mWindow.mClient.asBinder(); + mTarget.handleMotionEvent(false, dropX, dropY); + mToken = mWindow.mClient.asBinder(); + } finally { + appSession.kill(); + } } }