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.UiDeviceUtils.dragPointer; 21 import static android.server.wm.dndsourceapp.Components.DRAG_SOURCE; 22 import static android.server.wm.dndtargetapp.Components.DROP_TARGET; 23 import static android.server.wm.dndtargetappsdk23.Components.DROP_TARGET_SDK23; 24 import static android.view.Display.DEFAULT_DISPLAY; 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertNotNull; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 import static org.junit.Assume.assumeTrue; 30 31 import android.content.ComponentName; 32 import android.graphics.Point; 33 import android.graphics.Rect; 34 import android.os.SystemClock; 35 import android.platform.test.annotations.AppModeFull; 36 import android.platform.test.annotations.Presubmit; 37 import android.server.wm.WindowManagerState.ActivityTask; 38 import android.util.Log; 39 import android.view.Display; 40 import java.util.Map; 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 45 /** 46 * Build/Install/Run: 47 * atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests 48 */ 49 @Presubmit 50 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_STACKS") 51 public class CrossAppDragAndDropTests extends ActivityManagerTestBase { 52 private static final String TAG = "CrossAppDragAndDrop"; 53 54 private static final int SWIPE_STEPS = 100; 55 56 private static final String FILE_GLOBAL = "file_global"; 57 private static final String FILE_LOCAL = "file_local"; 58 private static final String DISALLOW_GLOBAL = "disallow_global"; 59 private static final String CANCEL_SOON = "cancel_soon"; 60 private static final String GRANT_NONE = "grant_none"; 61 private static final String GRANT_READ = "grant_read"; 62 private static final String GRANT_WRITE = "grant_write"; 63 private static final String GRANT_READ_PREFIX = "grant_read_prefix"; 64 private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix"; 65 private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable"; 66 67 private static final String REQUEST_NONE = "request_none"; 68 private static final String REQUEST_READ = "request_read"; 69 private static final String REQUEST_READ_NESTED = "request_read_nested"; 70 private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable"; 71 private static final String REQUEST_WRITE = "request_write"; 72 73 private static final String SOURCE_LOG_TAG = "DragSource"; 74 private static final String TARGET_LOG_TAG = "DropTarget"; 75 76 private static final String RESULT_KEY_START_DRAG = "START_DRAG"; 77 private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED"; 78 private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED"; 79 private static final String RESULT_KEY_EXTRAS = "EXTRAS"; 80 private static final String RESULT_KEY_DROP_RESULT = "DROP"; 81 private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE"; 82 private static final String RESULT_KEY_ACCESS_AFTER = "AFTER"; 83 private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR"; 84 private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR"; 85 private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR"; 86 87 private static final String RESULT_MISSING = "Missing"; 88 private static final String RESULT_OK = "OK"; 89 private static final String RESULT_EXCEPTION = "Exception"; 90 private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions"; 91 92 private static final String EXTRA_MODE = "mode"; 93 private static final String EXTRA_LOGTAG = "logtag"; 94 95 private Map<String, String> mSourceResults; 96 private Map<String, String> mTargetResults; 97 98 private String mSessionId; 99 private String mSourceLogTag; 100 private String mTargetLogTag; 101 102 @Before 103 @Override setUp()104 public void setUp() throws Exception { 105 super.setUp(); 106 assumeTrue(supportsSplitScreenMultiWindow() || supportsFreeform()); 107 108 // Use uptime in seconds as unique test invocation id. 109 mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000); 110 mSourceLogTag = SOURCE_LOG_TAG + mSessionId; 111 mTargetLogTag = TARGET_LOG_TAG + mSessionId; 112 113 cleanupState(); 114 mUseTaskOrganizer = false; 115 } 116 117 @After tearDown()118 public void tearDown() { 119 cleanupState(); 120 } 121 122 /** 123 * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager 124 * is in a good state. 125 */ cleanupState()126 private void cleanupState() { 127 stopTestPackage(DRAG_SOURCE.getPackageName()); 128 stopTestPackage(DROP_TARGET.getPackageName()); 129 stopTestPackage(DROP_TARGET_SDK23.getPackageName()); 130 } 131 132 /** 133 * @param displaySize size of the display 134 * @param leftSide {@code true} to launch the app taking up the left half of the display, 135 * {@code false} to launch the app taking up the right half of the display. 136 */ launchFreeformActivity(ComponentName componentName, String mode, String logtag, Point displaySize, boolean leftSide)137 private void launchFreeformActivity(ComponentName componentName, String mode, 138 String logtag, Point displaySize, boolean leftSide) throws Exception { 139 launchActivity(componentName, WINDOWING_MODE_FREEFORM, "mode", mode, "logtag", logtag); 140 Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0); 141 Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y); 142 resizeActivityTask(componentName, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); 143 waitAndAssertTopResumedActivity(componentName, DEFAULT_DISPLAY, 144 "Activity launched as freeform should be resumed"); 145 } 146 injectInput(Point from, Point to, int steps)147 private void injectInput(Point from, Point to, int steps) throws Exception { 148 dragPointer(from, to, steps); 149 } 150 getDisplaySize()151 private Point getDisplaySize() throws Exception { 152 final Point displaySize = new Point(); 153 mDm.getDisplay(Display.DEFAULT_DISPLAY).getRealSize(displaySize); 154 return displaySize; 155 } 156 getWindowCenter(ComponentName name)157 private Point getWindowCenter(ComponentName name) throws Exception { 158 final ActivityTask sideTask = mWmState.getTaskByActivity(name); 159 Rect bounds = sideTask.getBounds(); 160 if (bounds != null) { 161 return new Point(bounds.centerX(), bounds.centerY()); 162 } 163 return null; 164 } 165 assertDropResult(String sourceMode, String targetMode, String expectedDropResult)166 private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult) 167 throws Exception { 168 assertDragAndDropResults(DRAG_SOURCE, sourceMode, DROP_TARGET, targetMode, 169 RESULT_OK, expectedDropResult, RESULT_OK); 170 } 171 assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String expectedStartDragResult)172 private void assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode, 173 ComponentName targetComponentName, String expectedStartDragResult) 174 throws Exception { 175 assertDragAndDropResults( 176 sourceComponentName, sourceMode, targetComponentName, REQUEST_NONE, 177 expectedStartDragResult, RESULT_MISSING, RESULT_MISSING); 178 } 179 assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode, String expectedStartDragResult, String expectedDropResult, String expectedListenerResults)180 private void assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode, 181 ComponentName targetComponentName, String targetMode, 182 String expectedStartDragResult, String expectedDropResult, 183 String expectedListenerResults) throws Exception { 184 Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode 185 + ", target: " + targetMode); 186 187 if (supportsFreeform()) { 188 // Fallback to try to launch two freeform windows side by side. 189 Point displaySize = getDisplaySize(); 190 launchFreeformActivity(sourceComponentName, sourceMode, mSourceLogTag, 191 displaySize, true /* leftSide */); 192 launchFreeformActivity(targetComponentName, targetMode, mTargetLogTag, 193 displaySize, false /* leftSide */); 194 } else { 195 launchActivitiesInSplitScreen(getLaunchActivityBuilder() 196 .setTargetActivity(sourceComponentName) 197 .setIntentExtra(bundle -> { 198 bundle.putString(EXTRA_MODE, sourceMode); 199 bundle.putString(EXTRA_LOGTAG, mSourceLogTag); 200 }), 201 getLaunchActivityBuilder().setTargetActivity(targetComponentName) 202 .setIntentExtra(bundle -> { 203 bundle.putString(EXTRA_MODE, targetMode); 204 bundle.putString(EXTRA_LOGTAG, mTargetLogTag); 205 })); 206 } 207 208 Point p1 = getWindowCenter(sourceComponentName); 209 assertNotNull(p1); 210 Point p2 = getWindowCenter(targetComponentName); 211 assertNotNull(p2); 212 213 TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG); 214 TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED); 215 216 injectInput(p1, p2, SWIPE_STEPS); 217 218 mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000); 219 assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult); 220 221 mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000); 222 assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult); 223 if (!RESULT_MISSING.equals(expectedDropResult)) { 224 assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION); 225 assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION); 226 } 227 assertListenerResults(expectedListenerResults); 228 } 229 assertListenerResults(String expectedResult)230 private void assertListenerResults(String expectedResult) throws Exception { 231 assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult); 232 assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult); 233 assertTargetResult(RESULT_KEY_EXTRAS, expectedResult); 234 235 assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING); 236 assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING); 237 assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING); 238 } 239 assertSourceResult(String resultKey, String expectedResult)240 private void assertSourceResult(String resultKey, String expectedResult) throws Exception { 241 assertResult(mSourceResults, resultKey, expectedResult); 242 } 243 assertTargetResult(String resultKey, String expectedResult)244 private void assertTargetResult(String resultKey, String expectedResult) throws Exception { 245 assertResult(mTargetResults, resultKey, expectedResult); 246 } 247 assertResult(Map<String, String> results, String resultKey, String expectedResult)248 private void assertResult(Map<String, String> results, String resultKey, String expectedResult) 249 throws Exception { 250 if (RESULT_MISSING.equals(expectedResult)) { 251 if (results.containsKey(resultKey)) { 252 fail("Unexpected " + resultKey + "=" + results.get(resultKey)); 253 } 254 } else { 255 assertTrue("Missing " + resultKey, results.containsKey(resultKey)); 256 assertEquals(resultKey + " result mismatch,", expectedResult, 257 results.get(resultKey)); 258 } 259 } 260 261 @Test testCancelSoon()262 public void testCancelSoon() throws Exception { 263 assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING); 264 } 265 266 @Test testDisallowGlobal()267 public void testDisallowGlobal() throws Exception { 268 assertNoGlobalDragEvents(DRAG_SOURCE, DISALLOW_GLOBAL, DROP_TARGET, RESULT_OK); 269 } 270 271 @Test testDisallowGlobalBelowSdk24()272 public void testDisallowGlobalBelowSdk24() throws Exception { 273 assertNoGlobalDragEvents(DRAG_SOURCE, GRANT_NONE, DROP_TARGET_SDK23, RESULT_OK); 274 } 275 276 @Test testFileUriLocal()277 public void testFileUriLocal() throws Exception { 278 assertNoGlobalDragEvents(DRAG_SOURCE, FILE_LOCAL, DROP_TARGET, RESULT_OK); 279 } 280 281 @Test testFileUriGlobal()282 public void testFileUriGlobal() throws Exception { 283 assertNoGlobalDragEvents(DRAG_SOURCE, FILE_GLOBAL, DROP_TARGET, RESULT_EXCEPTION); 284 } 285 286 @Test testGrantNoneRequestNone()287 public void testGrantNoneRequestNone() throws Exception { 288 assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION); 289 } 290 291 @Test testGrantNoneRequestRead()292 public void testGrantNoneRequestRead() throws Exception { 293 assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS); 294 } 295 296 @Test testGrantNoneRequestWrite()297 public void testGrantNoneRequestWrite() throws Exception { 298 assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS); 299 } 300 301 @Test testGrantReadRequestNone()302 public void testGrantReadRequestNone() throws Exception { 303 assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION); 304 } 305 306 @Test testGrantReadRequestRead()307 public void testGrantReadRequestRead() throws Exception { 308 assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK); 309 } 310 311 @Test testGrantReadRequestWrite()312 public void testGrantReadRequestWrite() throws Exception { 313 assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION); 314 } 315 316 @Test testGrantReadNoPrefixRequestReadNested()317 public void testGrantReadNoPrefixRequestReadNested() throws Exception { 318 assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION); 319 } 320 321 @Test testGrantReadPrefixRequestReadNested()322 public void testGrantReadPrefixRequestReadNested() throws Exception { 323 assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK); 324 } 325 326 @Test testGrantPersistableRequestTakePersistable()327 public void testGrantPersistableRequestTakePersistable() throws Exception { 328 assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK); 329 } 330 331 @Test testGrantReadRequestTakePersistable()332 public void testGrantReadRequestTakePersistable() throws Exception { 333 assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION); 334 } 335 336 @Test testGrantWriteRequestNone()337 public void testGrantWriteRequestNone() throws Exception { 338 assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION); 339 } 340 341 @Test testGrantWriteRequestRead()342 public void testGrantWriteRequestRead() throws Exception { 343 assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION); 344 } 345 346 @Test testGrantWriteRequestWrite()347 public void testGrantWriteRequestWrite() throws Exception { 348 assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK); 349 } 350 } 351