1 /* 2 * Copyright (C) 2018 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.role.service; 18 19 import android.app.role.RoleControllerService; 20 import android.app.role.RoleManager; 21 import android.content.pm.ApplicationInfo; 22 import android.os.Process; 23 import android.os.UserHandle; 24 import android.util.ArrayMap; 25 import android.util.ArraySet; 26 import android.util.Log; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.WorkerThread; 30 31 import com.android.permissioncontroller.permission.utils.CollectionUtils; 32 import com.android.permissioncontroller.role.model.Role; 33 import com.android.permissioncontroller.role.model.Roles; 34 import com.android.permissioncontroller.role.utils.PackageUtils; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Implementation of {@link RoleControllerService}. 42 */ 43 public class RoleControllerServiceImpl extends RoleControllerService { 44 45 private static final String LOG_TAG = RoleControllerServiceImpl.class.getSimpleName(); 46 47 private static final boolean DEBUG = false; 48 49 private RoleManager mRoleManager; 50 51 @Override onCreate()52 public void onCreate() { 53 super.onCreate(); 54 55 mRoleManager = getSystemService(RoleManager.class); 56 } 57 58 @Override 59 @WorkerThread onGrantDefaultRoles()60 public boolean onGrantDefaultRoles() { 61 if (DEBUG) { 62 Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId()); 63 } 64 65 // Gather the available roles for current user. 66 ArrayMap<String, Role> roleMap = Roles.get(this); 67 List<Role> roles = new ArrayList<>(); 68 List<String> roleNames = new ArrayList<>(); 69 ArraySet<String> addedRoleNames = new ArraySet<>(); 70 int roleMapSize = roleMap.size(); 71 for (int i = 0; i < roleMapSize; i++) { 72 Role role = roleMap.valueAt(i); 73 74 if (!role.isAvailable(this)) { 75 continue; 76 } 77 roles.add(role); 78 String roleName = role.getName(); 79 roleNames.add(roleName); 80 if (!mRoleManager.isRoleAvailable(roleName)) { 81 addedRoleNames.add(roleName); 82 } 83 } 84 85 // TODO: Clean up holders of roles that will be removed. 86 87 // Set the available role names in RoleManager. 88 mRoleManager.setRoleNamesFromController(roleNames); 89 90 int addedRoleNamesSize = addedRoleNames.size(); 91 for (int i = 0; i < addedRoleNamesSize; i++) { 92 String roleName = addedRoleNames.valueAt(i); 93 94 Role role = roleMap.get(roleName); 95 role.onRoleAdded(this); 96 } 97 98 // Go through the holders of all roles. 99 int rolesSize = roles.size(); 100 for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) { 101 Role role = roles.get(rolesIndex); 102 103 String roleName = role.getName(); 104 105 // For each of the current holders, check if it is still qualified, redo grant if so, or 106 // remove it otherwise. 107 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 108 int currentPackageNamesSize = currentPackageNames.size(); 109 for (int currentPackageNamesIndex = 0; 110 currentPackageNamesIndex < currentPackageNamesSize; 111 currentPackageNamesIndex++) { 112 String packageName = currentPackageNames.get(currentPackageNamesIndex); 113 114 if (role.isPackageQualified(packageName, this)) { 115 // We should not override user set or fixed permissions because we are only 116 // redoing the grant here. Otherwise, user won't be able to revoke permissions 117 // granted by role. 118 addRoleHolderInternal(role, packageName, false, false, true); 119 } else { 120 Log.i(LOG_TAG, "Removing package that no longer qualifies for the role," 121 + " package: " + packageName + ", role: " + roleName); 122 removeRoleHolderInternal(role, packageName, false); 123 } 124 } 125 126 // If there is no holder for a role now, or the role is static, we need to add default 127 // or fallback holders, if any. 128 currentPackageNames = mRoleManager.getRoleHolders(roleName); 129 currentPackageNamesSize = currentPackageNames.size(); 130 boolean isStaticRole = role.isStatic(); 131 if (currentPackageNamesSize == 0 || isStaticRole) { 132 List<String> packageNamesToAdd = null; 133 if (addedRoleNames.contains(roleName) || isStaticRole) { 134 packageNamesToAdd = role.getDefaultHolders(this); 135 } 136 if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) { 137 packageNamesToAdd = CollectionUtils.singletonOrEmpty(role.getFallbackHolder( 138 this)); 139 } 140 141 int packageNamesToAddSize = packageNamesToAdd.size(); 142 for (int packageNamesToAddIndex = 0; packageNamesToAddIndex < packageNamesToAddSize; 143 packageNamesToAddIndex++) { 144 String packageName = packageNamesToAdd.get(packageNamesToAddIndex); 145 146 if (currentPackageNames.contains(packageName)) { 147 // This may happen when we are ensuring all default holders are added for 148 // static roles. 149 continue; 150 } 151 if (!role.isPackageQualified(packageName, this)) { 152 Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for" 153 + " the role, package: " + packageName + ", role: " + roleName); 154 continue; 155 } 156 Log.i(LOG_TAG, "Adding package as default/fallback role holder, package: " 157 + packageName + ", role: " + roleName); 158 // TODO: If we don't override user here, user might end up missing incoming 159 // phone calls or SMS, so we just keep the old behavior. But overriding user 160 // choice about permission without explicit user action is bad, so maybe we 161 // should at least show a notification? 162 addRoleHolderInternal(role, packageName, role.shouldOverrideUserWhenGranting()); 163 } 164 } 165 166 // Ensure that an exclusive role has at most one holder. 167 currentPackageNames = mRoleManager.getRoleHolders(roleName); 168 currentPackageNamesSize = currentPackageNames.size(); 169 if (role.isExclusive() && currentPackageNamesSize > 1) { 170 Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: " 171 + roleName); 172 // No good way to determine who should be the only one, just keep the first one. 173 for (int currentPackageNamesIndex = 1; 174 currentPackageNamesIndex < currentPackageNamesSize; 175 currentPackageNamesIndex++) { 176 String packageName = currentPackageNames.get(currentPackageNamesIndex); 177 178 Log.i(LOG_TAG, "Removing extraneous package for an exclusive role, package: " 179 + packageName + ", role: " + roleName); 180 removeRoleHolderInternal(role, packageName, false); 181 } 182 } 183 } 184 185 return true; 186 } 187 188 @Override 189 @WorkerThread onAddRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)190 public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, 191 int flags) { 192 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 193 return false; 194 } 195 196 Role role = Roles.get(this).get(roleName); 197 if (role == null) { 198 Log.e(LOG_TAG, "Unknown role: " + roleName); 199 return false; 200 } 201 if (!role.isAvailable(this)) { 202 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 203 return false; 204 } 205 206 if (!role.isPackageQualified(packageName, this)) { 207 Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName 208 + ", role: " + roleName); 209 return false; 210 } 211 212 boolean added = false; 213 if (role.isExclusive()) { 214 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 215 int currentPackageNamesSize = currentPackageNames.size(); 216 for (int i = 0; i < currentPackageNamesSize; i++) { 217 String currentPackageName = currentPackageNames.get(i); 218 219 if (Objects.equals(currentPackageName, packageName)) { 220 Log.i(LOG_TAG, "Package is already a role holder, package: " + packageName 221 + ", role: " + roleName); 222 added = true; 223 continue; 224 } 225 226 boolean removed = removeRoleHolderInternal(role, currentPackageName, false); 227 if (!removed) { 228 // TODO: Clean up? 229 return false; 230 } 231 } 232 } 233 234 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 235 added = addRoleHolderInternal(role, packageName, dontKillApp, 236 role.shouldOverrideUserWhenGranting(), added); 237 if (!added) { 238 return false; 239 } 240 241 role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this); 242 role.onHolderChangedAsUser(Process.myUserHandle(), this); 243 244 return true; 245 } 246 247 @Override 248 @WorkerThread onRemoveRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)249 public boolean onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, 250 int flags) { 251 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 252 return false; 253 } 254 255 Role role = Roles.get(this).get(roleName); 256 if (role == null) { 257 Log.e(LOG_TAG, "Unknown role: " + roleName); 258 return false; 259 } 260 if (!role.isAvailable(this)) { 261 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 262 return false; 263 } 264 265 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 266 boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp); 267 if (!removed) { 268 return false; 269 } 270 271 // TODO: Should we consider this successful regardless? 272 boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role); 273 if (!fallbackSuccessful) { 274 return false; 275 } 276 277 role.onHolderChangedAsUser(Process.myUserHandle(), this); 278 279 return true; 280 } 281 282 @Override 283 @WorkerThread onClearRoleHolders(@onNull String roleName, int flags)284 public boolean onClearRoleHolders(@NonNull String roleName, int flags) { 285 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 286 return false; 287 } 288 289 Role role = Roles.get(this).get(roleName); 290 if (role == null) { 291 Log.e(LOG_TAG, "Unknown role: " + roleName); 292 return false; 293 } 294 if (!role.isAvailable(this)) { 295 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 296 return false; 297 } 298 299 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 300 boolean cleared = clearRoleHoldersInternal(role, dontKillApp); 301 if (!cleared) { 302 return false; 303 } 304 305 // TODO: Should we consider this successful regardless? 306 boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role); 307 if (!fallbackSuccessful) { 308 return false; 309 } 310 311 role.onHolderChangedAsUser(Process.myUserHandle(), this); 312 313 return true; 314 } 315 316 @WorkerThread addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean overrideUserSetAndFixedPermissions)317 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 318 boolean overrideUserSetAndFixedPermissions) { 319 return addRoleHolderInternal(role, packageName, false, overrideUserSetAndFixedPermissions, 320 false); 321 } 322 323 @WorkerThread addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, boolean added)324 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 325 boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, boolean added) { 326 role.grant(packageName, dontKillApp, overrideUserSetAndFixedPermissions, this); 327 328 String roleName = role.getName(); 329 if (!added) { 330 added = mRoleManager.addRoleHolderFromController(roleName, packageName); 331 } 332 if (!added) { 333 Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName 334 + ", role: " + roleName); 335 } 336 return added; 337 } 338 339 @WorkerThread removeRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp)340 private boolean removeRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 341 boolean dontKillApp) { 342 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this); 343 if (applicationInfo == null) { 344 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 345 } 346 347 if (applicationInfo != null) { 348 role.revoke(packageName, dontKillApp, false, this); 349 } 350 351 String roleName = role.getName(); 352 boolean removed = mRoleManager.removeRoleHolderFromController(roleName, packageName); 353 if (!removed) { 354 Log.e(LOG_TAG, "Failed to remove role holder in RoleManager," + " package: " 355 + packageName + ", role: " + roleName); 356 } 357 return removed; 358 } 359 360 @WorkerThread clearRoleHoldersInternal(@onNull Role role, boolean dontKillApp)361 private boolean clearRoleHoldersInternal(@NonNull Role role, boolean dontKillApp) { 362 String roleName = role.getName(); 363 List<String> packageNames = mRoleManager.getRoleHolders(roleName); 364 boolean cleared = true; 365 366 int packageNamesSize = packageNames.size(); 367 for (int i = 0; i < packageNamesSize; i++) { 368 String packageName = packageNames.get(i); 369 boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp); 370 if (!removed) { 371 cleared = false; 372 } 373 } 374 375 if (!cleared) { 376 Log.e(LOG_TAG, "Failed to clear role holders, role: " + roleName); 377 } 378 return cleared; 379 } 380 381 @WorkerThread addFallbackRoleHolderMaybe(@onNull Role role)382 private boolean addFallbackRoleHolderMaybe(@NonNull Role role) { 383 String roleName = role.getName(); 384 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 385 if (!currentPackageNames.isEmpty()) { 386 return true; 387 } 388 389 String fallbackPackageName = role.getFallbackHolder(this); 390 if (fallbackPackageName == null) { 391 return true; 392 } 393 394 if (!role.isPackageQualified(fallbackPackageName, this)) { 395 Log.e(LOG_TAG, "Fallback role holder package doesn't qualify for the role, package: " 396 + fallbackPackageName + ", role: " + roleName); 397 return false; 398 } 399 400 Log.i(LOG_TAG, "Adding package as fallback role holder, package: " + fallbackPackageName 401 + ", role: " + roleName); 402 // TODO: If we don't override user here, user might end up missing incoming 403 // phone calls or SMS, so we just keep the old behavior. But overriding user 404 // choice about permission without explicit user action is bad, so maybe we 405 // should at least show a notification? 406 return addRoleHolderInternal(role, fallbackPackageName, 407 role.shouldOverrideUserWhenGranting()); 408 } 409 410 @Override onIsApplicationQualifiedForRole(@onNull String roleName, @NonNull String packageName)411 public boolean onIsApplicationQualifiedForRole(@NonNull String roleName, 412 @NonNull String packageName) { 413 // This API has been deprecated and Settings has been using onIsApplicationVisibleForRole() 414 // instead. 415 return false; 416 } 417 418 @Override onIsApplicationVisibleForRole(@onNull String roleName, @NonNull String packageName)419 public boolean onIsApplicationVisibleForRole(@NonNull String roleName, 420 @NonNull String packageName) { 421 Role role = Roles.get(this).get(roleName); 422 if (role == null) { 423 return false; 424 } 425 if (!role.isAvailable(this)) { 426 return false; 427 } 428 if (!role.isPackageQualified(packageName, this)) { 429 return false; 430 } 431 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this); 432 if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo, 433 Process.myUserHandle(), this)) { 434 return false; 435 } 436 return true; 437 } 438 439 @Override onIsRoleVisible(@onNull String roleName)440 public boolean onIsRoleVisible(@NonNull String roleName) { 441 Role role = Roles.get(this).get(roleName); 442 if (role == null) { 443 return false; 444 } 445 if (!role.isAvailable(this)) { 446 return false; 447 } 448 return role.isVisibleAsUser(Process.myUserHandle(), this); 449 } 450 checkFlags(int flags, int allowedFlags)451 private static boolean checkFlags(int flags, int allowedFlags) { 452 if ((flags & allowedFlags) != flags) { 453 Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags) 454 + ", allowed flags: 0x" + Integer.toHexString(allowedFlags)); 455 return false; 456 } 457 return true; 458 } 459 hasFlag(int flags, int flag)460 private static boolean hasFlag(int flags, int flag) { 461 return (flags & flag) == flag; 462 } 463 } 464