• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.bedstead.testapp;
17 
18 import com.android.bedstead.nene.appops.AppOpsMode;
19 import com.android.bedstead.nene.permissions.PermissionContext;
20 import com.android.bedstead.nene.permissions.PermissionsController;
21 import com.android.bedstead.nene.utils.Versions;
22 
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Set;
28 
29 /**
30  * Permissions management for a test app instance.
31  */
32 public final class TestAppInstancePermissions implements PermissionsController {
33 
34     private final List<TestAppPermissionContext> mPermissionContexts =
35             Collections.synchronizedList(new ArrayList<>());
36     private final TestAppInstance mTestAppInstance;
37 
TestAppInstancePermissions(TestAppInstance testAppInstance)38     TestAppInstancePermissions(TestAppInstance testAppInstance) {
39         mTestAppInstance = testAppInstance;
40     }
41 
42     /**
43      * Enter a {@link PermissionContext} where the given permissions are granted to the test app.
44      *
45      * <p>The permission will only be granted for calls made by the test app.
46      *
47      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
48      * thrown.
49      *
50      * <p>Note that only runtime and development permissions can be granted to test apps.
51      *
52      * <p>Recommended usage:
53      * {@code
54      *
55      * try (PermissionContext p = testApp.permissions().withPermission(PERMISSION1, PERMISSION2) {
56      * // Code which needs the permissions goes here
57      * }
58      * }
59      */
60     @Override
withPermission(String... permissions)61     public TestAppPermissionContext withPermission(String... permissions) {
62         TestAppPermissionContext context =
63                 new TestAppPermissionContext(this);
64         mPermissionContexts.add(context);
65         context.withPermission(permissions);
66 
67         return context;
68     }
69 
70     /**
71      * Enter a {@link PermissionContext} where the given permissions are granted to the test app.
72      *
73      * <p>The permission will only be granted for calls made by the test app.
74      *
75      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
76      * thrown.
77      *
78      * <p>Note that only runtime and development permissions can be granted to test apps.
79      *
80      * <p>The permission will only be granted on the given version.
81      *
82      * <p>Recommended usage:
83      * {@code
84      *
85      * try (PermissionContext p = testApp.permissions()
86      *     .withPermissionOnVersion(R, PERMISSION1, PERMISSION2) {
87      * // Code which needs the permissions goes here
88      * }
89      * }
90      */
91     @Override
withPermissionOnVersion(int sdkVersion, String... permissions)92     public TestAppPermissionContext withPermissionOnVersion(int sdkVersion, String... permissions) {
93         return withPermissionOnVersionBetween(sdkVersion, sdkVersion, permissions);
94     }
95 
96     /**
97      * Enter a {@link PermissionContext} where the given permissions are granted to the test app.
98      *
99      * <p>The permission will only be granted for calls made by the test app.
100      *
101      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
102      * thrown.
103      *
104      * <p>Note that only runtime and development permissions can be granted to test apps.
105      *
106      * <p>The permission will only be granted on the given version or higher.
107      *
108      * <p>Recommended usage:
109      * {@code
110      *
111      * try (PermissionContext p = testApp.permissions()
112      *     .withPermissionOnVersionAtLest(R, PERMISSION1, PERMISSION2) {
113      * // Code which needs the permissions goes here
114      * }
115      * }
116      */
117     @Override
withPermissionOnVersionAtLeast( int minSdkVersion, String... permissions)118     public TestAppPermissionContext withPermissionOnVersionAtLeast(
119             int minSdkVersion, String... permissions) {
120         return withPermissionOnVersionBetween(minSdkVersion, Versions.ANY, permissions);
121     }
122 
123     /**
124      * Enter a {@link PermissionContext} where the given permissions are granted to the test app.
125      *
126      * <p>The permission will only be granted for calls made by the test app.
127      *
128      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
129      * thrown.
130      *
131      * <p>Note that only runtime and development permissions can be granted to test apps.
132      *
133      * <p>The permission will only be granted on the given version or lower
134      *
135      * <p>Recommended usage:
136      * {@code
137      *
138      * try (PermissionContext p = testApp.permissions()
139      *     .withPermissionOnVersionAtMost(R, PERMISSION1, PERMISSION2) {
140      * // Code which needs the permissions goes here
141      * }
142      * }
143      */
144     @Override
withPermissionOnVersionAtMost( int maxSdkVersion, String... permissions)145     public TestAppPermissionContext withPermissionOnVersionAtMost(
146             int maxSdkVersion, String... permissions) {
147         return withPermissionOnVersionBetween(Versions.ANY, maxSdkVersion, permissions);
148     }
149 
150     /**
151      * Enter a {@link PermissionContext} where the given permissions are granted to the test app.
152      *
153      * <p>The permission will only be granted for calls made by the test app.
154      *
155      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
156      * thrown.
157      *
158      * <p>Note that only runtime and development permissions can be granted to test apps.
159      *
160      * <p>The permission will only be granted on versions between those given (inclusive).
161      *
162      * <p>Recommended usage:
163      * {@code
164      *
165      * try (PermissionContext p = testApp.permissions()
166      *     .withPermissionOnVersionBetween(R, T, PERMISSION1, PERMISSION2) {
167      * // Code which needs the permissions goes here
168      * }
169      * }
170      */
171     @Override
withPermissionOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... permissions)172     public TestAppPermissionContext withPermissionOnVersionBetween(
173             int minSdkVersion, int maxSdkVersion, String... permissions) {
174         TestAppPermissionContext context =
175                 new TestAppPermissionContext(this);
176         mPermissionContexts.add(context);
177         context.withPermissionOnVersionBetween(minSdkVersion, maxSdkVersion, permissions);
178 
179         return context;
180     }
181 
182     /**
183      * Enter a {@link PermissionContext} where the given permissions are not granted to the test
184      * app.
185      *
186      * <p>The permission will only guarantee to not be granted for calls made by the test app.
187      *
188      * <p>If the permissions cannot be denied an exception will be thrown.
189      *
190      * <p>Recommended usage:
191      * {@code
192      *
193      * try (PermissionContext p = testApp.permissions()
194      *     .withoutPermission(PERMISSION1, PERMISSION2) {
195      * // Code which needs the permissions goes here
196      * }
197      * }
198      */
199     @Override
withoutPermission(String... permissions)200     public TestAppPermissionContext withoutPermission(String... permissions) {
201         TestAppPermissionContext context =
202                 new TestAppPermissionContext(this);
203         mPermissionContexts.add(context);
204         context.withoutPermission(permissions);
205 
206         return context;
207     }
208 
209     /**
210      * Enter a {@link PermissionContext} where the given app op are granted to the test
211      * app.
212      *
213      * <p>The app op will only guarantee to be granted for calls made by the test app.
214      *
215      * <p>If the app op cannot be granted an exception will be thrown.
216      *
217      * <p>Recommended usage:
218      * {@code
219      *
220      * try (PermissionContext p = testApp.permissions()
221      *     .withAppOp(APP_OP1, APP_OP2) {
222      * // Code which needs the app op goes here
223      * }
224      * }
225      */
226     @Override
withAppOp(String... appOps)227     public TestAppPermissionContext withAppOp(String... appOps) {
228         TestAppPermissionContext context =
229                 new TestAppPermissionContext(this);
230         mPermissionContexts.add(context);
231         context.withAppOp(appOps);
232 
233         return context;
234     }
235 
236     /**
237      * Enter a {@link PermissionContext} where the given app op are granted to the test
238      * app.
239      *
240      * <p>The app op will only guarantee to be granted for calls made by the test app.
241      *
242      * <p>If the app op cannot be granted an exception will be thrown.
243      *
244      * <p>The app op will only be granted on the version given
245      *
246      * <p>Recommended usage:
247      * {@code
248      *
249      * try (PermissionContext p = testApp.permissions()
250      *     .withAppOpOnVersion(R, APP_OP1, APP_OP2) {
251      * // Code which needs the app op goes here
252      * }
253      * }
254      */
255     @Override
withAppOpOnVersion(int sdkVersion, String... appOps)256     public TestAppPermissionContext withAppOpOnVersion(int sdkVersion, String... appOps) {
257         return withAppOpOnVersionBetween(sdkVersion, sdkVersion, appOps);
258     }
259 
260     /**
261      * Enter a {@link PermissionContext} where the given app op are granted to the test
262      * app.
263      *
264      * <p>The app op will only guarantee to be granted for calls made by the test app.
265      *
266      * <p>If the app op cannot be granted an exception will be thrown.
267      *
268      * <p>The app op will only be granted on the version given and above
269      *
270      * <p>Recommended usage:
271      * {@code
272      *
273      * try (PermissionContext p = testApp.permissions()
274      *     .withAppOpOnVersionAtLeast(R, APP_OP1, APP_OP2) {
275      * // Code which needs the app op goes here
276      * }
277      * }
278      */
279     @Override
withAppOpOnVersionAtLeast(int minSdkVersion, String... appOps)280     public TestAppPermissionContext withAppOpOnVersionAtLeast(int minSdkVersion, String... appOps) {
281         return withAppOpOnVersionBetween(minSdkVersion, Versions.ANY, appOps);
282     }
283 
284     /**
285      * Enter a {@link PermissionContext} where the given app op are granted to the test
286      * app.
287      *
288      * <p>The app op will only guarantee to be granted for calls made by the test app.
289      *
290      * <p>If the app op cannot be granted an exception will be thrown.
291      *
292      * <p>The app op will only be granted on the version given and below
293      *
294      * <p>Recommended usage:
295      * {@code
296      *
297      * try (PermissionContext p = testApp.permissions()
298      *     .withAppOpOnVersionAtMost(S, APP_OP1, APP_OP2) {
299      * // Code which needs the app op goes here
300      * }
301      * }
302      */
303     @Override
withAppOpOnVersionAtMost(int maxSdkVersion, String... appOps)304     public TestAppPermissionContext withAppOpOnVersionAtMost(int maxSdkVersion, String... appOps) {
305         return withAppOpOnVersionBetween(Versions.ANY, maxSdkVersion, appOps);
306     }
307 
308     /**
309      * Enter a {@link PermissionContext} where the given app op are granted to the test
310      * app.
311      *
312      * <p>The app op will only guarantee to be granted for calls made by the test app.
313      *
314      * <p>If the app op cannot be granted an exception will be thrown.
315      *
316      * <p>The app op will only be granted on versions between the min and max (inclusive)
317      *
318      * <p>Recommended usage:
319      * {@code
320      *
321      * try (PermissionContext p = testApp.permissions()
322      *     .withAppOpOnVersionBetween(R, S, APP_OP1, APP_OP2) {
323      * // Code which needs the app op goes here
324      * }
325      * }
326      */
327     @Override
withAppOpOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... appOps)328     public TestAppPermissionContext withAppOpOnVersionBetween(
329             int minSdkVersion, int maxSdkVersion, String... appOps) {
330         TestAppPermissionContext context =
331                 new TestAppPermissionContext(this);
332         mPermissionContexts.add(context);
333         context.withAppOpOnVersionBetween(minSdkVersion, maxSdkVersion, appOps);
334 
335         return context;
336     }
337 
338     /**
339      * Enter a {@link PermissionContext} where the given app op are not granted to the test
340      * app.
341      *
342      * <p>The app op will only guarantee to not be granted for calls made by the test app.
343      *
344      * <p>If the app op cannot be denied an exception will be thrown.
345      *
346      * <p>Recommended usage:
347      * {@code
348      *
349      * try (PermissionContext p = testApp.permissions()
350      *     .withoutAppOp(APP_OP1, APP_OP2) {
351      * // Code which needs to not have the app op goes here
352      * }
353      * }
354      */
355     @Override
withoutAppOp(String... appOps)356     public TestAppPermissionContext withoutAppOp(String... appOps) {
357         TestAppPermissionContext context = new TestAppPermissionContext(this);
358         mPermissionContexts.add(context);
359         context.withoutAppOp(appOps);
360 
361         return context;
362     }
363 
applyPermissions()364     void applyPermissions() {
365         Set<String> grantedPermissions = new HashSet<>();
366         Set<String> deniedPermissions = new HashSet<>();
367         Set<String> grantedAppOps = new HashSet<>();
368         Set<String> deniedAppOps = new HashSet<>();
369         synchronized (mPermissionContexts) {
370             for (TestAppPermissionContext permissionContext : mPermissionContexts) {
371                 for (String permission : permissionContext.grantedPermissions()) {
372                     grantedPermissions.add(permission);
373                     deniedPermissions.remove(permission);
374                 }
375 
376                 for (String permission : permissionContext.deniedPermissions()) {
377                     grantedPermissions.remove(permission);
378                     deniedPermissions.add(permission);
379                 }
380 
381                 for (String appOp : permissionContext.grantedAppOps()) {
382                     grantedAppOps.add(appOp);
383                     deniedAppOps.remove(appOp);
384                 }
385 
386                 for (String appOp : permissionContext.deniedAppOps()) {
387                     grantedAppOps.remove(appOp);
388                     deniedAppOps.add(appOp);
389                 }
390             }
391         }
392 
393         for (String permission : grantedPermissions) {
394             mTestAppInstance.testApp().pkg().grantPermission(mTestAppInstance.user(), permission);
395         }
396         for (String permission : deniedPermissions) {
397             mTestAppInstance.testApp().pkg().denyPermission(mTestAppInstance.user(), permission);
398         }
399         for (String appOp : grantedAppOps) {
400             mTestAppInstance.testApp().pkg().appOps(mTestAppInstance.user()).set(
401                     appOp, AppOpsMode.ALLOWED);
402         }
403         for (String appOp : deniedAppOps) {
404             mTestAppInstance.testApp().pkg().appOps().set(appOp, AppOpsMode.IGNORED);
405         }
406     }
407 
undoPermission(TestAppPermissionContext permissionContext)408     void undoPermission(TestAppPermissionContext permissionContext) {
409         mPermissionContexts.remove(permissionContext);
410         applyPermissions();
411     }
412 
clearPermissions()413     void clearPermissions() {
414         mPermissionContexts.clear();
415         applyPermissions();
416     }
417 }
418