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.PermissionControllerService.SERVICE_INTERFACE;
23 
24 import static com.android.internal.util.Preconditions.checkArgument;
25 import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
26 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
27 import static com.android.internal.util.Preconditions.checkFlagsArgument;
28 import static com.android.internal.util.Preconditions.checkNotNull;
29 import static com.android.internal.util.Preconditions.checkStringNotEmpty;
30 
31 import android.Manifest;
32 import android.annotation.CallbackExecutor;
33 import android.annotation.IntDef;
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.annotation.RequiresPermission;
37 import android.annotation.SystemApi;
38 import android.annotation.SystemService;
39 import android.annotation.TestApi;
40 import android.app.ActivityThread;
41 import android.app.admin.DevicePolicyManager.PermissionGrantState;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.pm.PackageManager;
45 import android.content.pm.ResolveInfo;
46 import android.os.Binder;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.Process;
50 import android.os.UserHandle;
51 import android.util.ArrayMap;
52 import android.util.Log;
53 import android.util.Pair;
54 
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.infra.AndroidFuture;
57 import com.android.internal.infra.RemoteStream;
58 import com.android.internal.infra.ServiceConnector;
59 import com.android.internal.util.CollectionUtils;
60 
61 import libcore.util.EmptyArray;
62 
63 import java.io.FileDescriptor;
64 import java.lang.annotation.Retention;
65 import java.lang.annotation.RetentionPolicy;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.Collections;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.concurrent.Executor;
72 import java.util.concurrent.TimeUnit;
73 import java.util.function.Consumer;
74 
75 /**
76  * Interface for communicating with the permission controller.
77  *
78  * @hide
79  */
80 @TestApi
81 @SystemApi
82 @SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
83 public final class PermissionControllerManager {
84     private static final String TAG = PermissionControllerManager.class.getSimpleName();
85 
86     private static final long REQUEST_TIMEOUT_MILLIS = 60000;
87     private static final long UNBIND_TIMEOUT_MILLIS = 10000;
88     private static final int CHUNK_SIZE = 4 * 1024;
89 
90     private static final Object sLock = new Object();
91 
92     /**
93      * Global remote services (per user) used by all {@link PermissionControllerManager managers}
94      */
95     @GuardedBy("sLock")
96     private static ArrayMap<Pair<Integer, Thread>, ServiceConnector<IPermissionController>>
97             sRemoteServices = new ArrayMap<>(1);
98 
99     /** @hide */
100     @IntDef(prefix = { "REASON_" }, value = {
101             REASON_MALWARE,
102             REASON_INSTALLER_POLICY_VIOLATION,
103     })
104     @Retention(RetentionPolicy.SOURCE)
105     public @interface Reason {}
106 
107     /** The permissions are revoked because the apps holding the permissions are malware */
108     public static final int REASON_MALWARE = 1;
109 
110     /**
111      * The permissions are revoked because the apps holding the permissions violate a policy of the
112      * app that installed it.
113      *
114      * <p>If this reason is used only permissions of apps that are installed by the caller of the
115      * API can be revoked.
116      */
117     public static final int REASON_INSTALLER_POLICY_VIOLATION = 2;
118 
119     /** @hide */
120     @IntDef(prefix = { "COUNT_" }, value = {
121             COUNT_ONLY_WHEN_GRANTED,
122             COUNT_WHEN_SYSTEM,
123     }, flag = true)
124     @Retention(RetentionPolicy.SOURCE)
125     public @interface CountPermissionAppsFlag {}
126 
127     /** Count an app only if the permission is granted to the app. */
128     public static final int COUNT_ONLY_WHEN_GRANTED = 1;
129 
130     /** Count and app even if it is a system app. */
131     public static final int COUNT_WHEN_SYSTEM = 2;
132 
133     /**
134      * Callback for delivering the result of {@link #revokeRuntimePermissions}.
135      */
136     public abstract static class OnRevokeRuntimePermissionsCallback {
137         /**
138          * The result for {@link #revokeRuntimePermissions}.
139          *
140          * @param revoked The actually revoked permissions as
141          *                {@code Map<packageName, List<permission>>}
142          */
onRevokeRuntimePermissions(@onNull Map<String, List<String>> revoked)143         public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked);
144     }
145 
146     /**
147      * Callback for delivering the result of {@link #getAppPermissions}.
148      *
149      * @hide
150      */
151     @TestApi
152     public interface OnGetAppPermissionResultCallback {
153         /**
154          * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
155          * Handler)}.
156          *
157          * @param permissions The permissions list.
158          */
onGetAppPermissions(@onNull List<RuntimePermissionPresentationInfo> permissions)159         void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
160     }
161 
162     /**
163      * Callback for delivering the result of {@link #countPermissionApps}.
164      *
165      * @hide
166      */
167     @TestApi
168     public interface OnCountPermissionAppsResultCallback {
169         /**
170          * The result for {@link #countPermissionApps(List, int,
171          * OnCountPermissionAppsResultCallback, Handler)}.
172          *
173          * @param numApps The number of apps that have one of the permissions
174          */
onCountPermissionApps(int numApps)175         void onCountPermissionApps(int numApps);
176     }
177 
178     /**
179      * Callback for delivering the result of {@link #getPermissionUsages}.
180      *
181      * @hide
182      */
183     public interface OnPermissionUsageResultCallback {
184         /**
185          * The result for {@link #getPermissionUsages}.
186          *
187          * @param users The users list.
188          */
onPermissionUsageResult(@onNull List<RuntimePermissionUsageInfo> users)189         void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
190     }
191 
192     private final @NonNull Context mContext;
193     private final @NonNull ServiceConnector<IPermissionController> mRemoteService;
194     private final @NonNull Handler mHandler;
195 
196     /**
197      * Create a new {@link PermissionControllerManager}.
198      *
199      * @param context to create the manager for
200      * @param handler handler to schedule work
201      *
202      * @hide
203      */
PermissionControllerManager(@onNull Context context, @NonNull Handler handler)204     public PermissionControllerManager(@NonNull Context context, @NonNull Handler handler) {
205         synchronized (sLock) {
206             Pair<Integer, Thread> key = new Pair<>(context.getUserId(),
207                     handler.getLooper().getThread());
208             ServiceConnector<IPermissionController> remoteService = sRemoteServices.get(key);
209             if (remoteService == null) {
210                 Intent intent = new Intent(SERVICE_INTERFACE);
211                 intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
212                 ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
213                 remoteService = new ServiceConnector.Impl<IPermissionController>(
214                         ActivityThread.currentApplication() /* context */,
215                         new Intent(SERVICE_INTERFACE)
216                                 .setComponent(serviceInfo.getComponentInfo().getComponentName()),
217                         0 /* bindingFlags */, context.getUserId(),
218                         IPermissionController.Stub::asInterface) {
219 
220                     @Override
221                     protected Handler getJobHandler() {
222                         return handler;
223                     }
224 
225                     @Override
226                     protected long getRequestTimeoutMs() {
227                         return REQUEST_TIMEOUT_MILLIS;
228                     }
229 
230                     @Override
231                     protected long getAutoDisconnectTimeoutMs() {
232                         return UNBIND_TIMEOUT_MILLIS;
233                     }
234                 };
235                 sRemoteServices.put(key, remoteService);
236             }
237 
238             mRemoteService = remoteService;
239         }
240 
241         mContext = context;
242         mHandler = handler;
243     }
244 
245     /**
246      * Throw a {@link SecurityException} if not at least one of the permissions is granted.
247      *
248      * @param requiredPermissions A list of permissions. Any of of them if sufficient to pass the
249      *                            check
250      */
enforceSomePermissionsGrantedToSelf(@onNull String... requiredPermissions)251     private void enforceSomePermissionsGrantedToSelf(@NonNull String... requiredPermissions) {
252         for (String requiredPermission : requiredPermissions) {
253             if (mContext.checkSelfPermission(requiredPermission)
254                     == PackageManager.PERMISSION_GRANTED) {
255                 return;
256             }
257         }
258 
259         throw new SecurityException("At lest one of the following permissions is required: "
260                 + Arrays.toString(requiredPermissions));
261     }
262 
263     /**
264      * Revoke a set of runtime permissions for various apps.
265      *
266      * @param request The permissions to revoke as {@code Map<packageName, List<permission>>}
267      * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
268      * @param reason Why the permission should be revoked
269      * @param executor Executor on which to invoke the callback
270      * @param callback Callback to receive the result
271      */
272     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
revokeRuntimePermissions(@onNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback)273     public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request,
274             boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor,
275             @NonNull OnRevokeRuntimePermissionsCallback callback) {
276         // Check input to fail immediately instead of inside the async request
277         checkNotNull(executor);
278         checkNotNull(callback);
279         checkNotNull(request);
280         for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
281             checkNotNull(appRequest.getKey());
282             checkCollectionElementsNotNull(appRequest.getValue(), "permissions");
283         }
284 
285         // Check required permission to fail immediately instead of inside the oneway binder call
286         enforceSomePermissionsGrantedToSelf(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
287 
288         mRemoteService.postAsync(service -> {
289             Bundle bundledizedRequest = new Bundle();
290             for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
291                 bundledizedRequest.putStringArrayList(appRequest.getKey(),
292                         new ArrayList<>(appRequest.getValue()));
293             }
294 
295             AndroidFuture<Map<String, List<String>>> revokeRuntimePermissionsResult =
296                     new AndroidFuture<>();
297             service.revokeRuntimePermissions(bundledizedRequest, doDryRun, reason,
298                     mContext.getPackageName(),
299                     revokeRuntimePermissionsResult);
300             return revokeRuntimePermissionsResult;
301         }).whenCompleteAsync((revoked, err) -> {
302             long token = Binder.clearCallingIdentity();
303             try {
304                 if (err != null) {
305                     Log.e(TAG, "Failure when revoking runtime permissions " + revoked, err);
306                     callback.onRevokeRuntimePermissions(Collections.emptyMap());
307                 } else {
308                     callback.onRevokeRuntimePermissions(revoked);
309                 }
310             } finally {
311                 Binder.restoreCallingIdentity(token);
312             }
313         }, executor);
314     }
315 
316     /**
317      * Set the runtime permission state from a device admin.
318      *
319      * @param callerPackageName The package name of the admin requesting the change
320      * @param packageName Package the permission belongs to
321      * @param permission Permission to change
322      * @param grantState State to set the permission into
323      * @param executor Executor to run the {@code callback} on
324      * @param callback The callback
325      *
326      * @hide
327      */
328     @RequiresPermission(allOf = {Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
329             Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
330             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY},
331             conditional = true)
setRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)332     public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
333             @NonNull String packageName, @NonNull String permission,
334             @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor,
335             @NonNull Consumer<Boolean> callback) {
336         checkStringNotEmpty(callerPackageName);
337         checkStringNotEmpty(packageName);
338         checkStringNotEmpty(permission);
339         checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
340                 || grantState == PERMISSION_GRANT_STATE_DENIED
341                 || grantState == PERMISSION_GRANT_STATE_DEFAULT);
342         checkNotNull(executor);
343         checkNotNull(callback);
344 
345         mRemoteService.postAsync(service -> {
346             AndroidFuture<Boolean> setRuntimePermissionGrantStateResult = new AndroidFuture<>();
347             service.setRuntimePermissionGrantStateByDeviceAdmin(
348                     callerPackageName, packageName, permission, grantState,
349                     setRuntimePermissionGrantStateResult);
350             return setRuntimePermissionGrantStateResult;
351         }).whenCompleteAsync((setRuntimePermissionGrantStateResult, err) -> {
352             long token = Binder.clearCallingIdentity();
353             try {
354                 if (err != null) {
355                     Log.e(TAG, "Error setting permissions state for device admin " + packageName,
356                             err);
357                     callback.accept(false);
358                 } else {
359                     callback.accept(Boolean.TRUE.equals(setRuntimePermissionGrantStateResult));
360                 }
361             } finally {
362                 Binder.restoreCallingIdentity(token);
363             }
364         }, executor);
365     }
366 
367     /**
368      * Create a backup of the runtime permissions.
369      *
370      * @param user The user to be backed up
371      * @param executor Executor on which to invoke the callback
372      * @param callback Callback to receive the result. The resulting backup-file is opaque and no
373      *                 guarantees are made other than that the file can be send to
374      *                 {@link #restoreRuntimePermissionBackup} in this and future versions of
375      *                 Android.
376      */
377     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getRuntimePermissionBackup(@onNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<byte[]> callback)378     public void getRuntimePermissionBackup(@NonNull UserHandle user,
379             @NonNull @CallbackExecutor Executor executor,
380             @NonNull Consumer<byte[]> callback) {
381         checkNotNull(user);
382         checkNotNull(executor);
383         checkNotNull(callback);
384 
385         // Check required permission to fail immediately instead of inside the oneway binder call
386         enforceSomePermissionsGrantedToSelf(Manifest.permission.GET_RUNTIME_PERMISSIONS);
387 
388         mRemoteService.postAsync(service -> RemoteStream.receiveBytes(remotePipe -> {
389             service.getRuntimePermissionBackup(user, remotePipe);
390         })).whenCompleteAsync((bytes, err) -> {
391             if (err != null) {
392                 Log.e(TAG, "Error getting permission backup", err);
393                 callback.accept(EmptyArray.BYTE);
394             } else {
395                 callback.accept(bytes);
396             }
397         }, executor);
398     }
399 
400     /**
401      * Restore a {@link #getRuntimePermissionBackup backup-file} of the runtime permissions.
402      *
403      * <p>This might leave some part of the backup-file unapplied if an package mentioned in the
404      * backup-file is not yet installed. It is required that
405      * {@link #applyStagedRuntimePermissionBackup} is called after any package is installed to
406      * apply the rest of the backup-file.
407      *
408      * @param backup the backup-file to restore. The backup is sent asynchronously, hence it should
409      *               not be modified after calling this method.
410      * @param user The user to be restore
411      */
412     @RequiresPermission(anyOf = {
413             Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
414             Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
415     })
stageAndApplyRuntimePermissionsBackup(@onNull byte[] backup, @NonNull UserHandle user)416     public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[] backup,
417             @NonNull UserHandle user) {
418         checkNotNull(backup);
419         checkNotNull(user);
420 
421         // Check required permission to fail immediately instead of inside the oneway binder call
422         enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
423                 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
424 
425         mRemoteService.postAsync(service -> RemoteStream.sendBytes(remotePipe -> {
426             service.stageAndApplyRuntimePermissionsBackup(user, remotePipe);
427         }, backup))
428                 .whenComplete((nullResult, err) -> {
429                     if (err != null) {
430                         Log.e(TAG, "Error sending permission backup", err);
431                     }
432                 });
433     }
434 
435     /**
436      * Restore unapplied parts of a {@link #stageAndApplyRuntimePermissionsBackup previously staged}
437      * backup-file of the runtime permissions.
438      *
439      * <p>This should be called every time after a package is installed until the callback
440      * reports that there is no more unapplied backup left.
441      *
442      * @param packageName The package that is ready to have it's permissions restored.
443      * @param user The user the package belongs to
444      * @param executor Executor to execute the callback on
445      * @param callback Is called with {@code true} iff there is still more unapplied backup left
446      */
447     @RequiresPermission(anyOf = {
448             Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
449             Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
450     })
applyStagedRuntimePermissionBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)451     public void applyStagedRuntimePermissionBackup(@NonNull String packageName,
452             @NonNull UserHandle user,
453             @NonNull @CallbackExecutor Executor executor,
454             @NonNull Consumer<Boolean> callback) {
455         checkNotNull(packageName);
456         checkNotNull(user);
457         checkNotNull(executor);
458         checkNotNull(callback);
459 
460         // Check required permission to fail immediately instead of inside the oneway binder call
461         enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
462                 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
463 
464         mRemoteService.postAsync(service -> {
465             AndroidFuture<Boolean> applyStagedRuntimePermissionBackupResult =
466                     new AndroidFuture<>();
467             service.applyStagedRuntimePermissionBackup(packageName, user,
468                     applyStagedRuntimePermissionBackupResult);
469             return applyStagedRuntimePermissionBackupResult;
470         }).whenCompleteAsync((applyStagedRuntimePermissionBackupResult, err) -> {
471             long token = Binder.clearCallingIdentity();
472             try {
473                 if (err != null) {
474                     Log.e(TAG, "Error restoring delayed permissions for " + packageName, err);
475                     callback.accept(true);
476                 } else {
477                     callback.accept(
478                             Boolean.TRUE.equals(applyStagedRuntimePermissionBackupResult));
479                 }
480             } finally {
481                 Binder.restoreCallingIdentity(token);
482             }
483         }, executor);
484     }
485 
486     /**
487      * Dump permission controller state.
488      *
489      * @hide
490      */
dump(@onNull FileDescriptor fd, @Nullable String[] args)491     public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) {
492         try {
493             mRemoteService.post(service -> service.asBinder().dump(fd, args))
494                     .get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
495         } catch (Exception e) {
496             Log.e(TAG, "Could not get dump", e);
497         }
498     }
499 
500     /**
501      * Gets the runtime permissions for an app.
502      *
503      * @param packageName The package for which to query.
504      * @param callback Callback to receive the result.
505      * @param handler Handler on which to invoke the callback.
506      *
507      * @hide
508      */
509     @TestApi
510     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getAppPermissions(@onNull String packageName, @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler)511     public void getAppPermissions(@NonNull String packageName,
512             @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
513         checkNotNull(packageName);
514         checkNotNull(callback);
515         Handler finalHandler = handler != null ? handler : mHandler;
516 
517         mRemoteService.postAsync(service -> {
518             AndroidFuture<List<RuntimePermissionPresentationInfo>> getAppPermissionsResult =
519                     new AndroidFuture<>();
520             service.getAppPermissions(packageName, getAppPermissionsResult);
521             return getAppPermissionsResult;
522         }).whenComplete((getAppPermissionsResult, err) -> finalHandler.post(() -> {
523             if (err != null) {
524                 Log.e(TAG, "Error getting app permission", err);
525                 callback.onGetAppPermissions(Collections.emptyList());
526             } else {
527                 callback.onGetAppPermissions(CollectionUtils.emptyIfNull(getAppPermissionsResult));
528             }
529         }));
530     }
531 
532     /**
533      * Revoke the permission {@code permissionName} for app {@code packageName}
534      *
535      * @param packageName The package for which to revoke
536      * @param permissionName The permission to revoke
537      *
538      * @hide
539      */
540     @TestApi
541     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
revokeRuntimePermission(@onNull String packageName, @NonNull String permissionName)542     public void revokeRuntimePermission(@NonNull String packageName,
543             @NonNull String permissionName) {
544         checkNotNull(packageName);
545         checkNotNull(permissionName);
546 
547         mRemoteService.run(service -> service.revokeRuntimePermission(packageName, permissionName));
548     }
549 
550     /**
551      * Count how many apps have one of a set of permissions.
552      *
553      * @param permissionNames The permissions the app might have
554      * @param flags Modify which apps to count. By default all non-system apps that request a
555      *              permission are counted
556      * @param callback Callback to receive the result
557      * @param handler Handler on which to invoke the callback
558      *
559      * @hide
560      */
561     @TestApi
562     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
countPermissionApps(@onNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler)563     public void countPermissionApps(@NonNull List<String> permissionNames,
564             @CountPermissionAppsFlag int flags,
565             @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
566         checkCollectionElementsNotNull(permissionNames, "permissionNames");
567         checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED);
568         checkNotNull(callback);
569         Handler finalHandler = handler != null ? handler : mHandler;
570 
571         mRemoteService.postAsync(service -> {
572             AndroidFuture<Integer> countPermissionAppsResult = new AndroidFuture<>();
573             service.countPermissionApps(permissionNames, flags, countPermissionAppsResult);
574             return countPermissionAppsResult;
575         }).whenComplete((countPermissionAppsResult, err) -> finalHandler.post(() -> {
576             if (err != null) {
577                 Log.e(TAG, "Error counting permission apps", err);
578                 callback.onCountPermissionApps(0);
579             } else {
580                 callback.onCountPermissionApps(countPermissionAppsResult);
581             }
582         }));
583     }
584 
585     /**
586      * Count how many apps have used permissions.
587      *
588      * @param countSystem Also count system apps
589      * @param numMillis The number of milliseconds in the past to check for uses
590      * @param executor Executor on which to invoke the callback
591      * @param callback Callback to receive the result
592      *
593      * @hide
594      */
595     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getPermissionUsages(boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor, @NonNull OnPermissionUsageResultCallback callback)596     public void getPermissionUsages(boolean countSystem, long numMillis,
597             @NonNull @CallbackExecutor Executor executor,
598             @NonNull OnPermissionUsageResultCallback callback) {
599         checkArgumentNonnegative(numMillis);
600         checkNotNull(executor);
601         checkNotNull(callback);
602 
603 
604         mRemoteService.postAsync(service -> {
605             AndroidFuture<List<RuntimePermissionUsageInfo>> getPermissionUsagesResult =
606                     new AndroidFuture<>();
607             service.getPermissionUsages(countSystem, numMillis, getPermissionUsagesResult);
608             return getPermissionUsagesResult;
609         }).whenCompleteAsync((getPermissionUsagesResult, err) -> {
610             if (err != null) {
611                 Log.e(TAG, "Error getting permission usages", err);
612                 callback.onPermissionUsageResult(Collections.emptyList());
613             } else {
614                 long token = Binder.clearCallingIdentity();
615                 try {
616                     callback.onPermissionUsageResult(
617                             CollectionUtils.emptyIfNull(getPermissionUsagesResult));
618                 } finally {
619                     Binder.restoreCallingIdentity(token);
620                 }
621             }
622         }, executor);
623     }
624 
625     /**
626      * Grant or upgrade runtime permissions. The upgrade could be performed
627      * based on whether the device upgraded, whether the permission database
628      * version is old, or because the permission policy changed.
629      *
630      * @param executor Executor on which to invoke the callback
631      * @param callback Callback to receive the result
632      *
633      * @hide
634      */
635     @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
grantOrUpgradeDefaultRuntimePermissions( @onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)636     public void grantOrUpgradeDefaultRuntimePermissions(
637             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
638         mRemoteService.postAsync(service -> {
639             AndroidFuture<Boolean> grantOrUpgradeDefaultRuntimePermissionsResult =
640                     new AndroidFuture<>();
641             service.grantOrUpgradeDefaultRuntimePermissions(
642                     grantOrUpgradeDefaultRuntimePermissionsResult);
643             return grantOrUpgradeDefaultRuntimePermissionsResult;
644         }).whenCompleteAsync((grantOrUpgradeDefaultRuntimePermissionsResult, err) -> {
645             if (err != null) {
646                 Log.e(TAG, "Error granting or upgrading runtime permissions", err);
647                 callback.accept(false);
648             } else {
649                 callback.accept(Boolean.TRUE.equals(grantOrUpgradeDefaultRuntimePermissionsResult));
650             }
651         }, executor);
652     }
653 
654     /**
655      * @see PermissionControllerManager#updateUserSensitiveForApp
656      * @hide
657      */
updateUserSensitive()658     public void updateUserSensitive() {
659         updateUserSensitiveForApp(Process.INVALID_UID);
660     }
661 
662     /**
663      * @see PermissionControllerService#onUpdateUserSensitiveForApp
664      * @hide
665      */
updateUserSensitiveForApp(int uid)666     public void updateUserSensitiveForApp(int uid) {
667         mRemoteService.postAsync(service -> {
668             AndroidFuture<Void> future = new AndroidFuture<>();
669             service.updateUserSensitiveForApp(uid, future);
670             return future;
671         }).whenComplete((res, err) -> {
672             if (err != null) {
673                 Log.e(TAG, "Error updating user_sensitive flags for uid " + uid, err);
674             }
675         });
676     }
677 
678     /**
679      * Called when a package that has permissions registered as "one-time" is considered
680      * inactive.
681      *
682      * @param packageName The package which became inactive
683      *
684      * @hide
685      */
686     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
notifyOneTimePermissionSessionTimeout(@onNull String packageName)687     public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) {
688         mRemoteService.run(
689                 service -> service.notifyOneTimePermissionSessionTimeout(packageName));
690     }
691 }
692