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