• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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