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