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