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