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