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