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.permission;
18 
19 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
20 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
21 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
22 import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED;
23 import static android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM;
24 
25 import static com.android.internal.util.Preconditions.checkArgument;
26 import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
27 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
28 import static com.android.internal.util.Preconditions.checkFlagsArgument;
29 import static com.android.internal.util.Preconditions.checkNotNull;
30 import static com.android.internal.util.Preconditions.checkStringNotEmpty;
31 
32 import android.Manifest;
33 import android.annotation.BinderThread;
34 import android.annotation.NonNull;
35 import android.annotation.SystemApi;
36 import android.app.Service;
37 import android.app.admin.DevicePolicyManager.PermissionGrantState;
38 import android.content.Intent;
39 import android.content.pm.PackageInfo;
40 import android.content.pm.PackageManager;
41 import android.os.Bundle;
42 import android.os.IBinder;
43 import android.os.ParcelFileDescriptor;
44 import android.os.RemoteCallback;
45 import android.os.UserHandle;
46 import android.permission.PermissionControllerManager.CountPermissionAppsFlag;
47 import android.util.ArrayMap;
48 import android.util.Log;
49 
50 import com.android.internal.util.Preconditions;
51 
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.OutputStream;
55 import java.util.ArrayList;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.function.Consumer;
60 import java.util.function.IntConsumer;
61 
62 /**
63  * This service is meant to be implemented by the app controlling permissions.
64  *
65  * @see PermissionControllerManager
66  *
67  * @hide
68  */
69 @SystemApi
70 public abstract class PermissionControllerService extends Service {
71     private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();
72 
73     /**
74      * The {@link Intent} action that must be declared as handled by a service
75      * in its manifest for the system to recognize it as a runtime permission
76      * presenter service.
77      */
78     public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
79 
80     /**
81      * Revoke a set of runtime permissions for various apps.
82      *
83      * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>}
84      * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
85      * @param reason Why the permission should be revoked
86      * @param callerPackageName The package name of the calling app
87      * @param callback Callback waiting for the actually removed permissions as
88      * {@code Map<packageName, List<permission>>}
89      */
90     @BinderThread
onRevokeRuntimePermissions( @onNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName, @NonNull Consumer<Map<String, List<String>>> callback)91     public abstract void onRevokeRuntimePermissions(
92             @NonNull Map<String, List<String>> requests, boolean doDryRun,
93             @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName,
94             @NonNull Consumer<Map<String, List<String>>> callback);
95 
96     /**
97      * Create a backup of the runtime permissions.
98      *
99      * @param user The user to back up
100      * @param backup The stream to write the backup to
101      * @param callback Callback waiting for operation to be complete
102      */
103     @BinderThread
onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup, @NonNull Runnable callback)104     public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
105             @NonNull OutputStream backup, @NonNull Runnable callback);
106 
107     /**
108      * Restore a backup of the runtime permissions.
109      *
110      * <p>If an app mentioned in the backup is not installed the state should be saved to later
111      * be restored via {@link #onRestoreDelayedRuntimePermissionsBackup}.
112      *
113      * @param user The user to restore
114      * @param backup The stream to read the backup from
115      * @param callback Callback waiting for operation to be complete
116      */
117     @BinderThread
onRestoreRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup, @NonNull Runnable callback)118     public abstract void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user,
119             @NonNull InputStream backup, @NonNull Runnable callback);
120 
121     /**
122      * Restore the permission state of an app that was provided in
123      * {@link #onRestoreRuntimePermissionsBackup} but could not be restored back then.
124      *
125      * @param packageName The app to restore
126      * @param user The user to restore
127      * @param callback Callback waiting for whether there is still delayed backup left
128      */
129     @BinderThread
onRestoreDelayedRuntimePermissionsBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull Consumer<Boolean> callback)130     public abstract void onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName,
131             @NonNull UserHandle user, @NonNull Consumer<Boolean> callback);
132 
133     /**
134      * Gets the runtime permissions for an app.
135      *
136      * @param packageName The package for which to query.
137      * @param callback Callback waiting for the descriptions of the runtime permissions of the app
138      */
139     @BinderThread
onGetAppPermissions(@onNull String packageName, @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback)140     public abstract void onGetAppPermissions(@NonNull String packageName,
141             @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback);
142 
143     /**
144      * Revokes the permission {@code permissionName} for app {@code packageName}
145      *
146      * @param packageName The package for which to revoke
147      * @param permissionName The permission to revoke
148      * @param callback Callback waiting for operation to be complete
149      */
150     @BinderThread
onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName, @NonNull Runnable callback)151     public abstract void onRevokeRuntimePermission(@NonNull String packageName,
152             @NonNull String permissionName, @NonNull Runnable callback);
153 
154     /**
155      * Count how many apps have one of a set of permissions.
156      *
157      * @param permissionNames The permissions the app might have
158      * @param flags Modify which apps to count. By default all non-system apps that request a
159      *              permission are counted
160      * @param callback Callback waiting for the number of apps that have one of the permissions
161      */
162     @BinderThread
onCountPermissionApps(@onNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull IntConsumer callback)163     public abstract void onCountPermissionApps(@NonNull List<String> permissionNames,
164             @CountPermissionAppsFlag int flags, @NonNull IntConsumer callback);
165 
166     /**
167      * Count how many apps have used permissions.
168      *
169      * @param countSystem Also count system apps
170      * @param numMillis The number of milliseconds in the past to check for uses
171      * @param callback Callback waiting for the descriptions of the users of permissions
172      */
173     @BinderThread
onGetPermissionUsages(boolean countSystem, long numMillis, @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback)174     public abstract void onGetPermissionUsages(boolean countSystem, long numMillis,
175             @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback);
176 
177     /**
178      * Grant or upgrade runtime permissions. The upgrade could be performed
179      * based on whether the device upgraded, whether the permission database
180      * version is old, or because the permission policy changed.
181      *
182      * @param callback Callback waiting for operation to be complete
183      *
184      * @see PackageManager#isDeviceUpgrading()
185      * @see PermissionManager#getRuntimePermissionsVersion()
186      * @see PermissionManager#setRuntimePermissionsVersion(int)
187      */
188     @BinderThread
onGrantOrUpgradeDefaultRuntimePermissions(@onNull Runnable callback)189     public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback);
190 
191     /**
192      * Set the runtime permission state from a device admin.
193      *
194      * @param callerPackageName The package name of the admin requesting the change
195      * @param packageName Package the permission belongs to
196      * @param permission Permission to change
197      * @param grantState State to set the permission into
198      * @param callback Callback waiting for whether the state could be set or not
199      */
200     @BinderThread
onSetRuntimePermissionGrantStateByDeviceAdmin( @onNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState, @NonNull Consumer<Boolean> callback)201     public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(
202             @NonNull String callerPackageName, @NonNull String packageName,
203             @NonNull String permission, @PermissionGrantState int grantState,
204             @NonNull Consumer<Boolean> callback);
205 
206     @Override
onBind(Intent intent)207     public final @NonNull IBinder onBind(Intent intent) {
208         return new IPermissionController.Stub() {
209             @Override
210             public void revokeRuntimePermissions(
211                     Bundle bundleizedRequest, boolean doDryRun, int reason,
212                     String callerPackageName, RemoteCallback callback) {
213                 checkNotNull(bundleizedRequest, "bundleizedRequest");
214                 checkNotNull(callerPackageName);
215                 checkNotNull(callback);
216 
217                 Map<String, List<String>> request = new ArrayMap<>();
218                 for (String packageName : bundleizedRequest.keySet()) {
219                     Preconditions.checkNotNull(packageName);
220 
221                     ArrayList<String> permissions =
222                             bundleizedRequest.getStringArrayList(packageName);
223                     Preconditions.checkCollectionElementsNotNull(permissions, "permissions");
224 
225                     request.put(packageName, permissions);
226                 }
227 
228                 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
229 
230                 // Verify callerPackageName
231                 try {
232                     PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0);
233                     checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid);
234                 } catch (PackageManager.NameNotFoundException e) {
235                     throw new RuntimeException(e);
236                 }
237 
238                 onRevokeRuntimePermissions(request,
239                         doDryRun, reason, callerPackageName, revoked -> {
240                             checkNotNull(revoked);
241                             Bundle bundledizedRevoked = new Bundle();
242                             for (Map.Entry<String, List<String>> appRevocation :
243                                     revoked.entrySet()) {
244                                 checkNotNull(appRevocation.getKey());
245                                 checkCollectionElementsNotNull(appRevocation.getValue(),
246                                         "permissions");
247 
248                                 bundledizedRevoked.putStringArrayList(appRevocation.getKey(),
249                                         new ArrayList<>(appRevocation.getValue()));
250                             }
251 
252                             Bundle result = new Bundle();
253                             result.putBundle(PermissionControllerManager.KEY_RESULT,
254                                     bundledizedRevoked);
255                             callback.sendResult(result);
256                         });
257             }
258 
259             @Override
260             public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
261                 checkNotNull(user);
262                 checkNotNull(pipe);
263 
264                 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
265 
266                 try (OutputStream backup = new ParcelFileDescriptor.AutoCloseOutputStream(pipe)) {
267                     CountDownLatch latch = new CountDownLatch(1);
268                     onGetRuntimePermissionsBackup(user, backup, latch::countDown);
269                     latch.await();
270                 } catch (IOException e) {
271                     Log.e(LOG_TAG, "Could not open pipe to write backup to", e);
272                 } catch (InterruptedException e) {
273                     Log.e(LOG_TAG, "getRuntimePermissionBackup timed out", e);
274                 }
275             }
276 
277             @Override
278             public void restoreRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
279                 checkNotNull(user);
280                 checkNotNull(pipe);
281 
282                 enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
283 
284                 try (InputStream backup = new ParcelFileDescriptor.AutoCloseInputStream(pipe)) {
285                     CountDownLatch latch = new CountDownLatch(1);
286                     onRestoreRuntimePermissionsBackup(user, backup, latch::countDown);
287                     latch.await();
288                 } catch (IOException e) {
289                     Log.e(LOG_TAG, "Could not open pipe to read backup from", e);
290                 } catch (InterruptedException e) {
291                     Log.e(LOG_TAG, "restoreRuntimePermissionBackup timed out", e);
292                 }
293             }
294 
295             @Override
296             public void restoreDelayedRuntimePermissionBackup(String packageName, UserHandle user,
297                     RemoteCallback callback) {
298                 checkNotNull(packageName);
299                 checkNotNull(user);
300                 checkNotNull(callback);
301 
302                 enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
303 
304                 onRestoreDelayedRuntimePermissionsBackup(packageName, user,
305                         hasMoreBackup -> {
306                             Bundle result = new Bundle();
307                             result.putBoolean(PermissionControllerManager.KEY_RESULT,
308                                     hasMoreBackup);
309                             callback.sendResult(result);
310                         });
311             }
312 
313             @Override
314             public void getAppPermissions(String packageName, RemoteCallback callback) {
315                 checkNotNull(packageName, "packageName");
316                 checkNotNull(callback, "callback");
317 
318                 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
319 
320                 onGetAppPermissions(packageName,
321                         permissions -> {
322                             if (permissions != null && !permissions.isEmpty()) {
323                                 Bundle result = new Bundle();
324                                 result.putParcelableList(PermissionControllerManager.KEY_RESULT,
325                                         permissions);
326                                 callback.sendResult(result);
327                             } else {
328                                 callback.sendResult(null);
329                             }
330                         });
331             }
332 
333             @Override
334             public void revokeRuntimePermission(String packageName, String permissionName) {
335                 checkNotNull(packageName, "packageName");
336                 checkNotNull(permissionName, "permissionName");
337 
338                 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
339 
340                 CountDownLatch latch = new CountDownLatch(1);
341                 PermissionControllerService.this.onRevokeRuntimePermission(packageName,
342                         permissionName, latch::countDown);
343                 try {
344                     latch.await();
345                 } catch (InterruptedException e) {
346                     Log.e(LOG_TAG, "revokeRuntimePermission timed out", e);
347                 }
348             }
349 
350             @Override
351             public void countPermissionApps(List<String> permissionNames, int flags,
352                     RemoteCallback callback) {
353                 checkCollectionElementsNotNull(permissionNames, "permissionNames");
354                 checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED);
355                 checkNotNull(callback, "callback");
356 
357                 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
358 
359                 onCountPermissionApps(permissionNames, flags, numApps -> {
360                     Bundle result = new Bundle();
361                     result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
362                     callback.sendResult(result);
363                 });
364             }
365 
366             @Override
367             public void getPermissionUsages(boolean countSystem, long numMillis,
368                     RemoteCallback callback) {
369                 checkArgumentNonnegative(numMillis);
370                 checkNotNull(callback, "callback");
371 
372                 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
373 
374                 onGetPermissionUsages(countSystem, numMillis, users -> {
375                     if (users != null && !users.isEmpty()) {
376                         Bundle result = new Bundle();
377                         result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
378                         callback.sendResult(result);
379                     } else {
380                         callback.sendResult(null);
381                     }
382                 });
383             }
384 
385             @Override
386             public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName,
387                     String packageName, String permission, int grantState,
388                     RemoteCallback callback) {
389                 checkStringNotEmpty(callerPackageName);
390                 checkStringNotEmpty(packageName);
391                 checkStringNotEmpty(permission);
392                 checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
393                         || grantState == PERMISSION_GRANT_STATE_DENIED
394                         || grantState == PERMISSION_GRANT_STATE_DEFAULT);
395                 checkNotNull(callback);
396 
397                 if (grantState == PERMISSION_GRANT_STATE_DENIED) {
398                     enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
399                 }
400 
401                 if (grantState == PERMISSION_GRANT_STATE_DENIED) {
402                     enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
403                 }
404 
405                 enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
406                         null);
407 
408                 onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName,
409                         packageName, permission, grantState, wasSet -> {
410                             Bundle result = new Bundle();
411                             result.putBoolean(PermissionControllerManager.KEY_RESULT, wasSet);
412                             callback.sendResult(result);
413                         });
414             }
415 
416             @Override
417             public void grantOrUpgradeDefaultRuntimePermissions(@NonNull RemoteCallback callback) {
418                 checkNotNull(callback, "callback");
419 
420                 enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
421                         null);
422 
423                 onGrantOrUpgradeDefaultRuntimePermissions(() -> callback.sendResult(Bundle.EMPTY));
424             }
425         };
426     }
427 }
428