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.permission.PermissionControllerService.SERVICE_INTERFACE; 20 21 import static com.android.internal.util.FunctionalUtils.uncheckExceptions; 22 import static com.android.internal.util.Preconditions.checkArgumentNonnegative; 23 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; 24 import static com.android.internal.util.Preconditions.checkFlagsArgument; 25 import static com.android.internal.util.Preconditions.checkNotNull; 26 import static com.android.internal.util.Preconditions.checkStringNotEmpty; 27 28 import android.Manifest; 29 import android.annotation.CallbackExecutor; 30 import android.annotation.IntDef; 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.annotation.RequiresPermission; 34 import android.annotation.SystemApi; 35 import android.annotation.SystemService; 36 import android.annotation.TestApi; 37 import android.app.ActivityThread; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.PackageManager; 41 import android.content.pm.ResolveInfo; 42 import android.os.Binder; 43 import android.os.Bundle; 44 import android.os.Handler; 45 import android.os.Process; 46 import android.os.UserHandle; 47 import android.util.ArrayMap; 48 import android.util.Log; 49 import android.util.Pair; 50 51 import com.android.internal.annotations.GuardedBy; 52 import com.android.internal.infra.AndroidFuture; 53 import com.android.internal.infra.RemoteStream; 54 import com.android.internal.infra.ServiceConnector; 55 import com.android.internal.os.BackgroundThread; 56 import com.android.internal.util.CollectionUtils; 57 58 import libcore.util.EmptyArray; 59 60 import java.io.FileDescriptor; 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collections; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Objects; 69 import java.util.concurrent.Executor; 70 import java.util.concurrent.TimeUnit; 71 import java.util.function.Consumer; 72 import java.util.function.IntConsumer; 73 74 /** 75 * Interface for communicating with the permission controller. 76 * 77 * @hide 78 */ 79 @SystemApi 80 @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) 81 public final class PermissionControllerManager { 82 private static final String TAG = PermissionControllerManager.class.getSimpleName(); 83 84 private static final long REQUEST_TIMEOUT_MILLIS = 60000; 85 private static final long UNBIND_TIMEOUT_MILLIS = 10000; 86 private static final int CHUNK_SIZE = 4 * 1024; 87 88 private static final Object sLock = new Object(); 89 90 /** 91 * Global remote services (per user) used by all {@link PermissionControllerManager managers} 92 */ 93 @GuardedBy("sLock") 94 private static ArrayMap<Pair<Integer, Thread>, ServiceConnector<IPermissionController>> 95 sRemoteServices = new ArrayMap<>(1); 96 97 /** @hide */ 98 @IntDef(prefix = { "REASON_" }, value = { 99 REASON_MALWARE, 100 REASON_INSTALLER_POLICY_VIOLATION, 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface Reason {} 104 105 /** The permissions are revoked because the apps holding the permissions are malware */ 106 public static final int REASON_MALWARE = 1; 107 108 /** 109 * The permissions are revoked because the apps holding the permissions violate a policy of the 110 * app that installed it. 111 * 112 * <p>If this reason is used only permissions of apps that are installed by the caller of the 113 * API can be revoked. 114 */ 115 public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; 116 117 /** @hide */ 118 @IntDef(prefix = { "COUNT_" }, value = { 119 COUNT_ONLY_WHEN_GRANTED, 120 COUNT_WHEN_SYSTEM, 121 }, flag = true) 122 @Retention(RetentionPolicy.SOURCE) 123 public @interface CountPermissionAppsFlag {} 124 125 /** Count an app only if the permission is granted to the app. */ 126 public static final int COUNT_ONLY_WHEN_GRANTED = 1; 127 128 /** Count and app even if it is a system app. */ 129 public static final int COUNT_WHEN_SYSTEM = 2; 130 131 /** @hide */ 132 @IntDef(prefix = { "HIBERNATION_ELIGIBILITY_"}, value = { 133 HIBERNATION_ELIGIBILITY_UNKNOWN, 134 HIBERNATION_ELIGIBILITY_ELIGIBLE, 135 HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM, 136 HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER, 137 }) 138 @Retention(RetentionPolicy.SOURCE) 139 public @interface HibernationEligibilityFlag {} 140 141 /** 142 * Unknown whether package is eligible for hibernation. 143 * 144 * @hide 145 */ 146 @SystemApi 147 public static final int HIBERNATION_ELIGIBILITY_UNKNOWN = -1; 148 149 /** 150 * Package is eligible for app hibernation and may be hibernated when the job runs. 151 * 152 * @hide 153 */ 154 @SystemApi 155 public static final int HIBERNATION_ELIGIBILITY_ELIGIBLE = 0; 156 157 /** 158 * Package is not eligible for app hibernation because it is categorically exempt via the 159 * system. 160 * 161 * @hide 162 */ 163 @SystemApi 164 public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM = 1; 165 166 /** 167 * Package is not eligible for app hibernation because it has been exempt by the user's 168 * preferences. Note that this should not be set if the package is exempt from hibernation by 169 * the system as the user preference would have no effect. 170 * 171 * @hide 172 */ 173 @SystemApi 174 public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER = 2; 175 176 /** 177 * Callback for delivering the result of {@link #revokeRuntimePermissions}. 178 */ 179 public abstract static class OnRevokeRuntimePermissionsCallback { 180 /** 181 * The result for {@link #revokeRuntimePermissions}. 182 * 183 * @param revoked The actually revoked permissions as 184 * {@code Map<packageName, List<permission>>} 185 */ onRevokeRuntimePermissions(@onNull Map<String, List<String>> revoked)186 public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked); 187 } 188 189 /** 190 * Callback for delivering the result of {@link #getAppPermissions}. 191 * 192 * @hide 193 */ 194 @TestApi 195 public interface OnGetAppPermissionResultCallback { 196 /** 197 * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback, 198 * Handler)}. 199 * 200 * @param permissions The permissions list. 201 */ onGetAppPermissions(@onNull List<RuntimePermissionPresentationInfo> permissions)202 void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions); 203 } 204 205 /** 206 * Callback for delivering the result of {@link #countPermissionApps}. 207 * 208 * @hide 209 */ 210 @TestApi 211 public interface OnCountPermissionAppsResultCallback { 212 /** 213 * The result for {@link #countPermissionApps(List, int, 214 * OnCountPermissionAppsResultCallback, Handler)}. 215 * 216 * @param numApps The number of apps that have one of the permissions 217 */ onCountPermissionApps(int numApps)218 void onCountPermissionApps(int numApps); 219 } 220 221 /** 222 * Callback for delivering the result of {@link #getPermissionUsages}. 223 * 224 * @hide 225 */ 226 public interface OnPermissionUsageResultCallback { 227 /** 228 * The result for {@link #getPermissionUsages}. 229 * 230 * @param users The users list. 231 */ onPermissionUsageResult(@onNull List<RuntimePermissionUsageInfo> users)232 void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users); 233 } 234 235 private final @NonNull Context mContext; 236 private final @NonNull ServiceConnector<IPermissionController> mRemoteService; 237 private final @NonNull Handler mHandler; 238 239 /** 240 * Create a new {@link PermissionControllerManager}. 241 * 242 * @param context to create the manager for 243 * @param handler handler to schedule work 244 * 245 * @hide 246 */ PermissionControllerManager(@onNull Context context, @NonNull Handler handler)247 public PermissionControllerManager(@NonNull Context context, @NonNull Handler handler) { 248 synchronized (sLock) { 249 Pair<Integer, Thread> key = new Pair<>(context.getUserId(), 250 handler.getLooper().getThread()); 251 ServiceConnector<IPermissionController> remoteService = sRemoteServices.get(key); 252 if (remoteService == null) { 253 Intent intent = new Intent(SERVICE_INTERFACE); 254 String pkgName = context.getPackageManager().getPermissionControllerPackageName(); 255 intent.setPackage(pkgName); 256 ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); 257 if (serviceInfo == null) { 258 String errorMsg = "No PermissionController package (" + pkgName + ") for user " 259 + context.getUserId(); 260 Log.wtf(TAG, errorMsg); 261 throw new IllegalStateException(errorMsg); 262 } 263 remoteService = new ServiceConnector.Impl<IPermissionController>( 264 ActivityThread.currentApplication() /* context */, 265 new Intent(SERVICE_INTERFACE) 266 .setComponent(serviceInfo.getComponentInfo().getComponentName()), 267 0 /* bindingFlags */, context.getUserId(), 268 IPermissionController.Stub::asInterface) { 269 270 @Override 271 protected Handler getJobHandler() { 272 return handler; 273 } 274 275 @Override 276 protected long getRequestTimeoutMs() { 277 return REQUEST_TIMEOUT_MILLIS; 278 } 279 280 @Override 281 protected long getAutoDisconnectTimeoutMs() { 282 return UNBIND_TIMEOUT_MILLIS; 283 } 284 }; 285 sRemoteServices.put(key, remoteService); 286 } 287 288 mRemoteService = remoteService; 289 } 290 291 mContext = context; 292 mHandler = handler; 293 } 294 295 /** 296 * Throw a {@link SecurityException} if not at least one of the permissions is granted. 297 * 298 * @param requiredPermissions A list of permissions. Any of of them if sufficient to pass the 299 * check 300 */ enforceSomePermissionsGrantedToSelf(@onNull String... requiredPermissions)301 private void enforceSomePermissionsGrantedToSelf(@NonNull String... requiredPermissions) { 302 for (String requiredPermission : requiredPermissions) { 303 if (mContext.checkSelfPermission(requiredPermission) 304 == PackageManager.PERMISSION_GRANTED) { 305 return; 306 } 307 } 308 309 throw new SecurityException("At lest one of the following permissions is required: " 310 + Arrays.toString(requiredPermissions)); 311 } 312 313 /** 314 * Revoke a set of runtime permissions for various apps. 315 * 316 * @param request The permissions to revoke as {@code Map<packageName, List<permission>>} 317 * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them 318 * @param reason Why the permission should be revoked 319 * @param executor Executor on which to invoke the callback 320 * @param callback Callback to receive the result 321 */ 322 @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)323 public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request, 324 boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor, 325 @NonNull OnRevokeRuntimePermissionsCallback callback) { 326 // Check input to fail immediately instead of inside the async request 327 checkNotNull(executor); 328 checkNotNull(callback); 329 checkNotNull(request); 330 for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { 331 checkNotNull(appRequest.getKey()); 332 checkCollectionElementsNotNull(appRequest.getValue(), "permissions"); 333 } 334 335 // Check required permission to fail immediately instead of inside the oneway binder call 336 enforceSomePermissionsGrantedToSelf(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); 337 338 mRemoteService.postAsync(service -> { 339 Bundle bundledizedRequest = new Bundle(); 340 for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { 341 bundledizedRequest.putStringArrayList(appRequest.getKey(), 342 new ArrayList<>(appRequest.getValue())); 343 } 344 345 AndroidFuture<Map<String, List<String>>> revokeRuntimePermissionsResult = 346 new AndroidFuture<>(); 347 service.revokeRuntimePermissions(bundledizedRequest, doDryRun, reason, 348 mContext.getPackageName(), 349 revokeRuntimePermissionsResult); 350 return revokeRuntimePermissionsResult; 351 }).whenCompleteAsync((revoked, err) -> { 352 final long token = Binder.clearCallingIdentity(); 353 try { 354 if (err != null) { 355 Log.e(TAG, "Failure when revoking runtime permissions " + revoked, err); 356 callback.onRevokeRuntimePermissions(Collections.emptyMap()); 357 } else { 358 callback.onRevokeRuntimePermissions(revoked); 359 } 360 } finally { 361 Binder.restoreCallingIdentity(token); 362 } 363 }, executor); 364 } 365 366 /** 367 * Set the runtime permission state from a device admin. 368 * This variant takes into account whether the admin may or may not grant sensors-related 369 * permissions. 370 * 371 * @param callerPackageName The package name of the admin requesting the change 372 * @param params Information about the permission being granted. 373 * @param executor Executor to run the {@code callback} on 374 * @param callback The callback 375 * 376 * @hide 377 */ 378 @RequiresPermission(allOf = {Manifest.permission.GRANT_RUNTIME_PERMISSIONS, 379 Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, 380 Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY}, 381 conditional = true) setRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull AdminPermissionControlParams params, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)382 public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName, 383 @NonNull AdminPermissionControlParams params, 384 @NonNull @CallbackExecutor Executor executor, 385 @NonNull Consumer<Boolean> callback) { 386 checkStringNotEmpty(callerPackageName); 387 Objects.requireNonNull(executor); 388 Objects.requireNonNull(callback); 389 Objects.requireNonNull(params, "Admin control params must not be null."); 390 391 mRemoteService.postAsync(service -> { 392 AndroidFuture<Boolean> setRuntimePermissionGrantStateResult = new AndroidFuture<>(); 393 service.setRuntimePermissionGrantStateByDeviceAdminFromParams( 394 callerPackageName, params, 395 setRuntimePermissionGrantStateResult); 396 return setRuntimePermissionGrantStateResult; 397 }).whenCompleteAsync((setRuntimePermissionGrantStateResult, err) -> { 398 final long token = Binder.clearCallingIdentity(); 399 try { 400 if (err != null) { 401 Log.e(TAG, 402 "Error setting permissions state for device admin " 403 + callerPackageName, err); 404 callback.accept(false); 405 } else { 406 callback.accept(Boolean.TRUE.equals(setRuntimePermissionGrantStateResult)); 407 } 408 } finally { 409 Binder.restoreCallingIdentity(token); 410 } 411 }, executor); 412 } 413 414 /** 415 * Create a backup of the runtime permissions. 416 * 417 * @param user The user to be backed up 418 * @param executor Executor on which to invoke the callback 419 * @param callback Callback to receive the result. The resulting backup-file is opaque and no 420 * guarantees are made other than that the file can be send to 421 * {@link #restoreRuntimePermissionBackup} in this and future versions of 422 * Android. 423 */ 424 @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) getRuntimePermissionBackup(@onNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<byte[]> callback)425 public void getRuntimePermissionBackup(@NonNull UserHandle user, 426 @NonNull @CallbackExecutor Executor executor, 427 @NonNull Consumer<byte[]> callback) { 428 checkNotNull(user); 429 checkNotNull(executor); 430 checkNotNull(callback); 431 432 // Check required permission to fail immediately instead of inside the oneway binder call 433 enforceSomePermissionsGrantedToSelf(Manifest.permission.GET_RUNTIME_PERMISSIONS); 434 435 mRemoteService.postAsync(service -> RemoteStream.receiveBytes(remotePipe -> { 436 service.getRuntimePermissionBackup(user, remotePipe); 437 })).whenCompleteAsync((bytes, err) -> { 438 if (err != null) { 439 Log.e(TAG, "Error getting permission backup", err); 440 callback.accept(EmptyArray.BYTE); 441 } else { 442 callback.accept(bytes); 443 } 444 }, executor); 445 } 446 447 /** 448 * Restore a {@link #getRuntimePermissionBackup backup-file} of the runtime permissions. 449 * 450 * <p>This might leave some part of the backup-file unapplied if an package mentioned in the 451 * backup-file is not yet installed. It is required that 452 * {@link #applyStagedRuntimePermissionBackup} is called after any package is installed to 453 * apply the rest of the backup-file. 454 * 455 * @param backup the backup-file to restore. The backup is sent asynchronously, hence it should 456 * not be modified after calling this method. 457 * @param user The user to be restore 458 */ 459 @RequiresPermission(anyOf = { 460 Manifest.permission.GRANT_RUNTIME_PERMISSIONS, 461 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS 462 }) stageAndApplyRuntimePermissionsBackup(@onNull byte[] backup, @NonNull UserHandle user)463 public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[] backup, 464 @NonNull UserHandle user) { 465 checkNotNull(backup); 466 checkNotNull(user); 467 468 // Check required permission to fail immediately instead of inside the oneway binder call 469 enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, 470 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS); 471 472 mRemoteService.postAsync(service -> RemoteStream.sendBytes(remotePipe -> { 473 service.stageAndApplyRuntimePermissionsBackup(user, remotePipe); 474 }, backup)) 475 .whenComplete((nullResult, err) -> { 476 if (err != null) { 477 Log.e(TAG, "Error sending permission backup", err); 478 } 479 }); 480 } 481 482 /** 483 * Restore unapplied parts of a {@link #stageAndApplyRuntimePermissionsBackup previously staged} 484 * backup-file of the runtime permissions. 485 * 486 * <p>This should be called every time after a package is installed until the callback 487 * reports that there is no more unapplied backup left. 488 * 489 * @param packageName The package that is ready to have it's permissions restored. 490 * @param user The user the package belongs to 491 * @param executor Executor to execute the callback on 492 * @param callback Is called with {@code true} iff there is still more unapplied backup left 493 */ 494 @RequiresPermission(anyOf = { 495 Manifest.permission.GRANT_RUNTIME_PERMISSIONS, 496 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS 497 }) applyStagedRuntimePermissionBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)498 public void applyStagedRuntimePermissionBackup(@NonNull String packageName, 499 @NonNull UserHandle user, 500 @NonNull @CallbackExecutor Executor executor, 501 @NonNull Consumer<Boolean> callback) { 502 checkNotNull(packageName); 503 checkNotNull(user); 504 checkNotNull(executor); 505 checkNotNull(callback); 506 507 // Check required permission to fail immediately instead of inside the oneway binder call 508 enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, 509 Manifest.permission.RESTORE_RUNTIME_PERMISSIONS); 510 511 mRemoteService.postAsync(service -> { 512 AndroidFuture<Boolean> applyStagedRuntimePermissionBackupResult = 513 new AndroidFuture<>(); 514 service.applyStagedRuntimePermissionBackup(packageName, user, 515 applyStagedRuntimePermissionBackupResult); 516 return applyStagedRuntimePermissionBackupResult; 517 }).whenCompleteAsync((applyStagedRuntimePermissionBackupResult, err) -> { 518 final long token = Binder.clearCallingIdentity(); 519 try { 520 if (err != null) { 521 Log.e(TAG, "Error restoring delayed permissions for " + packageName, err); 522 callback.accept(true); 523 } else { 524 callback.accept( 525 Boolean.TRUE.equals(applyStagedRuntimePermissionBackupResult)); 526 } 527 } finally { 528 Binder.restoreCallingIdentity(token); 529 } 530 }, executor); 531 } 532 533 /** 534 * Dump permission controller state. 535 * 536 * @hide 537 */ dump(@onNull FileDescriptor fd, @Nullable String[] args)538 public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) { 539 try { 540 mRemoteService.postAsync(service -> { 541 return AndroidFuture.runAsync(uncheckExceptions(() -> { 542 service.asBinder().dump(fd, args); 543 }), BackgroundThread.getExecutor()); 544 }).get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 545 } catch (Exception e) { 546 Log.e(TAG, "Could not get dump", e); 547 } 548 } 549 550 /** 551 * Gets the runtime permissions for an app. 552 * 553 * @param packageName The package for which to query. 554 * @param callback Callback to receive the result. 555 * @param handler Handler on which to invoke the callback. 556 * 557 * @hide 558 */ 559 @TestApi 560 @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) getAppPermissions(@onNull String packageName, @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler)561 public void getAppPermissions(@NonNull String packageName, 562 @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) { 563 checkNotNull(packageName); 564 checkNotNull(callback); 565 Handler finalHandler = handler != null ? handler : mHandler; 566 567 mRemoteService.postAsync(service -> { 568 AndroidFuture<List<RuntimePermissionPresentationInfo>> getAppPermissionsResult = 569 new AndroidFuture<>(); 570 service.getAppPermissions(packageName, getAppPermissionsResult); 571 return getAppPermissionsResult; 572 }).whenComplete((getAppPermissionsResult, err) -> finalHandler.post(() -> { 573 if (err != null) { 574 Log.e(TAG, "Error getting app permission", err); 575 callback.onGetAppPermissions(Collections.emptyList()); 576 } else { 577 callback.onGetAppPermissions(CollectionUtils.emptyIfNull(getAppPermissionsResult)); 578 } 579 })); 580 } 581 582 /** 583 * Revoke the permission {@code permissionName} for app {@code packageName} 584 * 585 * @param packageName The package for which to revoke 586 * @param permissionName The permission to revoke 587 * 588 * @hide 589 */ 590 @TestApi 591 @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) revokeRuntimePermission(@onNull String packageName, @NonNull String permissionName)592 public void revokeRuntimePermission(@NonNull String packageName, 593 @NonNull String permissionName) { 594 checkNotNull(packageName); 595 checkNotNull(permissionName); 596 597 mRemoteService.run(service -> service.revokeRuntimePermission(packageName, permissionName)); 598 } 599 600 /** 601 * Count how many apps have one of a set of permissions. 602 * 603 * @param permissionNames The permissions the app might have 604 * @param flags Modify which apps to count. By default all non-system apps that request a 605 * permission are counted 606 * @param callback Callback to receive the result 607 * @param handler Handler on which to invoke the callback 608 * 609 * @hide 610 */ 611 @TestApi 612 @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) countPermissionApps(@onNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler)613 public void countPermissionApps(@NonNull List<String> permissionNames, 614 @CountPermissionAppsFlag int flags, 615 @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) { 616 checkCollectionElementsNotNull(permissionNames, "permissionNames"); 617 checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED); 618 checkNotNull(callback); 619 Handler finalHandler = handler != null ? handler : mHandler; 620 621 mRemoteService.postAsync(service -> { 622 AndroidFuture<Integer> countPermissionAppsResult = new AndroidFuture<>(); 623 service.countPermissionApps(permissionNames, flags, countPermissionAppsResult); 624 return countPermissionAppsResult; 625 }).whenComplete((countPermissionAppsResult, err) -> finalHandler.post(() -> { 626 if (err != null) { 627 Log.e(TAG, "Error counting permission apps", err); 628 callback.onCountPermissionApps(0); 629 } else { 630 callback.onCountPermissionApps(countPermissionAppsResult); 631 } 632 })); 633 } 634 635 /** 636 * Count how many apps have used permissions. 637 * 638 * @param countSystem Also count system apps 639 * @param numMillis The number of milliseconds in the past to check for uses 640 * @param executor Executor on which to invoke the callback 641 * @param callback Callback to receive the result 642 * 643 * @hide 644 */ 645 @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) getPermissionUsages(boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor, @NonNull OnPermissionUsageResultCallback callback)646 public void getPermissionUsages(boolean countSystem, long numMillis, 647 @NonNull @CallbackExecutor Executor executor, 648 @NonNull OnPermissionUsageResultCallback callback) { 649 checkArgumentNonnegative(numMillis); 650 checkNotNull(executor); 651 checkNotNull(callback); 652 653 654 mRemoteService.postAsync(service -> { 655 AndroidFuture<List<RuntimePermissionUsageInfo>> getPermissionUsagesResult = 656 new AndroidFuture<>(); 657 service.getPermissionUsages(countSystem, numMillis, getPermissionUsagesResult); 658 return getPermissionUsagesResult; 659 }).whenCompleteAsync((getPermissionUsagesResult, err) -> { 660 if (err != null) { 661 Log.e(TAG, "Error getting permission usages", err); 662 callback.onPermissionUsageResult(Collections.emptyList()); 663 } else { 664 final long token = Binder.clearCallingIdentity(); 665 try { 666 callback.onPermissionUsageResult( 667 CollectionUtils.emptyIfNull(getPermissionUsagesResult)); 668 } finally { 669 Binder.restoreCallingIdentity(token); 670 } 671 } 672 }, executor); 673 } 674 675 /** 676 * Grant or upgrade runtime permissions. The upgrade could be performed 677 * based on whether the device upgraded, whether the permission database 678 * version is old, or because the permission policy changed. 679 * 680 * @param executor Executor on which to invoke the callback 681 * @param callback Callback to receive the result 682 * 683 * @hide 684 */ 685 @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) grantOrUpgradeDefaultRuntimePermissions( @onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)686 public void grantOrUpgradeDefaultRuntimePermissions( 687 @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { 688 mRemoteService.postAsync(service -> { 689 AndroidFuture<Boolean> grantOrUpgradeDefaultRuntimePermissionsResult = 690 new AndroidFuture<>(); 691 service.grantOrUpgradeDefaultRuntimePermissions( 692 grantOrUpgradeDefaultRuntimePermissionsResult); 693 return grantOrUpgradeDefaultRuntimePermissionsResult; 694 }).whenCompleteAsync((grantOrUpgradeDefaultRuntimePermissionsResult, err) -> { 695 if (err != null) { 696 Log.e(TAG, "Error granting or upgrading runtime permissions", err); 697 callback.accept(false); 698 } else { 699 callback.accept(Boolean.TRUE.equals(grantOrUpgradeDefaultRuntimePermissionsResult)); 700 } 701 }, executor); 702 } 703 704 /** 705 * Gets the description of the privileges associated with the given device profiles 706 * 707 * @param profileName Name of the device profile 708 * @param executor Executor on which to invoke the callback 709 * @param callback Callback to receive the result 710 * 711 * @hide 712 */ 713 @RequiresPermission(Manifest.permission.MANAGE_COMPANION_DEVICES) getPrivilegesDescriptionStringForProfile( @onNull String profileName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<CharSequence> callback)714 public void getPrivilegesDescriptionStringForProfile( 715 @NonNull String profileName, 716 @NonNull @CallbackExecutor Executor executor, 717 @NonNull Consumer<CharSequence> callback) { 718 mRemoteService.postAsync(service -> { 719 AndroidFuture<String> future = new AndroidFuture<>(); 720 service.getPrivilegesDescriptionStringForProfile(profileName, future); 721 return future; 722 }).whenCompleteAsync((description, err) -> { 723 if (err != null) { 724 Log.e(TAG, "Error from getPrivilegesDescriptionStringForProfile", err); 725 callback.accept(null); 726 } else { 727 callback.accept(description); 728 } 729 }, executor); 730 } 731 732 /** 733 * @see PermissionControllerManager#updateUserSensitiveForApp 734 * @hide 735 */ updateUserSensitive()736 public void updateUserSensitive() { 737 updateUserSensitiveForApp(Process.INVALID_UID); 738 } 739 740 /** 741 * @see PermissionControllerService#onUpdateUserSensitiveForApp 742 * @hide 743 */ updateUserSensitiveForApp(int uid)744 public void updateUserSensitiveForApp(int uid) { 745 mRemoteService.postAsync(service -> { 746 AndroidFuture<Void> future = new AndroidFuture<>(); 747 service.updateUserSensitiveForApp(uid, future); 748 return future; 749 }).whenComplete((res, err) -> { 750 if (err != null) { 751 Log.e(TAG, "Error updating user_sensitive flags for uid " + uid, err); 752 } 753 }); 754 } 755 756 /** 757 * Called when a package that has permissions registered as "one-time" is considered 758 * inactive. 759 * 760 * @param packageName The package which became inactive 761 * 762 * @hide 763 */ 764 @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) notifyOneTimePermissionSessionTimeout(@onNull String packageName)765 public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) { 766 mRemoteService.run( 767 service -> service.notifyOneTimePermissionSessionTimeout(packageName)); 768 } 769 770 /** 771 * Get the platform permissions which belong to a particular permission group. 772 * 773 * @param permissionGroupName The permission group whose permissions are desired 774 * @param executor Executor on which to invoke the callback 775 * @param callback A callback which will receive a list of the platform permissions in the 776 * group, or empty if the group is not a valid platform group, or there 777 * was an exception. 778 * 779 * @hide 780 */ getPlatformPermissionsForGroup(@onNull String permissionGroupName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<List<String>> callback)781 public void getPlatformPermissionsForGroup(@NonNull String permissionGroupName, 782 @NonNull @CallbackExecutor Executor executor, 783 @NonNull Consumer<List<String>> callback) { 784 mRemoteService.postAsync(service -> { 785 AndroidFuture<List<String>> future = new AndroidFuture<>(); 786 service.getPlatformPermissionsForGroup(permissionGroupName, future); 787 return future; 788 }).whenCompleteAsync((result, err) -> { 789 final long token = Binder.clearCallingIdentity(); 790 try { 791 if (err != null) { 792 Log.e(TAG, "Failed to get permissions of " + permissionGroupName, err); 793 callback.accept(new ArrayList<>()); 794 } else { 795 callback.accept(result); 796 } 797 } finally { 798 Binder.restoreCallingIdentity(token); 799 } 800 }, executor); 801 } 802 803 /** 804 * Get the platform group of a particular permission, if the permission is a platform 805 * permission. 806 * 807 * @param permissionName The permission name whose group is desired 808 * @param executor Executor on which to invoke the callback 809 * @param callback A callback which will receive the name of the permission group this 810 * permission belongs to, or null if it has no group, is not a platform 811 * permission, or there was an exception. 812 * 813 * @hide 814 */ getGroupOfPlatformPermission(@onNull String permissionName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<String> callback)815 public void getGroupOfPlatformPermission(@NonNull String permissionName, 816 @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<String> callback) { 817 mRemoteService.postAsync(service -> { 818 AndroidFuture<String> future = new AndroidFuture<>(); 819 service.getGroupOfPlatformPermission(permissionName, future); 820 return future; 821 }).whenCompleteAsync((result, err) -> { 822 final long token = Binder.clearCallingIdentity(); 823 try { 824 if (err != null) { 825 Log.e(TAG, "Failed to get group of " + permissionName, err); 826 callback.accept(null); 827 } else { 828 callback.accept(result); 829 } 830 } finally { 831 Binder.restoreCallingIdentity(token); 832 } 833 }, executor); 834 } 835 836 /** 837 * Get the number of unused, hibernating apps for the user. 838 * 839 * @param executor executor to run callback on 840 * @param callback callback for when result is generated 841 */ getUnusedAppCount(@onNull @allbackExecutor Executor executor, @NonNull IntConsumer callback)842 public void getUnusedAppCount(@NonNull @CallbackExecutor Executor executor, 843 @NonNull IntConsumer callback) { 844 checkNotNull(executor); 845 checkNotNull(callback); 846 847 mRemoteService.postAsync(service -> { 848 AndroidFuture<Integer> unusedAppCountResult = new AndroidFuture<>(); 849 service.getUnusedAppCount(unusedAppCountResult); 850 return unusedAppCountResult; 851 }).whenCompleteAsync((count, err) -> { 852 if (err != null) { 853 Log.e(TAG, "Error getting unused app count", err); 854 callback.accept(0); 855 } else { 856 final long token = Binder.clearCallingIdentity(); 857 try { 858 callback.accept((int) count); 859 } finally { 860 Binder.restoreCallingIdentity(token); 861 } 862 } 863 }, executor); 864 } 865 866 /** 867 * Get the hibernation eligibility of a package. See {@link HibernationEligibilityFlag}. 868 * 869 * @param packageName package name to check eligibility 870 * @param executor executor to run callback on 871 * @param callback callback for when result is generated 872 */ 873 @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION) getHibernationEligibility(@onNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer callback)874 public void getHibernationEligibility(@NonNull String packageName, 875 @NonNull @CallbackExecutor Executor executor, 876 @NonNull IntConsumer callback) { 877 checkNotNull(executor); 878 checkNotNull(callback); 879 880 mRemoteService.postAsync(service -> { 881 AndroidFuture<Integer> eligibilityResult = new AndroidFuture<>(); 882 service.getHibernationEligibility(packageName, eligibilityResult); 883 return eligibilityResult; 884 }).whenCompleteAsync((eligibility, err) -> { 885 if (err != null) { 886 Log.e(TAG, "Error getting hibernation eligibility", err); 887 callback.accept(HIBERNATION_ELIGIBILITY_UNKNOWN); 888 } else { 889 final long token = Binder.clearCallingIdentity(); 890 try { 891 callback.accept(eligibility); 892 } finally { 893 Binder.restoreCallingIdentity(token); 894 } 895 } 896 }, executor); 897 } 898 899 /** 900 * Triggers the revocation of one or more permissions for a package, under the following 901 * conditions: 902 * <ul> 903 * <li>The package {@code packageName} must be under the same UID as the calling process 904 * (typically, the target package is the calling package). 905 * <li>Each permission in {@code permissions} must be granted to the package 906 * {@code packageName}. 907 * <li>Each permission in {@code permissions} must be a runtime permission. 908 * </ul> 909 * <p> 910 * Background permissions which have no corresponding foreground permission still granted once 911 * the revocation is effective will also be revoked. 912 * <p> 913 * This revocation happens asynchronously and kills all processes running in the same UID as 914 * {@code packageName}. It will be triggered once it is safe to do so. 915 * 916 * @param packageName The name of the package for which the permissions will be revoked. 917 * @param permissions List of permissions to be revoked. 918 * 919 * @see Context#revokeSelfPermissionsOnKill(java.util.Collection) 920 * 921 * @hide 922 */ revokeSelfPermissionsOnKill(@onNull String packageName, @NonNull List<String> permissions)923 public void revokeSelfPermissionsOnKill(@NonNull String packageName, 924 @NonNull List<String> permissions) { 925 mRemoteService.postAsync(service -> { 926 AndroidFuture<Void> callback = new AndroidFuture<>(); 927 service.revokeSelfPermissionsOnKill(packageName, permissions, callback); 928 return callback; 929 }).whenComplete((result, err) -> { 930 if (err != null) { 931 Log.e(TAG, "Failed to self revoke " + String.join(",", permissions) 932 + " for package " + packageName, err); 933 } 934 }); 935 } 936 } 937