blob: 28be6e743ba6ca13305eae30bea40aa02926c445 [file] [log] [blame]
/*
* Copyright (C) 2016 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.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.server.wm.CliIntentExtra.extraString;
import static android.server.wm.UiDeviceUtils.dragPointer;
import static android.server.wm.dndsourceapp.Components.DRAG_SOURCE;
import static android.server.wm.dndtargetapp.Components.DROP_TARGET;
import static android.server.wm.dndtargetappsdk23.Components.DROP_TARGET_SDK23;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.ActivityTask;
import android.util.Log;
import android.view.Display;
import com.google.common.collect.ImmutableSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests
*/
@Presubmit
@AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS")
public class CrossAppDragAndDropTests extends ActivityManagerTestBase {
private static final String TAG = "CrossAppDragAndDrop";
private static final int SWIPE_STEPS = 100;
private static final String FILE_GLOBAL = "file_global";
private static final String FILE_LOCAL = "file_local";
private static final String DISALLOW_GLOBAL = "disallow_global";
private static final String CANCEL_SOON = "cancel_soon";
private static final String GRANT_NONE = "grant_none";
private static final String GRANT_READ = "grant_read";
private static final String GRANT_WRITE = "grant_write";
private static final String GRANT_READ_PREFIX = "grant_read_prefix";
private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix";
private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable";
private static final String REQUEST_NONE = "request_none";
private static final String REQUEST_READ = "request_read";
private static final String REQUEST_READ_NESTED = "request_read_nested";
private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable";
private static final String REQUEST_WRITE = "request_write";
private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW =
"textview_on_receive_content_listener";
private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT =
"edittext_on_receive_content_listener";
private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT =
"linearlayout_on_receive_content_listener";
private static final ImmutableSet<String> ON_RECEIVE_CONTENT_LISTENER_MODES = ImmutableSet.of(
TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW,
TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT,
TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT
);
private static final String SOURCE_LOG_TAG = "DragSource";
private static final String TARGET_LOG_TAG = "DropTarget";
private static final String RESULT_KEY_START_DRAG = "START_DRAG";
private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
private static final String RESULT_KEY_EXTRAS = "EXTRAS";
private static final String RESULT_KEY_DROP_RESULT = "DROP";
private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
private static final String RESULT_MISSING = "Missing";
private static final String RESULT_OK = "OK";
private static final String RESULT_EXCEPTION = "Exception";
private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions";
private static final String EXTRA_MODE = "mode";
private static final String EXTRA_LOGTAG = "logtag";
private Map<String, String> mSourceResults;
private Map<String, String> mTargetResults;
private String mSessionId;
private String mSourceLogTag;
private String mTargetLogTag;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
assumeTrue(supportsSplitScreenMultiWindow() || supportsFreeform());
// Use uptime in seconds as unique test invocation id.
mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000);
mSourceLogTag = SOURCE_LOG_TAG + mSessionId;
mTargetLogTag = TARGET_LOG_TAG + mSessionId;
cleanupState();
}
@After
public void tearDown() {
cleanupState();
}
/**
* Make sure that the special activity stacks are removed and the ActivityManager/WindowManager
* is in a good state.
*/
private void cleanupState() {
stopTestPackage(DRAG_SOURCE.getPackageName());
stopTestPackage(DROP_TARGET.getPackageName());
stopTestPackage(DROP_TARGET_SDK23.getPackageName());
}
/**
* @param displaySize size of the display
* @param leftSide {@code true} to launch the app taking up the left half of the display,
* {@code false} to launch the app taking up the right half of the display.
*/
private void launchFreeformActivity(ComponentName componentName, String mode,
String logtag, Point displaySize, boolean leftSide) throws Exception {
launchActivity(componentName, WINDOWING_MODE_FREEFORM, extraString("mode", mode),
extraString("logtag", logtag));
Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
resizeActivityTask(componentName, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
waitAndAssertTopResumedActivity(componentName, DEFAULT_DISPLAY,
"Activity launched as freeform should be resumed");
}
private void injectInput(Point from, Point to, int steps) throws Exception {
dragPointer(from, to, steps);
}
private Point getDisplaySize() throws Exception {
final Point displaySize = new Point();
mDm.getDisplay(Display.DEFAULT_DISPLAY).getRealSize(displaySize);
return displaySize;
}
private Point getWindowCenter(ComponentName name) throws Exception {
final ActivityTask sideTask = mWmState.getTaskByActivity(name);
Rect bounds = sideTask.getBounds();
if (bounds != null) {
return new Point(bounds.centerX(), bounds.centerY());
}
return null;
}
private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
throws Exception {
assertDragAndDropResults(DRAG_SOURCE, sourceMode, DROP_TARGET, targetMode,
RESULT_OK, expectedDropResult, RESULT_OK);
}
private void assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode,
ComponentName targetComponentName, String expectedStartDragResult)
throws Exception {
assertDragAndDropResults(
sourceComponentName, sourceMode, targetComponentName, REQUEST_NONE,
expectedStartDragResult, RESULT_MISSING, RESULT_MISSING);
}
private void assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode,
ComponentName targetComponentName, String targetMode,
String expectedStartDragResult, String expectedDropResult,
String expectedListenerResults) throws Exception {
Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode
+ ", target: " + targetMode);
if (supportsFreeform()) {
// Fallback to try to launch two freeform windows side by side.
Point displaySize = getDisplaySize();
launchFreeformActivity(sourceComponentName, sourceMode, mSourceLogTag,
displaySize, true /* leftSide */);
launchFreeformActivity(targetComponentName, targetMode, mTargetLogTag,
displaySize, false /* leftSide */);
} else {
// Launch primary activity.
getLaunchActivityBuilder()
.setTargetActivity(sourceComponentName)
.setUseInstrumentation()
.setWaitForLaunched(true)
.setIntentExtra(bundle -> {
bundle.putString(EXTRA_MODE, sourceMode);
bundle.putString(EXTRA_LOGTAG, mSourceLogTag);
}).execute();
// Launch secondary activity.
getLaunchActivityBuilder().setTargetActivity(targetComponentName)
.setUseInstrumentation()
.setWaitForLaunched(true)
.setIntentExtra(bundle -> {
bundle.putString(EXTRA_MODE, targetMode);
bundle.putString(EXTRA_LOGTAG, mTargetLogTag);
}).execute();
moveActivitiesToSplitScreen(sourceComponentName, targetComponentName);
}
Point p1 = getWindowCenter(sourceComponentName);
assertNotNull(p1);
Point p2 = getWindowCenter(targetComponentName);
assertNotNull(p2);
TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG);
TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED);
injectInput(p1, p2, SWIPE_STEPS);
mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000);
assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000);
assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
// Skip the following assertions when testing OnReceiveContentListener, since it only
// handles drop events.
if (!ON_RECEIVE_CONTENT_LISTENER_MODES.contains(targetMode)) {
if (!RESULT_MISSING.equals(expectedDropResult)) {
assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
}
assertListenerResults(expectedListenerResults);
}
}
private void assertListenerResults(String expectedResult) throws Exception {
assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult);
assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult);
assertTargetResult(RESULT_KEY_EXTRAS, expectedResult);
assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING);
assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING);
}
private void assertSourceResult(String resultKey, String expectedResult) throws Exception {
assertResult(mSourceResults, resultKey, expectedResult);
}
private void assertTargetResult(String resultKey, String expectedResult) throws Exception {
assertResult(mTargetResults, resultKey, expectedResult);
}
private void assertResult(Map<String, String> results, String resultKey, String expectedResult)
throws Exception {
if (RESULT_MISSING.equals(expectedResult)) {
if (results.containsKey(resultKey)) {
fail("Unexpected " + resultKey + "=" + results.get(resultKey));
}
} else {
assertTrue("Missing " + resultKey, results.containsKey(resultKey));
assertEquals(resultKey + " result mismatch,", expectedResult,
results.get(resultKey));
}
}
@Test
public void testCancelSoon() throws Exception {
assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
}
@Test
public void testDisallowGlobal() throws Exception {
assertNoGlobalDragEvents(DRAG_SOURCE, DISALLOW_GLOBAL, DROP_TARGET, RESULT_OK);
}
@Test
public void testDisallowGlobalBelowSdk24() throws Exception {
assertNoGlobalDragEvents(DRAG_SOURCE, GRANT_NONE, DROP_TARGET_SDK23, RESULT_OK);
}
@Test
public void testFileUriLocal() throws Exception {
assertNoGlobalDragEvents(DRAG_SOURCE, FILE_LOCAL, DROP_TARGET, RESULT_OK);
}
@Test
public void testFileUriGlobal() throws Exception {
assertNoGlobalDragEvents(DRAG_SOURCE, FILE_GLOBAL, DROP_TARGET, RESULT_EXCEPTION);
}
@Test
public void testGrantNoneRequestNone() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
}
@Test
public void testGrantNoneRequestRead() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
}
@Test
public void testGrantNoneRequestWrite() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
}
@Test
public void testGrantReadRequestNone() throws Exception {
assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
}
@Test
public void testGrantReadRequestRead() throws Exception {
assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
}
@Test
public void testGrantReadRequestWrite() throws Exception {
assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
}
@Test
public void testGrantReadNoPrefixRequestReadNested() throws Exception {
assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
}
@Test
public void testGrantReadPrefixRequestReadNested() throws Exception {
assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
}
@Test
public void testGrantPersistableRequestTakePersistable() throws Exception {
assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
}
@Test
public void testGrantReadRequestTakePersistable() throws Exception {
assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
}
@Test
public void testGrantWriteRequestNone() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
}
@Test
public void testGrantWriteRequestRead() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
}
@Test
public void testGrantWriteRequestWrite() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
}
@Test
public void testOnReceiveContentListener_TextView_GrantRead() throws Exception {
assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW, RESULT_OK);
}
@Test
public void testOnReceiveContentListener_TextView_GrantNone() throws Exception {
assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW,
RESULT_EXCEPTION);
}
@Test
public void testOnReceiveContentListener_EditText_GrantRead() throws Exception {
assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT, RESULT_OK);
}
@Test
public void testOnReceiveContentListener_EditText_GrantNone() throws Exception {
assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT,
RESULT_EXCEPTION);
}
@Test
public void testOnReceiveContentListener_LinearLayout_GrantRead() throws Exception {
assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT, RESULT_OK);
}
@Test
public void testOnReceiveContentListener_LinearLayout_GrantNone() throws Exception {
assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT,
RESULT_EXCEPTION);
}
}