1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 20 import static android.server.wm.CliIntentExtra.extraString; 21 import static android.server.wm.UiDeviceUtils.dragPointer; 22 import static android.server.wm.dndsourceapp.Components.DRAG_SOURCE; 23 import static android.server.wm.dndtargetapp.Components.DROP_TARGET; 24 import static android.server.wm.dndtargetappsdk23.Components.DROP_TARGET_SDK23; 25 import static android.view.Display.DEFAULT_DISPLAY; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.content.ComponentName; 34 import android.graphics.Point; 35 import android.graphics.Rect; 36 import android.os.SystemClock; 37 import android.platform.test.annotations.AppModeFull; 38 import android.platform.test.annotations.Presubmit; 39 import android.server.wm.WindowManagerState.ActivityTask; 40 import android.util.Log; 41 import android.view.Display; 42 43 import com.google.common.collect.ImmutableSet; 44 45 import org.junit.After; 46 import org.junit.Before; 47 import org.junit.Test; 48 49 import java.util.Map; 50 51 /** 52 * Build/Install/Run: 53 * atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests 54 */ 55 @Presubmit 56 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS") 57 public class CrossAppDragAndDropTests extends ActivityManagerTestBase { 58 private static final String TAG = "CrossAppDragAndDrop"; 59 60 private static final int SWIPE_STEPS = 100; 61 62 private static final String FILE_GLOBAL = "file_global"; 63 private static final String FILE_LOCAL = "file_local"; 64 private static final String DISALLOW_GLOBAL = "disallow_global"; 65 private static final String CANCEL_SOON = "cancel_soon"; 66 private static final String GRANT_NONE = "grant_none"; 67 private static final String GRANT_READ = "grant_read"; 68 private static final String GRANT_WRITE = "grant_write"; 69 private static final String GRANT_READ_PREFIX = "grant_read_prefix"; 70 private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix"; 71 private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable"; 72 73 private static final String REQUEST_NONE = "request_none"; 74 private static final String REQUEST_READ = "request_read"; 75 private static final String REQUEST_READ_NESTED = "request_read_nested"; 76 private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable"; 77 private static final String REQUEST_WRITE = "request_write"; 78 79 private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW = 80 "textview_on_receive_content_listener"; 81 private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT = 82 "edittext_on_receive_content_listener"; 83 private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT = 84 "linearlayout_on_receive_content_listener"; 85 private static final ImmutableSet<String> ON_RECEIVE_CONTENT_LISTENER_MODES = ImmutableSet.of( 86 TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW, 87 TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT, 88 TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT 89 ); 90 91 private static final String SOURCE_LOG_TAG = "DragSource"; 92 private static final String TARGET_LOG_TAG = "DropTarget"; 93 94 private static final String RESULT_KEY_START_DRAG = "START_DRAG"; 95 private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED"; 96 private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED"; 97 private static final String RESULT_KEY_EXTRAS = "EXTRAS"; 98 private static final String RESULT_KEY_DROP_RESULT = "DROP"; 99 private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE"; 100 private static final String RESULT_KEY_ACCESS_AFTER = "AFTER"; 101 private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR"; 102 private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR"; 103 private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR"; 104 105 private static final String RESULT_MISSING = "Missing"; 106 private static final String RESULT_OK = "OK"; 107 private static final String RESULT_EXCEPTION = "Exception"; 108 private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions"; 109 110 private static final String EXTRA_MODE = "mode"; 111 private static final String EXTRA_LOGTAG = "logtag"; 112 113 private Map<String, String> mSourceResults; 114 private Map<String, String> mTargetResults; 115 116 private String mSessionId; 117 private String mSourceLogTag; 118 private String mTargetLogTag; 119 120 @Before 121 @Override setUp()122 public void setUp() throws Exception { 123 super.setUp(); 124 assumeTrue(supportsSplitScreenMultiWindow() || supportsFreeform()); 125 126 // Use uptime in seconds as unique test invocation id. 127 mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000); 128 mSourceLogTag = SOURCE_LOG_TAG + mSessionId; 129 mTargetLogTag = TARGET_LOG_TAG + mSessionId; 130 131 cleanupState(); 132 } 133 134 @After tearDown()135 public void tearDown() { 136 cleanupState(); 137 } 138 139 /** 140 * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager 141 * is in a good state. 142 */ cleanupState()143 private void cleanupState() { 144 stopTestPackage(DRAG_SOURCE.getPackageName()); 145 stopTestPackage(DROP_TARGET.getPackageName()); 146 stopTestPackage(DROP_TARGET_SDK23.getPackageName()); 147 } 148 149 /** 150 * @param displaySize size of the display 151 * @param leftSide {@code true} to launch the app taking up the left half of the display, 152 * {@code false} to launch the app taking up the right half of the display. 153 */ launchFreeformActivity(ComponentName componentName, String mode, String logtag, Point displaySize, boolean leftSide)154 private void launchFreeformActivity(ComponentName componentName, String mode, 155 String logtag, Point displaySize, boolean leftSide) throws Exception { 156 launchActivity(componentName, WINDOWING_MODE_FREEFORM, extraString("mode", mode), 157 extraString("logtag", logtag)); 158 Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0); 159 Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y); 160 resizeActivityTask(componentName, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); 161 waitAndAssertTopResumedActivity(componentName, DEFAULT_DISPLAY, 162 "Activity launched as freeform should be resumed"); 163 } 164 injectInput(Point from, Point to, int steps)165 private void injectInput(Point from, Point to, int steps) throws Exception { 166 dragPointer(from, to, steps); 167 } 168 getDisplaySize()169 private Point getDisplaySize() throws Exception { 170 final Point displaySize = new Point(); 171 mDm.getDisplay(Display.DEFAULT_DISPLAY).getRealSize(displaySize); 172 return displaySize; 173 } 174 getWindowCenter(ComponentName name)175 private Point getWindowCenter(ComponentName name) throws Exception { 176 final ActivityTask sideTask = mWmState.getTaskByActivity(name); 177 Rect bounds = sideTask.getBounds(); 178 if (bounds != null) { 179 return new Point(bounds.centerX(), bounds.centerY()); 180 } 181 return null; 182 } 183 assertDropResult(String sourceMode, String targetMode, String expectedDropResult)184 private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult) 185 throws Exception { 186 assertDragAndDropResults(DRAG_SOURCE, sourceMode, DROP_TARGET, targetMode, 187 RESULT_OK, expectedDropResult, RESULT_OK); 188 } 189 assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String expectedStartDragResult)190 private void assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode, 191 ComponentName targetComponentName, String expectedStartDragResult) 192 throws Exception { 193 assertDragAndDropResults( 194 sourceComponentName, sourceMode, targetComponentName, REQUEST_NONE, 195 expectedStartDragResult, RESULT_MISSING, RESULT_MISSING); 196 } 197 assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode, String expectedStartDragResult, String expectedDropResult, String expectedListenerResults)198 private void assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode, 199 ComponentName targetComponentName, String targetMode, 200 String expectedStartDragResult, String expectedDropResult, 201 String expectedListenerResults) throws Exception { 202 Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode 203 + ", target: " + targetMode); 204 205 if (supportsFreeform()) { 206 // Fallback to try to launch two freeform windows side by side. 207 Point displaySize = getDisplaySize(); 208 launchFreeformActivity(sourceComponentName, sourceMode, mSourceLogTag, 209 displaySize, true /* leftSide */); 210 launchFreeformActivity(targetComponentName, targetMode, mTargetLogTag, 211 displaySize, false /* leftSide */); 212 } else { 213 // Launch primary activity. 214 getLaunchActivityBuilder() 215 .setTargetActivity(sourceComponentName) 216 .setUseInstrumentation() 217 .setWaitForLaunched(true) 218 .setIntentExtra(bundle -> { 219 bundle.putString(EXTRA_MODE, sourceMode); 220 bundle.putString(EXTRA_LOGTAG, mSourceLogTag); 221 }).execute(); 222 223 // Launch secondary activity. 224 getLaunchActivityBuilder().setTargetActivity(targetComponentName) 225 .setUseInstrumentation() 226 .setWaitForLaunched(true) 227 .setIntentExtra(bundle -> { 228 bundle.putString(EXTRA_MODE, targetMode); 229 bundle.putString(EXTRA_LOGTAG, mTargetLogTag); 230 }).execute(); 231 moveActivitiesToSplitScreen(sourceComponentName, targetComponentName); 232 } 233 234 Point p1 = getWindowCenter(sourceComponentName); 235 assertNotNull(p1); 236 Point p2 = getWindowCenter(targetComponentName); 237 assertNotNull(p2); 238 239 TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG); 240 TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED); 241 242 injectInput(p1, p2, SWIPE_STEPS); 243 244 mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000); 245 assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult); 246 247 mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000); 248 assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult); 249 250 // Skip the following assertions when testing OnReceiveContentListener, since it only 251 // handles drop events. 252 if (!ON_RECEIVE_CONTENT_LISTENER_MODES.contains(targetMode)) { 253 if (!RESULT_MISSING.equals(expectedDropResult)) { 254 assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION); 255 assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION); 256 } 257 assertListenerResults(expectedListenerResults); 258 } 259 } 260 assertListenerResults(String expectedResult)261 private void assertListenerResults(String expectedResult) throws Exception { 262 assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult); 263 assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult); 264 assertTargetResult(RESULT_KEY_EXTRAS, expectedResult); 265 266 assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING); 267 assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING); 268 assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING); 269 } 270 assertSourceResult(String resultKey, String expectedResult)271 private void assertSourceResult(String resultKey, String expectedResult) throws Exception { 272 assertResult(mSourceResults, resultKey, expectedResult); 273 } 274 assertTargetResult(String resultKey, String expectedResult)275 private void assertTargetResult(String resultKey, String expectedResult) throws Exception { 276 assertResult(mTargetResults, resultKey, expectedResult); 277 } 278 assertResult(Map<String, String> results, String resultKey, String expectedResult)279 private void assertResult(Map<String, String> results, String resultKey, String expectedResult) 280 throws Exception { 281 if (RESULT_MISSING.equals(expectedResult)) { 282 if (results.containsKey(resultKey)) { 283 fail("Unexpected " + resultKey + "=" + results.get(resultKey)); 284 } 285 } else { 286 assertTrue("Missing " + resultKey, results.containsKey(resultKey)); 287 assertEquals(resultKey + " result mismatch,", expectedResult, 288 results.get(resultKey)); 289 } 290 } 291 292 @Test testCancelSoon()293 public void testCancelSoon() throws Exception { 294 assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING); 295 } 296 297 @Test testDisallowGlobal()298 public void testDisallowGlobal() throws Exception { 299 assertNoGlobalDragEvents(DRAG_SOURCE, DISALLOW_GLOBAL, DROP_TARGET, RESULT_OK); 300 } 301 302 @Test testDisallowGlobalBelowSdk24()303 public void testDisallowGlobalBelowSdk24() throws Exception { 304 assertNoGlobalDragEvents(DRAG_SOURCE, GRANT_NONE, DROP_TARGET_SDK23, RESULT_OK); 305 } 306 307 @Test testFileUriLocal()308 public void testFileUriLocal() throws Exception { 309 assertNoGlobalDragEvents(DRAG_SOURCE, FILE_LOCAL, DROP_TARGET, RESULT_OK); 310 } 311 312 @Test testFileUriGlobal()313 public void testFileUriGlobal() throws Exception { 314 assertNoGlobalDragEvents(DRAG_SOURCE, FILE_GLOBAL, DROP_TARGET, RESULT_EXCEPTION); 315 } 316 317 @Test testGrantNoneRequestNone()318 public void testGrantNoneRequestNone() throws Exception { 319 assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION); 320 } 321 322 @Test testGrantNoneRequestRead()323 public void testGrantNoneRequestRead() throws Exception { 324 assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS); 325 } 326 327 @Test testGrantNoneRequestWrite()328 public void testGrantNoneRequestWrite() throws Exception { 329 assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS); 330 } 331 332 @Test testGrantReadRequestNone()333 public void testGrantReadRequestNone() throws Exception { 334 assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION); 335 } 336 337 @Test testGrantReadRequestRead()338 public void testGrantReadRequestRead() throws Exception { 339 assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK); 340 } 341 342 @Test testGrantReadRequestWrite()343 public void testGrantReadRequestWrite() throws Exception { 344 assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION); 345 } 346 347 @Test testGrantReadNoPrefixRequestReadNested()348 public void testGrantReadNoPrefixRequestReadNested() throws Exception { 349 assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION); 350 } 351 352 @Test testGrantReadPrefixRequestReadNested()353 public void testGrantReadPrefixRequestReadNested() throws Exception { 354 assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK); 355 } 356 357 @Test testGrantPersistableRequestTakePersistable()358 public void testGrantPersistableRequestTakePersistable() throws Exception { 359 assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK); 360 } 361 362 @Test testGrantReadRequestTakePersistable()363 public void testGrantReadRequestTakePersistable() throws Exception { 364 assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION); 365 } 366 367 @Test testGrantWriteRequestNone()368 public void testGrantWriteRequestNone() throws Exception { 369 assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION); 370 } 371 372 @Test testGrantWriteRequestRead()373 public void testGrantWriteRequestRead() throws Exception { 374 assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION); 375 } 376 377 @Test testGrantWriteRequestWrite()378 public void testGrantWriteRequestWrite() throws Exception { 379 assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK); 380 } 381 382 @Test testOnReceiveContentListener_TextView_GrantRead()383 public void testOnReceiveContentListener_TextView_GrantRead() throws Exception { 384 assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW, RESULT_OK); 385 } 386 387 @Test testOnReceiveContentListener_TextView_GrantNone()388 public void testOnReceiveContentListener_TextView_GrantNone() throws Exception { 389 assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW, 390 RESULT_EXCEPTION); 391 } 392 393 @Test testOnReceiveContentListener_EditText_GrantRead()394 public void testOnReceiveContentListener_EditText_GrantRead() throws Exception { 395 assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT, RESULT_OK); 396 } 397 398 @Test testOnReceiveContentListener_EditText_GrantNone()399 public void testOnReceiveContentListener_EditText_GrantNone() throws Exception { 400 assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT, 401 RESULT_EXCEPTION); 402 } 403 404 @Test testOnReceiveContentListener_LinearLayout_GrantRead()405 public void testOnReceiveContentListener_LinearLayout_GrantRead() throws Exception { 406 assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT, RESULT_OK); 407 } 408 409 @Test testOnReceiveContentListener_LinearLayout_GrantNone()410 public void testOnReceiveContentListener_LinearLayout_GrantNone() throws Exception { 411 assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT, 412 RESULT_EXCEPTION); 413 } 414 } 415