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.utils.PackageUtils; 33 import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; 34 import com.android.role.controller.model.Role; 35 import com.android.role.controller.model.Roles; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Objects; 40 41 /** 42 * Implementation of {@link RoleControllerService}. 43 */ 44 public class RoleControllerServiceImpl extends RoleControllerService { 45 46 private static final String LOG_TAG = RoleControllerServiceImpl.class.getSimpleName(); 47 48 private static final boolean DEBUG = false; 49 50 private RoleManager mRoleManager; 51 52 @Override onCreate()53 public void onCreate() { 54 super.onCreate(); 55 56 mRoleManager = getSystemService(RoleManager.class); 57 } 58 59 @Override 60 @WorkerThread onGrantDefaultRoles()61 public boolean onGrantDefaultRoles() { 62 if (DEBUG) { 63 Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId()); 64 } 65 66 // Gather the available roles for current user. 67 ArrayMap<String, Role> roleMap = Roles.get(this); 68 List<Role> roles = new ArrayList<>(); 69 List<String> roleNames = new ArrayList<>(); 70 ArraySet<String> addedRoleNames = new ArraySet<>(); 71 int roleMapSize = roleMap.size(); 72 for (int i = 0; i < roleMapSize; i++) { 73 Role role = roleMap.valueAt(i); 74 75 if (!role.isAvailable(this)) { 76 continue; 77 } 78 roles.add(role); 79 String roleName = role.getName(); 80 roleNames.add(roleName); 81 if (!mRoleManager.isRoleAvailable(roleName)) { 82 addedRoleNames.add(roleName); 83 } 84 } 85 86 // TODO: Clean up holders of roles that will be removed. 87 88 // Set the available role names in RoleManager. 89 mRoleManager.setRoleNamesFromController(roleNames); 90 91 int addedRoleNamesSize = addedRoleNames.size(); 92 for (int i = 0; i < addedRoleNamesSize; i++) { 93 String roleName = addedRoleNames.valueAt(i); 94 95 Role role = roleMap.get(roleName); 96 role.onRoleAdded(this); 97 } 98 99 // Go through the holders of all roles. 100 int rolesSize = roles.size(); 101 for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) { 102 Role role = roles.get(rolesIndex); 103 104 String roleName = role.getName(); 105 106 // For each of the current holders, check if it is still qualified, redo grant if so, or 107 // remove it otherwise. 108 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 109 int currentPackageNamesSize = currentPackageNames.size(); 110 for (int currentPackageNamesIndex = 0; 111 currentPackageNamesIndex < currentPackageNamesSize; 112 currentPackageNamesIndex++) { 113 String packageName = currentPackageNames.get(currentPackageNamesIndex); 114 115 if (role.isPackageQualified(packageName, this)) { 116 // We should not override user set or fixed permissions because we are only 117 // redoing the grant here. Otherwise, user won't be able to revoke permissions 118 // granted by role. 119 addRoleHolderInternal(role, packageName, false, false, true); 120 } else { 121 Log.i(LOG_TAG, "Removing package that no longer qualifies for the role," 122 + " package: " + packageName + ", role: " + roleName); 123 removeRoleHolderInternal(role, packageName, false); 124 } 125 } 126 127 // If there is no holder for a role now, or the role is static, we need to add default 128 // or fallback holders, if any. 129 currentPackageNames = mRoleManager.getRoleHolders(roleName); 130 currentPackageNamesSize = currentPackageNames.size(); 131 boolean isStaticRole = role.isStatic(); 132 if (currentPackageNamesSize == 0 || isStaticRole) { 133 List<String> packageNamesToAdd = null; 134 if (addedRoleNames.contains(roleName) || isStaticRole) { 135 packageNamesToAdd = role.getDefaultHolders(this); 136 } 137 if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) { 138 packageNamesToAdd = CollectionUtils.singletonOrEmpty(role.getFallbackHolder( 139 this)); 140 } 141 142 int packageNamesToAddSize = packageNamesToAdd.size(); 143 for (int packageNamesToAddIndex = 0; packageNamesToAddIndex < packageNamesToAddSize; 144 packageNamesToAddIndex++) { 145 String packageName = packageNamesToAdd.get(packageNamesToAddIndex); 146 147 if (currentPackageNames.contains(packageName)) { 148 // This may happen when we are ensuring all default holders are added for 149 // static roles. 150 continue; 151 } 152 if (!role.isPackageQualified(packageName, this)) { 153 Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for" 154 + " the role, package: " + packageName + ", role: " + roleName); 155 continue; 156 } 157 Log.i(LOG_TAG, "Adding package as default/fallback role holder, package: " 158 + packageName + ", role: " + roleName); 159 // TODO: If we don't override user here, user might end up missing incoming 160 // phone calls or SMS, so we just keep the old behavior. But overriding user 161 // choice about permission without explicit user action is bad, so maybe we 162 // should at least show a notification? 163 addRoleHolderInternal(role, packageName, role.shouldOverrideUserWhenGranting()); 164 } 165 } 166 167 // Ensure that an exclusive role has at most one holder. 168 currentPackageNames = mRoleManager.getRoleHolders(roleName); 169 currentPackageNamesSize = currentPackageNames.size(); 170 if (role.isExclusive() && currentPackageNamesSize > 1) { 171 Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: " 172 + roleName); 173 // No good way to determine who should be the only one, just keep the first one. 174 for (int currentPackageNamesIndex = 1; 175 currentPackageNamesIndex < currentPackageNamesSize; 176 currentPackageNamesIndex++) { 177 String packageName = currentPackageNames.get(currentPackageNamesIndex); 178 179 Log.i(LOG_TAG, "Removing extraneous package for an exclusive role, package: " 180 + packageName + ", role: " + roleName); 181 removeRoleHolderInternal(role, packageName, false); 182 } 183 } 184 } 185 186 return true; 187 } 188 189 @Override 190 @WorkerThread onAddRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)191 public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, 192 int flags) { 193 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 194 return false; 195 } 196 197 Role role = Roles.get(this).get(roleName); 198 if (role == null) { 199 Log.e(LOG_TAG, "Unknown role: " + roleName); 200 return false; 201 } 202 if (!role.isAvailable(this)) { 203 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 204 return false; 205 } 206 207 if (!role.isPackageQualified(packageName, this)) { 208 Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName 209 + ", role: " + roleName); 210 return false; 211 } 212 213 boolean added = false; 214 if (role.isExclusive()) { 215 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 216 int currentPackageNamesSize = currentPackageNames.size(); 217 for (int i = 0; i < currentPackageNamesSize; i++) { 218 String currentPackageName = currentPackageNames.get(i); 219 220 if (Objects.equals(currentPackageName, packageName)) { 221 Log.i(LOG_TAG, "Package is already a role holder, package: " + packageName 222 + ", role: " + roleName); 223 added = true; 224 continue; 225 } 226 227 boolean removed = removeRoleHolderInternal(role, currentPackageName, false); 228 if (!removed) { 229 // TODO: Clean up? 230 return false; 231 } 232 } 233 } 234 235 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 236 added = addRoleHolderInternal(role, packageName, dontKillApp, 237 role.shouldOverrideUserWhenGranting(), added); 238 if (!added) { 239 return false; 240 } 241 242 role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this); 243 role.onHolderChangedAsUser(Process.myUserHandle(), this); 244 245 return true; 246 } 247 248 @Override 249 @WorkerThread onRemoveRoleHolder(@onNull String roleName, @NonNull String packageName, int flags)250 public boolean onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, 251 int flags) { 252 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 253 return false; 254 } 255 256 Role role = Roles.get(this).get(roleName); 257 if (role == null) { 258 Log.e(LOG_TAG, "Unknown role: " + roleName); 259 return false; 260 } 261 if (!role.isAvailable(this)) { 262 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 263 return false; 264 } 265 266 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 267 boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp); 268 if (!removed) { 269 return false; 270 } 271 272 // TODO: Should we consider this successful regardless? 273 boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role); 274 if (!fallbackSuccessful) { 275 return false; 276 } 277 278 role.onHolderChangedAsUser(Process.myUserHandle(), this); 279 280 return true; 281 } 282 283 @Override 284 @WorkerThread onClearRoleHolders(@onNull String roleName, int flags)285 public boolean onClearRoleHolders(@NonNull String roleName, int flags) { 286 if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) { 287 return false; 288 } 289 290 Role role = Roles.get(this).get(roleName); 291 if (role == null) { 292 Log.e(LOG_TAG, "Unknown role: " + roleName); 293 return false; 294 } 295 if (!role.isAvailable(this)) { 296 Log.e(LOG_TAG, "Role is unavailable: " + roleName); 297 return false; 298 } 299 300 boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP); 301 boolean cleared = clearRoleHoldersInternal(role, dontKillApp); 302 if (!cleared) { 303 return false; 304 } 305 306 // TODO: Should we consider this successful regardless? 307 boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role); 308 if (!fallbackSuccessful) { 309 return false; 310 } 311 312 role.onHolderChangedAsUser(Process.myUserHandle(), this); 313 314 return true; 315 } 316 317 @WorkerThread addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean overrideUser)318 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 319 boolean overrideUser) { 320 return addRoleHolderInternal(role, packageName, false, overrideUser, false); 321 } 322 323 @WorkerThread addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp, boolean overrideUser, boolean added)324 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 325 boolean dontKillApp, boolean overrideUser, boolean added) { 326 role.grant(packageName, dontKillApp, overrideUser, 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 || !RoleUiBehaviorUtils.isApplicationVisibleAsUser(role, 433 applicationInfo, 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 449 return RoleUiBehaviorUtils.isVisibleAsUser(role, Process.myUserHandle(), this); 450 } 451 checkFlags(int flags, int allowedFlags)452 private static boolean checkFlags(int flags, int allowedFlags) { 453 if ((flags & allowedFlags) != flags) { 454 Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags) 455 + ", allowed flags: 0x" + Integer.toHexString(allowedFlags)); 456 return false; 457 } 458 return true; 459 } 460 hasFlag(int flags, int flag)461 private static boolean hasFlag(int flags, int flag) { 462 return (flags & flag) == flag; 463 } 464 } 465