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