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