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.cts;
18 
19 import com.android.tradefed.device.CollectingOutputReceiver;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.log.LogUtil.CLog;
23 import com.android.tradefed.testtype.DeviceTestCase;
24 
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.regex.Pattern;
28 
29 public class CrossAppDragAndDropTests extends DeviceTestCase {
30     // Constants copied from ActivityManager.StackId. If they are changed there, these must be
31     // updated.
32     /** ID of stack where fullscreen activities are normally launched into. */
33     private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
34 
35     /** ID of stack where freeform/resized activities are normally launched into. */
36     private static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
37 
38     /** ID of stack that occupies a dedicated region of the screen. */
39     private static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
40 
41     /** ID of stack that always on top (always visible) when it exists. */
42     private static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
43 
44     private static final String AM_FORCE_STOP = "am force-stop ";
45     private static final String AM_MOVE_TASK = "am stack move-task ";
46     private static final String AM_RESIZE_TASK = "am task resize ";
47     private static final String AM_REMOVE_STACK = "am stack remove ";
48     private static final String AM_START_N = "am start -n ";
49     private static final String AM_STACK_LIST = "am stack list";
50     private static final String INPUT_MOUSE_SWIPE = "input mouse swipe ";
51     private static final String TASK_ID_PREFIX = "taskId";
52 
53     // Regex pattern to match adb shell am stack list output of the form:
54     // taskId=<TASK_ID>: <componentName> bounds=[LEFT,TOP][RIGHT,BOTTOM]
55     private static final String TASK_REGEX_PATTERN_STRING =
56             "taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]";
57 
58     private static final int SWIPE_DURATION_MS = 500;
59 
60     private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
61     private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
62     private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
63 
64 
65     private static final String SOURCE_ACTIVITY_NAME = "DragSource";
66     private static final String TARGET_ACTIVITY_NAME = "DropTarget";
67 
68     private static final String FILE_GLOBAL = "file_global";
69     private static final String FILE_LOCAL = "file_local";
70     private static final String DISALLOW_GLOBAL = "disallow_global";
71     private static final String CANCEL_SOON = "cancel_soon";
72     private static final String GRANT_NONE = "grant_none";
73     private static final String GRANT_READ = "grant_read";
74     private static final String GRANT_WRITE = "grant_write";
75     private static final String GRANT_READ_PREFIX = "grant_read_prefix";
76     private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix";
77     private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable";
78 
79     private static final String REQUEST_NONE = "request_none";
80     private static final String REQUEST_READ = "request_read";
81     private static final String REQUEST_READ_NESTED = "request_read_nested";
82     private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable";
83     private static final String REQUEST_WRITE = "request_write";
84 
85     private static final String SOURCE_LOG_TAG = "DragSource";
86     private static final String TARGET_LOG_TAG = "DropTarget";
87 
88     private static final String RESULT_KEY_START_DRAG = "START_DRAG";
89     private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
90     private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
91     private static final String RESULT_KEY_EXTRAS = "EXTRAS";
92     private static final String RESULT_KEY_DROP_RESULT = "DROP";
93     private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
94     private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
95     private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
96     private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
97     private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
98 
99     private static final String RESULT_MISSING = "Missing";
100     private static final String RESULT_OK = "OK";
101     private static final String RESULT_EXCEPTION = "Exception";
102     private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions";
103 
104     private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
105             "am supports-split-screen-multi-window";
106 
107     private ITestDevice mDevice;
108 
109     private Map<String, String> mSourceResults;
110     private Map<String, String> mTargetResults;
111 
112     private String mSourcePackageName;
113     private String mTargetPackageName;
114 
115     @Override
setUp()116     protected void setUp() throws Exception {
117         super.setUp();
118 
119         mDevice = getDevice();
120 
121         if (!supportsDragAndDrop()) {
122             return;
123         }
124 
125         mSourcePackageName = SOURCE_PACKAGE_NAME;
126         mTargetPackageName = TARGET_PACKAGE_NAME;
127         cleanupState();
128     }
129 
130     @Override
tearDown()131     protected void tearDown() throws Exception {
132         super.tearDown();
133 
134         if (!supportsDragAndDrop()) {
135             return;
136         }
137 
138         mDevice.executeShellCommand(AM_FORCE_STOP + mSourcePackageName);
139         mDevice.executeShellCommand(AM_FORCE_STOP + mTargetPackageName);
140     }
141 
executeShellCommand(String command)142     private String executeShellCommand(String command) throws DeviceNotAvailableException {
143         return mDevice.executeShellCommand(command);
144     }
145 
clearLogs()146     private void clearLogs() throws DeviceNotAvailableException {
147         executeShellCommand("logcat -c");
148     }
149 
getStartCommand(String componentName, String modeExtra)150     private String getStartCommand(String componentName, String modeExtra) {
151         return AM_START_N + componentName + " -e mode " + modeExtra;
152     }
153 
getMoveTaskCommand(int taskId, int stackId)154     private String getMoveTaskCommand(int taskId, int stackId) throws Exception {
155         return AM_MOVE_TASK + taskId + " " + stackId + " true";
156     }
157 
getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight)158     private String getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight)
159             throws Exception {
160         return AM_RESIZE_TASK + taskId + " " + topLeft.x + " " + topLeft.y + " " + bottomRight.x
161                 + " " + bottomRight.y;
162     }
163 
getComponentName(String packageName, String activityName)164     private String getComponentName(String packageName, String activityName) {
165         return packageName + "/" + packageName + "." + activityName;
166     }
167 
168     /**
169      * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager
170      * is in a good state.
171      */
cleanupState()172     private void cleanupState() throws Exception {
173         executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
174         executeShellCommand(AM_FORCE_STOP + TARGET_PACKAGE_NAME);
175         executeShellCommand(AM_FORCE_STOP + TARGET_23_PACKAGE_NAME);
176         unlockDevice();
177 
178         // Reinitialize the docked stack to force the window manager to reset its default bounds.
179         // See b/29068935.
180         clearLogs();
181         final String componentName = getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME);
182         executeShellCommand(getStartCommand(componentName, null) + " --stack " +
183                 FULLSCREEN_WORKSPACE_STACK_ID);
184         final int taskId = getActivityTaskId(componentName);
185         // Moving a task from the full screen stack to the docked stack resets
186         // WindowManagerService#mDockedStackCreateBounds.
187         executeShellCommand(getMoveTaskCommand(taskId, DOCKED_STACK_ID));
188         waitForResume(mSourcePackageName, SOURCE_ACTIVITY_NAME);
189         executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
190 
191         // Remove special stacks.
192         executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID);
193         executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID);
194         executeShellCommand(AM_REMOVE_STACK + FREEFORM_WORKSPACE_STACK_ID);
195     }
196 
launchDockedActivity(String packageName, String activityName, String mode)197     private void launchDockedActivity(String packageName, String activityName, String mode)
198             throws Exception {
199         clearLogs();
200         final String componentName = getComponentName(packageName, activityName);
201         executeShellCommand(getStartCommand(componentName, mode) + " --stack " + DOCKED_STACK_ID);
202         waitForResume(packageName, activityName);
203     }
204 
launchFullscreenActivity(String packageName, String activityName, String mode)205     private void launchFullscreenActivity(String packageName, String activityName, String mode)
206             throws Exception {
207         clearLogs();
208         final String componentName = getComponentName(packageName, activityName);
209         executeShellCommand(getStartCommand(componentName, mode) + " --stack "
210                 + FULLSCREEN_WORKSPACE_STACK_ID);
211         waitForResume(packageName, activityName);
212     }
213 
214     /**
215      * @param displaySize size of the display
216      * @param leftSide {@code true} to launch the app taking up the left half of the display,
217      *         {@code false} to launch the app taking up the right half of the display.
218      */
launchFreeformActivity(String packageName, String activityName, String mode, Point displaySize, boolean leftSide)219     private void launchFreeformActivity(String packageName, String activityName, String mode,
220             Point displaySize, boolean leftSide) throws Exception{
221         clearLogs();
222         final String componentName = getComponentName(packageName, activityName);
223         executeShellCommand(getStartCommand(componentName, mode) + " --stack "
224                 + FREEFORM_WORKSPACE_STACK_ID);
225         waitForResume(packageName, activityName);
226         Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
227         Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
228         executeShellCommand(getResizeTaskCommand(getActivityTaskId(componentName), topLeft,
229                 bottomRight));
230     }
231 
waitForResume(String packageName, String activityName)232     private void waitForResume(String packageName, String activityName) throws Exception {
233         final String fullActivityName = packageName + "." + activityName;
234         int retryCount = 3;
235         do {
236             Thread.sleep(500);
237             String logs = executeShellCommand("logcat -d -b events");
238             for (String line : logs.split("\\n")) {
239                 if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
240                     return;
241                 }
242             }
243         } while (retryCount-- > 0);
244 
245         throw new Exception(fullActivityName + " has failed to start");
246     }
247 
injectInput(Point from, Point to, int durationMs)248     private void injectInput(Point from, Point to, int durationMs) throws Exception {
249         executeShellCommand(
250                 INPUT_MOUSE_SWIPE + from.x + " " + from.y + " " + to.x + " " + to.y + " " +
251                 durationMs);
252     }
253 
254     static class Point {
255         public int x, y;
256 
Point(int _x, int _y)257         public Point(int _x, int _y) {
258             x=_x;
259             y=_y;
260         }
261 
Point()262         public Point() {}
263     }
264 
findTaskInfo(String name)265     private String findTaskInfo(String name) throws Exception {
266         CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
267         mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
268         final String output = outputReceiver.getOutput();
269         final StringBuilder builder = new StringBuilder();
270         builder.append("Finding task info for task: ");
271         builder.append(name);
272         builder.append("\nParsing adb shell am output: " );
273         builder.append(output);
274         CLog.i(builder.toString());
275         final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name));
276         for (String line : output.split("\\n")) {
277             final String truncatedLine;
278             // Only look for the activity name before the "topActivity" string.
279             final int pos = line.indexOf("topActivity");
280             if (pos > 0) {
281                 truncatedLine = line.substring(0, pos);
282             } else {
283                 truncatedLine = line;
284             }
285             if (pattern.matcher(truncatedLine).find()) {
286                 return truncatedLine;
287             }
288         }
289         return "";
290     }
291 
getWindowBounds(String name, Point from, Point to)292     private boolean getWindowBounds(String name, Point from, Point to) throws Exception {
293         final String taskInfo = findTaskInfo(name);
294         final String[] sections = taskInfo.split("\\[");
295         if (sections.length > 2) {
296             try {
297                 parsePoint(sections[1], from);
298                 parsePoint(sections[2], to);
299                 return true;
300             } catch (Exception e) {
301                 return false;
302             }
303         }
304         return false;
305     }
306 
getActivityTaskId(String name)307     private int getActivityTaskId(String name) throws Exception {
308         final String taskInfo = findTaskInfo(name);
309         for (String word : taskInfo.split("\\s+")) {
310             if (word.startsWith(TASK_ID_PREFIX)) {
311                 final String withColon = word.split("=")[1];
312                 return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
313             }
314         }
315         return -1;
316     }
317 
getDisplaySize()318     private Point getDisplaySize() throws Exception {
319         final String output = executeShellCommand("wm size");
320         final String[] sizes = output.split(" ")[2].split("x");
321         return new Point(Integer.valueOf(sizes[0].trim()), Integer.valueOf(sizes[1].trim()));
322     }
323 
getWindowCenter(String name)324     private Point getWindowCenter(String name) throws Exception {
325         Point p1 = new Point();
326         Point p2 = new Point();
327         if (getWindowBounds(name, p1, p2)) {
328             return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
329         }
330         return null;
331     }
332 
parsePoint(String string, Point point)333     private void parsePoint(String string, Point point) {
334         final String[] parts = string.split("[,|\\]]");
335         point.x = Integer.parseInt(parts[0]);
336         point.y = Integer.parseInt(parts[1]);
337     }
338 
unlockDevice()339     private void unlockDevice() throws DeviceNotAvailableException {
340         // Wake up the device, if necessary.
341         executeShellCommand("input keyevent 224");
342         // Unlock the screen.
343         executeShellCommand("input keyevent 82");
344     }
345 
getLogResults(String className, String lastResultKey)346     private Map<String, String> getLogResults(String className, String lastResultKey)
347             throws Exception {
348         int retryCount = 10;
349         Map<String, String> output = new HashMap<String, String>();
350         do {
351 
352             String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S");
353             for (String line : logs.split("\\n")) {
354                 if (line.startsWith("I/" + className)) {
355                     String payload = line.split(":")[1].trim();
356                     final String[] split = payload.split("=");
357                     if (split.length > 1) {
358                         output.put(split[0], split[1]);
359                     }
360                 }
361             }
362             if (output.containsKey(lastResultKey)) {
363                 return output;
364             }
365         } while (retryCount-- > 0);
366         return output;
367     }
368 
assertDropResult(String sourceMode, String targetMode, String expectedDropResult)369     private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
370             throws Exception {
371         assertDragAndDropResults(sourceMode, targetMode, RESULT_OK, expectedDropResult, RESULT_OK);
372     }
373 
assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult)374     private void assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult)
375             throws Exception {
376         assertDragAndDropResults(
377                 sourceMode, REQUEST_NONE, expectedStartDragResult, RESULT_MISSING, RESULT_MISSING);
378     }
379 
assertDragAndDropResults(String sourceMode, String targetMode, String expectedStartDragResult, String expectedDropResult, String expectedListenerResults)380     private void assertDragAndDropResults(String sourceMode, String targetMode,
381                                           String expectedStartDragResult, String expectedDropResult,
382                                           String expectedListenerResults) throws Exception {
383         if (!supportsDragAndDrop()) {
384             return;
385         }
386 
387         if (supportsSplitScreenMultiWindow()) {
388             launchDockedActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode);
389             launchFullscreenActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode);
390         } else if (supportsFreeformMultiWindow()) {
391             // Fallback to try to launch two freeform windows side by side.
392             Point displaySize = getDisplaySize();
393             launchFreeformActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode,
394                     displaySize, true /* leftSide */);
395             launchFreeformActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode,
396                     displaySize, false /* leftSide */);
397         } else {
398             return;
399         }
400 
401         clearLogs();
402 
403         injectInput(
404                 getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME)),
405                 getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME)),
406                 SWIPE_DURATION_MS);
407 
408         mSourceResults = getLogResults(SOURCE_LOG_TAG, RESULT_KEY_START_DRAG);
409         assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
410 
411         mTargetResults = getLogResults(TARGET_LOG_TAG, RESULT_KEY_DRAG_ENDED);
412         assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
413         if (!RESULT_MISSING.equals(expectedDropResult)) {
414             assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
415             assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
416         }
417         assertListenerResults(expectedListenerResults);
418     }
419 
assertListenerResults(String expectedResult)420     private void assertListenerResults(String expectedResult) throws Exception {
421         assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult);
422         assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult);
423         assertTargetResult(RESULT_KEY_EXTRAS, expectedResult);
424 
425         assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
426         assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING);
427         assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING);
428     }
429 
assertSourceResult(String resultKey, String expectedResult)430     private void assertSourceResult(String resultKey, String expectedResult) throws Exception {
431         assertResult(mSourceResults, resultKey, expectedResult);
432     }
433 
assertTargetResult(String resultKey, String expectedResult)434     private void assertTargetResult(String resultKey, String expectedResult) throws Exception {
435         assertResult(mTargetResults, resultKey, expectedResult);
436     }
437 
assertResult(Map<String, String> results, String resultKey, String expectedResult)438     private void assertResult(Map<String, String> results, String resultKey, String expectedResult)
439             throws Exception {
440         if (!supportsDragAndDrop()) {
441             return;
442         }
443 
444         if (RESULT_MISSING.equals(expectedResult)) {
445             if (results.containsKey(resultKey)) {
446                 fail("Unexpected " + resultKey + "=" + results.get(resultKey));
447             }
448         } else {
449             assertTrue("Missing " + resultKey, results.containsKey(resultKey));
450             assertEquals(resultKey + " result mismatch,", expectedResult,
451                     results.get(resultKey));
452         }
453     }
454 
supportsDragAndDrop()455     private boolean supportsDragAndDrop() throws Exception {
456         String supportsMultiwindow = mDevice.executeShellCommand("am supports-multiwindow").trim();
457         if ("true".equals(supportsMultiwindow)) {
458             return true;
459         } else if ("false".equals(supportsMultiwindow)) {
460             return false;
461         } else {
462             throw new Exception(
463                     "device does not support \"am supports-multiwindow\" shell command.");
464         }
465     }
466 
supportsSplitScreenMultiWindow()467     private boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException {
468         return !executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW).startsWith("false");
469     }
470 
supportsFreeformMultiWindow()471     private boolean supportsFreeformMultiWindow() throws DeviceNotAvailableException {
472         return mDevice.hasFeature("feature:android.software.freeform_window_management");
473     }
474 
testCancelSoon()475     public void testCancelSoon() throws Exception {
476         assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
477     }
478 
testDisallowGlobal()479     public void testDisallowGlobal() throws Exception {
480         assertNoGlobalDragEvents(DISALLOW_GLOBAL, RESULT_OK);
481     }
482 
testDisallowGlobalBelowSdk24()483     public void testDisallowGlobalBelowSdk24() throws Exception {
484         mTargetPackageName = TARGET_23_PACKAGE_NAME;
485         assertNoGlobalDragEvents(GRANT_NONE, RESULT_OK);
486     }
487 
testFileUriLocal()488     public void testFileUriLocal() throws Exception {
489         assertNoGlobalDragEvents(FILE_LOCAL, RESULT_OK);
490     }
491 
testFileUriGlobal()492     public void testFileUriGlobal() throws Exception {
493         assertNoGlobalDragEvents(FILE_GLOBAL, RESULT_EXCEPTION);
494     }
495 
testGrantNoneRequestNone()496     public void testGrantNoneRequestNone() throws Exception {
497         assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
498     }
499 
testGrantNoneRequestRead()500     public void testGrantNoneRequestRead() throws Exception {
501         assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
502     }
503 
testGrantNoneRequestWrite()504     public void testGrantNoneRequestWrite() throws Exception {
505         assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
506     }
507 
testGrantReadRequestNone()508     public void testGrantReadRequestNone() throws Exception {
509         assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
510     }
511 
testGrantReadRequestRead()512     public void testGrantReadRequestRead() throws Exception {
513         assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
514     }
515 
testGrantReadRequestWrite()516     public void testGrantReadRequestWrite() throws Exception {
517         assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
518     }
519 
testGrantReadNoPrefixRequestReadNested()520     public void testGrantReadNoPrefixRequestReadNested() throws Exception {
521         assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
522     }
523 
testGrantReadPrefixRequestReadNested()524     public void testGrantReadPrefixRequestReadNested() throws Exception {
525         assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
526     }
527 
testGrantPersistableRequestTakePersistable()528     public void testGrantPersistableRequestTakePersistable() throws Exception {
529         assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
530     }
531 
testGrantReadRequestTakePersistable()532     public void testGrantReadRequestTakePersistable() throws Exception {
533         assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
534     }
535 
testGrantWriteRequestNone()536     public void testGrantWriteRequestNone() throws Exception {
537         assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
538     }
539 
testGrantWriteRequestRead()540     public void testGrantWriteRequestRead() throws Exception {
541         assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
542     }
543 
testGrantWriteRequestWrite()544     public void testGrantWriteRequestWrite() throws Exception {
545         assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
546     }
547 }
548