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
This commit is contained in:
Daichi Hirono
2017-12-18 17:02:54 +09:00
parent 57fce6c63c
commit a1fb9be437
7 changed files with 102 additions and 70 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -3690,6 +3690,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
.setParent(mOverlayLayer);
}
/**
* Reparents the given surface to mOverlayLayer.
*/
void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
transaction.reparent(surface, mOverlayLayer.getHandle());
}
void applyMagnificationSpec(MagnificationSpec spec) {
applyMagnificationSpec(getPendingTransaction(), spec);
getPendingTransaction().apply();

View File

@@ -18,12 +18,10 @@ package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.content.ClipData;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -32,8 +30,8 @@ import android.os.Message;
import android.util.Slog;
import android.view.Display;
import android.view.IWindow;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
@@ -96,7 +94,7 @@ class DragDropController {
}
IBinder prepareDrag(SurfaceSession session, int callerPid,
int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
int callerUid, IWindow window, int flags, int width, int height) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
+ " flags=" + Integer.toHexString(flags) + " win=" + window
@@ -115,28 +113,13 @@ class DragDropController {
}
// TODO(multi-display): support other displays
final DisplayContent displayContent =
mService.getDefaultDisplayContentLocked();
final Display display = displayContent.getDisplay();
final SurfaceControl surface = new SurfaceControl.Builder(session)
.setName("drag surface")
.setSize(width, height)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
surface.setLayerStack(display.getLayerStack());
float alpha = 1;
if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
}
surface.setAlpha(alpha);
if (SHOW_TRANSACTIONS)
Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
IBinder token = new Binder();
mDragState = new DragState(mService, this, token, surface, flags, winBinder);
mDragState = new DragState(mService, this, token, /* surface */ null, flags, winBinder);
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
@@ -148,9 +131,9 @@ class DragDropController {
}
}
boolean performDrag(IWindow window, IBinder dragToken,
int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data) {
boolean performDrag(SurfaceSession session, IWindow window, IBinder dragToken,
SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX,
float thumbCenterY, ClipData data) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
}
@@ -202,6 +185,8 @@ class DragDropController {
return false;
}
mDragState.mSurfaceControl = surface;
surface = null;
mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
mDragState.broadcastDragStartedLocked(touchX, touchY);
@@ -213,22 +198,25 @@ class DragDropController {
// Make the surface visible at the proper location
final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> 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();
}

View File

@@ -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

View File

@@ -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();
}
}
}