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 static java.lang.Math.min;
32 
33 import android.Manifest;
34 import android.annotation.CallbackExecutor;
35 import android.annotation.IntDef;
36 import android.annotation.NonNull;
37 import android.annotation.Nullable;
38 import android.annotation.RequiresPermission;
39 import android.annotation.SystemApi;
40 import android.annotation.SystemService;
41 import android.annotation.TestApi;
42 import android.app.ActivityThread;
43 import android.app.admin.DevicePolicyManager.PermissionGrantState;
44 import android.content.ComponentName;
45 import android.content.Context;
46 import android.content.Intent;
47 import android.content.pm.PackageManager;
48 import android.content.pm.ResolveInfo;
49 import android.os.AsyncTask;
50 import android.os.Binder;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.ParcelFileDescriptor;
55 import android.os.RemoteCallback;
56 import android.os.RemoteException;
57 import android.os.UserHandle;
58 import android.util.ArrayMap;
59 import android.util.Log;
60 import android.util.Pair;
61 
62 import com.android.internal.annotations.GuardedBy;
63 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
64 import com.android.internal.infra.AbstractRemoteService;
65 import com.android.internal.util.Preconditions;
66 
67 import libcore.io.IoUtils;
68 
69 import java.io.ByteArrayOutputStream;
70 import java.io.IOException;
71 import java.io.InputStream;
72 import java.io.OutputStream;
73 import java.lang.annotation.Retention;
74 import java.lang.annotation.RetentionPolicy;
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.concurrent.Executor;
80 import java.util.function.Consumer;
81 
82 /**
83  * Interface for communicating with the permission controller.
84  *
85  * @hide
86  */
87 @TestApi
88 @SystemApi
89 @SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
90 public final class PermissionControllerManager {
91     private static final String TAG = PermissionControllerManager.class.getSimpleName();
92 
93     private static final Object sLock = new Object();
94 
95     /**
96      * Global remote services (per user) used by all {@link PermissionControllerManager managers}
97      */
98     @GuardedBy("sLock")
99     private static ArrayMap<Pair<Integer, Thread>, RemoteService> sRemoteServices
100             = new ArrayMap<>(1);
101 
102     /**
103      * The key for retrieving the result from the returned bundle.
104      *
105      * @hide
106      */
107     public static final String KEY_RESULT =
108             "android.permission.PermissionControllerManager.key.result";
109 
110     /** @hide */
111     @IntDef(prefix = { "REASON_" }, value = {
112             REASON_MALWARE,
113             REASON_INSTALLER_POLICY_VIOLATION,
114     })
115     @Retention(RetentionPolicy.SOURCE)
116     public @interface Reason {}
117 
118     /** The permissions are revoked because the apps holding the permissions are malware */
119     public static final int REASON_MALWARE = 1;
120 
121     /**
122      * The permissions are revoked because the apps holding the permissions violate a policy of the
123      * app that installed it.
124      *
125      * <p>If this reason is used only permissions of apps that are installed by the caller of the
126      * API can be revoked.
127      */
128     public static final int REASON_INSTALLER_POLICY_VIOLATION = 2;
129 
130     /** @hide */
131     @IntDef(prefix = { "COUNT_" }, value = {
132             COUNT_ONLY_WHEN_GRANTED,
133             COUNT_WHEN_SYSTEM,
134     }, flag = true)
135     @Retention(RetentionPolicy.SOURCE)
136     public @interface CountPermissionAppsFlag {}
137 
138     /** Count an app only if the permission is granted to the app. */
139     public static final int COUNT_ONLY_WHEN_GRANTED = 1;
140 
141     /** Count and app even if it is a system app. */
142     public static final int COUNT_WHEN_SYSTEM = 2;
143 
144     /**
145      * Callback for delivering the result of {@link #revokeRuntimePermissions}.
146      */
147     public abstract static class OnRevokeRuntimePermissionsCallback {
148         /**
149          * The result for {@link #revokeRuntimePermissions}.
150          *
151          * @param revoked The actually revoked permissions as
152          *                {@code Map<packageName, List<permission>>}
153          */
onRevokeRuntimePermissions(@onNull Map<String, List<String>> revoked)154         public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked);
155     }
156 
157     /**
158      * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
159      *
160      * @hide
161      */
162     public interface OnGetRuntimePermissionBackupCallback {
163         /**
164          * The result for {@link #getRuntimePermissionBackup}.
165          *
166          * @param backup The backup file
167          */
onGetRuntimePermissionsBackup(@onNull byte[] backup)168         void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
169     }
170 
171     /**
172      * Callback for delivering the result of {@link #getAppPermissions}.
173      *
174      * @hide
175      */
176     @TestApi
177     public interface OnGetAppPermissionResultCallback {
178         /**
179          * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
180          * Handler)}.
181          *
182          * @param permissions The permissions list.
183          */
onGetAppPermissions(@onNull List<RuntimePermissionPresentationInfo> permissions)184         void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
185     }
186 
187     /**
188      * Callback for delivering the result of {@link #countPermissionApps}.
189      *
190      * @hide
191      */
192     public interface OnCountPermissionAppsResultCallback {
193         /**
194          * The result for {@link #countPermissionApps(List, int,
195          * OnCountPermissionAppsResultCallback, Handler)}.
196          *
197          * @param numApps The number of apps that have one of the permissions
198          */
onCountPermissionApps(int numApps)199         void onCountPermissionApps(int numApps);
200     }
201 
202     /**
203      * Callback for delivering the result of {@link #getPermissionUsages}.
204      *
205      * @hide
206      */
207     public interface OnPermissionUsageResultCallback {
208         /**
209          * The result for {@link #getPermissionUsages}.
210          *
211          * @param users The users list.
212          */
onPermissionUsageResult(@onNull List<RuntimePermissionUsageInfo> users)213         void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
214     }
215 
216     private final @NonNull Context mContext;
217     private final @NonNull RemoteService mRemoteService;
218 
219     /**
220      * Create a new {@link PermissionControllerManager}.
221      *
222      * @param context to create the manager for
223      * @param handler handler to schedule work
224      *
225      * @hide
226      */
PermissionControllerManager(@onNull Context context, @NonNull Handler handler)227     public PermissionControllerManager(@NonNull Context context, @NonNull Handler handler) {
228         synchronized (sLock) {
229             Pair<Integer, Thread> key = new Pair<>(context.getUserId(),
230                     handler.getLooper().getThread());
231             RemoteService remoteService = sRemoteServices.get(key);
232             if (remoteService == null) {
233                 Intent intent = new Intent(SERVICE_INTERFACE);
234                 intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
235                 ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
236 
237                 remoteService = new RemoteService(ActivityThread.currentApplication(),
238                         serviceInfo.getComponentInfo().getComponentName(), handler,
239                         context.getUser());
240                 sRemoteServices.put(key, remoteService);
241             }
242 
243             mRemoteService = remoteService;
244         }
245 
246         mContext = context;
247     }
248 
249     /**
250      * Revoke a set of runtime permissions for various apps.
251      *
252      * @param request The permissions to revoke as {@code Map<packageName, List<permission>>}
253      * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
254      * @param reason Why the permission should be revoked
255      * @param executor Executor on which to invoke the callback
256      * @param callback Callback to receive the result
257      */
258     @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)259     public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request,
260             boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor,
261             @NonNull OnRevokeRuntimePermissionsCallback callback) {
262         // Check input to fail immediately instead of inside the async request
263         checkNotNull(executor);
264         checkNotNull(callback);
265         checkNotNull(request);
266         for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
267             checkNotNull(appRequest.getKey());
268             checkCollectionElementsNotNull(appRequest.getValue(), "permissions");
269         }
270 
271         // Check required permission to fail immediately instead of inside the oneway binder call
272         if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
273                 != PackageManager.PERMISSION_GRANTED) {
274             throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
275                     + " required");
276         }
277 
278         mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService,
279                 request, doDryRun, reason, mContext.getPackageName(), executor, callback));
280     }
281 
282     /**
283      * Set the runtime permission state from a device admin.
284      *
285      * @param callerPackageName The package name of the admin requesting the change
286      * @param packageName Package the permission belongs to
287      * @param permission Permission to change
288      * @param grantState State to set the permission into
289      * @param executor Executor to run the {@code callback} on
290      * @param callback The callback
291      *
292      * @hide
293      */
294     @RequiresPermission(allOf = {Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
295             Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
296             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY},
297             conditional = true)
setRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)298     public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
299             @NonNull String packageName, @NonNull String permission,
300             @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor,
301             @NonNull Consumer<Boolean> callback) {
302         checkStringNotEmpty(callerPackageName);
303         checkStringNotEmpty(packageName);
304         checkStringNotEmpty(permission);
305         checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
306                 || grantState == PERMISSION_GRANT_STATE_DENIED
307                 || grantState == PERMISSION_GRANT_STATE_DEFAULT);
308         checkNotNull(executor);
309         checkNotNull(callback);
310 
311         mRemoteService.scheduleRequest(new PendingSetRuntimePermissionGrantStateByDeviceAdmin(
312                 mRemoteService, callerPackageName, packageName, permission, grantState, executor,
313                 callback));
314     }
315 
316     /**
317      * Create a backup of the runtime permissions.
318      *
319      * @param user The user to be backed up
320      * @param executor Executor on which to invoke the callback
321      * @param callback Callback to receive the result
322      *
323      * @hide
324      */
325     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getRuntimePermissionBackup(@onNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull OnGetRuntimePermissionBackupCallback callback)326     public void getRuntimePermissionBackup(@NonNull UserHandle user,
327             @NonNull @CallbackExecutor Executor executor,
328             @NonNull OnGetRuntimePermissionBackupCallback callback) {
329         checkNotNull(user);
330         checkNotNull(executor);
331         checkNotNull(callback);
332 
333         mRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(mRemoteService,
334                 user, executor, callback));
335     }
336 
337     /**
338      * Restore a backup of the runtime permissions.
339      *
340      * @param backup the backup to restore. The backup is sent asynchronously, hence it should not
341      *               be modified after calling this method.
342      * @param user The user to be restore
343      *
344      * @hide
345      */
346     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
restoreRuntimePermissionBackup(@onNull byte[] backup, @NonNull UserHandle user)347     public void restoreRuntimePermissionBackup(@NonNull byte[] backup, @NonNull UserHandle user) {
348         checkNotNull(backup);
349         checkNotNull(user);
350 
351         mRemoteService.scheduleAsyncRequest(
352                 new PendingRestoreRuntimePermissionBackup(mRemoteService, backup, user));
353     }
354 
355     /**
356      * Restore a backup of the runtime permissions that has been delayed.
357      *
358      * @param packageName The package that is ready to have it's permissions restored.
359      * @param user The user to restore
360      * @param executor Executor to execute the callback on
361      * @param callback Is called with {@code true} iff there is still more delayed backup left
362      *
363      * @hide
364      */
365     @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
restoreDelayedRuntimePermissionBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)366     public void restoreDelayedRuntimePermissionBackup(@NonNull String packageName,
367             @NonNull UserHandle user,
368             @NonNull @CallbackExecutor Executor executor,
369             @NonNull Consumer<Boolean> callback) {
370         checkNotNull(packageName);
371         checkNotNull(user);
372         checkNotNull(executor);
373         checkNotNull(callback);
374 
375         mRemoteService.scheduleRequest(
376                 new PendingRestoreDelayedRuntimePermissionBackup(mRemoteService, packageName,
377                         user, executor, callback));
378     }
379 
380     /**
381      * Gets the runtime permissions for an app.
382      *
383      * @param packageName The package for which to query.
384      * @param callback Callback to receive the result.
385      * @param handler Handler on which to invoke the callback.
386      *
387      * @hide
388      */
389     @TestApi
390     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getAppPermissions(@onNull String packageName, @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler)391     public void getAppPermissions(@NonNull String packageName,
392             @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
393         checkNotNull(packageName);
394         checkNotNull(callback);
395 
396         mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
397                 packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
398     }
399 
400     /**
401      * Revoke the permission {@code permissionName} for app {@code packageName}
402      *
403      * @param packageName The package for which to revoke
404      * @param permissionName The permission to revoke
405      *
406      * @hide
407      */
408     @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
revokeRuntimePermission(@onNull String packageName, @NonNull String permissionName)409     public void revokeRuntimePermission(@NonNull String packageName,
410             @NonNull String permissionName) {
411         checkNotNull(packageName);
412         checkNotNull(permissionName);
413 
414         mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
415                 permissionName));
416     }
417 
418     /**
419      * Count how many apps have one of a set of permissions.
420      *
421      * @param permissionNames The permissions the app might have
422      * @param flags Modify which apps to count. By default all non-system apps that request a
423      *              permission are counted
424      * @param callback Callback to receive the result
425      * @param handler Handler on which to invoke the callback
426      *
427      * @hide
428      */
429     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
countPermissionApps(@onNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler)430     public void countPermissionApps(@NonNull List<String> permissionNames,
431             @CountPermissionAppsFlag int flags,
432             @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
433         checkCollectionElementsNotNull(permissionNames, "permissionNames");
434         checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED);
435         checkNotNull(callback);
436 
437         mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
438                 permissionNames, flags, callback,
439                 handler == null ? mRemoteService.getHandler() : handler));
440     }
441 
442     /**
443      * Count how many apps have used permissions.
444      *
445      * @param countSystem Also count system apps
446      * @param numMillis The number of milliseconds in the past to check for uses
447      * @param executor Executor on which to invoke the callback
448      * @param callback Callback to receive the result
449      *
450      * @hide
451      */
452     @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
getPermissionUsages(boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor, @NonNull OnPermissionUsageResultCallback callback)453     public void getPermissionUsages(boolean countSystem, long numMillis,
454             @NonNull @CallbackExecutor Executor executor,
455             @NonNull OnPermissionUsageResultCallback callback) {
456         checkArgumentNonnegative(numMillis);
457         checkNotNull(executor);
458         checkNotNull(callback);
459 
460         mRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(mRemoteService,
461                 countSystem, numMillis, executor, callback));
462     }
463 
464     /**
465      * Grant or upgrade runtime permissions. The upgrade could be performed
466      * based on whether the device upgraded, whether the permission database
467      * version is old, or because the permission policy changed.
468      *
469      * @param executor Executor on which to invoke the callback
470      * @param callback Callback to receive the result
471      *
472      * @hide
473      */
474     @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
grantOrUpgradeDefaultRuntimePermissions( @onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)475     public void grantOrUpgradeDefaultRuntimePermissions(
476             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
477         mRemoteService.scheduleRequest(new PendingGrantOrUpgradeDefaultRuntimePermissionsRequest(
478                 mRemoteService, executor, callback));
479     }
480 
481     /**
482      * A connection to the remote service
483      */
484     static final class RemoteService extends
485             AbstractMultiplePendingRequestsRemoteService<RemoteService, IPermissionController> {
486         private static final long UNBIND_TIMEOUT_MILLIS = 10000;
487         private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
488 
489         /**
490          * Create a connection to the remote service
491          *
492          * @param context A context to use
493          * @param componentName The component of the service to connect to
494          * @param user User the remote service should be connected as
495          */
RemoteService(@onNull Context context, @NonNull ComponentName componentName, @NonNull Handler handler, @NonNull UserHandle user)496         RemoteService(@NonNull Context context, @NonNull ComponentName componentName,
497                 @NonNull Handler handler, @NonNull UserHandle user) {
498             super(context, SERVICE_INTERFACE, componentName, user.getIdentifier(),
499                     service -> Log.e(TAG, "RemoteService " + service + " died"),
500                     handler, 0, false, 1);
501         }
502 
503         /**
504          * @return The default handler used by this service.
505          */
getHandler()506         Handler getHandler() {
507             return mHandler;
508         }
509 
510         @Override
getServiceInterface(@onNull IBinder binder)511         protected @NonNull IPermissionController getServiceInterface(@NonNull IBinder binder) {
512             return IPermissionController.Stub.asInterface(binder);
513         }
514 
515         @Override
getTimeoutIdleBindMillis()516         protected long getTimeoutIdleBindMillis() {
517             return UNBIND_TIMEOUT_MILLIS;
518         }
519 
520         @Override
getRemoteRequestMillis()521         protected long getRemoteRequestMillis() {
522             return MESSAGE_TIMEOUT_MILLIS;
523         }
524 
525         @Override
scheduleRequest(@onNull BasePendingRequest<RemoteService, IPermissionController> pendingRequest)526         public void scheduleRequest(@NonNull BasePendingRequest<RemoteService,
527                 IPermissionController> pendingRequest) {
528             super.scheduleRequest(pendingRequest);
529         }
530 
531         @Override
scheduleAsyncRequest(@onNull AsyncRequest<IPermissionController> request)532         public void scheduleAsyncRequest(@NonNull AsyncRequest<IPermissionController> request) {
533             super.scheduleAsyncRequest(request);
534         }
535     }
536 
537     /**
538      * Task to read a large amount of data from a remote service.
539      */
540     private static class FileReaderTask<Callback extends Consumer<byte[]>>
541             extends AsyncTask<Void, Void, byte[]> {
542         private ParcelFileDescriptor mLocalPipe;
543         private ParcelFileDescriptor mRemotePipe;
544 
545         private final @NonNull Callback mCallback;
546 
FileReaderTask(@onNull Callback callback)547         FileReaderTask(@NonNull Callback callback) {
548             mCallback = callback;
549         }
550 
551         @Override
onPreExecute()552         protected void onPreExecute() {
553             ParcelFileDescriptor[] pipe;
554             try {
555                 pipe = ParcelFileDescriptor.createPipe();
556             } catch (IOException e) {
557                 Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e);
558                 return;
559             }
560 
561             mLocalPipe = pipe[0];
562             mRemotePipe = pipe[1];
563         }
564 
565         /**
566          * Get the file descriptor the remote service should write the data to.
567          *
568          * <p>Needs to be closed <u>locally</u> before the FileReader can finish.
569          *
570          * @return The file the data should be written to
571          */
getRemotePipe()572         ParcelFileDescriptor getRemotePipe() {
573             return mRemotePipe;
574         }
575 
576         @Override
doInBackground(Void... ignored)577         protected byte[] doInBackground(Void... ignored) {
578             ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream();
579 
580             try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) {
581                 byte[] buffer = new byte[16 * 1024];
582 
583                 while (!isCancelled()) {
584                     int numRead = in.read(buffer);
585                     if (numRead == -1) {
586                         break;
587                     }
588 
589                     combinedBuffer.write(buffer, 0, numRead);
590                 }
591             } catch (IOException | NullPointerException e) {
592                 Log.e(TAG, "Error reading runtime permission backup", e);
593                 combinedBuffer.reset();
594             }
595 
596             return combinedBuffer.toByteArray();
597         }
598 
599         /**
600          * Interrupt the reading of the data.
601          *
602          * <p>Needs to be called when canceling this task as it might be hung.
603          */
interruptRead()604         void interruptRead() {
605             IoUtils.closeQuietly(mLocalPipe);
606         }
607 
608         @Override
onCancelled()609         protected void onCancelled() {
610             onPostExecute(new byte[]{});
611         }
612 
613         @Override
onPostExecute(byte[] backup)614         protected void onPostExecute(byte[] backup) {
615             IoUtils.closeQuietly(mLocalPipe);
616             mCallback.accept(backup);
617         }
618     }
619 
620     /**
621      * Task to send a large amount of data to a remote service.
622      */
623     private static class FileWriterTask extends AsyncTask<byte[], Void, Void> {
624         private static final int CHUNK_SIZE = 4 * 1024;
625 
626         private ParcelFileDescriptor mLocalPipe;
627         private ParcelFileDescriptor mRemotePipe;
628 
629         @Override
onPreExecute()630         protected void onPreExecute() {
631             ParcelFileDescriptor[] pipe;
632             try {
633                 pipe = ParcelFileDescriptor.createPipe();
634             } catch (IOException e) {
635                 Log.e(TAG, "Could not create pipe needed to send runtime permission backup",
636                         e);
637                 return;
638             }
639 
640             mRemotePipe = pipe[0];
641             mLocalPipe = pipe[1];
642         }
643 
644         /**
645          * Get the file descriptor the remote service should read the data from.
646          *
647          * @return The file the data should be read from
648          */
getRemotePipe()649         ParcelFileDescriptor getRemotePipe() {
650             return mRemotePipe;
651         }
652 
653         /**
654          * Send the data to the remove service.
655          *
656          * @param in The data to send
657          *
658          * @return ignored
659          */
660         @Override
doInBackground(byte[]... in)661         protected Void doInBackground(byte[]... in) {
662             byte[] buffer = in[0];
663             try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(mLocalPipe)) {
664                 for (int offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
665                     out.write(buffer, offset, min(CHUNK_SIZE, buffer.length - offset));
666                 }
667             } catch (IOException | NullPointerException e) {
668                 Log.e(TAG, "Error sending runtime permission backup", e);
669             }
670 
671             return null;
672         }
673 
674         /**
675          * Interrupt the send of the data.
676          *
677          * <p>Needs to be called when canceling this task as it might be hung.
678          */
interruptWrite()679         void interruptWrite() {
680             IoUtils.closeQuietly(mLocalPipe);
681         }
682 
683         @Override
onCancelled()684         protected void onCancelled() {
685             onPostExecute(null);
686         }
687 
688         @Override
onPostExecute(Void ignored)689         protected void onPostExecute(Void ignored) {
690             IoUtils.closeQuietly(mLocalPipe);
691         }
692     }
693 
694     /**
695      * Request for {@link #revokeRuntimePermissions}
696      */
697     private static final class PendingRevokeRuntimePermissionRequest extends
698             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
699         private final @NonNull Map<String, List<String>> mRequest;
700         private final boolean mDoDryRun;
701         private final int mReason;
702         private final @NonNull String mCallingPackage;
703         private final @NonNull Executor mExecutor;
704         private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
705 
706         private final @NonNull RemoteCallback mRemoteCallback;
707 
PendingRevokeRuntimePermissionRequest(@onNull RemoteService service, @NonNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull String callingPackage, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback)708         private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service,
709                 @NonNull Map<String, List<String>> request, boolean doDryRun,
710                 @Reason int reason, @NonNull String callingPackage,
711                 @NonNull @CallbackExecutor Executor executor,
712                 @NonNull OnRevokeRuntimePermissionsCallback callback) {
713             super(service);
714 
715             mRequest = request;
716             mDoDryRun = doDryRun;
717             mReason = reason;
718             mCallingPackage = callingPackage;
719             mExecutor = executor;
720             mCallback = callback;
721 
722             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
723                 long token = Binder.clearCallingIdentity();
724                 try {
725                     Map<String, List<String>> revoked = new ArrayMap<>();
726                     try {
727                         Bundle bundleizedRevoked = result.getBundle(KEY_RESULT);
728 
729                         for (String packageName : bundleizedRevoked.keySet()) {
730                             Preconditions.checkNotNull(packageName);
731 
732                             ArrayList<String> permissions =
733                                     bundleizedRevoked.getStringArrayList(packageName);
734                             Preconditions.checkCollectionElementsNotNull(permissions,
735                                     "permissions");
736 
737                             revoked.put(packageName, permissions);
738                         }
739                     } catch (Exception e) {
740                         Log.e(TAG, "Could not read result when revoking runtime permissions", e);
741                     }
742 
743                     callback.onRevokeRuntimePermissions(revoked);
744                 } finally {
745                     Binder.restoreCallingIdentity(token);
746 
747                     finish();
748                 }
749             }), null);
750         }
751 
752         @Override
onTimeout(RemoteService remoteService)753         protected void onTimeout(RemoteService remoteService) {
754             long token = Binder.clearCallingIdentity();
755             try {
756                 mExecutor.execute(
757                         () -> mCallback.onRevokeRuntimePermissions(Collections.emptyMap()));
758             } finally {
759                 Binder.restoreCallingIdentity(token);
760             }
761         }
762 
763         @Override
run()764         public void run() {
765             Bundle bundledizedRequest = new Bundle();
766             for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) {
767                 bundledizedRequest.putStringArrayList(appRequest.getKey(),
768                         new ArrayList<>(appRequest.getValue()));
769             }
770 
771             try {
772                 getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest,
773                         mDoDryRun, mReason, mCallingPackage, mRemoteCallback);
774             } catch (RemoteException e) {
775                 Log.e(TAG, "Error revoking runtime permission", e);
776             }
777         }
778     }
779 
780     /**
781      * Request for {@link #getRuntimePermissionBackup}
782      */
783     private static final class PendingGetRuntimePermissionBackup extends
784             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController>
785             implements Consumer<byte[]> {
786         private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader;
787         private final @NonNull Executor mExecutor;
788         private final @NonNull OnGetRuntimePermissionBackupCallback mCallback;
789         private final @NonNull UserHandle mUser;
790 
PendingGetRuntimePermissionBackup(@onNull RemoteService service, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull OnGetRuntimePermissionBackupCallback callback)791         private PendingGetRuntimePermissionBackup(@NonNull RemoteService service,
792                 @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
793                 @NonNull OnGetRuntimePermissionBackupCallback callback) {
794             super(service);
795 
796             mUser = user;
797             mExecutor = executor;
798             mCallback = callback;
799 
800             mBackupReader = new FileReaderTask<>(this);
801         }
802 
803         @Override
onTimeout(RemoteService remoteService)804         protected void onTimeout(RemoteService remoteService) {
805             mBackupReader.cancel(true);
806             mBackupReader.interruptRead();
807         }
808 
809         @Override
run()810         public void run() {
811             if (mBackupReader.getStatus() != AsyncTask.Status.PENDING) {
812                 return;
813             }
814             mBackupReader.execute();
815 
816             ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe();
817             try {
818                 getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe);
819             } catch (RemoteException e) {
820                 Log.e(TAG, "Error getting runtime permission backup", e);
821             } finally {
822                 // Remote pipe end is duped by binder call. Local copy is not needed anymore
823                 IoUtils.closeQuietly(remotePipe);
824             }
825         }
826 
827         /**
828          * Called when the {@link #mBackupReader} finished reading the file.
829          *
830          * @param backup The data read
831          */
832         @Override
accept(byte[] backup)833         public void accept(byte[] backup) {
834             long token = Binder.clearCallingIdentity();
835             try {
836                 mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup));
837             } finally {
838                 Binder.restoreCallingIdentity(token);
839             }
840 
841             finish();
842         }
843     }
844 
845     /**
846      * Request for {@link #getRuntimePermissionBackup}
847      */
848     private static final class PendingSetRuntimePermissionGrantStateByDeviceAdmin extends
849             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
850         private final @NonNull String mCallerPackageName;
851         private final @NonNull String mPackageName;
852         private final @NonNull String mPermission;
853         private final @PermissionGrantState int mGrantState;
854 
855         private final @NonNull Executor mExecutor;
856         private final @NonNull Consumer<Boolean> mCallback;
857         private final @NonNull RemoteCallback mRemoteCallback;
858 
PendingSetRuntimePermissionGrantStateByDeviceAdmin(@onNull RemoteService service, @NonNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)859         private PendingSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull RemoteService service,
860                 @NonNull String callerPackageName, @NonNull String packageName,
861                 @NonNull String permission, @PermissionGrantState int grantState,
862                 @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
863             super(service);
864 
865             mCallerPackageName = callerPackageName;
866             mPackageName = packageName;
867             mPermission = permission;
868             mGrantState = grantState;
869             mExecutor = executor;
870             mCallback = callback;
871 
872             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
873                 long token = Binder.clearCallingIdentity();
874                 try {
875                     callback.accept(result.getBoolean(KEY_RESULT, false));
876                 } finally {
877                     Binder.restoreCallingIdentity(token);
878 
879                     finish();
880                 }
881             }), null);
882         }
883 
884         @Override
onTimeout(RemoteService remoteService)885         protected void onTimeout(RemoteService remoteService) {
886             long token = Binder.clearCallingIdentity();
887             try {
888                 mExecutor.execute(() -> mCallback.accept(false));
889             } finally {
890                 Binder.restoreCallingIdentity(token);
891             }
892         }
893 
894         @Override
run()895         public void run() {
896             try {
897                 getService().getServiceInterface().setRuntimePermissionGrantStateByDeviceAdmin(
898                         mCallerPackageName, mPackageName, mPermission, mGrantState, mRemoteCallback);
899             } catch (RemoteException e) {
900                 Log.e(TAG, "Error setting permissions state for device admin " + mPackageName,
901                         e);
902             }
903         }
904     }
905 
906     /**
907      * Request for {@link #restoreRuntimePermissionBackup}
908      */
909     private static final class PendingRestoreRuntimePermissionBackup implements
910             AbstractRemoteService.AsyncRequest<IPermissionController> {
911         private final @NonNull FileWriterTask mBackupSender;
912         private final @NonNull byte[] mBackup;
913         private final @NonNull UserHandle mUser;
914 
PendingRestoreRuntimePermissionBackup(@onNull RemoteService service, @NonNull byte[] backup, @NonNull UserHandle user)915         private PendingRestoreRuntimePermissionBackup(@NonNull RemoteService service,
916                 @NonNull byte[] backup, @NonNull UserHandle user) {
917             mBackup = backup;
918             mUser = user;
919 
920             mBackupSender = new FileWriterTask();
921         }
922 
923         @Override
run(@onNull IPermissionController service)924         public void run(@NonNull IPermissionController service) {
925             if (mBackupSender.getStatus() != AsyncTask.Status.PENDING) {
926                 return;
927             }
928             mBackupSender.execute(mBackup);
929 
930             ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe();
931             try {
932                 service.restoreRuntimePermissionBackup(mUser, remotePipe);
933             } catch (RemoteException e) {
934                 Log.e(TAG, "Error sending runtime permission backup", e);
935                 mBackupSender.cancel(false);
936                 mBackupSender.interruptWrite();
937             } finally {
938                 // Remote pipe end is duped by binder call. Local copy is not needed anymore
939                 IoUtils.closeQuietly(remotePipe);
940             }
941         }
942     }
943 
944     /**
945      * Request for {@link #restoreDelayedRuntimePermissionBackup(String, UserHandle, Executor,
946      * Consumer<Boolean>)}
947      */
948     private static final class PendingRestoreDelayedRuntimePermissionBackup extends
949             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
950         private final @NonNull String mPackageName;
951         private final @NonNull UserHandle mUser;
952         private final @NonNull Executor mExecutor;
953         private final @NonNull Consumer<Boolean> mCallback;
954 
955         private final @NonNull RemoteCallback mRemoteCallback;
956 
PendingRestoreDelayedRuntimePermissionBackup(@onNull RemoteService service, @NonNull String packageName, @NonNull UserHandle user, @NonNull Executor executor, @NonNull Consumer<Boolean> callback)957         private PendingRestoreDelayedRuntimePermissionBackup(@NonNull RemoteService service,
958                 @NonNull String packageName, @NonNull UserHandle user, @NonNull Executor executor,
959                 @NonNull Consumer<Boolean> callback) {
960             super(service);
961 
962             mPackageName = packageName;
963             mUser = user;
964             mExecutor = executor;
965             mCallback = callback;
966 
967             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
968                 long token = Binder.clearCallingIdentity();
969                 try {
970                     callback.accept(result.getBoolean(KEY_RESULT, false));
971                 } finally {
972                     Binder.restoreCallingIdentity(token);
973 
974                     finish();
975                 }
976             }), null);
977         }
978 
979         @Override
onTimeout(RemoteService remoteService)980         protected void onTimeout(RemoteService remoteService) {
981             long token = Binder.clearCallingIdentity();
982             try {
983                 mExecutor.execute(
984                         () -> mCallback.accept(true));
985             } finally {
986                 Binder.restoreCallingIdentity(token);
987             }
988         }
989 
990         @Override
run()991         public void run() {
992             try {
993                 getService().getServiceInterface().restoreDelayedRuntimePermissionBackup(
994                         mPackageName, mUser, mRemoteCallback);
995             } catch (RemoteException e) {
996                 Log.e(TAG, "Error restoring delayed permissions for " + mPackageName, e);
997             }
998         }
999     }
1000 
1001     /**
1002      * Request for {@link #getAppPermissions}
1003      */
1004     private static final class PendingGetAppPermissionRequest extends
1005             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
1006         private final @NonNull String mPackageName;
1007         private final @NonNull OnGetAppPermissionResultCallback mCallback;
1008 
1009         private final @NonNull RemoteCallback mRemoteCallback;
1010 
PendingGetAppPermissionRequest(@onNull RemoteService service, @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback, @NonNull Handler handler)1011         private PendingGetAppPermissionRequest(@NonNull RemoteService service,
1012                 @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
1013                 @NonNull Handler handler) {
1014             super(service);
1015 
1016             mPackageName = packageName;
1017             mCallback = callback;
1018 
1019             mRemoteCallback = new RemoteCallback(result -> {
1020                 final List<RuntimePermissionPresentationInfo> reportedPermissions;
1021                 List<RuntimePermissionPresentationInfo> permissions = null;
1022                 if (result != null) {
1023                     permissions = result.getParcelableArrayList(KEY_RESULT);
1024                 }
1025                 if (permissions == null) {
1026                     permissions = Collections.emptyList();
1027                 }
1028                 reportedPermissions = permissions;
1029 
1030                 callback.onGetAppPermissions(reportedPermissions);
1031 
1032                 finish();
1033             }, handler);
1034         }
1035 
1036         @Override
onTimeout(RemoteService remoteService)1037         protected void onTimeout(RemoteService remoteService) {
1038             mCallback.onGetAppPermissions(Collections.emptyList());
1039         }
1040 
1041         @Override
run()1042         public void run() {
1043             try {
1044                 getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
1045             } catch (RemoteException e) {
1046                 Log.e(TAG, "Error getting app permission", e);
1047             }
1048         }
1049     }
1050 
1051     /**
1052      * Request for {@link #revokeRuntimePermission}
1053      */
1054     private static final class PendingRevokeAppPermissionRequest
1055             implements AbstractRemoteService.AsyncRequest<IPermissionController> {
1056         private final @NonNull String mPackageName;
1057         private final @NonNull String mPermissionName;
1058 
PendingRevokeAppPermissionRequest(@onNull String packageName, @NonNull String permissionName)1059         private PendingRevokeAppPermissionRequest(@NonNull String packageName,
1060                 @NonNull String permissionName) {
1061             mPackageName = packageName;
1062             mPermissionName = permissionName;
1063         }
1064 
1065         @Override
run(IPermissionController remoteInterface)1066         public void run(IPermissionController remoteInterface) {
1067             try {
1068                 remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
1069             } catch (RemoteException e) {
1070                 Log.e(TAG, "Error revoking app permission", e);
1071             }
1072         }
1073     }
1074 
1075     /**
1076      * Request for {@link #countPermissionApps}
1077      */
1078     private static final class PendingCountPermissionAppsRequest extends
1079             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
1080         private final @NonNull List<String> mPermissionNames;
1081         private final @NonNull OnCountPermissionAppsResultCallback mCallback;
1082         private final @CountPermissionAppsFlag int mFlags;
1083 
1084         private final @NonNull RemoteCallback mRemoteCallback;
1085 
PendingCountPermissionAppsRequest(@onNull RemoteService service, @NonNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull OnCountPermissionAppsResultCallback callback, @NonNull Handler handler)1086         private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
1087                 @NonNull List<String> permissionNames, @CountPermissionAppsFlag int flags,
1088                 @NonNull OnCountPermissionAppsResultCallback callback, @NonNull Handler handler) {
1089             super(service);
1090 
1091             mPermissionNames = permissionNames;
1092             mFlags = flags;
1093             mCallback = callback;
1094 
1095             mRemoteCallback = new RemoteCallback(result -> {
1096                 final int numApps;
1097                 if (result != null) {
1098                     numApps = result.getInt(KEY_RESULT);
1099                 } else {
1100                     numApps = 0;
1101                 }
1102 
1103                 callback.onCountPermissionApps(numApps);
1104 
1105                 finish();
1106             }, handler);
1107         }
1108 
1109         @Override
onTimeout(RemoteService remoteService)1110         protected void onTimeout(RemoteService remoteService) {
1111             mCallback.onCountPermissionApps(0);
1112         }
1113 
1114         @Override
run()1115         public void run() {
1116             try {
1117                 getService().getServiceInterface().countPermissionApps(mPermissionNames,
1118                         mFlags, mRemoteCallback);
1119             } catch (RemoteException e) {
1120                 Log.e(TAG, "Error counting permission apps", e);
1121             }
1122         }
1123     }
1124 
1125     /**
1126      * Request for {@link #getPermissionUsages}
1127      */
1128     private static final class PendingGetPermissionUsagesRequest extends
1129             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
1130         private final @NonNull OnPermissionUsageResultCallback mCallback;
1131         private final boolean mCountSystem;
1132         private final long mNumMillis;
1133 
1134         private final @NonNull RemoteCallback mRemoteCallback;
1135 
PendingGetPermissionUsagesRequest(@onNull RemoteService service, boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor, @NonNull OnPermissionUsageResultCallback callback)1136         private PendingGetPermissionUsagesRequest(@NonNull RemoteService service,
1137                 boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor,
1138                 @NonNull OnPermissionUsageResultCallback callback) {
1139             super(service);
1140 
1141             mCountSystem = countSystem;
1142             mNumMillis = numMillis;
1143             mCallback = callback;
1144 
1145             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
1146                 long token = Binder.clearCallingIdentity();
1147                 try {
1148                     final List<RuntimePermissionUsageInfo> reportedUsers;
1149                     List<RuntimePermissionUsageInfo> users = null;
1150                     if (result != null) {
1151                         users = result.getParcelableArrayList(KEY_RESULT);
1152                     } else {
1153                         users = Collections.emptyList();
1154                     }
1155                     reportedUsers = users;
1156 
1157                     callback.onPermissionUsageResult(reportedUsers);
1158                 } finally {
1159                     Binder.restoreCallingIdentity(token);
1160 
1161                     finish();
1162                 }
1163             }), null);
1164         }
1165 
1166         @Override
onTimeout(RemoteService remoteService)1167         protected void onTimeout(RemoteService remoteService) {
1168             mCallback.onPermissionUsageResult(Collections.emptyList());
1169         }
1170 
1171         @Override
run()1172         public void run() {
1173             try {
1174                 getService().getServiceInterface().getPermissionUsages(mCountSystem, mNumMillis,
1175                         mRemoteCallback);
1176             } catch (RemoteException e) {
1177                 Log.e(TAG, "Error counting permission users", e);
1178             }
1179         }
1180     }
1181 
1182     /**
1183      * Request for {@link #grantOrUpgradeDefaultRuntimePermissions(Executor, Consumer)}
1184      */
1185     private static final class PendingGrantOrUpgradeDefaultRuntimePermissionsRequest extends
1186             AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
1187         private final @NonNull Consumer<Boolean> mCallback;
1188 
1189         private final @NonNull RemoteCallback mRemoteCallback;
1190 
PendingGrantOrUpgradeDefaultRuntimePermissionsRequest( @onNull RemoteService service, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)1191         private PendingGrantOrUpgradeDefaultRuntimePermissionsRequest(
1192                 @NonNull RemoteService service,  @NonNull @CallbackExecutor Executor executor,
1193                 @NonNull Consumer<Boolean> callback) {
1194             super(service);
1195             mCallback = callback;
1196 
1197             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
1198                 long token = Binder.clearCallingIdentity();
1199                 try {
1200                     callback.accept(result != null);
1201                 } finally {
1202                     Binder.restoreCallingIdentity(token);
1203                     finish();
1204                 }
1205             }), null);
1206         }
1207 
1208         @Override
onTimeout(RemoteService remoteService)1209         protected void onTimeout(RemoteService remoteService) {
1210             long token = Binder.clearCallingIdentity();
1211             try {
1212                 mCallback.accept(false);
1213             } finally {
1214                 Binder.restoreCallingIdentity(token);
1215             }
1216         }
1217 
1218         @Override
run()1219         public void run() {
1220             try {
1221                 getService().getServiceInterface().grantOrUpgradeDefaultRuntimePermissions(
1222                         mRemoteCallback);
1223             } catch (RemoteException e) {
1224                 Log.e(TAG, "Error granting or upgrading runtime permissions", e);
1225             }
1226         }
1227     }
1228 }
1229