Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da0748d0 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Fix NPE occured when null ClipData is passed to startDrag.

Previously we have NPE for the following cases:

 * when specifying both DRAG_FLAG_GLOBAL and DRAG_FLAGS_URI_ACCESS, OR
 * when the userIds of source and target apps are different

Bug: 70477060
Test: com.android.server.wm.DragDropControllerTests,
      android.server.wm.CrossAppDragAndDropTests,
      manually check the drag and drop behavior on test app.
Change-Id: I454b434e4466cb159ce23e0982012cdb21c9d002
parent 01b64507
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -136,7 +136,7 @@ class DragDropController {
            outSurface.copyFrom(surface);
            outSurface.copyFrom(surface);
            final IBinder winBinder = window.asBinder();
            final IBinder winBinder = window.asBinder();
            IBinder token = new Binder();
            IBinder token = new Binder();
            mDragState = new DragState(mService, token, surface, flags, winBinder);
            mDragState = new DragState(mService, this, token, surface, flags, winBinder);
            mDragState.mPid = callerPid;
            mDragState.mPid = callerPid;
            mDragState.mUid = callerUid;
            mDragState.mUid = callerUid;
            mDragState.mOriginalAlpha = alpha;
            mDragState.mOriginalAlpha = alpha;
+8 −5
Original line number Original line Diff line number Diff line
@@ -118,10 +118,10 @@ class DragState {
    private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
    private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
    private Point mDisplaySize = new Point();
    private Point mDisplaySize = new Point();


    DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
    DragState(WindowManagerService service, DragDropController controller, IBinder token,
            int flags, IBinder localWin) {
            SurfaceControl surface, int flags, IBinder localWin) {
        mService = service;
        mService = service;
        mDragDropController = service.mDragDropController;
        mDragDropController = controller;
        mToken = token;
        mToken = token;
        mSurfaceControl = surface;
        mSurfaceControl = surface;
        mFlags = flags;
        mFlags = flags;
@@ -530,7 +530,8 @@ class DragState {
        final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
        final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());


        final DragAndDropPermissionsHandler dragAndDropPermissions;
        final DragAndDropPermissionsHandler dragAndDropPermissions;
        if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
        if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
                && mData != null) {
            dragAndDropPermissions = new DragAndDropPermissionsHandler(
            dragAndDropPermissions = new DragAndDropPermissionsHandler(
                    mData,
                    mData,
                    mUid,
                    mUid,
@@ -542,8 +543,10 @@ class DragState {
            dragAndDropPermissions = null;
            dragAndDropPermissions = null;
        }
        }
        if (mSourceUserId != targetUserId){
        if (mSourceUserId != targetUserId){
            if (mData != null) {
                mData.fixUris(mSourceUserId);
                mData.fixUris(mSourceUserId);
            }
            }
        }
        final int myPid = Process.myPid();
        final int myPid = Process.myPid();
        final IBinder token = touchedWin.mClient.asBinder();
        final IBinder token = touchedWin.mClient.asBinder();
        final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
        final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
+113 −17
Original line number Original line Diff line number Diff line
@@ -16,27 +16,38 @@


package com.android.server.wm;
package com.android.server.wm;


import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.content.ClipData;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.runner.AndroidJUnit4;
import android.view.InputChannel;
import android.view.InputChannel;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.SurfaceSession;
import android.view.View;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;

/**
/**
 * Tests for the {@link DragDropController} class.
 * Tests for the {@link DragDropController} class.
 *
 *
@@ -46,36 +57,92 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
@Presubmit
@Presubmit
public class DragDropControllerTests extends WindowTestsBase {
public class DragDropControllerTests extends WindowTestsBase {
    private static final int TIMEOUT_MS = 1000;
    private static final int TIMEOUT_MS = 3000;
    private DragDropController mTarget;
    private TestDragDropController mTarget;
    private WindowState mWindow;
    private WindowState mWindow;
    private IBinder mToken;
    private IBinder mToken;


    static class TestDragDropController extends DragDropController {
        @GuardedBy("sWm.mWindowMap")
        private Runnable mCloseCallback;

        TestDragDropController(WindowManagerService service, Looper looper) {
            super(service, looper);
        }

        void setOnClosedCallbackLocked(Runnable runnable) {
            assertTrue(dragDropActiveLocked());
            mCloseCallback = runnable;
        }

        @Override
        void onDragStateClosedLocked(DragState dragState) {
            super.onDragStateClosedLocked(dragState);
            if (mCloseCallback != null) {
                mCloseCallback.run();
                mCloseCallback = null;
            }
        }
    }

    /**
     * Creates a window state which can be used as a drop target.
     */
    private WindowState createDropTargetWindow(String name, int ownerId) {
        final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(
                mDisplayContent);
        final TaskStack stack = createStackControllerOnStackOnDisplay(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
        final Task task = createTaskInStack(stack, ownerId);
        task.addChild(token, 0);

        final WindowState window = createWindow(
                null, TYPE_BASE_APPLICATION, token, name, ownerId, false);
        window.mInputChannel = new InputChannel();
        window.mHasSurface = true;
        return window;
    }

    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        final UserManagerInternal userManager = mock(UserManagerInternal.class);
        LocalServices.addService(UserManagerInternal.class, userManager);

        super.setUp();
        super.setUp();
        assertNotNull(sWm.mDragDropController);

        mTarget = sWm.mDragDropController;
        mTarget = new TestDragDropController(sWm, sWm.mH.getLooper());
        mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
        mDisplayContent = spy(mDisplayContent);
        mWindow = createDropTargetWindow("Drag test window", 0);
        when(mDisplayContent.getTouchableWinAtPointLocked(0, 0)).thenReturn(mWindow);
        when(sWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);

        synchronized (sWm.mWindowMap) {
        synchronized (sWm.mWindowMap) {
            // Because sWm is a static object, the previous operation may remain.
            sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
            assertFalse(mTarget.dragDropActiveLocked());
        }
        }
    }
    }


    @After
    @After
    public void tearDown() {
    public void tearDown() throws Exception {
        LocalServices.removeServiceForTest(UserManagerInternal.class);
        final CountDownLatch latch;
        synchronized (sWm.mWindowMap) {
            if (!mTarget.dragDropActiveLocked()) {
                return;
            }
            if (mToken != null) {
            if (mToken != null) {
                mTarget.cancelDragAndDrop(mToken);
                mTarget.cancelDragAndDrop(mToken);
            }
            }
            latch = new CountDownLatch(1);
            mTarget.setOnClosedCallbackLocked(() -> {
                latch.countDown();
            });
        }
        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
    }
    }


    @Test
    @Test
    public void testPrepareDrag() throws Exception {
    public void testDragFlow() throws Exception {
        final Surface surface = new Surface();
        dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0);
        mToken = mTarget.prepareDrag(
                new SurfaceSession(), 0, 0, mWindow.mClient, 0, 100, 100, surface);
        assertNotNull(mToken);
    }
    }


    @Test
    @Test
@@ -85,4 +152,33 @@ public class DragDropControllerTests extends WindowTestsBase {
                new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface);
                new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface);
        assertNull(mToken);
        assertNull(mToken);
    }
    }

    @Test
    public void testPerformDrag_NullDataWithGrantUri() throws Exception {
        dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
    }

    @Test
    public void testPerformDrag_NullDataToOtherUser() throws Exception {
        final WindowState otherUsersWindow =
                createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE);
        when(mDisplayContent.getTouchableWinAtPointLocked(10, 10))
                .thenReturn(otherUsersWindow);

        dragFlow(0, null, 10, 10);
    }

    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);
        assertNotNull(mToken);

        assertTrue(sWm.mInputManager.transferTouchFocus(null, null));
        assertTrue(mTarget.performDrag(
                mWindow.mClient, mToken, 0, 0, 0, 0, 0, data));

        mTarget.handleMotionEvent(false, dropX, dropY);
        mToken = mWindow.mClient.asBinder();
    }
}
}
+6 −4
Original line number Original line Diff line number Diff line
@@ -230,20 +230,22 @@ class WindowTestsBase {
            boolean ownerCanAddInternalSystemWindow) {
            boolean ownerCanAddInternalSystemWindow) {
        final WindowToken token = createWindowToken(
        final WindowToken token = createWindowToken(
                dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
                dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
        return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow);
        return createWindow(parent, type, token, name, 0 /* ownerId */,
                ownerCanAddInternalSystemWindow);
    }
    }


    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
        return createWindow(parent, type, token, name, false /* ownerCanAddInternalSystemWindow */);
        return createWindow(parent, type, token, name, 0 /* ownerId */,
                false /* ownerCanAddInternalSystemWindow */);
    }
    }


    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
            boolean ownerCanAddInternalSystemWindow) {
            int ownerId, boolean ownerCanAddInternalSystemWindow) {
        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
        attrs.setTitle(name);
        attrs.setTitle(name);


        final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
        final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
                0, attrs, VISIBLE, 0, ownerCanAddInternalSystemWindow);
                0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow);
        // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
        // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
        // adding it to the token...
        // adding it to the token...
        token.addWindow(w);
        token.addWindow(w);