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 com.android.permissioncontroller.permission.service; 18 19 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT; 20 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED; 21 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; 22 import static android.content.pm.PackageManager.GET_PERMISSIONS; 23 import static android.permission.PermissionControllerManager.REASON_INSTALLER_POLICY_VIOLATION; 24 import static android.permission.PermissionControllerManager.REASON_MALWARE; 25 import static android.util.Xml.newSerializer; 26 27 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_ONE_TIME_PERMISSION_REVOKED; 28 29 import static java.nio.charset.StandardCharsets.UTF_8; 30 31 import android.Manifest; 32 import android.content.Intent; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.os.AsyncTask; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Process; 39 import android.os.UserHandle; 40 import android.os.UserManager; 41 import android.permission.AdminPermissionControlParams; 42 import android.permission.PermissionManager; 43 import android.permission.RuntimePermissionPresentationInfo; 44 import android.permission.RuntimePermissionUsageInfo; 45 import android.util.ArrayMap; 46 import android.util.ArraySet; 47 import android.util.Log; 48 import android.util.Xml; 49 50 import androidx.annotation.NonNull; 51 import androidx.annotation.Nullable; 52 53 import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto; 54 import com.android.permissioncontroller.PermissionControllerStatsLog; 55 import com.android.permissioncontroller.permission.model.AppPermissionGroup; 56 import com.android.permissioncontroller.permission.model.AppPermissions; 57 import com.android.permissioncontroller.permission.model.Permission; 58 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo; 59 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState; 60 import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier; 61 import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils; 62 import com.android.permissioncontroller.permission.utils.ArrayUtils; 63 import com.android.permissioncontroller.permission.utils.KotlinUtils; 64 import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils; 65 import com.android.permissioncontroller.permission.utils.Utils; 66 import com.android.permissioncontroller.role.model.Role; 67 import com.android.permissioncontroller.role.model.Roles; 68 69 import org.xmlpull.v1.XmlPullParser; 70 import org.xmlpull.v1.XmlSerializer; 71 72 import java.io.FileDescriptor; 73 import java.io.FileOutputStream; 74 import java.io.IOException; 75 import java.io.InputStream; 76 import java.io.OutputStream; 77 import java.io.PrintWriter; 78 import java.nio.charset.StandardCharsets; 79 import java.util.ArrayList; 80 import java.util.Collections; 81 import java.util.HashSet; 82 import java.util.List; 83 import java.util.Map; 84 import java.util.Set; 85 import java.util.concurrent.Executor; 86 import java.util.function.Consumer; 87 import java.util.function.IntConsumer; 88 import java.util.stream.Collectors; 89 90 import kotlin.Pair; 91 import kotlinx.coroutines.BuildersKt; 92 import kotlinx.coroutines.GlobalScope; 93 94 /** 95 * Calls from the system into the permission controller. 96 * 97 * All reading methods are called async, and all writing method are called on the AsyncTask single 98 * thread executor so that multiple writes won't override each other concurrently. 99 */ 100 public final class PermissionControllerServiceImpl extends PermissionControllerLifecycleService { 101 private static final String LOG_TAG = PermissionControllerServiceImpl.class.getSimpleName(); 102 public static final String ONE_TIME_PERMISSION_REVOKED_REASON = "one-time permission revoked"; 103 private static final int MAX_RETRY_ATTEMPTS = 3; 104 private static final long RETRY_DELAY_MS = 500; 105 106 107 private final PermissionControllerServiceModel mServiceModel = new 108 PermissionControllerServiceModel(this); 109 110 @Override onUnbind(@ullable Intent intent)111 public boolean onUnbind(@Nullable Intent intent) { 112 mServiceModel.removeObservers(); 113 return super.onUnbind(intent); 114 } 115 116 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)117 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 118 PermissionControllerDumpProto dump; 119 try { 120 dump = BuildersKt.runBlocking( 121 GlobalScope.INSTANCE.getCoroutineContext(), 122 (coroutineScope, continuation) -> mServiceModel.onDump(continuation)); 123 } catch (Exception e) { 124 Log.e(LOG_TAG, "Cannot produce dump", e); 125 return; 126 } 127 128 if (ArrayUtils.contains(args, "--proto")) { 129 try (OutputStream out = new FileOutputStream(fd)) { 130 dump.writeTo(out); 131 } catch (IOException e) { 132 Log.e(LOG_TAG, "Cannot write dump", e); 133 } 134 } else { 135 writer.println(dump.toString()); 136 writer.flush(); 137 } 138 } 139 140 /** 141 * Expand {@code perms} by split permissions for an app with the given targetSDK. 142 * 143 * @param perms The permissions that should be expanded 144 * @param targetSDK The target SDK to expand for 145 * 146 * @return The expanded permissions 147 */ addSplitPermissions(@onNull List<String> perms, int targetSDK)148 private @NonNull ArrayList<String> addSplitPermissions(@NonNull List<String> perms, 149 int targetSDK) { 150 List<PermissionManager.SplitPermissionInfo> splitPerms = 151 getSystemService(PermissionManager.class).getSplitPermissions(); 152 153 // Add split permissions to the request 154 ArrayList<String> expandedPerms = new ArrayList<>(perms); 155 int numReqPerms = perms.size(); 156 for (int reqPermNum = 0; reqPermNum < numReqPerms; reqPermNum++) { 157 String reqPerm = perms.get(reqPermNum); 158 159 int numSplitPerms = splitPerms.size(); 160 for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { 161 PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(splitPermNum); 162 163 if (targetSDK < splitPerm.getTargetSdk() 164 && splitPerm.getSplitPermission().equals(reqPerm)) { 165 expandedPerms.addAll(splitPerm.getNewPermissions()); 166 } 167 } 168 } 169 170 return expandedPerms; 171 } 172 173 /** 174 * Get the package info for a package. 175 * 176 * @param pkg The package name 177 * 178 * @return the package info or {@code null} if the package could not be found 179 */ getPkgInfo(@onNull String pkg)180 private @Nullable PackageInfo getPkgInfo(@NonNull String pkg) { 181 try { 182 return getPackageManager().getPackageInfo(pkg, GET_PERMISSIONS); 183 } catch (PackageManager.NameNotFoundException e) { 184 Log.w(LOG_TAG, pkg + " not found", e); 185 return null; 186 } 187 } 188 189 /** 190 * Given a set of permissions, find all permission groups of an app that can be revoked and that 191 * contain any of the permissions. 192 * 193 * @param permissions The permissions to revoke 194 * @param appPerms The {@link AppPermissions} for the app that is currently investigated 195 * 196 * @return The groups to revoke 197 */ getRevocableGroupsForPermissions( @onNull ArrayList<String> permissions, @NonNull AppPermissions appPerms)198 private @NonNull ArrayList<AppPermissionGroup> getRevocableGroupsForPermissions( 199 @NonNull ArrayList<String> permissions, @NonNull AppPermissions appPerms) { 200 ArrayList<AppPermissionGroup> groupsToRevoke = new ArrayList<>(); 201 int numGroups = appPerms.getPermissionGroups().size(); 202 for (int groupNum = 0; groupNum < numGroups; groupNum++) { 203 AppPermissionGroup group = appPerms.getPermissionGroups().get(groupNum); 204 205 // Do not override fixed permissions 206 if (group.isPolicyFixed() || group.isSystemFixed()) { 207 continue; 208 } 209 210 int numPerms = permissions.size(); 211 for (int permNum = 0; permNum < numPerms; permNum++) { 212 String reqPerm = permissions.get(permNum); 213 214 if (group.hasPermission(reqPerm)) { 215 groupsToRevoke.add(group); 216 217 // If fg permissions get revoked also revoke bg permissions as bg 218 // permissions require fg permissions. 219 AppPermissionGroup bgPerms = group.getBackgroundPermissions(); 220 if (bgPerms != null) { 221 groupsToRevoke.add(bgPerms); 222 } 223 } else { 224 AppPermissionGroup bgPerms = group.getBackgroundPermissions(); 225 if (bgPerms != null && bgPerms.hasPermission(reqPerm)) { 226 groupsToRevoke.add(bgPerms); 227 } 228 } 229 } 230 } 231 232 return groupsToRevoke; 233 } 234 235 /** 236 * Revoke all permissions of some groups. 237 * 238 * @param groupsToRevoke The groups 239 * 240 * @return The permissions that were revoked 241 */ revokePermissionGroups( @onNull ArrayList<AppPermissionGroup> groupsToRevoke)242 private @NonNull ArrayList<String> revokePermissionGroups( 243 @NonNull ArrayList<AppPermissionGroup> groupsToRevoke) { 244 ArrayList<String> revokedPerms = new ArrayList<>(); 245 246 int numGroupsToRevoke = groupsToRevoke.size(); 247 for (int groupsToRevokeNum = 0; groupsToRevokeNum < numGroupsToRevoke; 248 groupsToRevokeNum++) { 249 AppPermissionGroup group = groupsToRevoke.get(groupsToRevokeNum); 250 ArrayList<Permission> perms = group.getPermissions(); 251 252 // Mark the permissions as reviewed as we don't want to use to accidentally grant 253 // the permission during review 254 group.unsetReviewRequired(); 255 256 int numPerms = perms.size(); 257 for (int permNum = 0; permNum < numPerms; permNum++) { 258 Permission perm = perms.get(permNum); 259 260 // Only count individual permissions that are actually revoked 261 if (perm.isGrantedIncludingAppOp()) { 262 revokedPerms.add(perm.getName()); 263 } 264 } 265 266 group.revokeRuntimePermissions(false); 267 } 268 269 return revokedPerms; 270 } 271 272 @Override onRevokeRuntimePermissions(@onNull Map<String, List<String>> request, boolean doDryRun, int reason, @NonNull String callerPackageName, @NonNull Consumer<Map<String, List<String>>> callback)273 public void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> request, 274 boolean doDryRun, int reason, @NonNull String callerPackageName, 275 @NonNull Consumer<Map<String, List<String>>> callback) { 276 AsyncTask.execute(() -> callback.accept(onRevokeRuntimePermissions(request, doDryRun, 277 reason, callerPackageName))); 278 } 279 onRevokeRuntimePermissions( @onNull Map<String, List<String>> request, boolean doDryRun, int reason, @NonNull String callerPackageName)280 private @NonNull Map<String, List<String>> onRevokeRuntimePermissions( 281 @NonNull Map<String, List<String>> request, boolean doDryRun, 282 int reason, @NonNull String callerPackageName) { 283 // The reason parameter is not checked by platform code as this might need to be updated 284 // async to platform releases. 285 if (reason != REASON_MALWARE && reason != REASON_INSTALLER_POLICY_VIOLATION) { 286 Log.e(LOG_TAG, "Invalid reason " + reason); 287 return Collections.emptyMap(); 288 } 289 290 PackageManager pm = getPackageManager(); 291 292 PackageInfo callerPkgInfo = getPkgInfo(callerPackageName); 293 if (callerPkgInfo == null) { 294 return Collections.emptyMap(); 295 } 296 int callerTargetSdk = callerPkgInfo.applicationInfo.targetSdkVersion; 297 298 Map<String, List<String>> actuallyRevokedPerms = new ArrayMap<>(); 299 ArrayList<AppPermissions> appsWithRevokedPerms = new ArrayList<>(); 300 301 for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { 302 PackageInfo requestedPkgInfo = getPkgInfo(appRequest.getKey()); 303 if (requestedPkgInfo == null) { 304 continue; 305 } 306 307 // Permissions are per UID. Hence permissions will be removed from all apps sharing an 308 // UID. 309 String[] pkgNames = pm.getPackagesForUid(requestedPkgInfo.applicationInfo.uid); 310 if (pkgNames == null) { 311 continue; 312 } 313 314 int numPkgNames = pkgNames.length; 315 for (int pkgNum = 0; pkgNum < numPkgNames; pkgNum++) { 316 String pkgName = pkgNames[pkgNum]; 317 318 PackageInfo pkgInfo = getPkgInfo(pkgName); 319 if (pkgInfo == null) { 320 continue; 321 } 322 323 // If the revocation is because of a market policy violation only the installer can 324 // revoke the permissions. 325 if (reason == REASON_INSTALLER_POLICY_VIOLATION 326 && !callerPackageName.equals(pm.getInstallerPackageName(pkgName))) { 327 Log.i(LOG_TAG, "Ignoring " + pkgName + " as it is not installed by " 328 + callerPackageName); 329 continue; 330 } 331 332 // In rare cases the caller does not know about the permissions that have been added 333 // due to splits. Hence add them now. 334 ArrayList<String> expandedPerms = addSplitPermissions(appRequest.getValue(), 335 callerTargetSdk); 336 337 AppPermissions appPerms = new AppPermissions(this, pkgInfo, false, true, null); 338 339 // First find the groups that should be revoked and then revoke all permissions of 340 // these groups. This is needed as soon as a single permission in the group is 341 // granted, all other permissions get auto-granted on request. 342 ArrayList<AppPermissionGroup> groupsToRevoke = getRevocableGroupsForPermissions( 343 expandedPerms, appPerms); 344 ArrayList<String> revokedPerms = revokePermissionGroups(groupsToRevoke); 345 346 // In racy conditions the group might not have had granted permissions anymore 347 if (!revokedPerms.isEmpty()) { 348 actuallyRevokedPerms.put(pkgName, revokedPerms); 349 appsWithRevokedPerms.add(appPerms); 350 } 351 } 352 } 353 354 // Persist changes after we computed everything to remove 355 // This is necessary as we would otherwise only look at the first app of a shared UID. 356 if (!doDryRun) { 357 int numChangedApps = appsWithRevokedPerms.size(); 358 for (int i = 0; i < numChangedApps; i++) { 359 appsWithRevokedPerms.get(i).persistChanges(true); 360 } 361 } 362 363 return actuallyRevokedPerms; 364 } 365 366 @Override onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup, @NonNull Runnable callback)367 public void onGetRuntimePermissionsBackup(@NonNull UserHandle user, 368 @NonNull OutputStream backup, @NonNull Runnable callback) { 369 AsyncTask.execute(() -> { 370 onGetRuntimePermissionsBackup(user, backup); 371 callback.run(); 372 }); 373 } 374 onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup)375 private void onGetRuntimePermissionsBackup(@NonNull UserHandle user, 376 @NonNull OutputStream backup) { 377 BackupHelper backupHelper = new BackupHelper(this, user); 378 379 try { 380 XmlSerializer serializer = newSerializer(); 381 serializer.setOutput(backup, UTF_8.name()); 382 383 backupHelper.writeState(serializer); 384 serializer.flush(); 385 } catch (Exception e) { 386 Log.e(LOG_TAG, "Unable to write permissions backup", e); 387 } 388 } 389 390 @Override onStageAndApplyRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup, @NonNull Runnable callback)391 public void onStageAndApplyRuntimePermissionsBackup(@NonNull UserHandle user, 392 @NonNull InputStream backup, @NonNull Runnable callback) { 393 AsyncTask.execute(() -> { 394 onRestoreRuntimePermissionsBackup(user, backup); 395 callback.run(); 396 }); 397 } 398 onRestoreRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup)399 private void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user, 400 @NonNull InputStream backup) { 401 try { 402 XmlPullParser parser = Xml.newPullParser(); 403 parser.setInput(backup, StandardCharsets.UTF_8.name()); 404 405 new BackupHelper(this, user).restoreState(parser); 406 } catch (Exception e) { 407 Log.e(LOG_TAG, "Exception restoring permissions: " + e.getMessage()); 408 } 409 } 410 411 @Override onApplyStagedRuntimePermissionBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull Consumer<Boolean> callback)412 public void onApplyStagedRuntimePermissionBackup(@NonNull String packageName, 413 @NonNull UserHandle user, @NonNull Consumer<Boolean> callback) { 414 AsyncTask.execute(() -> callback.accept( 415 onRestoreDelayedRuntimePermissionsBackup(packageName, user))); 416 } 417 onRestoreDelayedRuntimePermissionsBackup(@onNull String packageName, @NonNull UserHandle user)418 private boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName, 419 @NonNull UserHandle user) { 420 try { 421 return new BackupHelper(this, user).restoreDelayedState(packageName); 422 } catch (Exception e) { 423 Log.e(LOG_TAG, "Exception restoring delayed permissions: " + e.getMessage()); 424 return false; 425 } 426 } 427 428 @Override onGetAppPermissions(@onNull String packageName, @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback)429 public void onGetAppPermissions(@NonNull String packageName, 430 @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback) { 431 mServiceModel.onGetAppPermissions(packageName, (groupUiInfos) -> { 432 List<RuntimePermissionPresentationInfo> permissions = new ArrayList<>(); 433 434 for (Pair<String, AppPermGroupUiInfo> groupNameAndUiInfo : groupUiInfos) { 435 String groupName = groupNameAndUiInfo.getFirst(); 436 AppPermGroupUiInfo uiInfo = groupNameAndUiInfo.getSecond(); 437 boolean isPlatform = Utils.getPlatformPermissionGroups().contains(groupName); 438 CharSequence label = KotlinUtils.INSTANCE.getPermGroupLabel(this, groupName); 439 440 RuntimePermissionPresentationInfo permission = 441 new RuntimePermissionPresentationInfo(label, 442 uiInfo.getPermGrantState() != PermGrantState.PERMS_DENIED 443 && uiInfo.getPermGrantState() != PermGrantState.PERMS_ASK, 444 isPlatform); 445 permissions.add(permission); 446 } 447 callback.accept(permissions); 448 }); 449 } 450 451 @Override onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName, @NonNull Runnable callback)452 public void onRevokeRuntimePermission(@NonNull String packageName, 453 @NonNull String permissionName, @NonNull Runnable callback) { 454 AsyncTask.execute(() -> { 455 onRevokeRuntimePermission(packageName, permissionName); 456 callback.run(); 457 }); 458 } 459 onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName)460 private void onRevokeRuntimePermission(@NonNull String packageName, 461 @NonNull String permissionName) { 462 try { 463 final PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 464 GET_PERMISSIONS); 465 final AppPermissions appPermissions = new AppPermissions(this, packageInfo, false, 466 null); 467 468 final AppPermissionGroup appPermissionGroup = appPermissions.getGroupForPermission( 469 permissionName); 470 471 if (appPermissionGroup != null) { 472 appPermissionGroup.revokeRuntimePermissions(false); 473 } 474 } catch (PackageManager.NameNotFoundException e) { 475 Log.e(LOG_TAG, "Error getting package:" + packageName, e); 476 } 477 } 478 479 @Override onCountPermissionApps(@onNull List<String> permissionNames, int flags, @NonNull IntConsumer callback)480 public void onCountPermissionApps(@NonNull List<String> permissionNames, int flags, 481 @NonNull IntConsumer callback) { 482 // There is no data processing needed, so we just directly pass the result onto the callback 483 mServiceModel.onCountPermissionAppsLiveData(permissionNames, flags, 484 callback); 485 } 486 487 /** 488 * Deprecated api call, only returns null. 489 */ 490 @Override 491 @Deprecated onGetPermissionUsages(boolean countSystem, long numMillis, @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback)492 public void onGetPermissionUsages(boolean countSystem, long numMillis, 493 @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback) { 494 callback.accept(null); 495 } 496 497 @Override onSetRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, @NonNull Consumer<Boolean> callback)498 public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName, 499 @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, 500 @NonNull Consumer<Boolean> callback) { 501 AsyncTask.execute(() -> callback.accept(onSetRuntimePermissionGrantStateByDeviceAdmin( 502 callerPackageName, packageName, unexpandedPermission, grantState, true))); 503 } 504 505 /** 506 * Admin control based on params. 507 */ 508 @Override onSetRuntimePermissionGrantStateByDeviceAdmin( @onNull String callerPackageName, @NonNull AdminPermissionControlParams params, @NonNull Consumer<Boolean> callback)509 public void onSetRuntimePermissionGrantStateByDeviceAdmin( 510 @NonNull String callerPackageName, @NonNull AdminPermissionControlParams params, 511 @NonNull Consumer<Boolean> callback) { 512 AsyncTask.execute(() -> callback.accept(onSetRuntimePermissionGrantStateByDeviceAdmin( 513 callerPackageName, params.getGranteePackageName(), params.getPermission(), 514 params.getGrantState(), params.canAdminGrantSensorsPermissions()))); 515 } 516 onSetRuntimePermissionGrantStateByDeviceAdmin(@onNull String callerPackageName, @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, boolean canAdminGrantSensorsPermissions)517 private boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName, 518 @NonNull String packageName, @NonNull String unexpandedPermission, int grantState, 519 boolean canAdminGrantSensorsPermissions) { 520 PackageInfo callerPkgInfo = getPkgInfo(callerPackageName); 521 if (callerPkgInfo == null) { 522 Log.w(LOG_TAG, "Cannot fix " + unexpandedPermission + " as admin " 523 + callerPackageName + " cannot be found"); 524 return false; 525 } 526 527 PackageInfo pkgInfo = getPkgInfo(packageName); 528 if (pkgInfo == null) { 529 Log.w(LOG_TAG, "Cannot fix " + unexpandedPermission + " as " + packageName 530 + " cannot be found"); 531 return false; 532 } 533 534 ArrayList<String> expandedPermissions = addSplitPermissions( 535 Collections.singletonList(unexpandedPermission), 536 callerPkgInfo.applicationInfo.targetSdkVersion); 537 538 AppPermissions app = new AppPermissions(this, pkgInfo, false, true, null); 539 AutoGrantPermissionsNotifier autoGrantPermissionsNotifier = 540 new AutoGrantPermissionsNotifier(this, pkgInfo); 541 542 final boolean isManagedProfile = getSystemService(UserManager.class).isManagedProfile(); 543 544 int numPerms = expandedPermissions.size(); 545 for (int i = 0; i < numPerms; i++) { 546 String permName = expandedPermissions.get(i); 547 AppPermissionGroup group = app.getGroupForPermission(permName); 548 if (group == null || group.isSystemFixed()) { 549 continue; 550 } 551 552 Permission perm = group.getPermission(permName); 553 if (perm == null) { 554 continue; 555 } 556 557 switch (grantState) { 558 case PERMISSION_GRANT_STATE_GRANTED: 559 if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(perm.getName(), 560 canAdminGrantSensorsPermissions, isManagedProfile)) { 561 perm.setPolicyFixed(true); 562 group.grantRuntimePermissions(false, false, new String[]{permName}); 563 autoGrantPermissionsNotifier.onPermissionAutoGranted(permName); 564 } else { 565 // similar to PERMISSION_GRANT_STATE_DEFAULT 566 perm.setPolicyFixed(false); 567 } 568 break; 569 case PERMISSION_GRANT_STATE_DENIED: 570 perm.setPolicyFixed(true); 571 group.revokeRuntimePermissions(false, new String[]{permName}); 572 break; 573 case PERMISSION_GRANT_STATE_DEFAULT: 574 perm.setPolicyFixed(false); 575 break; 576 default: 577 return false; 578 } 579 } 580 581 app.persistChanges(grantState == PERMISSION_GRANT_STATE_DENIED 582 || !callerPackageName.equals(packageName)); 583 autoGrantPermissionsNotifier.notifyOfAutoGrantPermissions(false); 584 585 return true; 586 } 587 588 @Override onGrantOrUpgradeDefaultRuntimePermissions(@onNull Runnable callback)589 public void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback) { 590 performDefaultPermissionGrants(); 591 RuntimePermissionsUpgradeController.INSTANCE.upgradeIfNeeded(this, () -> { 592 callback.run(); 593 }); 594 } 595 performDefaultPermissionGrants()596 private void performDefaultPermissionGrants() { 597 // TODO: Default permission grants should go here 598 } 599 600 @Override onUpdateUserSensitivePermissionFlags(int uid, Executor executor, Runnable callback)601 public void onUpdateUserSensitivePermissionFlags(int uid, Executor executor, 602 Runnable callback) { 603 onUpdateUserSensistivePermissionFlagsWithRetry(uid, executor, callback, 0); 604 } 605 onUpdateUserSensistivePermissionFlagsWithRetry(int uid, Executor executor, Runnable callback, int numAttempts)606 private void onUpdateUserSensistivePermissionFlagsWithRetry(int uid, Executor executor, 607 Runnable callback, int numAttempts) { 608 String idString = uid == Process.INVALID_UID 609 ? "user " + Process.myUserHandle().getIdentifier() : "uid " + uid; 610 try { 611 Log.i(LOG_TAG, "Updating user sensitive for " + idString); 612 if (uid == Process.INVALID_UID) { 613 UserSensitiveFlagsUtils.updateUserSensitiveForUser(Process.myUserHandle(), 614 () -> executor.execute(callback)); 615 } else { 616 UserSensitiveFlagsUtils.updateUserSensitiveForUid(uid, 617 () -> executor.execute(callback)); 618 } 619 } catch (Exception e) { 620 // We specifically want to catch DeadSystemExceptions, but cannot explicitly request 621 // them, as it results in a compiler error 622 Log.w(LOG_TAG, "Failed to complete user sensitive update for " + idString 623 + ", attempt number " + (numAttempts + 1) + " of " + MAX_RETRY_ATTEMPTS, e); 624 if (numAttempts == MAX_RETRY_ATTEMPTS) { 625 throw e; 626 } else { 627 int attempts = numAttempts + 1; 628 Handler h = new Handler(Looper.getMainLooper()); 629 h.postDelayed(() -> onUpdateUserSensistivePermissionFlagsWithRetry(uid, 630 executor, callback, attempts), RETRY_DELAY_MS); 631 } 632 } 633 634 } 635 636 @Override onOneTimePermissionSessionTimeout(@onNull String packageName)637 public void onOneTimePermissionSessionTimeout(@NonNull String packageName) { 638 PackageManager pm = getPackageManager(); 639 PackageInfo packageInfo; 640 int uid; 641 try { 642 packageInfo = pm.getPackageInfo(packageName, GET_PERMISSIONS); 643 uid = pm.getPackageUid(packageName, 0); 644 } catch (PackageManager.NameNotFoundException e) { 645 throw new RuntimeException(e); 646 } 647 648 String[] permissions = packageInfo.requestedPermissions; 649 if (permissions == null) { 650 return; 651 } 652 653 Set<AppPermissionGroup> groups = new ArraySet<>(); 654 for (String permission : permissions) { 655 AppPermissionGroup group = AppPermissionGroup.create(this, packageInfo, permission, 656 true); 657 if (group != null) { 658 AppPermissionGroup bgGroup = group.getBackgroundPermissions(); 659 boolean isBgGroupOneTime = bgGroup != null && bgGroup.isOneTime(); 660 if (group.isOneTime() || isBgGroupOneTime) { 661 groups.add(group); 662 } 663 } 664 } 665 long requestId = Utils.getValidSessionId(); 666 for (AppPermissionGroup group : groups) { 667 AppPermissionGroup bgGroup = group.getBackgroundPermissions(); 668 if (group.areRuntimePermissionsGranted(null, true, false)) { 669 logOneTimeSessionRevoke(packageName, uid, group, requestId); 670 // Revoke only one time granted permissions if not all 671 List<String> oneTimeGrantedPermissions = group.getPermissions().stream() 672 .filter(Permission::isOneTime).filter(Permission::isGranted) 673 .map(Permission::getName).collect(Collectors.toList()); 674 if (group.getPermissions().size() == oneTimeGrantedPermissions.size()) { 675 group.revokeRuntimePermissions(false); 676 } else { 677 group.revokeRuntimePermissions(false, 678 oneTimeGrantedPermissions.toArray(new String[0])); 679 } 680 for (String permissionName : oneTimeGrantedPermissions) { 681 // We only reset the USER_SET flag if the permission was granted. 682 Permission permission = group.getPermission(permissionName); 683 if (permission != null) { 684 permission.setUserSet(false); 685 } 686 } 687 if (bgGroup != null) { 688 // We also revoke background permissions if all foreground permissions are 689 // getting revoked. 690 if (group.getPermissions().size() == oneTimeGrantedPermissions.size()) { 691 bgGroup.revokeRuntimePermissions(false); 692 } else { 693 bgGroup.revokeRuntimePermissions(false, 694 bgGroup.getPermissions().stream() 695 .filter(Permission::isOneTime).filter(Permission::isGranted) 696 .map(Permission::getName).toArray(String[]::new)); 697 } 698 } 699 } 700 if (!group.supportsOneTimeGrant()) { 701 group.setOneTime(false); 702 } 703 group.persistChanges(false, ONE_TIME_PERMISSION_REVOKED_REASON); 704 if (bgGroup != null) { 705 if (!bgGroup.supportsOneTimeGrant()) { 706 bgGroup.setOneTime(false); 707 } 708 bgGroup.persistChanges(false, ONE_TIME_PERMISSION_REVOKED_REASON); 709 } 710 } 711 } 712 logOneTimeSessionRevoke(@onNull String packageName, int uid, AppPermissionGroup group, long requestId)713 private void logOneTimeSessionRevoke(@NonNull String packageName, int uid, 714 AppPermissionGroup group, long requestId) { 715 // used to keep lines below 100 chars 716 int r = PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_ONE_TIME_PERMISSION_REVOKED; 717 718 for (Permission permission : group.getPermissions()) { 719 if (permission.isGranted()) { 720 String permName = permission.getName(); 721 Log.v(LOG_TAG, 722 "Permission grant result requestId=" + requestId + " callingUid=" 723 + uid + " callingPackage=" + packageName + " permission=" 724 + permName + " isImplicit=false" + " result=" + r); 725 726 PermissionControllerStatsLog.write( 727 PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, 728 requestId, uid, packageName, permName, false, r); 729 } 730 } 731 } 732 733 @Override getPrivilegesDescriptionStringForProfile(@onNull String deviceProfileName)734 public String getPrivilegesDescriptionStringForProfile(@NonNull String deviceProfileName) { 735 Role role = Roles.get(this).get(deviceProfileName); 736 if (role == null) { 737 throw new IllegalArgumentException("No such role: " + deviceProfileName); 738 } 739 return getString(role.getDescriptionResource(), "APP_NAME"); 740 } 741 742 @Override onGetPlatformPermissionsForGroup(@onNull String permissionGroupName, @NonNull Consumer<List<String>> callback)743 public void onGetPlatformPermissionsForGroup(@NonNull String permissionGroupName, 744 @NonNull Consumer<List<String>> callback) { 745 callback.accept(Utils.getPlatformPermissionNamesOfGroup(permissionGroupName)); 746 } 747 748 @Override onGetGroupOfPlatformPermission(@onNull String permissionName, @NonNull Consumer<String> callback)749 public void onGetGroupOfPlatformPermission(@NonNull String permissionName, 750 @NonNull Consumer<String> callback) { 751 callback.accept(Utils.getGroupOfPlatformPermission(permissionName)); 752 } 753 754 @Override onGetUnusedAppCount(@onNull IntConsumer callback)755 public void onGetUnusedAppCount(@NonNull IntConsumer callback) { 756 mServiceModel.onCountUnusedApps(callback); 757 } 758 759 @Override onGetHibernationEligibility(@onNull String packageName, @NonNull IntConsumer callback)760 public void onGetHibernationEligibility(@NonNull String packageName, 761 @NonNull IntConsumer callback) { 762 mServiceModel.onGetHibernationEligibility(packageName, callback); 763 } 764 765 @Override onRevokeSelfPermissionsOnKill(@onNull String packageName, @NonNull List<String> permissions, @NonNull Runnable callback)766 public void onRevokeSelfPermissionsOnKill(@NonNull String packageName, 767 @NonNull List<String> permissions, @NonNull Runnable callback) { 768 PackageInfo pkgInfo = getPkgInfo(packageName); 769 if (pkgInfo == null) { 770 throw new SecurityException("Cannot revoke permission " + String.join(",", permissions) 771 + " for package " + packageName); 772 } 773 Set<AppPermissionGroup> groups = new HashSet<>(); 774 AppPermissions app = new AppPermissions(this, pkgInfo, false, true, null); 775 for (String permName : permissions) { 776 AppPermissionGroup group = app.getGroupForPermission(permName); 777 if (group == null) { 778 throw new IllegalArgumentException("Cannot revoke permission " + permName 779 + " for package " + packageName + " since " + permName 780 + " does not belong to a permission group"); 781 } 782 if (!group.doesSupportRuntimePermissions()) { 783 throw new IllegalArgumentException("Cannot revoke permission " + permName 784 + " for package " + packageName + " since it is not a runtime permission"); 785 } 786 Permission perm = group.getPermission(permName); 787 if (!perm.isGranted()) { 788 continue; 789 } 790 perm.setOneTime(true); 791 groups.add(group); 792 793 if (permName.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) { 794 group.getPermission(Manifest.permission.ACCESS_FINE_LOCATION).setOneTime(true); 795 } else if (permName.equals(Manifest.permission.ACCESS_FINE_LOCATION)) { 796 // Set coarse as the selected location accuracy 797 perm.setSelectedLocationAccuracy(false); 798 group.getPermission(Manifest.permission.ACCESS_COARSE_LOCATION) 799 .setSelectedLocationAccuracy(true); 800 } 801 802 if (group.isStrictlyOneTime()) { 803 // All remaining permissions in the group are one-time, we should also revoke 804 // the background permissions if there are any 805 Permission bgPerm = perm.getBackgroundPermission(); 806 if (bgPerm != null && bgPerm.isGranted()) { 807 bgPerm.setOneTime(true); 808 AppPermissionGroup bgGroup = group.getBackgroundPermissions(); 809 groups.add(bgGroup); 810 } 811 } 812 } 813 for (AppPermissionGroup group : groups) { 814 group.setSelfRevoked(); 815 group.persistChanges(false); 816 } 817 getMainExecutor().execute(callback); 818 } 819 } 820