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