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.window;
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.dndsourceapp.Components.DRAG_SOURCE_DROP_TARGET;
24 import static android.server.wm.dndtargetapp.Components.DROP_TARGET;
25 import static android.server.wm.dndtargetappsdk23.Components.DROP_TARGET_SDK23;
26 import static android.view.Display.DEFAULT_DISPLAY;
27 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assert.fail;
32 import static org.junit.Assume.assumeTrue;
33 
34 import android.content.ComponentName;
35 import android.graphics.Point;
36 import android.graphics.Rect;
37 import android.os.SystemClock;
38 import android.platform.test.annotations.AppModeFull;
39 import android.platform.test.annotations.Presubmit;
40 import android.platform.test.annotations.RequiresFlagsEnabled;
41 import android.server.wm.ActivityManagerTestBase;
42 import android.server.wm.DeprecatedTargetSdkUtils;
43 import android.server.wm.TestLogService;
44 import android.server.wm.WindowManagerState.Task;
45 import android.util.Log;
46 import android.view.Display;
47 
48 import com.android.window.flags.Flags;
49 
50 import com.google.common.collect.ImmutableSet;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 import org.junit.runners.Parameterized;
57 
58 import java.util.Map;
59 
60 /**
61  * Build/Install/Run:
62  *     atest CtsWindowManagerDeviceWindow:CrossAppDragAndDropTests
63  */
64 @Presubmit
65 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS")
66 @RunWith(Parameterized.class)
67 @android.server.wm.annotation.Group2
68 public class CrossAppDragAndDropTests extends ActivityManagerTestBase {
69     private static final String TAG = "CrossAppDragAndDrop";
70 
71     private static final int SWIPE_STEPS = 100;
72 
73     private static final String FILE_GLOBAL = "file_global";
74     private static final String FILE_LOCAL = "file_local";
75     private static final String DISALLOW_GLOBAL = "disallow_global";
76     private static final String GLOBAL_SAME_APP = "global_same_app";
77     private static final String CANCEL_SOON = "cancel_soon";
78     private static final String GRANT_NONE = "grant_none";
79     private static final String GRANT_READ = "grant_read";
80     private static final String GRANT_WRITE = "grant_write";
81     private static final String GRANT_READ_PREFIX = "grant_read_prefix";
82     private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix";
83     private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable";
84 
85     private static final String REQUEST_NONE = "request_none";
86     private static final String REQUEST_READ = "request_read";
87     private static final String REQUEST_READ_NESTED = "request_read_nested";
88     private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable";
89     private static final String REQUEST_WRITE = "request_write";
90 
91     private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW =
92             "textview_on_receive_content_listener";
93     private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT =
94             "edittext_on_receive_content_listener";
95     private static final String TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT =
96             "linearlayout_on_receive_content_listener";
97     private static final ImmutableSet<String> ON_RECEIVE_CONTENT_LISTENER_MODES = ImmutableSet.of(
98             TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW,
99             TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT,
100             TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT
101     );
102 
103     private static final String SOURCE_LOG_TAG = "DragSource";
104     private static final String TARGET_LOG_TAG = "DropTarget";
105 
106     private static final String RESULT_KEY_START_DRAG = "START_DRAG";
107     private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
108     private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
109     private static final String RESULT_KEY_EXTRAS = "EXTRAS";
110     private static final String RESULT_KEY_DROP_RESULT = "DROP";
111     private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
112     private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
113     private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
114     private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
115     private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
116 
117     private static final String RESULT_IGNORE = "Ignore";
118     private static final String RESULT_MISSING = "Missing";
119     private static final String RESULT_OK = "OK";
120     private static final String RESULT_EXCEPTION = "Exception";
121     private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions";
122 
123     private static final String EXTRA_MODE = "mode";
124     private static final String EXTRA_LOGTAG = "logtag";
125 
126     private static final ActivityLaunchModeContext SPLIT_SCREEN_LAUNCH_MODE_CONTEXT =
127             new ActivityLaunchModeContext() {
128                 @Override
129                 void assumeDeviceSupportsLaunchMode() {
130                     assumeTrue(mTestCase.supportsSplitScreenMultiWindow());
131                 }
132 
133                 @Override
134                 void launchActivities(ComponentName sourceComponentName, String sourceMode,
135                         ComponentName targetComponentName, String targetMode) {
136                     mTestCase.launchSplitScreenActivities(
137                             sourceComponentName, sourceMode, targetComponentName, targetMode);
138                 }
139 
140                 @Override
141                 public String toString() {
142                     return "SplitScreen";
143                 }
144             };
145 
146     private static final ActivityLaunchModeContext FREEFORM_LAUNCH_MODE_CONTEXT =
147             new ActivityLaunchModeContext() {
148                 @Override
149                 void assumeDeviceSupportsLaunchMode() {
150                     assumeTrue(mTestCase.supportsFreeform());
151                 }
152 
153                 @Override
154                 void launchActivities(ComponentName sourceComponentName, String sourceMode,
155                         ComponentName targetComponentName, String targetMode) throws Exception {
156                     mTestCase.launchFreeformActivities(
157                             sourceComponentName, sourceMode, targetComponentName, targetMode);
158                 }
159 
160                 @Override
161                 public String toString() {
162                     return "Freeform";
163                 }
164             };
165 
166 
167     @Parameterized.Parameters(name = "{0}")
activityLaunchModes()168     public static Object[] activityLaunchModes() {
169         return new Object[] {
170                 FREEFORM_LAUNCH_MODE_CONTEXT,
171                 SPLIT_SCREEN_LAUNCH_MODE_CONTEXT
172         };
173     }
174 
175     @Parameterized.Parameter
176     public ActivityLaunchModeContext mActivityLaunchModeContext;
177 
178     private Map<String, String> mSourceResults;
179     private Map<String, String> mTargetResults;
180 
181     private String mSessionId;
182     private String mSourceLogTag;
183     private String mTargetLogTag;
184 
185     @Before
186     @Override
setUp()187     public void setUp() throws Exception {
188         super.setUp();
189         mActivityLaunchModeContext.mTestCase = this;
190         mActivityLaunchModeContext.assumeDeviceSupportsLaunchMode();
191 
192         // Use uptime in seconds as unique test invocation id.
193         mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000);
194         mSourceLogTag = SOURCE_LOG_TAG + mSessionId;
195         mTargetLogTag = TARGET_LOG_TAG + mSessionId;
196 
197         cleanupState();
198     }
199 
200     @After
tearDown()201     public void tearDown() {
202         cleanupState();
203     }
204 
205     /**
206      * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager
207      * is in a good state.
208      */
cleanupState()209     private void cleanupState() {
210         stopTestPackage(DRAG_SOURCE.getPackageName());
211         stopTestPackage(DROP_TARGET.getPackageName());
212         stopTestPackage(DROP_TARGET_SDK23.getPackageName());
213     }
214 
launchFreeformActivities(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode)215     private void launchFreeformActivities(ComponentName sourceComponentName,
216             String sourceMode, ComponentName targetComponentName, String targetMode)
217             throws Exception {
218         // Fallback to try to launch two freeform windows side by side.
219         Point displaySize = getDisplaySize();
220         launchFreeformActivity(sourceComponentName, sourceMode, mSourceLogTag,
221                 displaySize, true /* leftSide */);
222         launchFreeformActivity(targetComponentName, targetMode, mTargetLogTag,
223                 displaySize, false /* leftSide */);
224     }
225 
226     /**
227      * @param displaySize size of the display
228      * @param leftSide    {@code true} to launch the app taking up the left half of the display,
229      *                    {@code false} to launch the app taking up the right half of the display.
230      */
launchFreeformActivity(ComponentName componentName, String mode, String logtag, Point displaySize, boolean leftSide)231     private void launchFreeformActivity(ComponentName componentName, String mode,
232             String logtag, Point displaySize, boolean leftSide) throws Exception {
233         launchActivity(componentName, WINDOWING_MODE_FREEFORM, extraString("mode", mode),
234                 extraString("logtag", logtag));
235         Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
236         Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
237         resizeActivityTask(componentName, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
238         waitAndAssertTopResumedActivity(componentName, DEFAULT_DISPLAY,
239                 "Activity launched as freeform should be resumed");
240     }
241 
launchSplitScreenActivities(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode)242     private void launchSplitScreenActivities(ComponentName sourceComponentName,
243             String sourceMode, ComponentName targetComponentName, String targetMode) {
244         // Launch primary activity.
245         getLaunchActivityBuilder()
246                 .setTargetActivity(sourceComponentName)
247                 .setUseInstrumentation()
248                 .setWaitForLaunched(true)
249                 .setIntentExtra(bundle -> {
250                     bundle.putString(EXTRA_MODE, sourceMode);
251                     bundle.putString(EXTRA_LOGTAG, mSourceLogTag);
252                 }).execute();
253 
254         // Launch secondary activity.
255         getLaunchActivityBuilder().setTargetActivity(targetComponentName)
256                 .setUseInstrumentation()
257                 .setWaitForLaunched(true)
258                 .setIntentExtra(bundle -> {
259                     bundle.putString(EXTRA_MODE, targetMode);
260                     bundle.putString(EXTRA_LOGTAG, mTargetLogTag);
261                 }).execute();
262         moveActivitiesToSplitScreen(sourceComponentName, targetComponentName);
263     }
264 
injectInput(Point from, Point to, int steps)265     private void injectInput(Point from, Point to, int steps) throws Exception {
266         dragPointer(from, to, steps);
267     }
268 
getDisplaySize()269     private Point getDisplaySize() throws Exception {
270         final Point displaySize = new Point();
271         mDm.getDisplay(Display.DEFAULT_DISPLAY).getRealSize(displaySize);
272         return displaySize;
273     }
274 
getWindowCenter(ComponentName name)275     private Point getWindowCenter(ComponentName name) throws Exception {
276         final Task sideTask = mWmState.getTaskByActivity(name);
277         Rect bounds = sideTask.getBounds();
278         if (bounds != null) {
279             return new Point(bounds.centerX(), bounds.centerY());
280         }
281         return null;
282     }
283 
assertDropResult(String sourceMode, String targetMode, String expectedDropResult)284     private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
285             throws Exception {
286         assertDragAndDropResults(DRAG_SOURCE, sourceMode, DROP_TARGET, targetMode,
287                 RESULT_OK, expectedDropResult, RESULT_OK);
288     }
289 
assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String expectedStartDragResult)290     private void assertNoGlobalDragEvents(ComponentName sourceComponentName, String sourceMode,
291             ComponentName targetComponentName, String expectedStartDragResult)
292             throws Exception {
293         assertDragAndDropResults(
294                 sourceComponentName, sourceMode, targetComponentName, REQUEST_NONE,
295                 expectedStartDragResult, RESULT_MISSING, RESULT_MISSING);
296     }
297 
assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode, String expectedStartDragResult, String expectedDropResult, String expectedListenerResults)298     private void assertDragAndDropResults(ComponentName sourceComponentName, String sourceMode,
299             ComponentName targetComponentName, String targetMode,
300             String expectedStartDragResult, String expectedDropResult,
301             String expectedListenerResults) throws Exception {
302         Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode
303                 + ", target: " + targetMode);
304 
305         mActivityLaunchModeContext.launchActivities(
306                 sourceComponentName, sourceMode, targetComponentName, targetMode);
307         if (DROP_TARGET_SDK23.equals(targetComponentName)) {
308             DeprecatedTargetSdkUtils.waitAndDismissDeprecatedTargetSdkDialog(mWmState);
309         }
310 
311         Point p1 = getWindowCenter(sourceComponentName);
312         assertNotNull(p1);
313         Point p2 = getWindowCenter(targetComponentName);
314         assertNotNull(p2);
315 
316         TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG);
317         TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED);
318 
319         injectInput(p1, p2, SWIPE_STEPS);
320 
321         mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000);
322         assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
323 
324         mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000);
325         assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
326 
327         // Skip the following assertions when testing OnReceiveContentListener, since it only
328         // handles drop events.
329         if (!RESULT_IGNORE.equals(expectedListenerResults)
330                 && !ON_RECEIVE_CONTENT_LISTENER_MODES.contains(targetMode)) {
331             if (!RESULT_MISSING.equals(expectedDropResult)) {
332                 assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
333                 assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
334             }
335             assertListenerResults(expectedListenerResults);
336         }
337     }
338 
assertListenerResults(String expectedResult)339     private void assertListenerResults(String expectedResult) throws Exception {
340         assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult);
341         assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult);
342         assertTargetResult(RESULT_KEY_EXTRAS, expectedResult);
343 
344         assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
345         assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING);
346         assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING);
347     }
348 
assertSourceResult(String resultKey, String expectedResult)349     private void assertSourceResult(String resultKey, String expectedResult) throws Exception {
350         assertResult(mSourceResults, resultKey, expectedResult);
351     }
352 
assertTargetResult(String resultKey, String expectedResult)353     private void assertTargetResult(String resultKey, String expectedResult) throws Exception {
354         assertResult(mTargetResults, resultKey, expectedResult);
355     }
356 
assertResult(Map<String, String> results, String resultKey, String expectedResult)357     private void assertResult(Map<String, String> results, String resultKey, String expectedResult)
358             throws Exception {
359         if (RESULT_MISSING.equals(expectedResult)) {
360             if (results.containsKey(resultKey)) {
361                 fail("Unexpected " + resultKey + "=" + results.get(resultKey));
362             }
363         } else {
364             assertTrue("Missing " + resultKey, results.containsKey(resultKey));
365             assertEquals(resultKey + " result mismatch,", expectedResult, results.get(resultKey));
366         }
367     }
368 
369     @Test
testCancelSoon()370     public void testCancelSoon() throws Exception {
371         assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
372     }
373 
374     @Test
testDisallowGlobal()375     public void testDisallowGlobal() throws Exception {
376         // Verify that dragging to another window doesn't work without FLAG_DRAG_GLOBAL
377         assertNoGlobalDragEvents(DRAG_SOURCE, DISALLOW_GLOBAL, DROP_TARGET, RESULT_OK);
378     }
379 
380     @Test
testDisallowGlobalBelowSdk24()381     public void testDisallowGlobalBelowSdk24() throws Exception {
382         assertNoGlobalDragEvents(DRAG_SOURCE, GRANT_NONE, DROP_TARGET_SDK23, RESULT_OK);
383     }
384 
385     @Test
386     @RequiresFlagsEnabled(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS)
testDisallowGlobalSameAppWithDifferentUids()387     public void testDisallowGlobalSameAppWithDifferentUids() throws Exception {
388         // Verify that dragging to another window from a different app doesn't work with
389         // FLAG_DRAG_GLOBAL_SAME_APP
390         assertNoGlobalDragEvents(DRAG_SOURCE, GLOBAL_SAME_APP, DROP_TARGET, RESULT_OK);
391     }
392 
393     @Test
394     @RequiresFlagsEnabled(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS)
testGlobalSameAppWithSameUids()395     public void testGlobalSameAppWithSameUids() throws Exception {
396         // Verify that dragging to another window from the same app works with
397         // FLAG_DRAG_GLOBAL_SAME_APP
398         assertDragAndDropResults(DRAG_SOURCE, GLOBAL_SAME_APP, DRAG_SOURCE_DROP_TARGET,
399                 REQUEST_NONE, RESULT_OK, RESULT_OK, RESULT_IGNORE);
400     }
401 
402     @Test
testFileUriLocal()403     public void testFileUriLocal() throws Exception {
404         assertNoGlobalDragEvents(DRAG_SOURCE, FILE_LOCAL, DROP_TARGET, RESULT_OK);
405     }
406 
407     @Test
testFileUriGlobal()408     public void testFileUriGlobal() throws Exception {
409         assertNoGlobalDragEvents(DRAG_SOURCE, FILE_GLOBAL, DROP_TARGET, RESULT_EXCEPTION);
410     }
411 
412     @Test
testGrantNoneRequestNone()413     public void testGrantNoneRequestNone() throws Exception {
414         assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
415     }
416 
417     @Test
testGrantNoneRequestRead()418     public void testGrantNoneRequestRead() throws Exception {
419         assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
420     }
421 
422     @Test
testGrantNoneRequestWrite()423     public void testGrantNoneRequestWrite() throws Exception {
424         assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
425     }
426 
427     @Test
testGrantReadRequestNone()428     public void testGrantReadRequestNone() throws Exception {
429         assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
430     }
431 
432     @Test
testGrantReadRequestRead()433     public void testGrantReadRequestRead() throws Exception {
434         assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
435     }
436 
437     @Test
testGrantReadRequestWrite()438     public void testGrantReadRequestWrite() throws Exception {
439         assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
440     }
441 
442     @Test
testGrantReadNoPrefixRequestReadNested()443     public void testGrantReadNoPrefixRequestReadNested() throws Exception {
444         assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
445     }
446 
447     @Test
testGrantReadPrefixRequestReadNested()448     public void testGrantReadPrefixRequestReadNested() throws Exception {
449         assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
450     }
451 
452     @Test
testGrantPersistableRequestTakePersistable()453     public void testGrantPersistableRequestTakePersistable() throws Exception {
454         assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
455     }
456 
457     @Test
testGrantReadRequestTakePersistable()458     public void testGrantReadRequestTakePersistable() throws Exception {
459         assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
460     }
461 
462     @Test
testGrantWriteRequestNone()463     public void testGrantWriteRequestNone() throws Exception {
464         assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
465     }
466 
467     @Test
testGrantWriteRequestRead()468     public void testGrantWriteRequestRead() throws Exception {
469         assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
470     }
471 
472     @Test
testGrantWriteRequestWrite()473     public void testGrantWriteRequestWrite() throws Exception {
474         assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
475     }
476 
477     @Test
testOnReceiveContentListener_TextView_GrantRead()478     public void testOnReceiveContentListener_TextView_GrantRead() throws Exception {
479         assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW, RESULT_OK);
480     }
481 
482     @Test
testOnReceiveContentListener_TextView_GrantNone()483     public void testOnReceiveContentListener_TextView_GrantNone() throws Exception {
484         assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_TEXT_VIEW,
485                 RESULT_EXCEPTION);
486     }
487 
488     @Test
testOnReceiveContentListener_EditText_GrantRead()489     public void testOnReceiveContentListener_EditText_GrantRead() throws Exception {
490         assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT, RESULT_OK);
491     }
492 
493     @Test
testOnReceiveContentListener_EditText_GrantNone()494     public void testOnReceiveContentListener_EditText_GrantNone() throws Exception {
495         assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_EDIT_TEXT,
496                 RESULT_EXCEPTION);
497     }
498 
499     @Test
testOnReceiveContentListener_LinearLayout_GrantRead()500     public void testOnReceiveContentListener_LinearLayout_GrantRead() throws Exception {
501         assertDropResult(GRANT_READ, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT, RESULT_OK);
502     }
503 
504     @Test
testOnReceiveContentListener_LinearLayout_GrantNone()505     public void testOnReceiveContentListener_LinearLayout_GrantNone() throws Exception {
506         assertDropResult(GRANT_NONE, TARGET_ON_RECEIVE_CONTENT_LISTENER_LINEAR_LAYOUT,
507                 RESULT_EXCEPTION);
508     }
509 
510     private abstract static class ActivityLaunchModeContext {
511         CrossAppDragAndDropTests mTestCase;
512 
assumeDeviceSupportsLaunchMode()513         abstract void assumeDeviceSupportsLaunchMode() throws Exception;
514 
launchActivities(ComponentName sourceComponentName, String sourceMode, ComponentName targetComponentName, String targetMode)515         abstract void launchActivities(ComponentName sourceComponentName,
516                 String sourceMode, ComponentName targetComponentName, String targetMode)
517                 throws Exception;
518     }
519 }
520